diff --git a/.github/actions/ipfs/action.yml b/.github/actions/ipfs/action.yml new file mode 100644 index 0000000000..24bf8428ee --- /dev/null +++ b/.github/actions/ipfs/action.yml @@ -0,0 +1,101 @@ +name: pkgx/pantry/ipfs +description: Generates IPFS CID, uploads to IPFS, and pins to cluster + +inputs: + bottle-file: + description: Path to the bottle file to upload + required: true + dry-run: + description: If true, skip actual uploads + required: false + default: 'false' + ipfs-api-url: + description: IPFS API endpoint URL + required: false + ipfs-api-token: + description: Bearer token for IPFS API authentication + required: false + ipfs-cluster-api-url: + description: IPFS Cluster API endpoint URL + required: false + ipfs-cluster-auth: + description: Authorization header value for Cluster API + required: false + ipfs-gateway-url: + description: Public IPFS gateway URL for generating IPFS URLs + required: false + default: 'https://ipfs.pkgx.dev' + +outputs: + cid: + description: IPFS Content Identifier (CID) for the bottle + value: ${{ steps.cid.outputs.cid }} + success: + description: Whether the upload succeeded ('true' or 'false') + value: ${{ steps.upload.outputs.success || 'false' }} + +runs: + using: composite + steps: + - name: Generate IPFS CID + id: cid + shell: bash + run: | + pkgx ipfs init --profile=lowpower >/dev/null 2>&1 + CID=$(pkgx ipfs add --only-hash --quieter --cid-version=1 "${{ inputs.bottle-file }}") + echo "cid=$CID" >> $GITHUB_OUTPUT + echo "$CID" > "${{ inputs.bottle-file }}.cid" + echo "Generated CID: $CID" + env: + IPFS_PATH: /tmp/ipfs-$$ + + - name: Upload to IPFS + id: upload + shell: bash + continue-on-error: true + run: | + if [ "${{ inputs.dry-run }}" = "true" ] || [ -z "${{ inputs.ipfs-api-url }}" ] || [ -z "${{ inputs.ipfs-api-token }}" ]; then + echo "success=false" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "Uploading to IPFS..." + RESPONSE=$(curl -sS -X POST \ + -F file=@${{ inputs.bottle-file }} \ + -H "Authorization: Bearer ${{ inputs.ipfs-api-token }}" \ + "${{ inputs.ipfs-api-url }}/api/v0/add?cid-version=1&pin=true&quieter=true") + + UPLOAD_CID=$(echo "$RESPONSE" | jq -r .Hash) + + if [ "$UPLOAD_CID" != "${{ steps.cid.outputs.cid }}" ]; then + echo "::error::CID mismatch! Expected ${{ steps.cid.outputs.cid }}, got $UPLOAD_CID" + echo "success=false" >> $GITHUB_OUTPUT + exit 1 + fi + + echo "Successfully uploaded to IPFS: $UPLOAD_CID" + echo "success=true" >> $GITHUB_OUTPUT + + - name: Pin to IPFS Cluster + if: ${{ inputs.dry-run != 'true' && inputs.ipfs-cluster-api-url != '' && steps.upload.outcome == 'success' }} + shell: bash + continue-on-error: true + run: | + echo "Pinning to IPFS Cluster..." + + AUTH_HEADER="" + if [ -n "${{ inputs.ipfs-cluster-auth }}" ]; then + AUTH_HEADER="Authorization: Bearer ${{ inputs.ipfs-cluster-auth }}" + fi + + HTTP_CODE=$(curl -s -o /tmp/cluster-response.txt -w "%{http_code}" -X POST \ + -H "Content-Type: application/json" \ + ${AUTH_HEADER:+-H "$AUTH_HEADER"} \ + -d '{"cid":"${{ steps.cid.outputs.cid }}","replication_factor_min":1,"replication_factor_max":3}' \ + "${{ inputs.ipfs-cluster-api-url }}/pins/${{ steps.cid.outputs.cid }}") + + if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then + echo "Pinned to IPFS Cluster (HTTP $HTTP_CODE)" + else + echo "::warning::IPFS Cluster pinning failed (HTTP $HTTP_CODE): $(cat /tmp/cluster-response.txt)" + fi diff --git a/.github/workflows/pkg-platform.yml b/.github/workflows/pkg-platform.yml index 2b8a2e7f2d..cc2be385d2 100644 --- a/.github/workflows/pkg-platform.yml +++ b/.github/workflows/pkg-platform.yml @@ -50,6 +50,10 @@ on: AWS_S3_BUCKET: { required: true } AWS_SECRET_ACCESS_KEY: { required: true } AWS_CF_DISTRIBUTION_ID: { required: true } + IPFS_API_URL: { required: false } + IPFS_API_TOKEN: { required: false } + IPFS_CLUSTER_API_URL: { required: false } + IPFS_CLUSTER_AUTH: { required: false } env: BREWKIT_PKGJSON: ${{ inputs.pkg }} @@ -81,7 +85,6 @@ jobs: version: 1.1.6 PKGX_DIR: /opt - - uses: pkgxdev/brewkit/build@v1 with: pkg: ${{ inputs.pkg }} @@ -139,6 +142,8 @@ jobs: with: version: 1.1.6 + - uses: actions/checkout@v4 + - uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} @@ -178,15 +183,36 @@ jobs: sha256sum ${{ steps.bottle.outputs.filename }} > ${{ steps.bottle.outputs.filename }}.sha256sum + - name: ipfs + id: ipfs + uses: ./.github/actions/ipfs + with: + bottle-file: ${{ steps.bottle.outputs.filename }} + dry-run: ${{ inputs.dry-run }} + ipfs-api-url: ${{ secrets.IPFS_API_URL }} + ipfs-api-token: ${{ secrets.IPFS_API_TOKEN }} + ipfs-cluster-api-url: ${{ secrets.IPFS_CLUSTER_API_URL }} + ipfs-cluster-auth: ${{ secrets.IPFS_CLUSTER_AUTH }} + ipfs-gateway-url: ${{ secrets.IPFS_GATEWAY_URL }} + - name: s3 put run: | - $AWS s3 cp ${{ steps.bottle.outputs.filename }} $URL - $AWS s3 cp ${{ steps.bottle.outputs.filename }}.asc $URL.asc - $AWS s3 cp ${{ steps.bottle.outputs.filename }}.sha256sum $URL.sha256sum + $AWS s3 cp $FILENAME $URL + $AWS s3 cp $FILENAME.asc $URL.asc + $AWS s3 cp $FILENAME.sha256sum $URL.sha256sum + + if ! test -z "$CID"; then + echo "$CID" >$FILENAME.cid + $AWS s3 cp $FILENAME.cid $URL.cid + echo "cf-paths=/$PREFIX /$PREFIX.asc /$PREFIX.sha256sum /$PREFIX.cid" >> $GITHUB_OUTPUT + else + echo "cf-paths=/$PREFIX /$PREFIX.asc /$PREFIX.sha256sum" >> $GITHUB_OUTPUT + fi - echo "cf-paths=/$PREFIX /$PREFIX.asc /$PREFIX.sha256sum" >> $GITHUB_OUTPUT env: + FILENAME: ${{ steps.bottle.outputs.filename }} URL: s3://${{ secrets.AWS_S3_BUCKET }}/${{ env.PREFIX }} + CID: ${{ steps.ipfs.outputs.cid }} id: put - name: s3 put file listing