From 51d16d06c57d41abf03ea60df815ac1877a0d5d7 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Wed, 14 Oct 2026 12:22:52 +0000 Subject: [PATCH] Use CLI release tags for Docker image versions --- .github/workflows/cli-docker.yml | 11 ++++++- src/apps/cli/README.md | 2 ++ updates.md | 7 +++++ utils/cli-ci.unit.spec.ts | 50 +++++++++++++++++++++++++++++++- 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cli-docker.yml b/.github/workflows/cli-docker.yml index 47347626..b8dd04a1 100644 --- a/.github/workflows/cli-docker.yml +++ b/.github/workflows/cli-docker.yml @@ -1,5 +1,5 @@ # Build and push the CLI Docker image to GitHub Container Registry (GHCR). -# Image tag format: -cli +# Release image tag format: -cli # Example: 1.0.36-cli # # Stable releases are also tagged with their major-minor version and 'latest'. @@ -72,6 +72,15 @@ jobs: DRY_RUN: ${{ inputs.dry_run }} run: | VERSION=$(jq -r '.version' manifest.json) + if [[ "${REF}" == refs/tags/*-cli ]]; then + # CLI releases use their Git tag independently of the plug-in version. + VERSION="${REF#refs/tags/}" + VERSION="${VERSION%-cli}" + if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$ ]]; then + printf 'Invalid CLI release tag: %s\n' "$REF" >&2 + exit 1 + fi + fi MAJOR_MINOR=$(echo "${VERSION}" | cut -d. -f1,2) SHORT_SHA=$(git rev-parse --short HEAD) IMAGE="ghcr.io/${{ github.repository_owner }}/livesync-cli" diff --git a/src/apps/cli/README.md b/src/apps/cli/README.md index 8761bb08..9f55fa63 100644 --- a/src/apps/cli/README.md +++ b/src/apps/cli/README.md @@ -193,6 +193,8 @@ The generated systemd unit uses the wrapper's absolute path and does not depend ### Docker +CLI-only Docker patches use their full release tag, such as `ghcr.io/vrtmrz/livesync-cli:1.0.36-patch1-cli`. Patch and RC images receive version and SHA-qualified tags. The `latest` and major-minor tags are updated by ordinary stable releases. CLI image versions come from the release Git tag, so a CLI-only patch does not require changing the Obsidian plug-in version. + A Docker image is provided for headless / server deployments. Build from the repository root: ```bash diff --git a/updates.md b/updates.md index 2a25ed9f..1776f24b 100644 --- a/updates.md +++ b/updates.md @@ -12,6 +12,13 @@ Earlier releases remain available in the 1.0 release history, the 1.0 preview hi ## Unreleased +### CLI + +#### Fixed + +- We can select CLI-only Docker patches by their full release tag, such as `1.0.36-patch1-cli`, while keeping the Obsidian plug-in version. + - Patch and RC images receive version and SHA-qualified tags. The `latest` and major-minor tags remain reserved for ordinary stable releases. + ## 1.0.36 13th October, 2026 diff --git a/utils/cli-ci.unit.spec.ts b/utils/cli-ci.unit.spec.ts index c6841b57..500de60e 100644 --- a/utils/cli-ci.unit.spec.ts +++ b/utils/cli-ci.unit.spec.ts @@ -273,9 +273,51 @@ describe("CLI CI selection", () => { true, ["1.0.36-rc.1-cli", "1.0.36-rc.1-sha-deadbee-cli"], ], + [ + "1.0.36", + "refs/tags/1.0.36-patch1-cli", + "push", + "false", + true, + ["1.0.36-patch1-cli", "1.0.36-patch1-sha-deadbee-cli"], + ], + [ + "1.0.36", + "refs/tags/1.0.36-rc.1-cli", + "push", + "false", + true, + ["1.0.36-rc.1-cli", "1.0.36-rc.1-sha-deadbee-cli"], + ], + [ + "1.0.36", + "refs/tags/1.0.37-cli", + "push", + "false", + true, + ["1.0.37-cli", "1.0-cli", "latest", "1.0.37-sha-deadbee-cli"], + ], + [ + "1.0.36", + "refs/tags/1.0.36-patch2-cli", + "workflow_dispatch", + "true", + false, + ["1.0.36-patch2-cli", "1.0.36-patch2-sha-deadbee-cli"], + ], + [ + "1.0.36", + "refs/tags/1.0.36", + "workflow_dispatch", + "true", + false, + ["1.0.36-cli", "1.0-cli", "latest", "1.0.36-sha-deadbee-cli"], + ], + ["1.0.36", "refs/tags/not-a-version-cli", "workflow_dispatch", "true", false, null], + ["1.0.36", "refs/tags/1.0.36+build-cli", "push", "false", false, null], ["1.0.36", "refs/heads/main", "push", "false", true, ["edge"]], ["1.0.36", "refs/heads/test", "workflow_dispatch", "true", false, ["1.0.36-dev-sha-deadbee-cli"]], - ] as const)("preserves tag selection for %s on %s", (version, ref, event, dryRun, push, tags) => { + ] as const)("derives publication metadata for manifest %s on %s", (version, ref, event, dryRun, push, tags) => { const metadata = workflow("cli-docker.yml").jobs.metadata.steps?.find((step) => step.id === "meta"); expect(metadata?.run).toBeDefined(); const directory = mkdtempSync(join(tmpdir(), "livesync-cli-image-metadata-")); @@ -298,6 +340,12 @@ describe("CLI CI selection", () => { }, encoding: "utf8", }); + if (tags === null) { + expect(result.status).not.toBe(0); + expect(result.stderr).toContain("Invalid CLI release tag"); + expect(existsSync(output)).toBe(false); + return; + } expect(result.status, result.stderr).toBe(0); const image = "ghcr.io/test-owner/livesync-cli"; expect(readFileSync(output, "utf8").trim().split("\n")).toEqual([