diff --git a/.github/workflows/approve-bot-pr.yml b/.github/workflows/approve-bot-pr.yml index c798e64..d958d9a 100644 --- a/.github/workflows/approve-bot-pr.yml +++ b/.github/workflows/approve-bot-pr.yml @@ -10,7 +10,7 @@ jobs: download: name: Download PR Artifact if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 outputs: pr-author: ${{ steps.pr-data.outputs.author }} pr-number: ${{ steps.pr-data.outputs.number }} @@ -32,7 +32,7 @@ jobs: name: Approve Bot PRs needs: download if: ${{ needs.download.outputs.pr-author == 'paketo-bot' || needs.download.outputs.pr-author == 'dependabot[bot]' }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Check Commit Verification id: unverified-commits @@ -52,7 +52,7 @@ jobs: - name: Checkout if: steps.human-commits.outputs.human_commits == 'false' && steps.unverified-commits.outputs.unverified_commits == 'false' - uses: actions/checkout@v3 + uses: actions/checkout@v5 - name: Approve if: steps.human-commits.outputs.human_commits == 'false' && steps.unverified-commits.outputs.unverified_commits == 'false' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 09253a6..8729f8a 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -6,12 +6,12 @@ on: pull_request: branches: [ main ] schedule: - - cron: '34 5 * * *' # daily at 5:34am UTC + - cron: '34 5 * * *' # daily at 5:34am UTC jobs: analyze: name: Analyze - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -21,15 +21,15 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v5 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v4 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v4 diff --git a/.github/workflows/compile-dependency.yml b/.github/workflows/compile-dependency.yml new file mode 100644 index 0000000..c9f208f --- /dev/null +++ b/.github/workflows/compile-dependency.yml @@ -0,0 +1,106 @@ +name: 'Compile Dependency on Target - Reusable Workflow' + +description: | + Compiles Dependency on given target, os, and arch + +on: + workflow_call: + inputs: + version: + description: 'dependency version' + required: true + type: string + target: + description: 'dependency OS target variant' + required: true + type: string + os: + description: 'platform OS (e.g., linux)' + required: true + type: string + arch: + description: 'platform architecture (e.g., amd64)' + required: true + type: string + shouldCompile: + description: 'whether to compile the dependency' + required: true + type: boolean + shouldTest: + description: 'whether to test the dependency after compilation' + required: true + type: boolean + uploadArtifactName: + description: 'name of the artifact to upload' + required: true + type: string + +jobs: + compile: + # Speed up compilation by using runners that match os and arch when they are set, otherwise fall back to emulation. + runs-on: ${{ (inputs.os == 'linux' && inputs.arch == 'arm64') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} + + steps: + - name: Check out code + uses: actions/checkout@v5 + + - name: Enable experimental features for Docker daemon and CLI + run: | + echo '{"experimental": true}' | sudo tee /etc/docker/daemon.json + sudo systemctl restart docker + mkdir -p ~/.docker + echo '{"experimental": "enabled"}' | sudo tee ~/.docker/config.json + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Setup before compilation + id: compile-setup + run: | + echo "outputdir=$(mktemp -d)" >> "$GITHUB_OUTPUT" + + - name: docker build + id: docker-build + env: + SKIP_LOGIN: true + if: ${{ inputs.shouldCompile == true || inputs.shouldCompile == 'true' }} + uses: actions-hub/docker/cli@master + with: + args: "build ${{ (inputs.os != '' && inputs.arch != '') && format('--platform {0}/{1}', inputs.os, inputs.arch) || '' }} -t compilation -f dependency/actions/compile/${{ inputs.target }}.Dockerfile dependency/actions/compile" + + - name: docker run + id: docker-run + uses: actions-hub/docker/cli@master + env: + SKIP_LOGIN: true + if: ${{ inputs.shouldCompile == true || inputs.shouldCompile == 'true' }} + with: + args: "run ${{ (inputs.os != '' && inputs.arch != '') && format('--platform {0}/{1}', inputs.os, inputs.arch) || '' }} -v ${{ steps.compile-setup.outputs.outputdir }}:/home compilation --outputDir /home --target ${{ inputs.target }} --version ${{ inputs.version }} ${{ inputs.os != '' && format('--os {0}', inputs.os) || '' }} ${{ inputs.arch != '' && format('--arch {0}', inputs.arch) || '' }}" + + - name: Print contents of output dir + shell: bash + run: ls -lah ${{ steps.compile-setup.outputs.outputdir }} + + - name: Test Dependency + working-directory: dependency + if: ${{ (inputs.shouldCompile == true || inputs.shouldCompile == 'true') && (inputs.shouldTest == true || inputs.shouldTest == 'true') }} + run: | + #!/usr/bin/env bash + set -euo pipefail + shopt -s inherit_errexit + + make test \ + version="${{ inputs.version }}" \ + tarballPath="${{ steps.compile-setup.outputs.outputdir }}/*.tgz" \ + os="${{ inputs.os }}" \ + arch="${{ inputs.arch }}" + + - name: Upload compiled artifact + uses: actions/upload-artifact@v4 + if: ${{ inputs.shouldCompile == true || inputs.shouldCompile == 'true' }} + with: + name: '${{ inputs.uploadArtifactName }}' + path: '${{ steps.compile-setup.outputs.outputdir }}/*' diff --git a/.github/workflows/create-draft-release.yml b/.github/workflows/create-draft-release.yml index acc723e..1c947a9 100644 --- a/.github/workflows/create-draft-release.yml +++ b/.github/workflows/create-draft-release.yml @@ -17,16 +17,16 @@ concurrency: release jobs: unit: name: Unit Tests - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 outputs: builders: ${{ steps.builders.outputs.builders }} steps: + - name: Checkout + uses: actions/checkout@v5 - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v6 with: - go-version: 'stable' - - name: Checkout - uses: actions/checkout@v3 + go-version-file: go.mod - name: Run Unit Tests run: ./scripts/unit.sh - name: Get builders from integration.json @@ -39,19 +39,19 @@ jobs: integration: name: Integration Tests - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: unit strategy: matrix: builder: ${{ fromJSON(needs.unit.outputs.builders) }} fail-fast: false # don't cancel all test jobs when one fails steps: + - name: Checkout + uses: actions/checkout@v5 - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v6 with: - go-version: 'stable' - - name: Checkout - uses: actions/checkout@v3 + go-version-file: go.mod - name: Run Integration Tests run: ./scripts/integration.sh --builder ${{ matrix.builder }} --token ${{ github.token }} env: @@ -59,19 +59,25 @@ jobs: release: name: Release - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: integration - steps: - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 'stable' + services: + registry: + image: registry:3 + ports: + - 5000:5000 + steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v5 with: fetch-tags: true + - name: Setup Go + uses: actions/setup-go@v6 + with: + go-version-file: go.mod + - name: Reset Draft Release id: reset uses: paketo-buildpacks/github-config/actions/release/reset-draft@main @@ -110,6 +116,16 @@ jobs: echo "buildpack_type=buildpack" >> "$GITHUB_OUTPUT" fi + - name: Get buildpack path + id: get_buildpack_path + run: | + + if [ -f "build/buildpackage.cnb" ]; then + echo "path=build/buildpackage.cnb" >> "$GITHUB_OUTPUT" + else + echo "path=build/buildpackage-linux-amd64.cnb" >> "$GITHUB_OUTPUT" + fi + - name: Create Release Notes id: create-release-notes uses: paketo-buildpacks/github-config/actions/release/notes@main @@ -117,6 +133,69 @@ jobs: repo: ${{ github.repository }} token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} buildpack_type: ${{ steps.get_buildpack_type.outputs.buildpack_type }} + buildpackage_path: ${{ steps.get_buildpack_path.outputs.path }} + + - name: Get Image Digest + id: image_digest + run: | + image_name="localhost:5000/npm-install:latest" + + ./scripts/publish.sh \ + --buildpack-type ${{ steps.get_buildpack_type.outputs.buildpack_type }} \ + --image-ref $image_name + + echo "digest=$(sudo skopeo inspect "docker://${image_name}" --tls-verify=false | jq -r .Digest)" >> "$GITHUB_OUTPUT" + + - name: Set Correct Image Digest on the Release notes + run: | + printf '${{ steps.create-release-notes.outputs.release_body }}' \ + | sed -E \ + "s/\*\*Digest:\*\* \`sha256:[a-f0-9]{64}\`/\*\*Digest:\*\* \`${{ steps.image_digest.outputs.digest }}\`/" \ + > ./release_notes + + printf '${{ steps.image_digest.outputs.digest }}' > ./index-digest.sha256 + + - name: Create release assets + id: create_release_assets + run: | + release_assets=$(jq -n --arg repo_name "${{ github.event.repository.name }}" --arg tag "${{ steps.tag.outputs.tag }}" ' + [ + { + "path": "build/buildpack.tgz", + "name": ($repo_name + "-" + $tag + ".tgz"), + "content_type": "application/gzip" + }, + { + "path": "./index-digest.sha256", + "name": ($repo_name + "-" + $tag + "-" + "index-digest.sha256"), + "content_type": "text/plain" + } + ]') + + for filepath in build/*.cnb; do + filename=$(basename "$filepath") + asset_name="" + if [[ "$filename" == "buildpackage-linux-amd64.cnb" ]]; then + asset_name="${{ github.event.repository.name }}-${{ steps.tag.outputs.tag }}.cnb" + elif [[ "$filename" == "buildpackage.cnb" ]]; then + asset_name="${{ github.event.repository.name }}-${{ steps.tag.outputs.tag }}.cnb" + else + formatted_filename="${filename#buildpackage-}" + asset_name="${{ github.event.repository.name }}-${{ steps.tag.outputs.tag }}-${formatted_filename}" + fi + + release_assets=$(echo "$release_assets" | jq --arg asset_name "${asset_name}" --arg filepath "$filepath" ' + . + [ + { + "path": $filepath, + "name": $asset_name, + "content_type": "application/gzip" + } + ]') + done + + release_assets=$(jq -c <<< "$release_assets" ) + printf "release_assets=%s\n" "${release_assets}" >> "$GITHUB_OUTPUT" - name: Create Release uses: paketo-buildpacks/github-config/actions/release/create@main @@ -126,25 +205,13 @@ jobs: tag_name: v${{ steps.tag.outputs.tag }} target_commitish: ${{ github.sha }} name: v${{ steps.tag.outputs.tag }} - body: ${{ steps.create-release-notes.outputs.release_body }} + body_filepath: "./release_notes" draft: true - assets: | - [ - { - "path": "build/buildpack.tgz", - "name": "${{ github.event.repository.name }}-${{ steps.tag.outputs.tag }}.tgz", - "content_type": "application/gzip" - }, - { - "path": "build/buildpackage.cnb", - "name": "${{ github.event.repository.name }}-${{ steps.tag.outputs.tag }}.cnb", - "content_type": "application/gzip" - } - ] + assets: ${{ steps.create_release_assets.outputs.release_assets }} failure: name: Alert on Failure - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: [ unit, integration, release ] if: ${{ always() && needs.unit.result == 'failure' || needs.integration.result == 'failure' || needs.release.result == 'failure' }} steps: diff --git a/.github/workflows/label-pr.yml b/.github/workflows/label-pr.yml index b09cdf0..55f6850 100644 --- a/.github/workflows/label-pr.yml +++ b/.github/workflows/label-pr.yml @@ -15,10 +15,10 @@ concurrency: pr_labels_${{ github.event.number }} jobs: autolabel: name: Ensure Minimal Semver Labels - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Check Minimal Semver Labels - uses: mheap/github-action-required-labels@v3 + uses: mheap/github-action-required-labels@v5 with: count: 1 labels: semver:major, semver:minor, semver:patch diff --git a/.github/workflows/lint-yaml.yml b/.github/workflows/lint-yaml.yml index e6f4a8b..f4f032b 100644 --- a/.github/workflows/lint-yaml.yml +++ b/.github/workflows/lint-yaml.yml @@ -8,18 +8,18 @@ on: jobs: lintYaml: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 - name: Checkout github-config - uses: actions/checkout@v3 + uses: actions/checkout@v5 with: repository: paketo-buildpacks/github-config path: github-config - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d100818..31737ef 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,15 +11,15 @@ on: jobs: golangci: name: lint - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: + - name: Checkout + uses: actions/checkout@v5 + - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v6 with: - go-version: 'stable' - - - name: Checkout - uses: actions/checkout@v3 + go-version-file: go.mod - name: golangci-lint uses: golangci/golangci-lint-action@v3 diff --git a/.github/workflows/publish-releases.yml b/.github/workflows/publish-releases.yml index 495341a..a1ffa05 100644 --- a/.github/workflows/publish-releases.yml +++ b/.github/workflows/publish-releases.yml @@ -11,7 +11,7 @@ concurrency: jobs: publish: name: Publish - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Publish Draft Release With Highest Semantic Version id: drafts @@ -23,7 +23,7 @@ jobs: failure: name: Alert on Failure - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: [ publish ] if: ${{ always() && needs.publish.result == 'failure' }} steps: diff --git a/.github/workflows/push-buildpackage.yml b/.github/workflows/push-buildpackage.yml index c2f5a39..f340585 100644 --- a/.github/workflows/push-buildpackage.yml +++ b/.github/workflows/push-buildpackage.yml @@ -4,17 +4,26 @@ on: release: types: - published + env: REGISTRIES_FILENAME: "registries.json" jobs: push: name: Push - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 + env: + GCR_REGISTRY: "gcr.io" + GCR_PASSWORD: ${{ secrets.GCR_PUSH_BOT_JSON_KEY }} + GCR_USERNAME: "_json_key" + DOCKERHUB_REGISTRY: docker.io + DOCKERHUB_USERNAME: ${{ secrets.PAKETO_BUILDPACKS_DOCKERHUB_USERNAME }} + DOCKERHUB_PASSWORD: ${{ secrets.PAKETO_BUILDPACKS_DOCKERHUB_PASSWORD }} + steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Parse Event id: event @@ -25,16 +34,31 @@ jobs: echo "tag_full=${FULL_VERSION}" >> "$GITHUB_OUTPUT" echo "tag_minor=${MINOR_VERSION}" >> "$GITHUB_OUTPUT" echo "tag_major=${MAJOR_VERSION}" >> "$GITHUB_OUTPUT" - echo "download_url=$(jq -r '.release.assets[] | select(.name | endswith(".cnb")) | .url' "${GITHUB_EVENT_PATH}")" >> "$GITHUB_OUTPUT" + echo "download_tgz_file_url=$(jq -r '.release.assets[] | select(.name | endswith(".tgz")) | .url' "${GITHUB_EVENT_PATH}")" >> "$GITHUB_OUTPUT" + echo "download_cnb_file_url=$(jq -r --arg tag_full "$FULL_VERSION" '.release.assets[] | select(.name | endswith($tag_full + ".cnb")) | .url' "${GITHUB_EVENT_PATH}")" >> "$GITHUB_OUTPUT" + echo "download_sha256_file_url=$(jq -r '.release.assets[] | select(.name | endswith("index-digest.sha256")) | .url' "${GITHUB_EVENT_PATH}")" >> "$GITHUB_OUTPUT" - - name: Download - id: download + - name: Download .cnb buildpack uses: paketo-buildpacks/github-config/actions/release/download-asset@main with: - url: ${{ steps.event.outputs.download_url }} + url: ${{ steps.event.outputs.download_cnb_file_url }} output: "/github/workspace/buildpackage.cnb" token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} + - name: Download .tgz buildpack + uses: paketo-buildpacks/github-config/actions/release/download-asset@main + with: + url: ${{ steps.event.outputs.download_tgz_file_url }} + output: "/github/workspace/buildpack.tgz" + token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} + + - name: Download .sha digest + uses: paketo-buildpacks/github-config/actions/release/download-asset@main + with: + url: ${{ steps.event.outputs.download_sha256_file_url }} + output: "/github/workspace/index-digest.sha256" + token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} + - name: Parse Configs id: parse_configs run: | @@ -64,46 +88,83 @@ jobs: exit 1 fi - - name: Push to GCR - if: ${{ steps.parse_configs.outputs.push_to_gcr == 'true' }} - env: - GCR_PUSH_BOT_JSON_KEY: ${{ secrets.GCR_PUSH_BOT_JSON_KEY }} + - name: Get buildpack type + id: get_buildpack_type run: | - echo "${GCR_PUSH_BOT_JSON_KEY}" | sudo skopeo login --username _json_key --password-stdin gcr.io - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/buildpackage.cnb" "docker://gcr.io/${{ github.repository }}:${{ steps.event.outputs.tag_full }}" - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/buildpackage.cnb" "docker://gcr.io/${{ github.repository }}:${{ steps.event.outputs.tag_minor }}" - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/buildpackage.cnb" "docker://gcr.io/${{ github.repository }}:${{ steps.event.outputs.tag_major }}" - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/buildpackage.cnb" "docker://gcr.io/${{ github.repository }}:latest" + if [ -f "extension.toml" ]; then + echo "buildpack_type=extension" >> "$GITHUB_OUTPUT" + else + echo "buildpack_type=buildpack" >> "$GITHUB_OUTPUT" + fi + - name: Docker login docker.io + uses: docker/login-action@v3 + with: + username: ${{ env.DOCKERHUB_USERNAME }} + password: ${{ env.DOCKERHUB_PASSWORD }} + registry: ${{ env.DOCKERHUB_REGISTRY }} + + - name: Docker login gcr.io + uses: docker/login-action@v3 + if: ${{ steps.parse_configs.outputs.push_to_gcr == 'true' }} + with: + username: ${{ env.GCR_USERNAME }} + password: ${{ env.GCR_PASSWORD }} + registry: ${{ env.GCR_REGISTRY }} + + - uses: buildpacks/github-actions/setup-tools@v5.9.5 - name: Push to DockerHub if: ${{ steps.parse_configs.outputs.push_to_dockerhub == 'true' }} id: push env: - DOCKERHUB_USERNAME: ${{ secrets.PAKETO_BUILDPACKS_DOCKERHUB_USERNAME }} - DOCKERHUB_PASSWORD: ${{ secrets.PAKETO_BUILDPACKS_DOCKERHUB_PASSWORD }} GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }} run: | - REPOSITORY="${GITHUB_REPOSITORY_OWNER/-/}/${GITHUB_REPOSITORY#${GITHUB_REPOSITORY_OWNER}/}" # translates 'paketo-buildpacks/bundle-install' to 'paketobuildpacks/bundle-install' - IMAGE="index.docker.io/${REPOSITORY}" - echo "${DOCKERHUB_PASSWORD}" | sudo skopeo login --username "${DOCKERHUB_USERNAME}" --password-stdin index.docker.io - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/buildpackage.cnb" "docker://${IMAGE}:${{ steps.event.outputs.tag_full }}" - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/buildpackage.cnb" "docker://${IMAGE}:${{ steps.event.outputs.tag_minor }}" - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/buildpackage.cnb" "docker://${IMAGE}:${{ steps.event.outputs.tag_major }}" - sudo skopeo copy "oci-archive:${GITHUB_WORKSPACE}/buildpackage.cnb" "docker://${IMAGE}:latest" + IMAGE="${GITHUB_REPOSITORY_OWNER/-/}/${GITHUB_REPOSITORY#${GITHUB_REPOSITORY_OWNER}/}" # translates 'paketo-buildpacks/bundle-install' to 'paketobuildpacks/bundle-install' + echo "${DOCKERHUB_PASSWORD}" | sudo skopeo login --username "${DOCKERHUB_USERNAME}" --password-stdin ${DOCKERHUB_REGISTRY} + + ./scripts/publish.sh \ + --archive-path ./buildpack.tgz \ + --buildpack-type ${{ steps.get_buildpack_type.outputs.buildpack_type }} \ + --image-ref "${DOCKERHUB_REGISTRY}/${IMAGE}:${{ steps.event.outputs.tag_full }}" + + ## Validate that the image index digest pushed to registry matches with the one on the release notes + pushed_image_index_digest=$(crane digest "${DOCKERHUB_REGISTRY}/${IMAGE}:${{ steps.event.outputs.tag_full }}" | xargs) + + echo "Index digest from release notes: $(cat ./index-digest.sha256)" + echo "Index digest pushed to registry: $pushed_image_index_digest" + + if [ "$(cat ./index-digest.sha256)" != "$pushed_image_index_digest" ]; then + echo "The image index digest pushed to registry does not match the expected digest from release notes" + exit 1; + fi + + sudo skopeo copy "docker://${DOCKERHUB_REGISTRY}/${IMAGE}:${{ steps.event.outputs.tag_full }}" "docker://${DOCKERHUB_REGISTRY}/${IMAGE}:${{ steps.event.outputs.tag_minor }}" --multi-arch all + sudo skopeo copy "docker://${DOCKERHUB_REGISTRY}/${IMAGE}:${{ steps.event.outputs.tag_full }}" "docker://${DOCKERHUB_REGISTRY}/${IMAGE}:${{ steps.event.outputs.tag_major }}" --multi-arch all + sudo skopeo copy "docker://${DOCKERHUB_REGISTRY}/${IMAGE}:${{ steps.event.outputs.tag_full }}" "docker://${DOCKERHUB_REGISTRY}/${IMAGE}:latest" --multi-arch all echo "image=${IMAGE}" >> "$GITHUB_OUTPUT" - echo "digest=$(sudo skopeo inspect "oci-archive:${GITHUB_WORKSPACE}/buildpackage.cnb" | jq -r .Digest)" >> "$GITHUB_OUTPUT" + echo "digest=$pushed_image_index_digest" >> "$GITHUB_OUTPUT" + + - name: Push to GCR + if: ${{ steps.parse_configs.outputs.push_to_gcr == 'true' }} + run: | + echo "${GCR_PASSWORD}" | sudo skopeo login --username "${GCR_USERNAME}" --password-stdin "${GCR_REGISTRY}" + + sudo skopeo copy "docker://${DOCKERHUB_REGISTRY}/${{ steps.push.outputs.image }}" "docker://${GCR_REGISTRY}/${{ github.repository }}:${{ steps.event.outputs.tag_full }}" --multi-arch all + sudo skopeo copy "docker://${DOCKERHUB_REGISTRY}/${{ steps.push.outputs.image }}" "docker://${GCR_REGISTRY}/${{ github.repository }}:${{ steps.event.outputs.tag_minor }}" --multi-arch all + sudo skopeo copy "docker://${DOCKERHUB_REGISTRY}/${{ steps.push.outputs.image }}" "docker://${GCR_REGISTRY}/${{ github.repository }}:${{ steps.event.outputs.tag_major }}" --multi-arch all + sudo skopeo copy "docker://${DOCKERHUB_REGISTRY}/${{ steps.push.outputs.image }}" "docker://${GCR_REGISTRY}/${{ github.repository }}:latest" --multi-arch all - name: Register with CNB Registry uses: docker://ghcr.io/buildpacks/actions/registry/request-add-entry:main with: id: ${{ github.repository }} version: ${{ steps.event.outputs.tag_full }} - address: ${{ steps.push.outputs.image }}@${{ steps.push.outputs.digest }} + address: index.docker.io/${{ steps.push.outputs.image }}@${{ steps.push.outputs.digest }} token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} failure: name: Alert on Failure - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: [push] if: ${{ always() && needs.push.result == 'failure' }} steps: diff --git a/.github/workflows/synchronize-labels.yml b/.github/workflows/synchronize-labels.yml index 3aaa017..fc0f516 100644 --- a/.github/workflows/synchronize-labels.yml +++ b/.github/workflows/synchronize-labels.yml @@ -10,9 +10,9 @@ jobs: synchronize: name: Synchronize Labels runs-on: - - ubuntu-22.04 + - ubuntu-24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 - uses: micnncim/action-label-syncer@v1 env: GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/test-pull-request.yml b/.github/workflows/test-pull-request.yml index cd25980..d86d4dd 100644 --- a/.github/workflows/test-pull-request.yml +++ b/.github/workflows/test-pull-request.yml @@ -13,17 +13,17 @@ concurrency: jobs: unit: name: Unit Tests - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 outputs: builders: ${{ steps.builders.outputs.builders }} steps: + - name: Checkout + uses: actions/checkout@v5 + - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v6 with: - go-version: 'stable' - - - name: Checkout - uses: actions/checkout@v3 + go-version-file: go.mod - name: Run Unit Tests env: @@ -41,20 +41,20 @@ jobs: integration: name: Integration Tests with Builders - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: unit strategy: matrix: builder: ${{ fromJSON(needs.unit.outputs.builders) }} fail-fast: false # don't cancel all test jobs when one fails steps: + - name: Checkout + uses: actions/checkout@v5 + - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v6 with: - go-version: 'stable' - - - name: Checkout - uses: actions/checkout@v3 + go-version-file: go.mod - name: Run Integration Tests run: ./scripts/integration.sh --builder ${{ matrix.builder }} --token ${{ github.token }} @@ -64,7 +64,7 @@ jobs: roundup: name: Integration Tests if: ${{ always() }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: integration steps: - run: | @@ -79,7 +79,7 @@ jobs: upload: name: Upload Workflow Event Payload - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Upload Artifact uses: actions/upload-artifact@v4 diff --git a/.github/workflows/update-github-config.yml b/.github/workflows/update-github-config.yml index 79164ac..d3f8b49 100644 --- a/.github/workflows/update-github-config.yml +++ b/.github/workflows/update-github-config.yml @@ -2,7 +2,7 @@ name: Update shared github-config on: schedule: - - cron: '27 13 * * *' # daily at 13:27 UTC + - cron: '27 13 * * *' # daily at 13:27 UTC workflow_dispatch: {} concurrency: github_config_update @@ -10,16 +10,16 @@ concurrency: github_config_update jobs: build: name: Create PR to update shared files - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v5 with: token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} - name: Checkout github-config - uses: actions/checkout@v3 + uses: actions/checkout@v5 with: repository: paketo-buildpacks/github-config path: github-config @@ -63,7 +63,7 @@ jobs: failure: name: Alert on Failure - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: [build] if: ${{ always() && needs.build.result == 'failure' }} steps: diff --git a/.github/workflows/update-go-mod-version.yml b/.github/workflows/update-go-mod-version.yml index b68db8a..44248a4 100644 --- a/.github/workflows/update-go-mod-version.yml +++ b/.github/workflows/update-go-mod-version.yml @@ -13,16 +13,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Checkout PR Branch uses: paketo-buildpacks/github-config/actions/pull-request/checkout-branch@main with: branch: automation/go-mod-update/update-main - name: Setup Go id: setup-go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: - go-version: 'stable' + go-version-file: go.mod - name: Get current go toolchain version id: current-go-version uses: paketo-buildpacks/github-config/actions/update-go-mod-version@main @@ -75,7 +75,7 @@ jobs: failure: name: Alert on Failure - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: [update-go] if: ${{ always() && needs.update-go.result == 'failure' }} steps: diff --git a/scripts/.util/tools.json b/scripts/.util/tools.json index 89988cc..c8845b4 100644 --- a/scripts/.util/tools.json +++ b/scripts/.util/tools.json @@ -1,5 +1,6 @@ { "createpackage": "v1.73.0", - "jam": "v2.11.5", - "pack": "v0.37.0" + "jam": "v2.15.0", + "libpaktools": "v0.3.0", + "pack": "v0.38.2" } diff --git a/scripts/.util/tools.sh b/scripts/.util/tools.sh index 8feecf3..226012e 100644 --- a/scripts/.util/tools.sh +++ b/scripts/.util/tools.sh @@ -31,6 +31,8 @@ function util::tools::arch() { amd64|x86_64) if [[ "${1:-}" == "--blank-amd64" ]]; then echo "" + elif [[ "${1:-}" == "--format-amd64-x86_64" ]]; then + echo "x86_64" else echo "amd64" fi @@ -196,6 +198,64 @@ function util::tools::packager::install () { fi } +function util::tools::libpak-tools::install () { + local dir token + token="" + + while [[ "${#}" != 0 ]]; do + case "${1}" in + --directory) + dir="${2}" + shift 2 + ;; + + --token) + token="${2}" + shift 2 + ;; + + *) + util::print::error "unknown argument \"${1}\"" + esac + done + + mkdir -p "${dir}" + util::tools::path::export "${dir}" + + + if [[ ! -f "${dir}/libpak-tools" ]]; then + local version curl_args os arch + + version="$(jq -r .libpaktools "$(dirname "${BASH_SOURCE[0]}")/tools.json")" + + curl_args=( + "--fail" + "--silent" + "--location" + "--output" "${dir}/libpak-tools.tar.gz" + ) + + if [[ "${token}" != "" ]]; then + curl_args+=("--header" "Authorization: Token ${token}") + fi + + util::print::title "Installing libpak-tools ${version}" + + os=$(util::tools::os) + arch=$(util::tools::arch --format-amd64-x86_64) + + curl "https://github.com/paketo-buildpacks/libpak-tools/releases/download/${version}/libpak-tools_${os}_${arch}.tar.gz" \ + "${curl_args[@]}" + + tar -xzf "${dir}/libpak-tools.tar.gz" -C $dir + rm "${dir}/libpak-tools.tar.gz" + + chmod +x "${dir}/libpak-tools" + else + util::print::info "Using libpak-tools" + fi +} + function util::tools::create-package::install () { local dir version while [[ "${#}" != 0 ]]; do diff --git a/scripts/build.sh b/scripts/build.sh index c6e1413..11ab80b 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -3,10 +3,15 @@ set -eu set -o pipefail +readonly ROOT_DIR="$(cd "$(dirname "${0}")/.." && pwd)" readonly PROGDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly BUILDPACKDIR="$(cd "${PROGDIR}/.." && pwd)" +# shellcheck source=SCRIPTDIR/.util/print.sh +source "${ROOT_DIR}/scripts/.util/print.sh" + function main() { + local targets=() while [[ "${#}" != 0 ]]; do case "${1}" in --help|-h) @@ -15,6 +20,11 @@ function main() { exit 0 ;; + --target) + targets+=("${2}") + shift 2 + ;; + "") # skip if the argument is empty shift 1 @@ -27,8 +37,18 @@ function main() { mkdir -p "${BUILDPACKDIR}/bin" + if [[ ${#targets[@]} -eq 0 ]]; then + targets=("linux/amd64") + util::print::info "Setting default target platform architecture to: linux/amd64" + fi + run::build cmd::build + + ## For backwards compatibility with amd64 wokflows + if [[ ${#targets[@]} -eq 1 && "${targets[0]}" == "linux/amd64" ]]; then + cp -r "${BUILDPACKDIR}/linux/amd64/bin/" "${BUILDPACKDIR}/" + fi } function usage() { @@ -38,39 +58,49 @@ build.sh [OPTIONS] Builds the buildpack executables. OPTIONS - --help -h prints the command usage + --target strings Target platforms to build for. + Targets should be in the format '[os][/arch][/variant]'. + - To specify two different architectures: '--target "linux/amd64" --target "linux/arm64"' + --help -h prints the command usage USAGE } function run::build() { if [[ -f "${BUILDPACKDIR}/run/main.go" ]]; then - pushd "${BUILDPACKDIR}/bin" > /dev/null || return - printf "%s" "Building run... " + pushd "${BUILDPACKDIR}" > /dev/null || return + for target in "${targets[@]}"; do + platform=$(echo "${target}" | cut -d '/' -f1) + arch=$(echo "${target}" | cut -d'/' -f2) - GOOS=linux \ - CGO_ENABLED=0 \ - go build \ - -ldflags="-s -w" \ - -o "run" \ - "${BUILDPACKDIR}/run" + util::print::title "Building run... for platform: ${platform} and arch: ${arch}" - echo "Success!" + GOOS=$platform \ + GOARCH=$arch \ + CGO_ENABLED=0 \ + go build \ + -ldflags="-s -w" \ + -o "${platform}/${arch}/bin/run" \ + "${BUILDPACKDIR}/run" - names=("detect") + echo "Success!" - if [ -f "${BUILDPACKDIR}/extension.toml" ]; then - names+=("generate") - else - names+=("build") - fi + names=("detect") - for name in "${names[@]}"; do - printf "%s" "Linking ${name}... " + if [ -f "${BUILDPACKDIR}/extension.toml" ]; then + names+=("generate") + else + names+=("build") + fi - ln -sf "run" "${name}" + for name in "${names[@]}"; do + printf "%s" "Linking ${name}... " - echo "Success!" + ln -fs "run" "${platform}/${arch}/bin/${name}" + + echo "Success!" + done done + popd > /dev/null || return fi } @@ -80,21 +110,26 @@ function cmd::build() { local name for src in "${BUILDPACKDIR}"/cmd/*; do name="$(basename "${src}")" - - if [[ -f "${src}/main.go" ]]; then - printf "%s" "Building ${name}... " - - GOOS="linux" \ - CGO_ENABLED=0 \ - go build \ - -ldflags="-s -w" \ - -o "${BUILDPACKDIR}/bin/${name}" \ - "${src}/main.go" - - echo "Success!" - else - printf "%s" "Skipping ${name}... " - fi + for target in "${targets[@]}"; do + platform=$(echo "${target}" | cut -d '/' -f1) + arch=$(echo "${target}" | cut -d'/' -f2) + + if [[ -f "${src}/main.go" ]]; then + util::print::title "Building ${name}... for platform: ${platform} and arch: ${arch}" + + GOOS=$platform \ + GOARCH=$arch \ + CGO_ENABLED=0 \ + go build \ + -ldflags="-s -w" \ + -o "${BUILDPACKDIR}/${platform}/${arch}/bin/${name}" \ + "${src}/main.go" + + echo "Success!" + else + printf "%s" "Skipping ${name}... " + fi + done done fi } diff --git a/scripts/integration.sh b/scripts/integration.sh index 46ffbdb..5de24a6 100755 --- a/scripts/integration.sh +++ b/scripts/integration.sh @@ -110,6 +110,9 @@ function tools::install() { --directory "${BUILDPACKDIR}/.bin" \ --token "${token}" + util::tools::libpak-tools::install \ + --directory "${BUILDPACKDIR}/.bin" + util::tools::create-package::install \ --directory "${BUILDPACKDIR}/.bin" diff --git a/scripts/package.sh b/scripts/package.sh index 916363e..cb79bfd 100755 --- a/scripts/package.sh +++ b/scripts/package.sh @@ -144,26 +144,15 @@ function buildpackage::create() { util::print::title "Packaging ${buildpack_type}... ${output}" - if [ "$buildpack_type" == "extension" ]; then - cwd=$(pwd) - cd ${BUILD_DIR} - mkdir cnbdir - cd cnbdir - cp ../buildpack.tgz . - tar -xvf buildpack.tgz - rm buildpack.tgz - - pack \ - extension package "${output}" \ - --format file - - cd $cwd - else - pack \ - buildpack package "${output}" \ - --path "${BUILD_DIR}/buildpack.tgz" \ - --format file - fi + mkdir ${BUILD_DIR}/cnbdir + tar -xvf ${BUILD_DIR}/buildpack.tgz -C ${BUILD_DIR}/cnbdir + + pack \ + "${buildpack_type}" package "${output}" \ + --path ${BUILD_DIR}/cnbdir \ + --format file + + rm -rf ${BUILD_DIR}/cnbdir } main "${@:-}" \ No newline at end of file diff --git a/scripts/publish.sh b/scripts/publish.sh new file mode 100755 index 0000000..6668542 --- /dev/null +++ b/scripts/publish.sh @@ -0,0 +1,142 @@ +#!/usr/bin/env bash + +set -eu +set -o pipefail + +readonly ROOT_DIR="$(cd "$(dirname "${0}")/.." && pwd)" +readonly BIN_DIR="${ROOT_DIR}/.bin" + +# shellcheck source=SCRIPTDIR/.util/tools.sh +source "${ROOT_DIR}/scripts/.util/tools.sh" + +# shellcheck source=SCRIPTDIR/.util/print.sh +source "${ROOT_DIR}/scripts/.util/print.sh" + +function main { + local archive_path buildpack_type image_ref token + token="" + + while [[ "${#}" != 0 ]]; do + case "${1}" in + --archive-path | -a) + archive_path="${2}" + shift 2 + ;; + + --buildpack-type | -bt) + buildpack_type="${2}" + shift 2 + ;; + + --image-ref | -i) + image_ref="${2}" + shift 2 + ;; + + --token | -t) + token="${2}" + shift 2 + ;; + + --help | -h) + shift 1 + usage + exit 0 + ;; + + "") + # skip if the argument is empty + shift 1 + ;; + + *) + util::print::error "unknown argument \"${1}\"" + ;; + esac + done + + if [[ -z "${image_ref:-}" ]]; then + usage + util::print::error "--image-ref is required" + fi + + if [[ -z "${buildpack_type:-}" ]]; then + usage + util::print::error "--buildpack-type is required" + fi + + if [[ ${buildpack_type} != "buildpack" && ${buildpack_type} != "extension" ]]; then + usage + util::print::error "--buildpack-type accepted values: [\"buildpack\",\"extension\"]" + fi + + if [[ -z "${archive_path:-}" ]]; then + util::print::info "Using default archive path: ${ROOT_DIR}/build/buildpack.tgz" + archive_path="${ROOT_DIR}/build/buildpack.tgz" + else + archive_path="${archive_path}" + fi + + repo::prepare + + tools::install "${token}" + + buildpack::publish "${image_ref}" "${buildpack_type}" "${archive_path}" +} + +function usage() { + cat <<-USAGE +Publishes a buildpack or an extension in to a registry. + +OPTIONS + -a, --archive-path Path to the buildpack or extension arhive (default: ${ROOT_DIR}/build/buildpack.tgz) (optional) + -h, --help Prints the command usage + -i, --image-ref List of image reference to publish to (required) + -bt --buildpack-type Type of buildpack to publish (accepted values: buildpack, extension) (required) + -t, --token Token used to download assets from GitHub (e.g. jam, pack, etc) (optional) + +USAGE +} + +function repo::prepare() { + util::print::title "Preparing repo..." + + mkdir -p "${BIN_DIR}" + + export PATH="${BIN_DIR}:${PATH}" +} + +function tools::install() { + local token + token="${1}" + + util::tools::pack::install \ + --directory "${BIN_DIR}" \ + --token "${token}" +} + +function buildpack::publish() { + + local image_ref buildpack_type archive_path + image_ref="${1}" + buildpack_type="${2}" + archive_path="${3}" + + util::print::title "Publishing ${buildpack_type}..." + + util::print::info "Extracting archive..." + tmp_dir=$(mktemp -d -p $ROOT_DIR) + tar -xvf $archive_path -C $tmp_dir + + util::print::info "Publishing ${buildpack_type} to ${image_ref}" + + pack \ + ${buildpack_type} package $image_ref \ + --path $tmp_dir \ + --format image \ + --publish + + rm -rf $tmp_dir +} + +main "${@:-}"