name: Build Cronicle image on: workflow_dispatch: # Nightly upstream check. 13:00 UTC / 6AM Pacific, deliberately offset from # aws-zig's 12:30 nightly because the runner has capacity: 1. schedule: - cron: '0 13 * * *' push: branches: - master env: # Hardcoded rather than derived from ${{ github.repository }}: the packaging # repo is cronicle-docker, but the image it produces is plain Cronicle. IMAGE: git.lerch.org/lobo/cronicle jobs: build: runs-on: ubuntu-latest container: image: ghcr.io/catthehacker/ubuntu:act-22.04 steps: - name: Check out repository code uses: actions/checkout@v4 # Upstream publishes a GitHub Release per tag, so releases/latest is the # authoritative "newest stable" pointer. Cronicle is marked # "private": true in package.json, so npm is not an option -- the # Dockerfile builds from the release tarball. # # NOTE: every `run:` block here is POSIX sh. The runner executes these # with dash, which has no `set -o pipefail`. Rather than depend on shell # selection, the fetch and the parse are separate statements: under # `set -e`, an assignment from a failing command substitution aborts, so # `curl -f` failing is caught without needing pipefail. - name: Resolve latest upstream Cronicle version id: upstream run: | set -eu release_json="$(curl -fsSL https://api.github.com/repos/jhuckaby/Cronicle/releases/latest)" version="$(printf '%s' "$release_json" | jq -r '.tag_name // empty' | sed 's/^v//')" if [ -z "$version" ]; then echo "Could not resolve upstream Cronicle version" >&2 exit 1 fi echo "version=$version" >> "$GITHUB_OUTPUT" echo "Upstream latest release: v$version" - name: Compute image tags id: tags run: | set -eu shortsha="$(git rev-parse --short HEAD)" echo "shortsha=$shortsha" >> "$GITHUB_OUTPUT" echo "immutable=${{ steps.upstream.outputs.version }}-$shortsha" >> "$GITHUB_OUTPUT" - name: Login to Gitea uses: docker/login-action@v3 with: registry: git.lerch.org username: ${{ github.actor }} password: ${{ secrets.PACKAGE_PUSH }} # Set up before the existence check below, which uses buildx. - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 # The immutable tag encodes BOTH the upstream version and the packaging # commit, so this single check covers both reasons to rebuild: a new # Cronicle release, or a change to this repo. Nightly runs that find # nothing new become a no-op with no state file to maintain. # # `buildx imagetools inspect` rather than `docker manifest inspect`: # Forgejo's registry serves OCI media types # (application/vnd.oci.image.manifest.v1+json) and authenticates via the # Bearer token flow. imagetools handles both natively, whereas # `docker manifest inspect` fails outright on OCI manifests with older # docker CLIs ("unsupported manifest media type and no default # available"). imagetools reuses the credentials login-action just wrote. - name: Skip if this exact image was already built id: check run: | set -u tag="${IMAGE}:${{ steps.tags.outputs.immutable }}" if docker buildx imagetools inspect "$tag" >/dev/null 2>&1; then echo "exists=true" >> "$GITHUB_OUTPUT" echo "$tag is already published; nothing to do." else echo "exists=false" >> "$GITHUB_OUTPUT" echo "$tag not found; building." fi # Three tags: one immutable (what deployments should pin), two moving. - name: Build and push if: steps.check.outputs.exists == 'false' uses: docker/build-push-action@v6 with: context: . push: true build-args: | CRONICLE_VERSION=${{ steps.upstream.outputs.version }} tags: | ${{ env.IMAGE }}:${{ steps.tags.outputs.immutable }} ${{ env.IMAGE }}:${{ steps.upstream.outputs.version }} ${{ env.IMAGE }}:latest # Only ping when a build was actually attempted -- a nightly "nothing to # do" notification 365 times a year is noise. Uses != 'true' rather than # == 'false' so that a failure *before* the check step still notifies. - name: Notify uses: https://git.lerch.org/lobo/action-notify-ntfy@v2 if: always() && steps.check.outputs.exists != 'true' with: host: ${{ secrets.NTFY_HOST }} topic: ${{ secrets.NTFY_TOPIC }} status: ${{ job.status }} user: ${{ secrets.NTFY_USER }} password: ${{ secrets.NTFY_PASSWORD }}