Skip to content

CI

CI #549

Workflow file for this run

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_slug": "${{ 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