# Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial # Please see LICENSE files in the repository root for full details. name: Build on: push: branches: - main - "release/**" tags: - "v*" # Run when there is a label change on the pull request # This runs only if the 'Z-Build-Workflow' is added to the pull request pull_request: types: [labeled] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: CARGO_TERM_COLOR: always CARGO_NET_GIT_FETCH_WITH_CLI: "true" SCCACHE_GHA_ENABLED: "true" RUSTC_WRAPPER: "sccache" IMAGE: ghcr.io/element-hq/matrix-authentication-service BUILDCACHE: ghcr.io/element-hq/matrix-authentication-service/buildcache DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index jobs: compute-version: name: Compute version using git describe if: github.event_name == 'push' || github.event.label.name == 'Z-Build-Workflow' runs-on: ubuntu-24.04 permissions: contents: read outputs: describe: ${{ steps.git.outputs.describe }} timestamp: ${{ steps.git.outputs.timestamp }} steps: - name: Checkout the code uses: actions/checkout@v5 with: # Need a full clone so that `git describe` reports the right version fetch-depth: 0 - name: Compute version and timestamp out of git history id: git run: | echo "describe=$(git describe --tags --match 'v*.*.*' --always)" >> $GITHUB_OUTPUT echo "timestamp=$(git log -1 --format=%ct)" >> $GITHUB_OUTPUT build-assets: name: Build assets if: github.event_name == 'push' || github.event.label.name == 'Z-Build-Workflow' runs-on: ubuntu-24.04 permissions: contents: read steps: - name: Checkout the code uses: actions/checkout@v5 - uses: ./.github/actions/build-frontend - uses: ./.github/actions/build-policies - name: Prepare assets artifact run: | mkdir -p assets-dist/share cp policies/policy.wasm assets-dist/share/policy.wasm cp frontend/dist/manifest.json assets-dist/share/manifest.json cp -r frontend/dist/ assets-dist/share/assets cp -r templates/ assets-dist/share/templates cp -r translations/ assets-dist/share/translations cp LICENSE assets-dist/LICENSE chmod -R u=rwX,go=rX assets-dist/ - name: Upload assets uses: actions/upload-artifact@v4.6.2 with: name: assets path: assets-dist build-binaries: name: Build binaries if: github.event_name == 'push' || github.event.label.name == 'Z-Build-Workflow' runs-on: ubuntu-24.04 needs: - compute-version strategy: matrix: include: - target: x86_64-unknown-linux-gnu - target: aarch64-unknown-linux-gnu env: VERGEN_GIT_DESCRIBE: ${{ needs.compute-version.outputs.describe }} SOURCE_DATE_EPOCH: ${{ needs.compute-version.outputs.timestamp }} permissions: contents: read steps: - name: Checkout the code uses: actions/checkout@v5 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: targets: | ${{ matrix.target }} - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 - name: Install zig uses: goto-bus-stop/setup-zig@v2 with: version: 0.13.0 - name: Install cargo-zigbuild uses: taiki-e/install-action@v2 with: tool: cargo-zigbuild - name: Build the binary run: | cargo zigbuild \ --release \ --target ${{ matrix.target }}.2.17 \ --no-default-features \ --features dist \ -p mas-cli - name: Upload binary artifact uses: actions/upload-artifact@v4.6.2 with: name: binary-${{ matrix.target }} path: target/${{ matrix.target }}/release/mas-cli assemble-archives: name: Assemble release archives if: github.event_name == 'push' || github.event.label.name == 'Z-Build-Workflow' runs-on: ubuntu-24.04 needs: - build-assets - build-binaries permissions: contents: read steps: - name: Download assets uses: actions/download-artifact@v5 with: name: assets path: assets-dist - name: Download binary x86_64 uses: actions/download-artifact@v5 with: name: binary-x86_64-unknown-linux-gnu path: binary-x86_64 - name: Download binary aarch64 uses: actions/download-artifact@v5 with: name: binary-aarch64-unknown-linux-gnu path: binary-aarch64 - name: Create final archives run: | for arch in x86_64 aarch64; do mkdir -p dist/${arch}/share cp -r assets-dist/share/* dist/${arch}/share/ cp assets-dist/LICENSE dist/${arch}/LICENSE cp binary-$arch/mas-cli dist/${arch}/mas-cli chmod -R u=rwX,go=rX dist/${arch}/ chmod u=rwx,go=rx dist/${arch}/mas-cli tar -czvf mas-cli-${arch}-linux.tar.gz --owner=0 --group=0 -C dist/${arch}/ . done - name: Upload aarch64 archive uses: actions/upload-artifact@v4.6.2 with: name: mas-cli-aarch64-linux path: mas-cli-aarch64-linux.tar.gz - name: Upload x86_64 archive uses: actions/upload-artifact@v4.6.2 with: name: mas-cli-x86_64-linux path: mas-cli-x86_64-linux.tar.gz build-image: name: Build and push Docker image if: github.event_name == 'push' || github.event.label.name == 'Z-Build-Workflow' runs-on: ubuntu-24.04 outputs: metadata: ${{ steps.output.outputs.metadata }} permissions: contents: read packages: write id-token: write needs: - compute-version env: VERGEN_GIT_DESCRIBE: ${{ needs.compute-version.outputs.describe }} SOURCE_DATE_EPOCH: ${{ needs.compute-version.outputs.timestamp }} steps: - name: Docker meta id: meta uses: docker/metadata-action@v5.8.0 with: images: "${{ env.IMAGE }}" bake-target: docker-metadata-action flavor: | latest=auto tags: | type=ref,event=branch type=ref,event=pr type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} type=sha - name: Docker meta (debug variant) id: meta-debug uses: docker/metadata-action@v5.8.0 with: images: "${{ env.IMAGE }}" bake-target: docker-metadata-action-debug flavor: | latest=auto suffix=-debug,onlatest=true tags: | type=ref,event=branch type=ref,event=pr type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} type=sha - name: Setup Cosign uses: sigstore/cosign-installer@v4.0.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3.11.1 with: buildkitd-config-inline: | [registry."docker.io"] mirrors = ["mirror.gcr.io"] - name: Login to GitHub Container Registry uses: docker/login-action@v3.6.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push id: bake uses: docker/bake-action@v6.9.0 with: files: | ./docker-bake.hcl cwd://${{ steps.meta.outputs.bake-file }} cwd://${{ steps.meta-debug.outputs.bake-file }} set: | base.output=type=image,push=true base.cache-from=type=registry,ref=${{ env.BUILDCACHE }}:buildcache base.cache-to=type=registry,ref=${{ env.BUILDCACHE }}:buildcache,mode=max - name: Transform bake output # This transforms the ouput to an object which looks like this: # { reguar: { digest: "…", tags: ["…", "…"] }, debug: { digest: "…", tags: ["…"] }, … } id: output run: | echo 'metadata<> $GITHUB_OUTPUT echo '${{ steps.bake.outputs.metadata }}' | jq -c 'with_entries(select(.value | (type == "object" and has("containerimage.digest")))) | map_values({ digest: .["containerimage.digest"], tags: (.["image.name"] | split(",")) })' >> $GITHUB_OUTPUT echo 'EOF' >> $GITHUB_OUTPUT - name: Sign the images with GitHub Actions provided token # Only sign on tags and on commits on main branch if: | github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') env: REGULAR_DIGEST: ${{ steps.output.outputs.metadata && fromJSON(steps.output.outputs.metadata).regular.digest }} DEBUG_DIGEST: ${{ steps.output.outputs.metadata && fromJSON(steps.output.outputs.metadata).debug.digest }} run: |- cosign sign --yes \ "$IMAGE@$REGULAR_DIGEST" \ "$IMAGE@$DEBUG_DIGEST" \ release: name: Release if: startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-24.04 needs: - assemble-archives - build-image steps: - name: Download the artifacts from the previous job uses: actions/download-artifact@v5 with: pattern: mas-cli-* path: artifacts merge-multiple: true - name: Prepare a release uses: softprops/action-gh-release@v2.4.1 with: generate_release_notes: true body: | ### Docker image Regular image: - Digest: ``` ${{ env.IMAGE }}@${{ fromJSON(needs.build-image.outputs.metadata).regular.digest }} ``` - Tags: ``` ${{ join(fromJSON(needs.build-image.outputs.metadata).regular.tags, ' ') }} ``` Debug variant: - Digest: ``` ${{ env.IMAGE }}@${{ fromJSON(needs.build-image.outputs.metadata).debug.digest }} ``` - Tags: ``` ${{ join(fromJSON(needs.build-image.outputs.metadata).debug.tags, ' ') }} ``` files: | artifacts/mas-cli-aarch64-linux.tar.gz artifacts/mas-cli-x86_64-linux.tar.gz draft: true unstable: name: Update the unstable release if: github.ref == 'refs/heads/main' runs-on: ubuntu-24.04 needs: - assemble-archives - build-image permissions: contents: write steps: - name: Checkout the code uses: actions/checkout@v5 with: sparse-checkout: | .github/scripts - name: Download the artifacts from the previous job uses: actions/download-artifact@v5 with: pattern: mas-cli-* path: artifacts merge-multiple: true - name: Update unstable git tag uses: actions/github-script@v8.0.0 with: script: | const script = require('./.github/scripts/update-unstable-tag.cjs'); await script({ core, github, context }); - name: Update unstable release uses: softprops/action-gh-release@v2.4.1 with: name: "Unstable build" tag_name: unstable body: | This is an automatically updated unstable release containing the latest builds from the main branch. **⚠️ Warning: These are development builds and may be unstable.** Last updated: ${{ github.event.head_commit.timestamp }} Commit: ${{ github.sha }} ### Docker image Regular image: - Digest: ``` ${{ env.IMAGE }}@${{ fromJSON(needs.build-image.outputs.metadata).regular.digest }} ``` - Tags: ``` ${{ join(fromJSON(needs.build-image.outputs.metadata).regular.tags, ' ') }} ``` Debug variant: - Digest: ``` ${{ env.IMAGE }}@${{ fromJSON(needs.build-image.outputs.metadata).debug.digest }} ``` - Tags: ``` ${{ join(fromJSON(needs.build-image.outputs.metadata).debug.tags, ' ') }} ``` files: | artifacts/mas-cli-aarch64-linux.tar.gz artifacts/mas-cli-x86_64-linux.tar.gz prerelease: true make_latest: false pr-cleanup: name: "Remove workflow build PR label and comment on it" runs-on: ubuntu-24.04 if: github.event_name == 'pull_request' && github.event.label.name == 'Z-Build-Workflow' needs: - build-image permissions: contents: read pull-requests: write steps: - name: Checkout the code uses: actions/checkout@v5 with: sparse-checkout: | .github/scripts - name: Remove label and comment uses: actions/github-script@v8.0.0 env: BUILD_IMAGE_MANIFEST: ${{ needs.build-image.outputs.metadata }} with: script: | const script = require('./.github/scripts/cleanup-pr.cjs'); await script({ core, github, context });