ci: build and publish the remote-server image from this repo
All checks were successful
container / vibe-kanban-remote (push) Successful in 4m31s
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.
This commit is contained in:
110
.gitea/workflows/container.yml
Normal file
110
.gitea/workflows/container.yml
Normal file
@@ -0,0 +1,110 @@
|
||||
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}"
|
||||
Reference in New Issue
Block a user