name: Release / Builds # Cancel in-progress runs when a new push is made to the same branch concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false # Don't cancel release builds on: push: paths-ignore: - "*.md" - "**/*.md" - ".gitlab-ci.yml" - ".gitignore" - "renovate.json" - "debian/**" - "docker/**" - "docs/**" # Allows you to run this workflow manually from the Actions tab workflow_dispatch: env: BUILTIN_REGISTRY_ENABLED: "${{ vars.BUILTIN_REGISTRY != '' && ((vars.BUILTIN_REGISTRY_USER && secrets.BUILTIN_REGISTRY_PASSWORD) || (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)) && 'true' || 'false' }}" jobs: prepare: name: Prepare Build Matrix 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: Define build matrix and registries uses: https://github.com/actions/github-script@v7 id: var with: script: | const githubRepo = '${{ github.repository }}'.toLowerCase() const repoId = githubRepo.split('/')[1] core.setOutput('github_repository', githubRepo) console.log('GitHub repository:', githubRepo) const registry = '${{ vars.BUILTIN_REGISTRY }}' console.log('Registry:', registry || '(not set)') const builtinImage = registry ? `${registry}/${githubRepo}` : '' console.log('Built-in image:', builtinImage || '(registry not configured)') let images = [] if (process.env.BUILTIN_REGISTRY_ENABLED === "true") { images.push(builtinImage) } console.log('Registry enabled:', process.env.BUILTIN_REGISTRY_ENABLED) console.log('Images:', images.length > 0 ? images : '(none)') core.setOutput('images', images.join("\n")) core.setOutput('images_list', images.join(",")) const platforms = ['linux/amd64', 'linux/arm64'] const buildMatrix = { platform: platforms, target_cpu: ['base'], include: platforms.map(platform => { return { platform, slug: platform.replace('/', '-') }}) } console.log('Build matrix:', JSON.stringify(buildMatrix, null, 2)) core.setOutput('build_matrix', JSON.stringify(buildMatrix)) build: name: Build Images & Binaries runs-on: dind needs: prepare permissions: contents: read packages: write attestations: write id-token: write strategy: matrix: { "target_cpu": ["base"], "profile": ["release"], "include": [ { "platform": "linux/amd64", "slug": "linux-amd64" }, { "platform": "linux/arm64", "slug": "linux-arm64" }, ], "platform": ["linux/amd64", "linux/arm64"], } steps: - name: Display build matrix run: | echo "Strategy: ${{ toJSON(fromJSON(needs.prepare.outputs.build_matrix)) }}" echo "Matrix: ${{ toJSON(matrix) }}" - name: Checkout repository uses: actions/checkout@v4 with: persist-credentials: false - name: Install Rust toolchain id: rust-toolchain uses: ./.forgejo/actions/rust-toolchain - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Login to container registry if: vars.BUILTIN_REGISTRY != '' uses: docker/login-action@v3 with: registry: ${{ vars.BUILTIN_REGISTRY }} username: ${{ vars.BUILTIN_REGISTRY_USER || github.actor }} password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }} - name: Extract Docker metadata id: meta uses: docker/metadata-action@v5 with: images: ${{needs.prepare.outputs.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 run: | calculatedSha=$(git rev-parse --short ${{ github.sha }}) echo "Short SHA: $calculatedSha (from full SHA: ${{ github.sha }})" echo "COMMIT_SHORT_SHA=$calculatedSha" >> $GITHUB_ENV - name: Get commit timestamp run: | timestamp=$(git log -1 --pretty=%ct) echo "Commit timestamp: $timestamp ($(date -d @$timestamp))" echo "TIMESTAMP=$timestamp" >> $GITHUB_ENV - uses: ./.forgejo/actions/timelord with: key: timelord-v0 path: . - name: Cache Rust registry uses: actions/cache@v3 with: path: | .cargo/registry .cargo/git key: rust-registry-${{ matrix.slug }}-${{ hashFiles('**/Cargo.lock') }} restore-keys: | rust-registry-${{ matrix.slug }}- rust-registry- - name: Cache cargo target id: cache-cargo-target uses: actions/cache@v3 with: path: | cargo-target-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }} key: cargo-target-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }}-${{ hashFiles('**/Cargo.lock', '**/Cargo.toml') }}-${{ steps.rust-toolchain.outputs.rustc_version }} restore-keys: | cargo-target-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }}-${{ hashFiles('**/Cargo.lock') }}- cargo-target-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }}- - name: Cache apt packages id: cache-apt uses: actions/cache@v3 with: path: | var-cache-apt-${{ matrix.slug }} var-lib-apt-${{ matrix.slug }} key: apt-${{ matrix.slug }}-${{ hashFiles('docker/Dockerfile') }} restore-keys: | apt-${{ matrix.slug }}- - name: Inject build cache uses: https://github.com/reproducible-containers/buildkit-cache-dance@v3.1.0 with: cache-map: | { ".cargo/registry": "/usr/local/cargo/registry", ".cargo/git/db": "/usr/local/cargo/git/db", "cargo-target-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }}": { "target": "/app/target", "id": "cargo-target-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }}" }, "var-cache-apt-${{ matrix.slug }}": "/var/cache/apt", "var-lib-apt-${{ matrix.slug }}": "/var/lib/apt" } skip-extraction: ${{ steps.cache.outputs.cache-hit }} - name: Build Docker image id: build uses: docker/build-push-action@v6 with: 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 }} platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} annotations: ${{ steps.meta.outputs.annotations }} cache-from: | type=registry,ref=${{ vars.BUILTIN_REGISTRY }}/${{ github.repository }}:buildcache-${{ matrix.slug }} type=registry,ref=${{ vars.BUILTIN_REGISTRY }}/${{ github.repository }}:buildcache cache-to: type=registry,ref=${{ vars.BUILTIN_REGISTRY }}/${{ github.repository }}:buildcache-${{ matrix.slug }},mode=max sbom: true outputs: type=image,"name=${{ needs.prepare.outputs.images_list }}",push-by-digest=true,name-canonical=true,push=true env: SOURCE_DATE_EPOCH: ${{ env.TIMESTAMP }} - name: Export image digest run: | mkdir -p /tmp/digests digest="${{ steps.build.outputs.digest }}" touch "/tmp/digests/${digest#sha256:}" - name: Create container from image id: extract-binary-image run: | mkdir -p /tmp/binaries digest="${{ steps.build.outputs.digest }}" echo "container_id=$(docker create --platform ${{ matrix.platform }} ${{ needs.prepare.outputs.images_list }}@$digest)" >> $GITHUB_OUTPUT - name: Extract binary from container run: docker cp ${{ steps.extract-binary-image.outputs.container_id }}:/sbin/conduwuit /tmp/binaries/conduwuit-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }} - name: Clean up container run: docker rm ${{ steps.extract-binary-image.outputs.container_id }} - name: Upload binary artifact uses: forgejo/upload-artifact@v4 with: name: conduwuit-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }} path: /tmp/binaries/conduwuit-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }} if-no-files-found: error - name: Upload digest uses: forgejo/upload-artifact@v4 with: name: digests-${{ matrix.slug }} path: /tmp/digests/* if-no-files-found: error retention-days: 5 publish: name: Publish Multi-platform Manifest runs-on: dind needs: [prepare, build] steps: - name: Download digests uses: forgejo/download-artifact@v4 with: path: /tmp/digests pattern: digests-* merge-multiple: true - name: Login to container registry if: vars.BUILTIN_REGISTRY != '' uses: docker/login-action@v3 with: registry: ${{ vars.BUILTIN_REGISTRY }} username: ${{ vars.BUILTIN_REGISTRY_USER || github.actor }} password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Extract Docker tags id: meta uses: docker/metadata-action@v5 with: tags: | type=semver,pattern={{version}},prefix=v type=semver,pattern={{major}}.{{minor}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.0.') }},prefix=v type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }},prefix=v type=ref,event=branch,prefix=${{ format('refs/heads/{0}', github.event.repository.default_branch) != github.ref && 'branch-' || '' }} type=ref,event=pr type=sha,format=long type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }} images: ${{needs.prepare.outputs.images}} # default labels & annotations: https://github.com/docker/metadata-action/blob/master/src/meta.ts#L509 env: DOCKER_METADATA_ANNOTATIONS_LEVELS: index - name: Create and push manifest working-directory: /tmp/digests env: IMAGES: ${{needs.prepare.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 env: IMAGES: ${{needs.prepare.outputs.images}} shell: bash run: | IMAGES_LIST=($IMAGES) for REPO in "${IMAGES_LIST[@]}"; do docker buildx imagetools inspect $REPO:${{ steps.meta.outputs.version }} done