From 542dff50bddd1a875457e6f1d7c77450cfdc1228 Mon Sep 17 00:00:00 2001 From: Tom Foster Date: Fri, 12 Sep 2025 10:45:06 +0100 Subject: [PATCH] ci: Split Docker builds into sequential release and max-perf stages Separate fast release builds from slow max-perf builds to optimise runner utilisation and provide quicker feedback. Release builds complete first with standard optimisations, followed by Haswell-optimised dragrace builds once the safe builds pass successfully. Extract build logic into focused composite actions for better log visibility in Forgejo UI. Split monolithic build action into prepare-docker-build, inline docker build step, and upload-docker-artifacts to ensure each phase completes independently and shows logs immediately. Creates separate manifests at each stage to avoid waiting for all builds before publishing. --- .../actions/create-docker-manifest/action.yml | 104 +++++ .../actions/prepare-docker-build/action.yml | 169 ++++++++ .../upload-docker-artifacts/action.yml | 70 ++++ .forgejo/workflows/release-image.yml | 367 ++++++------------ 4 files changed, 461 insertions(+), 249 deletions(-) create mode 100644 .forgejo/actions/create-docker-manifest/action.yml create mode 100644 .forgejo/actions/prepare-docker-build/action.yml create mode 100644 .forgejo/actions/upload-docker-artifacts/action.yml diff --git a/.forgejo/actions/create-docker-manifest/action.yml b/.forgejo/actions/create-docker-manifest/action.yml new file mode 100644 index 00000000..10c9953a --- /dev/null +++ b/.forgejo/actions/create-docker-manifest/action.yml @@ -0,0 +1,104 @@ +name: create-manifest +description: | + Create and push a multi-platform Docker manifest from individual platform digests. + Handles downloading digests, creating manifest lists, and pushing to registry. + +inputs: + digest_pattern: + description: Glob pattern to match digest artifacts (e.g. "digests-linux-{amd64,arm64}") + required: true + tag_suffix: + description: Suffix to add to all Docker tags (e.g. "-maxperf") + required: false + default: "" + images: + description: Container registry images (newline-separated) + required: true + registry_user: + description: Registry username for authentication + required: false + registry_password: + description: Registry password for authentication + required: false + +outputs: + version: + description: The version tag created for the manifest + value: ${{ steps.meta.outputs.version }} + tags: + description: All tags created for the manifest + value: ${{ steps.meta.outputs.tags }} + +runs: + using: composite + steps: + - name: Download digests + if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} + uses: forgejo/download-artifact@v4 + with: + path: /tmp/digests + pattern: ${{ inputs.digest_pattern }} + merge-multiple: true + + - name: Login to builtin registry + if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} + uses: docker/login-action@v3 + with: + registry: ${{ env.BUILTIN_REGISTRY }} + username: ${{ inputs.registry_user }} + password: ${{ inputs.registry_password }} + + - name: Set up Docker Buildx + if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} + uses: docker/setup-buildx-action@v3 + with: + # Use persistent BuildKit if BUILDKIT_ENDPOINT is set (e.g. tcp://buildkit:8125) + driver: ${{ env.BUILDKIT_ENDPOINT != '' && 'remote' || 'docker-container' }} + endpoint: ${{ env.BUILDKIT_ENDPOINT || '' }} + + - name: Extract metadata (tags) for Docker + if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} + id: meta + uses: docker/metadata-action@v5 + with: + tags: | + type=semver,pattern={{version}},prefix=v,suffix=${{ inputs.tag_suffix }} + type=semver,pattern={{major}}.{{minor}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.0.') }},prefix=v,suffix=${{ inputs.tag_suffix }} + type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }},prefix=v,suffix=${{ inputs.tag_suffix }} + type=ref,event=branch,prefix=${{ format('refs/heads/{0}', github.event.repository.default_branch) != github.ref && 'branch-' || '' }},suffix=${{ inputs.tag_suffix }} + type=ref,event=pr,suffix=${{ inputs.tag_suffix }} + type=sha,format=short,suffix=${{ inputs.tag_suffix }} + type=raw,value=latest${{ inputs.tag_suffix }},enable=${{ startsWith(github.ref, 'refs/tags/v') }} + images: ${{ inputs.images }} + # default labels & annotations: https://github.com/docker/metadata-action/blob/master/src/meta.ts#L509 + env: + DOCKER_METADATA_ANNOTATIONS_LEVELS: index + + - name: Create manifest list and push + if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} + working-directory: /tmp/digests + shell: bash + env: + IMAGES: ${{ inputs.images }} + run: | + IFS=$'\n' + IMAGES_LIST=($IMAGES) + ANNOTATIONS_LIST=($DOCKER_METADATA_OUTPUT_ANNOTATIONS) + TAGS_LIST=($DOCKER_METADATA_OUTPUT_TAGS) + for REPO in "${IMAGES_LIST[@]}"; do + docker buildx imagetools create \ + $(for tag in "${TAGS_LIST[@]}"; do echo "--tag"; echo "$tag"; done) \ + $(for annotation in "${ANNOTATIONS_LIST[@]}"; do echo "--annotation"; echo "$annotation"; done) \ + $(for reference in *; do printf "$REPO@sha256:%s\n" $reference; done) + done + + - name: Inspect image + if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} + shell: bash + env: + IMAGES: ${{ inputs.images }} + run: | + IMAGES_LIST=($IMAGES) + for REPO in "${IMAGES_LIST[@]}"; do + docker buildx imagetools inspect $REPO:${{ steps.meta.outputs.version }} + done diff --git a/.forgejo/actions/prepare-docker-build/action.yml b/.forgejo/actions/prepare-docker-build/action.yml new file mode 100644 index 00000000..6a397b8a --- /dev/null +++ b/.forgejo/actions/prepare-docker-build/action.yml @@ -0,0 +1,169 @@ +name: prepare-docker-build +description: | + Prepare the Docker build environment for Continuwuity builds. + Sets up Rust toolchain, Docker Buildx, caching, and extracts metadata for Docker builds. + +inputs: + platform: + description: Target platform (e.g. linux/amd64, linux/arm64) + required: true + slug: + description: Platform slug for artifact naming (e.g. linux-amd64, linux-arm64) + required: true + target_cpu: + description: Target CPU architecture (e.g. haswell, empty for base) + required: false + default: "" + profile: + description: Cargo build profile (release or release-max-perf) + required: true + images: + description: Container registry images (newline-separated) + required: true + registry_user: + description: Registry username for authentication + required: false + registry_password: + description: Registry password for authentication + required: false + +outputs: + cpu_suffix: + description: CPU suffix for artifact naming + value: ${{ steps.cpu-suffix.outputs.suffix }} + metadata_labels: + description: Docker labels for the image + value: ${{ steps.meta.outputs.labels }} + metadata_annotations: + description: Docker annotations for the image + value: ${{ steps.meta.outputs.annotations }} + +runs: + using: composite + steps: + - name: Set CPU suffix variable + id: cpu-suffix + shell: bash + run: | + if [[ -n "${{ inputs.target_cpu }}" ]]; then + echo "suffix=-${{ inputs.target_cpu }}" >> $GITHUB_OUTPUT + echo "CPU_SUFFIX=-${{ inputs.target_cpu }}" >> $GITHUB_ENV + else + echo "suffix=" >> $GITHUB_OUTPUT + echo "CPU_SUFFIX=" >> $GITHUB_ENV + fi + + - name: Echo matrix configuration + shell: bash + run: | + echo "Platform: ${{ inputs.platform }}" + echo "Slug: ${{ inputs.slug }}" + echo "Target CPU: ${{ inputs.target_cpu }}" + echo "Profile: ${{ inputs.profile }}" + + - name: Install rust + if: ${{ env.BUILDKIT_ENDPOINT == '' }} + id: rust-toolchain + uses: ./.forgejo/actions/rust-toolchain + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + # Use persistent BuildKit if BUILDKIT_ENDPOINT is set (e.g. tcp://buildkit:8125) + driver: ${{ env.BUILDKIT_ENDPOINT != '' && 'remote' || 'docker-container' }} + endpoint: ${{ env.BUILDKIT_ENDPOINT || '' }} + + - name: Set up QEMU + if: ${{ env.BUILDKIT_ENDPOINT == '' }} + uses: docker/setup-qemu-action@v3 + + - name: Login to builtin registry + if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} + uses: docker/login-action@v3 + with: + registry: ${{ env.BUILTIN_REGISTRY }} + username: ${{ inputs.registry_user }} + password: ${{ inputs.registry_password }} + + - name: Extract metadata (labels, annotations) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ inputs.images }} + # default labels & annotations: https://github.com/docker/metadata-action/blob/master/src/meta.ts#L509 + env: + DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index + + - name: Get short git commit SHA + id: sha + shell: bash + run: | + calculatedSha=$(git rev-parse --short ${{ github.sha }}) + echo "COMMIT_SHORT_SHA=$calculatedSha" >> $GITHUB_ENV + echo "Short SHA: $calculatedSha" + + - name: Get Git commit timestamps + shell: bash + run: | + timestamp=$(git log -1 --pretty=%ct) + echo "TIMESTAMP=$timestamp" >> $GITHUB_ENV + echo "Commit timestamp: $timestamp" + + - uses: ./.forgejo/actions/timelord + id: timelord + + - name: Cache Rust registry + if: ${{ env.BUILDKIT_ENDPOINT == '' }} + uses: actions/cache@v3 + with: + path: | + .cargo/git + .cargo/git/checkouts + .cargo/registry + .cargo/registry/src + key: rust-registry-image-${{hashFiles('**/Cargo.lock') }} + + - name: Cache cargo target + if: ${{ env.BUILDKIT_ENDPOINT == '' }} + id: cache-cargo-target + uses: actions/cache@v3 + with: + path: | + cargo-target${{ env.CPU_SUFFIX }}-${{ inputs.slug }}-${{ inputs.profile }} + key: cargo-target${{ env.CPU_SUFFIX }}-${{ inputs.slug }}-${{ inputs.profile }}-${{hashFiles('**/Cargo.lock') }}-${{steps.rust-toolchain.outputs.rustc_version}} + + - name: Cache apt cache + if: ${{ env.BUILDKIT_ENDPOINT == '' }} + id: cache-apt + uses: actions/cache@v3 + with: + path: | + var-cache-apt-${{ inputs.slug }} + key: var-cache-apt-${{ inputs.slug }} + + - name: Cache apt lib + if: ${{ env.BUILDKIT_ENDPOINT == '' }} + id: cache-apt-lib + uses: actions/cache@v3 + with: + path: | + var-lib-apt-${{ inputs.slug }} + key: var-lib-apt-${{ inputs.slug }} + + - name: inject cache into docker + if: ${{ env.BUILDKIT_ENDPOINT == '' }} + uses: https://github.com/reproducible-containers/buildkit-cache-dance@v3.3.0 + with: + cache-map: | + { + ".cargo/registry": "/usr/local/cargo/registry", + ".cargo/git/db": "/usr/local/cargo/git/db", + "cargo-target${{ env.CPU_SUFFIX }}-${{ inputs.slug }}-${{ inputs.profile }}": { + "target": "/app/target", + "id": "cargo-target${{ env.CPU_SUFFIX }}-${{ inputs.slug }}-${{ inputs.profile }}" + }, + "var-cache-apt-${{ inputs.slug }}": "/var/cache/apt", + "var-lib-apt-${{ inputs.slug }}": "/var/lib/apt", + "${{ steps.timelord.outputs.database-path }}":"/timelord" + } + skip-extraction: ${{ steps.cache.outputs.cache-hit }} diff --git a/.forgejo/actions/upload-docker-artifacts/action.yml b/.forgejo/actions/upload-docker-artifacts/action.yml new file mode 100644 index 00000000..ac91e31a --- /dev/null +++ b/.forgejo/actions/upload-docker-artifacts/action.yml @@ -0,0 +1,70 @@ +name: upload-docker-artifacts +description: | + Upload Docker build artifacts including binary and digest files. + Handles artifact naming and conditional digest uploads for registry publishing. + +inputs: + slug: + description: Platform slug for artifact naming (e.g. linux-amd64, linux-arm64) + required: true + cpu_suffix: + description: CPU suffix for artifact naming (e.g. -haswell) + required: false + default: "" + artifact_suffix: + description: Suffix for binary artifacts (e.g. -maxperf) + required: false + default: "" + digest_suffix: + description: Suffix for digest artifacts (e.g. -maxperf) + required: false + default: "" + digest: + description: The digest of the built Docker image + required: true + +outputs: + binary_artifact_name: + description: The name of the uploaded binary artifact + value: conduwuit${{ inputs.cpu_suffix }}-${{ inputs.slug }}${{ inputs.artifact_suffix }} + +runs: + using: composite + steps: + - name: Export digest + if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} + shell: bash + run: | + mkdir -p /tmp/digests + digest="${{ inputs.digest }}" + echo "🔍 Build step digest output: '$digest'" + if [[ -z "$digest" ]]; then + echo "❌ ERROR: No digest found from build step" + exit 1 + fi + digest_file="/tmp/digests/${digest#sha256:}" + echo "📁 Creating digest file: $digest_file" + touch "$digest_file" + echo "✅ Digest file created successfully" + echo "📋 Contents of /tmp/digests:" + ls -la /tmp/digests/ + + - name: Rename extracted binary + shell: bash + run: mv /tmp/binaries/sbin/conduwuit /tmp/binaries/conduwuit${{ inputs.cpu_suffix }}-${{ inputs.slug }}${{ inputs.artifact_suffix }} + + - name: Upload binary artifact + uses: forgejo/upload-artifact@v4 + with: + name: conduwuit${{ inputs.cpu_suffix }}-${{ inputs.slug }}${{ inputs.artifact_suffix }} + path: /tmp/binaries/conduwuit${{ inputs.cpu_suffix }}-${{ inputs.slug }}${{ inputs.artifact_suffix }} + if-no-files-found: error + + - name: Upload digest + if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} + uses: forgejo/upload-artifact@v4 + with: + name: digests${{ inputs.digest_suffix }}-${{ inputs.slug }}${{ inputs.cpu_suffix }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 5 diff --git a/.forgejo/workflows/release-image.yml b/.forgejo/workflows/release-image.yml index b35195bf..c7accff3 100644 --- a/.forgejo/workflows/release-image.yml +++ b/.forgejo/workflows/release-image.yml @@ -32,12 +32,12 @@ env: jobs: define-variables: + name: "Setup Variables" runs-on: ubuntu-latest outputs: images: ${{ steps.var.outputs.images }} images_list: ${{ steps.var.outputs.images_list }} - build_matrix: ${{ steps.var.outputs.build_matrix }} steps: - name: Setting variables @@ -59,18 +59,9 @@ jobs: } core.setOutput('images', images.join("\n")) core.setOutput('images_list', images.join(",")) - const platforms = ['linux/amd64', 'linux/arm64'] - core.setOutput('build_matrix', JSON.stringify({ - platform: platforms, - target_cpu: ['base'], - include: platforms.map(platform => { return { - platform, - slug: platform.replace('/', '-') - }}) - })) - build-image: - name: "Build ${{ matrix.slug }}${{ matrix.target_cpu != '' && format('-{0}', matrix.target_cpu) || '' }}" + build-release: + name: "Build ${{ matrix.slug }} (release)" runs-on: dind needs: define-variables permissions: @@ -83,139 +74,25 @@ jobs: include: - platform: "linux/amd64" slug: "linux-amd64" - target_cpu: "" - profile: "release" - - platform: "linux/amd64" - slug: "linux-amd64" - target_cpu: "haswell" - profile: "release-max-perf" - platform: "linux/arm64" slug: "linux-arm64" - target_cpu: "" - profile: "release" steps: - - name: Set CPU suffix variable - run: | - if [[ -n "${{ matrix.target_cpu }}" ]]; then - echo "CPU_SUFFIX=-${{ matrix.target_cpu }}" >> $GITHUB_ENV - else - echo "CPU_SUFFIX=" >> $GITHUB_ENV - fi - - - name: Echo strategy - run: echo '${{ toJSON(fromJSON(needs.define-variables.outputs.build_matrix)) }}' - - name: Echo matrix - run: echo '${{ toJSON(matrix) }}' - - name: Checkout repository uses: actions/checkout@v5 with: persist-credentials: false - - name: Install rust - if: ${{ env.BUILDKIT_ENDPOINT == '' }} - id: rust-toolchain - uses: ./.forgejo/actions/rust-toolchain - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + - name: Prepare Docker build environment + id: prepare + uses: ./.forgejo/actions/prepare-docker-build with: - # Use persistent BuildKit if BUILDKIT_ENDPOINT is set (e.g. tcp://buildkit:8125) - driver: ${{ env.BUILDKIT_ENDPOINT != '' && 'remote' || 'docker-container' }} - endpoint: ${{ env.BUILDKIT_ENDPOINT || '' }} - - name: Set up QEMU - if: ${{ env.BUILDKIT_ENDPOINT == '' }} - uses: docker/setup-qemu-action@v3 - # Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here. - - name: Login to builtin registry - if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} - uses: docker/login-action@v3 - with: - registry: ${{ env.BUILTIN_REGISTRY }} - username: ${{ vars.BUILTIN_REGISTRY_USER || github.actor }} - password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }} - - # This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels. - - name: Extract metadata (labels, annotations) for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{needs.define-variables.outputs.images}} - # default labels & annotations: https://github.com/docker/metadata-action/blob/master/src/meta.ts#L509 - env: - DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index - - # This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages. - # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository. - # It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step. - # It will not push images generated from a pull request - - name: Get short git commit SHA - id: sha - run: | - calculatedSha=$(git rev-parse --short ${{ github.sha }}) - echo "COMMIT_SHORT_SHA=$calculatedSha" >> $GITHUB_ENV - echo "Short SHA: $calculatedSha" - - name: Get Git commit timestamps - run: | - timestamp=$(git log -1 --pretty=%ct) - echo "TIMESTAMP=$timestamp" >> $GITHUB_ENV - echo "Commit timestamp: $timestamp" - - - uses: ./.forgejo/actions/timelord - id: timelord - - - name: Cache Rust registry - if: ${{ env.BUILDKIT_ENDPOINT == '' }} - uses: actions/cache@v3 - with: - path: | - .cargo/git - .cargo/git/checkouts - .cargo/registry - .cargo/registry/src - key: rust-registry-image-${{hashFiles('**/Cargo.lock') }} - - name: Cache cargo target - if: ${{ env.BUILDKIT_ENDPOINT == '' }} - id: cache-cargo-target - uses: actions/cache@v3 - with: - path: | - cargo-target${{ env.CPU_SUFFIX }}-${{ matrix.slug }}-${{ matrix.profile }} - key: cargo-target${{ env.CPU_SUFFIX }}-${{ matrix.slug }}-${{ matrix.profile }}-${{hashFiles('**/Cargo.lock') }}-${{steps.rust-toolchain.outputs.rustc_version}} - - name: Cache apt cache - if: ${{ env.BUILDKIT_ENDPOINT == '' }} - id: cache-apt - uses: actions/cache@v3 - with: - path: | - var-cache-apt-${{ matrix.slug }} - key: var-cache-apt-${{ matrix.slug }} - - name: Cache apt lib - if: ${{ env.BUILDKIT_ENDPOINT == '' }} - id: cache-apt-lib - uses: actions/cache@v3 - with: - path: | - var-lib-apt-${{ matrix.slug }} - key: var-lib-apt-${{ matrix.slug }} - - name: inject cache into docker - if: ${{ env.BUILDKIT_ENDPOINT == '' }} - uses: https://github.com/reproducible-containers/buildkit-cache-dance@v3.3.0 - with: - cache-map: | - { - ".cargo/registry": "/usr/local/cargo/registry", - ".cargo/git/db": "/usr/local/cargo/git/db", - "cargo-target${{ env.CPU_SUFFIX }}-${{ matrix.slug }}-${{ matrix.profile }}": { - "target": "/app/target", - "id": "cargo-target${{ env.CPU_SUFFIX }}-${{ matrix.slug }}-${{ matrix.profile }}" - }, - "var-cache-apt-${{ matrix.slug }}": "/var/cache/apt", - "var-lib-apt-${{ matrix.slug }}": "/var/lib/apt", - "{{ steps.timelord.outputs.database-path }}":"/timelord" - } - skip-extraction: ${{ steps.cache.outputs.cache-hit }} - + platform: ${{ matrix.platform }} + slug: ${{ matrix.slug }} + target_cpu: "" + profile: "release" + images: ${{ needs.define-variables.outputs.images }} + registry_user: ${{ vars.BUILTIN_REGISTRY_USER || github.actor }} + registry_password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }} - name: Build and push Docker image by digest id: build uses: docker/build-push-action@v6 @@ -228,11 +105,11 @@ jobs: GIT_REMOTE_URL=${{github.event.repository.html_url }} GIT_REMOTE_COMMIT_URL=${{github.event.head_commit.url }} CARGO_INCREMENTAL=${{ env.BUILDKIT_ENDPOINT != '' && '1' || '0' }} - TARGET_CPU=${{ matrix.target_cpu }} - RUST_PROFILE=${{ matrix.profile }} + TARGET_CPU= + RUST_PROFILE=release platforms: ${{ matrix.platform }} - labels: ${{ steps.meta.outputs.labels }} - annotations: ${{ steps.meta.outputs.annotations }} + labels: ${{ steps.prepare.outputs.metadata_labels }} + annotations: ${{ steps.prepare.outputs.metadata_annotations }} cache-from: type=gha # cache-to: type=gha,mode=max sbom: true @@ -241,124 +118,116 @@ jobs: type=local,dest=/tmp/binaries env: SOURCE_DATE_EPOCH: ${{ env.TIMESTAMP }} - - # For publishing multi-platform manifests - - name: Export digest - if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} - run: | - mkdir -p /tmp/digests - digest="${{ steps.build.outputs.digest }}" - echo "🔍 Build step digest output: '$digest'" - if [[ -z "$digest" ]]; then - echo "❌ ERROR: No digest found from build step" - exit 1 - fi - digest_file="/tmp/digests/${digest#sha256:}" - echo "📁 Creating digest file: $digest_file" - touch "$digest_file" - echo "✅ Digest file created successfully" - echo "📋 Contents of /tmp/digests:" - ls -la /tmp/digests/ - - # Binary extracted via local output for all builds - - name: Rename extracted binary - run: mv /tmp/binaries/sbin/conduwuit /tmp/binaries/conduwuit${{ env.CPU_SUFFIX }}-${{ matrix.slug }}-${{ matrix.profile }} - - - name: Upload binary artifact - uses: forgejo/upload-artifact@v4 + - name: Upload Docker artifacts + uses: ./.forgejo/actions/upload-docker-artifacts with: - name: conduwuit${{ env.CPU_SUFFIX }}-${{ matrix.slug }}-${{ matrix.profile }} - path: /tmp/binaries/conduwuit${{ env.CPU_SUFFIX }}-${{ matrix.slug }}-${{ matrix.profile }} - if-no-files-found: error + slug: ${{ matrix.slug }} + cpu_suffix: ${{ steps.prepare.outputs.cpu_suffix }} + artifact_suffix: "" + digest_suffix: "" + digest: ${{ steps.build.outputs.digest }} - - name: Upload digest - if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} - uses: forgejo/upload-artifact@v4 - with: - name: digests-${{ matrix.slug }}${{ env.CPU_SUFFIX }} - path: /tmp/digests/* - if-no-files-found: error - retention-days: 5 - - merge: - name: "Create ${{ matrix.target_cpu == '' && 'Multi-arch' || 'Haswell' }} Manifest" + merge-release: + name: "Create Multi-arch Release Manifest" runs-on: dind - needs: [define-variables, build-image] + needs: [define-variables, build-release] + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + persist-credentials: false + - name: Create multi-platform manifest + uses: ./.forgejo/actions/create-docker-manifest + with: + digest_pattern: "digests-linux-{amd64,arm64}" + tag_suffix: "" + images: ${{ needs.define-variables.outputs.images }} + registry_user: ${{ vars.BUILTIN_REGISTRY_USER || github.actor }} + registry_password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }} + + build-maxperf: + name: "Build ${{ matrix.slug }} (max-perf)" + runs-on: dind + needs: [define-variables, build-release] + permissions: + contents: read + packages: write + attestations: write + id-token: write strategy: matrix: include: - - target_cpu: "" - digest_pattern: "digests-linux-{amd64,arm64}" - - target_cpu: "haswell" - digest_pattern: "digests-linux-amd64-haswell" + - platform: "linux/amd64" + slug: "linux-amd64" + target_cpu: "haswell" + - platform: "linux/arm64" + slug: "linux-arm64" + target_cpu: "" + steps: - - name: Download digests - if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} - uses: forgejo/download-artifact@v4 + - name: Checkout repository + uses: actions/checkout@v5 with: - path: /tmp/digests - pattern: ${{ matrix.digest_pattern }} - merge-multiple: true - # Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here. - - name: Login to builtin registry - if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} - uses: docker/login-action@v3 + persist-credentials: false + - name: Prepare max-perf Docker build environment + id: prepare + uses: ./.forgejo/actions/prepare-docker-build with: - registry: ${{ env.BUILTIN_REGISTRY }} - username: ${{ vars.BUILTIN_REGISTRY_USER || github.actor }} - password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }} - - - name: Set up Docker Buildx - if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} - uses: docker/setup-buildx-action@v3 + platform: ${{ matrix.platform }} + slug: ${{ matrix.slug }} + target_cpu: ${{ matrix.target_cpu }} + profile: "release-max-perf" + images: ${{ needs.define-variables.outputs.images }} + registry_user: ${{ vars.BUILTIN_REGISTRY_USER || github.actor }} + registry_password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }} + - name: Build and push max-perf Docker image by digest + id: build + uses: docker/build-push-action@v6 with: - # Use persistent BuildKit if BUILDKIT_ENDPOINT is set (e.g. tcp://buildkit:8125) - driver: ${{ env.BUILDKIT_ENDPOINT != '' && 'remote' || 'docker-container' }} - endpoint: ${{ env.BUILDKIT_ENDPOINT || '' }} - - - name: Extract metadata (tags) for Docker - if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} - id: meta - uses: docker/metadata-action@v5 - with: - tags: | - type=semver,pattern={{version}},prefix=v,suffix=${{ matrix.target_cpu != '' && format('-{0}', matrix.target_cpu) || '' }} - type=semver,pattern={{major}}.{{minor}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.0.') }},prefix=v,suffix=${{ matrix.target_cpu != '' && format('-{0}', matrix.target_cpu) || '' }} - type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }},prefix=v,suffix=${{ matrix.target_cpu != '' && format('-{0}', matrix.target_cpu) || '' }} - type=ref,event=branch,prefix=${{ format('refs/heads/{0}', github.event.repository.default_branch) != github.ref && 'branch-' || '' }},suffix=${{ matrix.target_cpu != '' && format('-{0}', matrix.target_cpu) || '' }} - type=ref,event=pr,suffix=${{ matrix.target_cpu != '' && format('-{0}', matrix.target_cpu) || '' }} - type=sha,format=short,suffix=${{ matrix.target_cpu != '' && format('-{0}', matrix.target_cpu) || '' }} - type=raw,value=latest${{ matrix.target_cpu != '' && format('-{0}', matrix.target_cpu) || '' }},enable=${{ startsWith(github.ref, 'refs/tags/v') }} - images: ${{needs.define-variables.outputs.images}} - # default labels & annotations: https://github.com/docker/metadata-action/blob/master/src/meta.ts#L509 + context: . + file: "docker/Dockerfile" + build-args: | + GIT_COMMIT_HASH=${{ github.sha }} + GIT_COMMIT_HASH_SHORT=${{ env.COMMIT_SHORT_SHA }} + GIT_REMOTE_URL=${{github.event.repository.html_url }} + GIT_REMOTE_COMMIT_URL=${{github.event.head_commit.url }} + CARGO_INCREMENTAL=${{ env.BUILDKIT_ENDPOINT != '' && '1' || '0' }} + TARGET_CPU=${{ matrix.target_cpu }} + RUST_PROFILE=release-max-perf + platforms: ${{ matrix.platform }} + labels: ${{ steps.prepare.outputs.metadata_labels }} + annotations: ${{ steps.prepare.outputs.metadata_annotations }} + cache-from: type=gha + # cache-to: type=gha,mode=max + sbom: true + outputs: | + ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' && format('type=image,"name={0}",push-by-digest=true,name-canonical=true,push=true', needs.define-variables.outputs.images_list) || format('type=image,"name={0}",push=false', needs.define-variables.outputs.images_list) }} + type=local,dest=/tmp/binaries env: - DOCKER_METADATA_ANNOTATIONS_LEVELS: index + SOURCE_DATE_EPOCH: ${{ env.TIMESTAMP }} + - name: Upload max-perf Docker artifacts + uses: ./.forgejo/actions/upload-docker-artifacts + with: + slug: ${{ matrix.slug }} + cpu_suffix: ${{ steps.prepare.outputs.cpu_suffix }} + artifact_suffix: "-maxperf" + digest_suffix: "-maxperf" + digest: ${{ steps.build.outputs.digest }} - - name: Create manifest list and push - if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} - working-directory: /tmp/digests - env: - IMAGES: ${{needs.define-variables.outputs.images}} - shell: bash - run: | - IFS=$'\n' - IMAGES_LIST=($IMAGES) - ANNOTATIONS_LIST=($DOCKER_METADATA_OUTPUT_ANNOTATIONS) - TAGS_LIST=($DOCKER_METADATA_OUTPUT_TAGS) - for REPO in "${IMAGES_LIST[@]}"; do - docker buildx imagetools create \ - $(for tag in "${TAGS_LIST[@]}"; do echo "--tag"; echo "$tag"; done) \ - $(for annotation in "${ANNOTATIONS_LIST[@]}"; do echo "--annotation"; echo "$annotation"; done) \ - $(for reference in *; do printf "$REPO@sha256:%s\n" $reference; done) - done - - - name: Inspect image - if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }} - env: - IMAGES: ${{needs.define-variables.outputs.images}} - shell: bash - run: | - IMAGES_LIST=($IMAGES) - for REPO in "${IMAGES_LIST[@]}"; do - docker buildx imagetools inspect $REPO:${{ steps.meta.outputs.version }} - done + merge-maxperf: + name: "Create Max-Perf Manifest" + runs-on: dind + needs: [define-variables, build-maxperf] + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + persist-credentials: false + - name: Create max-perf manifest + uses: ./.forgejo/actions/create-docker-manifest + with: + digest_pattern: "digests-maxperf-linux-{amd64-haswell,arm64}" + tag_suffix: "-maxperf" + images: ${{ needs.define-variables.outputs.images }} + registry_user: ${{ vars.BUILTIN_REGISTRY_USER || github.actor }} + registry_password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }}