Build and Publish Dev Container Images #183
This file contains hidden or 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: Build and Publish Dev Container Images | |
| # This workflow uses actions that are not certified by GitHub. | |
| # They are provided by a third-party and are governed by | |
| # separate terms of service, privacy policy, and support | |
| # documentation. | |
| on: | |
| schedule: | |
| - cron: "19 15 * * *" | |
| push: | |
| branches: ["main"] | |
| # Publish semver tags as releases. | |
| tags: ["v*.*.*"] | |
| pull_request: | |
| branches: ["main"] | |
| workflow_dispatch: | |
| env: | |
| # Use docker.io for Docker Hub if empty | |
| REGISTRY: ghcr.io | |
| jobs: | |
| preparation: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| folders: ${{ steps.devcontainer-folders.outputs.folders }} | |
| repo: ${{ steps.lowercase-repo.outputs.repo }} | |
| image_timestamp: ${{ steps.timestamp.outputs.image_timestamp }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| - name: Get devcontainer folders | |
| id: devcontainer-folders | |
| run: echo "folders=$(ls src | jq -R -s -c 'split("\n")[:-1]')" | tee $GITHUB_OUTPUT | |
| - name: Get lowercase repo name | |
| id: lowercase-repo | |
| run: echo "repo=${GITHUB_REPOSITORY@L}" | tee $GITHUB_OUTPUT | |
| # TODO: this should be replaced by regular calver versioning, but for now we need a unique tag | |
| # to be able to push to ghcr without hitting "already exists" errors, because we want to keep the | |
| # latest tag up to date for users who don't specify a version. | |
| - name: Create timestamp tag | |
| id: timestamp | |
| run: echo "image_timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" | tee $GITHUB_OUTPUT | |
| runtime-versions: | |
| name: Get runtime versions | |
| runs-on: ubuntu-latest | |
| needs: preparation | |
| strategy: | |
| matrix: | |
| folder: ${{ fromJson(needs.preparation.outputs.folders) }} | |
| outputs: | |
| runtime_versions: ${{ steps.runtime-versions.outputs.runtime_versions }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| - name: Read runtime name from config.json | |
| id: runtime-name | |
| run: | | |
| runtime_name=$(jq -r '.ci.runtime_name' src/${{ matrix.folder }}/config.json) | |
| echo "RUNTIME_NAME=${runtime_name}" | tee $GITHUB_ENV | |
| - name: Load ${{ env.RUNTIME_NAME }} runtime versions to process from config.json | |
| id: runtime-versions | |
| run: | | |
| runtime_versions=$(jq -c --arg runtime_name "${RUNTIME_NAME}_versions" '.ci.[$runtime_name]' src/${{ matrix.folder }}/config.json) | |
| echo "runtime_versions=$runtime_versions" | tee $GITHUB_OUTPUT | |
| env: | |
| RUNTIME_NAME: ${{ env.RUNTIME_NAME }} | |
| build: | |
| runs-on: ubuntu-latest | |
| needs: [preparation, runtime-versions] | |
| strategy: | |
| matrix: | |
| folder: ${{ fromJson(needs.preparation.outputs.folders) }} | |
| runtime_version: ${{ fromJson(needs.runtime-versions.outputs.runtime_versions) }} | |
| permissions: | |
| contents: read | |
| packages: write | |
| # This is used to complete the identity challenge | |
| # with sigstore/fulcio when running outside of PRs. | |
| id-token: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| # read image tag from subdirectory propery file if exists | |
| - name: Install Skopeo | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y skopeo | |
| - name: Load environment variables from .env file | |
| uses: xom9ikk/dotenv@v2.4.0 | |
| with: | |
| path: "src/${{ matrix.folder }}" | |
| mode: '' | |
| load-mode: 'skip' | |
| env: | |
| RUNTIME_VERSION: ${{ matrix.runtime_version }} | |
| - name: Get image base digest | |
| id: get-image-base-digest | |
| run: | | |
| if [[ -n "${IMAGE_BASE_TAG}" ]]; then | |
| image_base_reference="${IMAGE_BASE_NAME}:${IMAGE_BASE_TAG}" | |
| else | |
| image_base_reference="${IMAGE_BASE_NAME}" | |
| fi | |
| image_base_digest=$(skopeo inspect --no-creds docker://${image_base_reference} | jq -r .Digest) | |
| echo "IMAGE_BASE_DIGEST=${image_base_digest}" | tee $GITHUB_ENV | |
| # Login against a Docker registry except on PR | |
| # https://github.com/docker/login-action | |
| - name: Log into registry ${{ env.REGISTRY }} | |
| if: github.event_name != 'pull_request' | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Calculate next calver version | |
| id: calculate-next-calver | |
| run: | | |
| current_calver_major_minor=$(date "+%Y.%m") | |
| last_imagecalvertag=$(skopeo list-tags --no-creds docker://ghcr.io/${TARGET_IMAGE_NAME} | \ | |
| jq -r --arg TARGET_IMAGE_BASETAG "$TARGET_IMAGE_BASETAG" '.Tags | sort_by(.) | [ .[] | select(.|startswith($TARGET_IMAGE_BASETAG))]|[last][0]') | |
| last_imagecalver=${last_imagecalvertag##*-} | |
| if [[ ${last_imagecalver} == "${current_calver_major_minor}"* ]]; then | |
| current_patch_number=${last_imagecalver#${current_calver_major_minor}.} | |
| next_patch_number=$((current_patch_number + 1)) | |
| next_calver="${current_calver_major_minor}.${next_patch_number}" | |
| else | |
| next_calver="${current_calver_major_minor}.0" | |
| fi | |
| echo "NEXT_CALVER=${next_calver}" | tee $GITHUB_ENV | |
| env: | |
| TARGET_IMAGE_NAME: ${{ env.REGISTRY }}/${{ needs.preparation.outputs.repo }}/${{ matrix.folder }} | |
| TARGET_IMAGE_BASETAG: ${{ env.TARGET_IMAGE_BASETAG }} # set in .env file in each subdirectory | |
| - name: Pre-build dev container image | |
| uses: devcontainers/ci@v0.3 | |
| with: | |
| subFolder: src/${{ matrix.folder }} | |
| imageName: ${{ env.TARGET_IMAGE_NAME }} | |
| imageTag: ${{ env.TARGET_IMAGE_BASETAG }}, ${{ env.TARGET_IMAGE_BASETAG }}-${{ env.NEXT_CALVER }} | |
| cacheFrom: ${{ env.TARGET_IMAGE_NAME }}:${{ env.TARGET_IMAGE_BASETAG }} | |
| refFilterForPush: "refs/heads/main" | |
| env: | |
| TARGET_IMAGE_NAME: ${{ env.REGISTRY }}/${{ needs.preparation.outputs.repo }}/${{ matrix.folder }} | |
| TARGET_IMAGE_BASETAG: ${{ env.TARGET_IMAGE_BASETAG }} | |
| IMAGE_BASE_NAME: ${{ env.IMAGE_BASE_NAME }} | |
| IMAGE_BASE_TAG: ${{ env.IMAGE_BASE_TAG }} | |
| IMAGE_BASE_DIGEST: ${{ env.IMAGE_BASE_DIGEST }} | |
| IMAGE_TIMESTAMP: ${{ needs.preparation.outputs.image_timestamp }} | |
| NEXT_CALVER: ${{ env.NEXT_CALVER }} | |
| COMMIT_SHA: ${{ github.sha }} | |
| MATRIX_FOLDER: ${{ matrix.folder }} |