Status LEDs, image model selection, backup restore, provider test, monthly versions

- Sidebar: three status LEDs (server, models, MCP) with an overview popover (/api/status)
- Images: preview stage, purpose-based model choice and reference images
  (Nano Banana Pro edit refines the chosen preview)
- Settings: restore a stored or uploaded backup (staged, applied on restart)
- Provider form: test connection; saving or reloading re-checks the status at once
- opencode Zen/Go: send x-opencode-session with a stable id per conversation
- Releases are named vYYYYMM-NNNNN; scripts/release.sh numbers, tags, builds and publishes
- Fix: backups made in the same second no longer overwrite each other

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-09 09:31:24 +01:00
1 parent 49d404eec7
commit 1f69d63b09
35 files changed
+1156 -88

No files matched your search

+122
View File
@@ -0,0 +1,122 @@
#!/usr/bin/env bash
# Publish a CodeCraft release named vYYYYMM-NNNNN (year, month, then a counter that
# starts at 00001 every month):
# 1. pick the next number from the existing tags of this month,
# 2. rename "## [Unreleased]" in CHANGELOG.md and the "unreleased" entry in
# web/src/whatsnew.ts to the version, update the README badge, commit,
# 3. tag and push branch and tag,
# 4. build the multi-arch image (amd64 + arm64) with the version embedded and push
# :vYYYYMM-NNNNN (and :latest),
# 5. create the Gitea release with the changelog section as notes.
#
# Usage: scripts/release.sh [vYYYYMM-NNNNN] [--dry-run] [--no-latest] [--skip-image]
# (without a version the next number of the current month is used)
#
# Environment:
# REGISTRY image name without tag (default git.familie-zink.org/zink/codecraft)
# GITEA_URL Gitea base URL (default https://git.familie-zink.org)
# GITEA_REPO owner/repo (default zink/codecraft)
# GITEA_TOKEN API token; if empty, the git credential helper is asked
# PLATFORMS buildx platforms (default linux/amd64,linux/arm64)
# BUILDER buildx builder name (default codecraft-multi, created if missing)
#
# Requires: git, docker with buildx (logged in to the registry), curl, jq.
# Building for a foreign architecture needs QEMU/binfmt:
# docker run --privileged --rm tonistiigi/binfmt --install all
set -euo pipefail
REGISTRY="${REGISTRY:-git.familie-zink.org/zink/codecraft}"
GITEA_URL="${GITEA_URL:-https://git.familie-zink.org}"
GITEA_REPO="${GITEA_REPO:-zink/codecraft}"
PLATFORMS="${PLATFORMS:-linux/amd64,linux/arm64}"
BUILDER="${BUILDER:-codecraft-multi}"
tag="" dry=0 latest=1 image=1
for arg in "$@"; do
case "$arg" in
--dry-run) dry=1 ;;
--no-latest) latest=0 ;;
--skip-image) image=0 ;;
-h|--help) sed -n '2,26p' "$0"; exit 0 ;;
-*) echo "unknown option $arg" >&2; exit 2 ;;
*) tag="$arg" ;;
esac
done
cd "$(git rev-parse --show-toplevel)"
step() { printf '\n==> %s\n' "$*"; }
run() { if (( dry )); then echo "[dry-run] $*"; else "$@"; fi; }
fail() { echo "error: $*" >&2; exit 1; }
step "Checking the working tree"
for cmd in git docker curl jq; do command -v "$cmd" >/dev/null || fail "$cmd is not installed"; done
[[ -z "$(git status --porcelain --untracked-files=no)" ]] || fail "uncommitted changes – commit or stash them first"
run git fetch --tags --quiet origin
if [[ -z "$tag" ]]; then
month=$(date +%Y%m)
last=$(git tag -l "v$month-*" | sed -n "s/^v$month-\([0-9]\{5\}\)$/\1/p" | sort -n | tail -1)
tag=$(printf 'v%s-%05d' "$month" $((10#${last:-0} + 1)))
fi
[[ "$tag" =~ ^v[0-9]{6}-[0-9]{5}$ ]] || fail "version must look like vYYYYMM-NNNNN, got $tag"
git rev-parse -q --verify "refs/tags/$tag" >/dev/null && fail "tag $tag already exists"
version=${tag#v}
today=$(date +%Y-%m-%d)
echo "release $tag"
# Release notes: the [Unreleased] section of the changelog (without its heading).
notes=$(awk '/^## \[Unreleased\]/ { found=1; next } found && /^## \[/ { exit } found { print }' CHANGELOG.md)
[[ -n "${notes//[[:space:]]/}" ]] || fail "CHANGELOG.md has no (or an empty) \"## [Unreleased]\" section"
notes+=$'\n\n'"Image: \`$REGISTRY:$tag\` ($(tr ',' ' ' <<<"$PLATFORMS"))."
step "Naming the release in CHANGELOG.md, whatsnew.ts and README.md"
if (( dry )); then
echo "[dry-run] ## [Unreleased] -> ## [$tag] – $today; whatsnew unreleased -> \"$version\"; README badge"
else
sed -i "s/^## \[Unreleased\].*/## [$tag] – $today/" CHANGELOG.md
grep -q '^ unreleased: {' web/src/whatsnew.ts && sed -i "s/^ unreleased: {/ \"$version\": {/" web/src/whatsnew.ts
# shields.io escapes "-" as "--".
sed -i -E "s|(alt=\"Version )[^\"]*(\" src=\"https://img.shields.io/badge/version-)[^?]*-([0-9a-f]{6}\?)|\1$tag\2${tag//-/--}-\3|" README.md
git add CHANGELOG.md web/src/whatsnew.ts README.md
git commit -q -m "Release $tag"
fi
step "Tagging $tag and pushing"
branch=$(git rev-parse --abbrev-ref HEAD)
run git tag -a "$tag" -m "CodeCraft $tag"
run git push origin "$branch" "$tag"
if (( image )); then
step "Building and pushing $REGISTRY:$tag for $PLATFORMS"
docker buildx inspect "$BUILDER" >/dev/null 2>&1 || run docker buildx create --name "$BUILDER" --driver docker-container >/dev/null
tags=(-t "$REGISTRY:$tag")
(( latest )) && tags+=(-t "$REGISTRY:latest")
run docker buildx build --builder "$BUILDER" --platform "$PLATFORMS" \
--build-arg GIT_COMMIT="$(git rev-parse --short HEAD)" --build-arg CODECRAFT_VERSION="$version" \
"${tags[@]}" --push .
fi
step "Creating the Gitea release $tag"
token="${GITEA_TOKEN:-}"
user=""
if [[ -z "$token" ]]; then
host=${GITEA_URL#*://}
creds=$(printf 'protocol=%s\nhost=%s\n\n' "${GITEA_URL%%://*}" "${host%%/*}" | git credential fill 2>/dev/null || true)
user=$(sed -n 's/^username=//p' <<<"$creds")
token=$(sed -n 's/^password=//p' <<<"$creds")
fi
[[ -n "$token" ]] || fail "no Gitea credentials – set GITEA_TOKEN"
auth=(-H "Authorization: token $token")
[[ -n "$user" ]] && auth=(-u "$user:$token")
body=$(jq -n --arg tag "$tag" --arg name "CodeCraft $tag" --arg body "$notes" \
'{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: false}')
if (( dry )); then
echo "[dry-run] POST $GITEA_URL/api/v1/repos/$GITEA_REPO/releases"
else
url=$(curl -fsS "${auth[@]}" -H 'Content-Type: application/json' -X POST \
"$GITEA_URL/api/v1/repos/$GITEA_REPO/releases" -d "$body" | jq -r .html_url)
echo "published: $url"
fi
step "Done"
echo "Update a deployment: set CODECRAFT_VERSION=$tag in .env, then docker compose pull && docker compose up -d"