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:
1 parent
49d404eec7
commit
1f69d63b09
35 files changed
+1156
-88
No files matched your search
Executable
+122
@@ -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"
|
||||
Reference in new issue
Block a user