mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2025-09-09 13:53:03 +02:00
Updates the timelord action to fall back to git-warp-time when the cache is completely empty, enabling timestamp restoration even on fresh builds. When git-warp-time is used, performs an unshallow fetch to get full history, while subsequent runs use normal fetches. Simplifies the interface by making inputs optional with sensible defaults. Adds binary caching for timelord-cli and git-warp-time tools to avoid repeated installations, and updates paths to use /usr/share/rust/.cargo/bin/ for the catthehacker runner image used by the dind profile (may need updating if/when switching to standard image). The main timelord restore now happens inside the Dockerfile itself, as Docker intentionally wipes all file mtimes on COPY/ADD operations.
342 lines
14 KiB
YAML
342 lines
14 KiB
YAML
name: Release Docker Image
|
|
concurrency:
|
|
group: "release-image-${{ github.ref }}"
|
|
|
|
on:
|
|
pull_request:
|
|
paths-ignore:
|
|
- "*.md"
|
|
- "**/*.md"
|
|
- ".gitlab-ci.yml"
|
|
- ".gitignore"
|
|
- "renovate.json"
|
|
- "pkg/**"
|
|
- "docs/**"
|
|
push:
|
|
branches:
|
|
- main
|
|
paths-ignore:
|
|
- "*.md"
|
|
- "**/*.md"
|
|
- ".gitlab-ci.yml"
|
|
- ".gitignore"
|
|
- "renovate.json"
|
|
- "pkg/**"
|
|
- "docs/**"
|
|
# Allows you to run this workflow manually from the Actions tab
|
|
workflow_dispatch:
|
|
|
|
env:
|
|
BUILTIN_REGISTRY: forgejo.ellis.link
|
|
BUILTIN_REGISTRY_ENABLED: "${{ ((vars.BUILTIN_REGISTRY_USER && secrets.BUILTIN_REGISTRY_PASSWORD) || (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)) && 'true' || 'false' }}"
|
|
|
|
jobs:
|
|
define-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
|
|
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)
|
|
const builtinImage = '${{ env.BUILTIN_REGISTRY }}/' + githubRepo
|
|
let images = []
|
|
if (process.env.BUILTIN_REGISTRY_ENABLED === "true") {
|
|
images.push(builtinImage)
|
|
} else {
|
|
// Fallback to official registry for forks/PRs without credentials
|
|
images.push('forgejo.ellis.link/continuwuation/continuwuity')
|
|
}
|
|
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:
|
|
runs-on: dind
|
|
needs: define-variables
|
|
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: 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@v4
|
|
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
|
|
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: Copy timelord cache to workspace
|
|
shell: bash
|
|
run: |
|
|
mkdir -p ./timelord
|
|
if [ -d "${{ steps.timelord.outputs.database-path }}" ]; then
|
|
cp -r "${{ steps.timelord.outputs.database-path }}"/* ./timelord/
|
|
echo "Copied timelord cache to workspace"
|
|
else
|
|
echo "No timelord cache to copy"
|
|
fi
|
|
|
|
- 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-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }}
|
|
key: cargo-target-${{ matrix.target_cpu }}-${{ 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-${{ 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 and push Docker image by digest
|
|
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 }}
|
|
CARGO_INCREMENTAL=${{ env.BUILDKIT_ENDPOINT != '' && '1' || '0' }}
|
|
platforms: ${{ matrix.platform }}
|
|
labels: ${{ steps.meta.outputs.labels }}
|
|
annotations: ${{ steps.meta.outputs.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:
|
|
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 }}"
|
|
touch "/tmp/digests/${digest#sha256:}"
|
|
|
|
# Binary extracted via local output for all builds
|
|
- name: Rename extracted binary
|
|
run: mv /tmp/binaries/sbin/conduwuit /tmp/binaries/conduwuit-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }}
|
|
|
|
- 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
|
|
if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }}
|
|
uses: forgejo/upload-artifact@v4
|
|
with:
|
|
name: digests-${{ matrix.slug }}
|
|
path: /tmp/digests/*
|
|
if-no-files-found: error
|
|
retention-days: 5
|
|
|
|
merge:
|
|
runs-on: dind
|
|
needs: [define-variables, build-image]
|
|
steps:
|
|
- name: Download digests
|
|
if: ${{ env.BUILTIN_REGISTRY_ENABLED == 'true' }}
|
|
uses: forgejo/download-artifact@v4
|
|
with:
|
|
path: /tmp/digests
|
|
pattern: digests-*
|
|
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
|
|
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
|
|
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
|
|
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.define-variables.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 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
|