CI #456
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: CI | |
on: | |
push: | |
# Avoid duplicate builds on PRs. | |
branches: | |
- main | |
tags: | |
- v* | |
schedule: | |
- cron: "0 0 * * 1-5" | |
pull_request: | |
workflow_dispatch: | |
permissions: | |
contents: read | |
id-token: write | |
jobs: | |
shellcheck: | |
runs-on: ubuntu-24.04 | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- name: Run shellcheck | |
run: find . -type f \( -name "*.sh" -o -path "*/bin/*" \) ! -name '*.jq' | xargs -t shellcheck | |
build: | |
name: "Build heroku-${{ matrix.stack-version }} (${{ matrix.arch }})" | |
needs: | |
- shellcheck | |
runs-on: ${{ matrix.arch == 'arm64' && 'pub-hk-ubuntu-24.04-arm-medium' || 'ubuntu-24.04' }} | |
strategy: | |
fail-fast: false | |
matrix: | |
arch: ["amd64", "arm64"] | |
stack-version: ["20", "22", "24"] | |
exclude: | |
- arch: "arm64" | |
stack-version: "20" | |
- arch: "arm64" | |
stack-version: "22" | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- name: Build base image groups | |
run: bin/build.sh "${{ matrix.stack-version }}" "${{ matrix.arch }}" | |
- name: Check that the generated files are in sync | |
run: |- | |
status="$(git status --porcelain)" | |
if [[ -n "$status" ]]; then | |
echo "Generated files differ from checked-in versions! Run bin/build.sh to regenerate them locally." | |
echo -e "\nChanged files:\n${status}\n" | |
git diff | |
exit 1 | |
fi | |
- name: Generate Heroku runtime image files | |
if: matrix.arch == 'amd64' | |
run: | | |
bin/generate-runtime-images.sh ${{ matrix.stack-version }} | |
- name: Export base images from the Docker daemon | |
if: github.ref_name == 'main' || github.ref_type == 'tag' | |
run: | | |
docker save $(docker images --format '{{.Repository}}:{{.Tag}}' | grep "heroku/heroku:${{ matrix.stack-version }}") | zstd -T0 --long=31 -o images.tar.zst | |
- name: Save OCI base image exports to the cache | |
if: github.ref_name == 'main' || github.ref_type == 'tag' | |
uses: actions/cache/save@v4 | |
with: | |
key: ${{ github.run_id}}-${{ matrix.stack-version }}-${{ matrix.arch }} | |
path: images.tar.zst | |
- name: Save Heroku runtime image files to the cache | |
if: matrix.arch == 'amd64' && github.ref_type == 'tag' | |
uses: actions/cache/save@v4 | |
with: | |
key: runtime-images-${{ github.run_id}}-${{ matrix.stack-version }} | |
path: /tmp/heroku-${{ matrix.stack-version }}* | |
upload-runtime-images: | |
if: github.ref_type == 'tag' | |
name: "Upload heroku-${{ matrix.stack-version }} runtime images" | |
needs: | |
- build | |
runs-on: ubuntu-24.04 | |
strategy: | |
fail-fast: false | |
matrix: | |
stack-version: ["20", "22", "24"] | |
env: | |
MANIFEST_APP_TOKEN: "${{ secrets.MANIFEST_APP_TOKEN }}" | |
MANIFEST_APP_URL: "${{ secrets.MANIFEST_APP_URL }}" | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- name: Restore heroku runtime images from the cache | |
uses: actions/cache/restore@v4 | |
with: | |
fail-on-cache-miss: true | |
key: runtime-images-${{ github.run_id}}-${{ matrix.stack-version }} | |
path: /tmp/heroku-${{ matrix.stack-version }}* | |
- name: Upload heroku runtime images to staging | |
run: | | |
bin/upload-runtime-images.sh ${{ matrix.stack-version }} ${{ github.sha }} | |
publish-images: | |
if: github.ref_name == 'main' || github.ref_type == 'tag' | |
name: "Publish heroku-${{ matrix.stack-version }} (${{ matrix.arch }})" | |
needs: | |
- build | |
runs-on: ${{ matrix.arch == 'arm64' && 'pub-hk-ubuntu-24.04-arm-medium' || 'ubuntu-24.04' }} | |
env: | |
TAG_SUFFIX: ".${{ github.ref_type == 'tag' && github.ref_name || 'nightly' }}" | |
strategy: | |
fail-fast: false | |
matrix: | |
arch: ["amd64", "arm64"] | |
stack-version: ["20", "22", "24"] | |
exclude: | |
- arch: "arm64" | |
stack-version: "20" | |
- arch: "arm64" | |
stack-version: "22" | |
steps: | |
- name: Restore base images from the cache | |
uses: actions/cache/restore@v4 | |
with: | |
fail-on-cache-miss: true | |
key: ${{ github.run_id}}-${{ matrix.stack-version }}-${{ matrix.arch }} | |
path: images.tar.zst | |
env: | |
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1 | |
- name: Load Docker images into the Docker daemon | |
run: zstd -dc --long=31 images.tar.zst | docker load | |
- name: Log in to Docker Hub | |
run: echo '${{ secrets.DOCKER_HUB_TOKEN }}' | docker login -u '${{ secrets.DOCKER_HUB_USERNAME }}' --password-stdin | |
- name: Configure AWS credentials | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ECR_ROLE }} | |
aws-region: ${{ vars.AWS_REGION }} | |
- name: Log in to Amazon ECR Public | |
id: login-ecr-public | |
uses: aws-actions/amazon-ecr-login@v2 | |
with: | |
registry-type: public | |
- name: Publish base images to registries | |
run: | | |
variants=("" "-build") | |
platformSuffix="" | |
if (( ${{ matrix.stack-version }} >= 24 )); then | |
platformSuffix="_linux-${{ matrix.arch }}" | |
else | |
variants+=("-cnb" "-cnb-build") | |
fi | |
for variant in "${variants[@]}"; do | |
srcTag="heroku/heroku:${{ matrix.stack-version}}${variant}" | |
destTag="${srcTag}${platformSuffix}${TAG_SUFFIX}" | |
for host in "docker.io" "public.ecr.aws"; do | |
docker tag "${srcTag}" "${host}/${destTag}" | |
docker push "${host}/${destTag}" | |
done | |
done | |
publish-indices: | |
if: github.ref_name == 'main' || github.ref_type == 'tag' | |
name: "Publish heroku-${{ matrix.stack-version }} indices" | |
needs: | |
- publish-images | |
runs-on: ubuntu-24.04 | |
env: | |
TAG_SUFFIX: ".${{ github.ref_type == 'tag' && github.ref_name || 'nightly' }}" | |
strategy: | |
fail-fast: false | |
matrix: | |
stack-version: ["24"] | |
steps: | |
- name: Log in to Docker Hub | |
run: echo '${{ secrets.DOCKER_HUB_TOKEN }}' | docker login -u '${{ secrets.DOCKER_HUB_USERNAME }}' --password-stdin | |
- name: Configure AWS credentials | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ECR_ROLE }} | |
aws-region: ${{ vars.AWS_REGION }} | |
- name: Log in to Amazon ECR Public | |
id: login-ecr-public | |
uses: aws-actions/amazon-ecr-login@v2 | |
with: | |
registry-type: public | |
- name: Publish multi-arch image index | |
run: | | |
for variant in '' '-build'; do | |
indexTag="heroku/heroku:${{ matrix.stack-version }}${variant}${TAG_SUFFIX}" | |
armTag="heroku/heroku:${{ matrix.stack-version }}${variant}_linux-arm64${TAG_SUFFIX}" | |
amdTag="heroku/heroku:${{ matrix.stack-version }}${variant}_linux-amd64${TAG_SUFFIX}" | |
for host in 'docker.io' 'public.ecr.aws'; do | |
docker manifest create "${host}/${indexTag}" "${host}/${amdTag}" "${host}/${armTag}" | |
docker manifest push "${host}/${indexTag}" | |
done | |
done | |
ctc-check: | |
name: Obtain CTC Lock | |
if: github.ref_type == 'tag' | |
runs-on: ubuntu-24.04 | |
steps: | |
- name: Obtain CTC Lock via TPS API | |
run: | | |
curl -sS --connect-timeout 5 --fail-with-body --retry-connrefused --retry 5 \ | |
-X PUT \ | |
-H "ACCEPT: application/json" \ | |
-H "Content-Type: application/json" \ | |
-H "Authorization: Token ${{ secrets.TPS_TOKEN }}" \ | |
-d '{"lock": {"sha": "${{ github.sha }}", "component_name": "${{ vars.tps_component }}"}}' \ | |
"${{ secrets.TPS_CTC_API_URL }}" | |
promote-tags: | |
if: github.ref_type == 'tag' | |
name: "Promote heroku-${{ matrix.stack-version }} tags" | |
needs: | |
- ctc-check | |
- publish-images | |
- publish-indices | |
runs-on: ubuntu-24.04 | |
strategy: | |
fail-fast: false | |
matrix: | |
stack-version: ["20", "22", "24"] | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- name: Log in to Docker Hub | |
run: echo '${{ secrets.DOCKER_HUB_TOKEN }}' | docker login -u '${{ secrets.DOCKER_HUB_USERNAME }}' --password-stdin | |
- name: Configure AWS credentials | |
uses: aws-actions/configure-aws-credentials@v4 | |
with: | |
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ECR_ROLE }} | |
aws-region: ${{ vars.AWS_REGION }} | |
- name: Log in to Amazon ECR Public | |
id: login-ecr-public | |
uses: aws-actions/amazon-ecr-login@v2 | |
with: | |
registry-type: public | |
- name: Install crane | |
uses: buildpacks/github-actions/[email protected] | |
- name: Promote images to stable tag | |
run: | | |
destTags=( ) | |
if (( ${{ matrix.stack-version }} >= 24 )); then | |
for variant in '' '-build'; do | |
for arch in 'amd64' 'arm64'; do | |
destTags+=("heroku/heroku:${{ matrix.stack-version }}${variant}_linux-${arch}") | |
done | |
destTags+=("heroku/heroku:${{ matrix.stack-version }}${variant}") | |
done | |
else | |
for variant in '' '-build' '-cnb' '-cnb-build'; do | |
destTags+=("heroku/heroku:${{ matrix.stack-version }}${variant}") | |
done | |
fi | |
for destTag in "${destTags[@]}"; do | |
srcTag="${destTag}.${{ github.ref_name }}" | |
for host in "docker.io" "public.ecr.aws"; do | |
crane copy "${host}/${srcTag}" "${host}/${destTag}" | |
done | |
done |