All checks were successful
container / vibe-kanban-remote (push) Successful in 4m31s
The build lived in lair/containers, which is for third-party images built
from someone else's source. We own this fork now, so it belongs next to
the code it builds: a source change and the image it produces become one
commit rather than two repos that can drift.
Differences from the lair/containers job it replaces:
- Builds the checked-out tree instead of resolving a tag from the mirror
and building `<mirror-url>#<tag>`. There is no upstream release to track
any more; what is on main is what ships.
- Publishes an immutable ${version}-g${sha} tag alongside :latest, and
prints the pin. A floating tag decides which build you are running based
on whenever something last pulled, which is not a deployment strategy.
- Drops the `skopeo inspect` already-published guard. skopeo is not
installed on the metal runner, so that check silently failed and always
fell through to a build -- an accident, not a policy. Building every
push to main is the intent here anyway.
- Adds a guard that greps the built bundle for the sunset page and refuses
to publish if it reappears. This fork exists to remove it; a bad merge
should fail the build, not ship a dead product. Verified against the
currently-deployed image, where it correctly fires.
concurrency keeps two builds from racing over :latest.
111 lines
4.2 KiB
YAML
111 lines
4.2 KiB
YAML
name: container
|
|
# Build the self-hosted remote-server image from THIS repo and publish it to the
|
|
# Gitea registry as git.lair.cafe/lair/vibe-kanban-remote.
|
|
#
|
|
# This build used to live in lair/containers, which is for *third-party* images
|
|
# built from someone else's source. We own this fork now, so the build belongs
|
|
# next to the code it builds — a source change and its image are one commit.
|
|
#
|
|
# vibe-kanban ships its own Dockerfile (crates/remote/Dockerfile, context = repo
|
|
# root), so there is no vendored Containerfile here. Nothing in this workflow
|
|
# reaches out to GitHub: upstream is sunset and may disappear, and a build that
|
|
# asks github.com what to build would disappear with it.
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- "crates/**"
|
|
- "packages/**"
|
|
- "shared/**"
|
|
- "assets/**"
|
|
- "patches/**"
|
|
- "Cargo.toml"
|
|
- "Cargo.lock"
|
|
- "package.json"
|
|
- "pnpm-lock.yaml"
|
|
- "pnpm-workspace.yaml"
|
|
- "rust-toolchain.toml"
|
|
- ".gitea/workflows/container.yml"
|
|
workflow_dispatch:
|
|
|
|
# Never let two builds race: they would fight over the :latest tag and the
|
|
# loser's digest would win at random.
|
|
concurrency:
|
|
group: container
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
vibe-kanban-remote:
|
|
runs-on:
|
|
- metal
|
|
- podman
|
|
# The Rust workspace builds in release mode from a cold cache on a fresh
|
|
# runner; the default job timeout is not enough headroom.
|
|
timeout-minutes: 120
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: derive image tags
|
|
id: meta
|
|
run: |
|
|
set -euo pipefail
|
|
version=$(jq -r .version package.json)
|
|
short=$(git rev-parse --short HEAD)
|
|
# Immutable tag first. :latest is a convenience for humans, never a
|
|
# deployment target -- a floating tag decides which build you are
|
|
# running based on whenever something last pulled.
|
|
echo "version=${version}" >> "$GITHUB_OUTPUT"
|
|
echo "immutable=${version}-g${short}" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: login to registry
|
|
run: podman login -u ${{ gitea.actor }} -p ${{ secrets.REGISTRY_TOKEN }} git.lair.cafe
|
|
|
|
- name: build
|
|
env:
|
|
IMMUTABLE: ${{ steps.meta.outputs.immutable }}
|
|
run: |
|
|
set -euo pipefail
|
|
IMAGE=git.lair.cafe/lair/vibe-kanban-remote
|
|
# FEATURES is deliberately unset: the Dockerfile strips the private
|
|
# billing crate only when it is empty (the self-host path), and we have
|
|
# no access to BloopAI/vibe-kanban-private.
|
|
# VITE_RELAY_API_BASE_URL is baked into the SPA at build time; empty
|
|
# disables the relay/tunnel features, which we do not deploy. Changing
|
|
# it requires a rebuild, not a restart.
|
|
podman build --pull=newer \
|
|
-f crates/remote/Dockerfile \
|
|
--build-arg VITE_RELAY_API_BASE_URL= \
|
|
-t "${IMAGE}:${IMMUTABLE}" \
|
|
-t "${IMAGE}:latest" \
|
|
.
|
|
|
|
- name: verify the sunset really is gone
|
|
env:
|
|
IMMUTABLE: ${{ steps.meta.outputs.immutable }}
|
|
run: |
|
|
set -euo pipefail
|
|
IMAGE=git.lair.cafe/lair/vibe-kanban-remote
|
|
# This fork exists because upstream replaced the board with an
|
|
# export-only page. If a merge or a bad rebase ever reinstates it, fail
|
|
# here rather than publish an image that greets us with a dead product.
|
|
if podman run --rm --entrypoint sh "${IMAGE}:${IMMUTABLE}" -c \
|
|
'grep -rlq "Project functionality has been retired" /srv/static' 2>/dev/null; then
|
|
echo "ERROR: the sunset page is present in the built bundle."
|
|
echo "The revert of #3387 has been lost -- refusing to publish."
|
|
exit 1
|
|
fi
|
|
echo "ok: no sunset page in the bundle"
|
|
|
|
- name: push
|
|
env:
|
|
IMMUTABLE: ${{ steps.meta.outputs.immutable }}
|
|
run: |
|
|
set -euo pipefail
|
|
IMAGE=git.lair.cafe/lair/vibe-kanban-remote
|
|
podman push "${IMAGE}:${IMMUTABLE}"
|
|
podman push "${IMAGE}:latest"
|
|
echo "published ${IMAGE}:${IMMUTABLE} (and :latest)"
|
|
echo
|
|
echo "pin the quadlet to the immutable tag:"
|
|
echo " Image=${IMAGE}:${IMMUTABLE}"
|