Build and Push Images #1802
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 Push Images | |
| on: | |
| workflow_run: | |
| workflows: | |
| - CI | |
| types: | |
| - completed | |
| branches: | |
| - main | |
| - epic/** | |
| - release/** | |
| push: | |
| tags: | |
| - v*.*.* | |
| create: | |
| tags: | |
| - v*.*.* | |
| branches: | |
| - release/** | |
| jobs: | |
| prepare: | |
| if: > | |
| ( | |
| (github.ref_type == 'tag' && startsWith(github.ref_name, 'v')) && | |
| (github.event_name == 'push' || github.event_name == 'create') | |
| ) || | |
| ( | |
| github.event_name == 'create' && | |
| ( | |
| startsWith(github.ref_name, 'release/') || | |
| startsWith(github.ref_name, 'epic/') | |
| ) | |
| ) || | |
| ( | |
| github.event_name == 'workflow_run' && | |
| github.event.workflow_run.conclusion == 'success' && | |
| ( | |
| github.event.workflow_run.head_branch == 'main' || | |
| startsWith(github.event.workflow_run.head_branch, 'release/') || | |
| startsWith(github.event.workflow_run.head_branch, 'epic/') | |
| ) | |
| ) || | |
| ( | |
| github.event_name == 'pull_request' && | |
| ( | |
| github.base_ref == 'main' || | |
| startsWith(github.base_ref, 'release/') || | |
| startsWith(github.base_ref, 'epic/') | |
| ) | |
| ) | |
| runs-on: ubuntu-latest | |
| env: | |
| # Prefer workflow_run context if present, otherwise fall back defaults | |
| HEAD_BRANCH: ${{ github.event.workflow_run.head_branch || (github.ref_type == 'branch' && github.ref_name) }} | |
| HEAD_TAG: ${{ github.event.workflow_run.head_branch == null && github.ref_type == 'tag' && github.ref_name || '' }} | |
| HEAD_SHA: ${{ github.event.workflow_run.head_sha || github.sha }} | |
| outputs: | |
| matrix: ${{ steps.read-matrix.outputs.matrix }} | |
| build_type: ${{ steps.build-type.outputs.build_type }} | |
| build_tag_base: ${{ steps.build-type.outputs.build_tag_base }} | |
| build_tag_inc: ${{ steps.build-type.outputs.build_tag_inc }} | |
| release_version: ${{ steps.build-type.outputs.release_version }} | |
| version: ${{ steps.pkg-details.outputs.version }} | |
| description: ${{ steps.pkg-details.outputs.description }} | |
| build_tag: ${{ steps.incremental-tag.outputs.build_tag }} | |
| version_bump_type: ${{ steps.build-type.outputs.version_bump_type }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.HEAD_SHA }} | |
| fetch-depth: 0 | |
| - name: Read build matrix | |
| id: read-matrix | |
| shell: bash | |
| run: | | |
| MATRIX=$(cat ./.github/build.matrix.json | jq -c .) | |
| echo "matrix=${MATRIX}" >> $GITHUB_OUTPUT | |
| - name: Read package.json/repository version | |
| id: pkg-details | |
| run: | | |
| echo "version=$(cat package.json | jq .version -r)" >> $GITHUB_OUTPUT | |
| echo "description=$(cat package.json | jq .description -r)" >> $GITHUB_OUTPUT | |
| - name: Determine build type | |
| id: build-type | |
| shell: bash | |
| env: | |
| PKGVER: ${{ steps.pkg-details.outputs.version }} | |
| run: | | |
| if [[ -n "$HEAD_TAG" ]]; then | |
| # Build release for production | |
| echo "release tag ${HEAD_TAG#v}" | |
| echo "build_type=release" >> $GITHUB_OUTPUT | |
| echo "build_tag_base=${HEAD_TAG#v}" >> $GITHUB_OUTPUT | |
| echo "release_version=${HEAD_TAG#v}" >> $GITHUB_OUTPUT | |
| echo "version_bump_type=patch" >> $GITHUB_OUTPUT | |
| elif [[ "$HEAD_BRANCH" == "main" ]]; then | |
| # Build CI snapshot | |
| echo "CI snapshot (main)" | |
| echo "build_type=snapshot" >> $GITHUB_OUTPUT | |
| echo "build_tag_base=${PKGVER}-ci" >> $GITHUB_OUTPUT | |
| echo "version_bump_type=patch" >> $GITHUB_OUTPUT | |
| elif [[ "$HEAD_BRANCH" == epic/* ]]; then | |
| # Build incremental epic | |
| echo "build_type=epic" >> $GITHUB_OUTPUT | |
| echo "build_tag_inc=true" >> $GITHUB_OUTPUT | |
| BASE=$(echo "$HEAD_BRANCH" | awk -F"/|-" '{print $1 "-" $2 "-" $3}') | |
| echo "build_tag_base=${BASE}" >> $GITHUB_OUTPUT | |
| echo "Epic Branch ${BASE}" | |
| elif [[ "$HEAD_BRANCH" == release/* ]]; then | |
| # Build incremental release candidate | |
| echo "build_type=rc" >> $GITHUB_OUTPUT | |
| echo "build_tag_inc=true" >> $GITHUB_OUTPUT | |
| echo "build_tag_base=${HEAD_BRANCH#release/}-rc" >> $GITHUB_OUTPUT | |
| echo "release_version=${HEAD_BRANCH#release/}" >> $GITHUB_OUTPUT | |
| echo "Release Branch ${HEAD_BRANCH}" | |
| fi | |
| - name: Login to GHCR | |
| if: steps.build-type.outputs.build_tag_inc == 'true' | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Install crane | |
| uses: imjasonh/setup-crane@v0.4 | |
| - name: Determine incremental build tag | |
| id: incremental-tag | |
| if: steps.build-type.outputs.build_tag_inc == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| BRANCH_TAG="${{ steps.build-type.outputs.build_tag_base }}" | |
| LATEST=$(crane ls "ghcr.io/bostads-ab-mimer/onecore/onecore-core" \ | |
| | grep "^${BRANCH_TAG}\." \ | |
| | sed "s/^${BRANCH_TAG}\.//" \ | |
| | sort -n \ | |
| | tail -1) | |
| if [ -z "$LATEST" ]; then | |
| NEXT=1 | |
| else | |
| NEXT=$((LATEST + 1)) | |
| fi | |
| echo "build_tag=${BRANCH_TAG}.${NEXT}" >> $GITHUB_OUTPUT | |
| echo "Using tag: ${BRANCH_TAG}.${NEXT}" | |
| - name: Enforce package.json matches release version | |
| if: steps.build-type.outputs.build_tag_version != '' | |
| run: | | |
| WANT="${{ steps.build-type.outputs.release_version }}" | |
| HAVE="${{ steps.pkg-details.outputs.version }}" | |
| if [[ "$WANT" != "$HAVE" ]]; then | |
| echo "::error::package.json version ($HAVE) does not match release target version ($WANT)"; | |
| exit 1; | |
| fi | |
| build-and-push: | |
| needs: prepare | |
| runs-on: ubuntu-latest | |
| env: | |
| HEAD_SHA: ${{ github.event.workflow_run.head_sha || github.sha }} | |
| strategy: | |
| matrix: | |
| include: ${{ fromJson(needs.prepare.outputs.matrix) }} | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.HEAD_SHA }} | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to GHCR | |
| if: github.event.repository.fork == false | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ghcr.io/${{ github.repository }}/${{ matrix.imageName || matrix.name }} | |
| tags: | | |
| type=raw,value=${{ needs.prepare.outputs.build_tag || needs.prepare.outputs.build_tag_base }} | |
| type=raw,value=latest,enable=${{ github.event.workflow_run.head_branch == null && github.ref_type == 'tag' && github.ref_name && 'true' || 'false' }} | |
| labels: | | |
| org.opencontainers.image.title=${{ matrix.label }} | |
| org.opencontainers.image.description=${{ needs.prepare.outputs.description }} | |
| - name: Render build_args | |
| id: render-build-args | |
| env: | |
| BUILD_TYPE: ${{ needs.prepare.outputs.build_type }} | |
| RELEASE_BUILD_ARGS: ${{ (matrix.build_args.release && toJSON(matrix.build_args.release)) }} | |
| TEST_BUILD_ARGS: ${{ (matrix.build_args.test && toJSON(matrix.build_args.test)) }} | |
| run: | | |
| if [ "${BUILD_TYPE}" == "release" ]; then | |
| BUILD_ARGS_JSON="${RELEASE_BUILD_ARGS}" | |
| else | |
| BUILD_ARGS_JSON="${TEST_BUILD_ARGS}" | |
| fi | |
| if [ -n "${BUILD_ARGS_JSON}" ]; then | |
| { | |
| printf "build_args<<EOF\n" | |
| echo "$BUILD_ARGS_JSON" | jq -r 'to_entries | .[]? | "\(.key)=\(.value)"' | |
| printf "EOF\n" | |
| } >> $GITHUB_OUTPUT | |
| fi | |
| - name: Build and push ${{ steps.meta.outputs.tags }} | |
| uses: docker/build-push-action@v5 | |
| if: matrix.docker | |
| with: | |
| context: . | |
| file: ./${{ matrix.path }}/Dockerfile | |
| push: ${{ env.ACT != 'true' }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha,scope=${{ matrix.name }} | |
| cache-to: type=gha,mode=max,scope=${{ matrix.name }} | |
| build-args: ${{ steps.render-build-args.outputs.build_args }} | |
| bump-version: | |
| needs: [build-and-push, prepare] | |
| if: ${{ needs.prepare.outputs.version_bump_type != '' }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.HEAD_SHA }} | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10.28.0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "22" | |
| cache: pnpm | |
| - name: Configure Git | |
| run: | | |
| git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git config --global user.name "github-actions[bot]" | |
| - name: Update version | |
| run: | | |
| # Enforce global version, in case of version drift by first bumping the root version... | |
| pnpm version ${{ needs.prepare.outputs.version_bump_type }} -r --no-commit-hooks --no-git-tag-version | |
| # And then explicitly setting that version for the entire workspace | |
| new_version=$(jq -r .version package.json) | |
| pnpm version "${new_version}" -r --no-commit-hooks --no-git-tag-version --include-workspace-root --allow-same-version | |
| git add $(git ls-files -m '*package*.json') | |
| git commit -m "chore: bump version to ${new_version} [skip ci]" | |
| git push --follow-tags |