diff --git a/.codex/skills/release-version/SKILL.md b/.codex/skills/release-version/SKILL.md index 157faea3..4931cb63 100644 --- a/.codex/skills/release-version/SKILL.md +++ b/.codex/skills/release-version/SKILL.md @@ -1,225 +1,234 @@ --- name: release-version -description: Use when preparing or publishing a new semantic version release, updating bilingual changelogs, creating Git tags, or publishing GitHub/Gitee release pages for this repository. +description: Use when preparing or publishing a new release of this fork, updating CHANGELOG.md, reconciling dev with main, creating Git tags, or publishing the GitHub Release page for DOS/Crove-Desk. --- # Release Version ## Overview -Create releases with a strict `vx.y.z` tag, produce bilingual changelog entries from actual Git history, publish both the `docs` submodule update and repository tag, and create the GitHub and Gitee Release page entries in one controlled workflow. +Create a release with a `vX.Y.Z-crove.N` tag, derive the changelog entry from the +real Git range, reconcile `dev` with `main` before tagging, and publish the GitHub +Release page. Pushing the tag is what builds the production image. -Run the workflow from the repository root. Read [references/changelog-style.md](references/changelog-style.md) before drafting the human-facing update notes. +Run the workflow from the repository root. Read +[references/changelog-style.md](references/changelog-style.md) before drafting the +notes. + +This skill was inherited from upstream `huabeitech/agent-desk` and has been +rewritten for the fork. Three things it used to say are no longer true here: the +`docs` submodule was removed (upstream PR #35) so there is no bilingual docs +changelog, there is no Gitee mirror, and the release repository is +`DOS/Crove-Desk` rather than `huabeitech/agent-desk`. ## Workflow 1. Validate the requested version. -2. Inspect the repository and determine the comparison range. -3. Draft bilingual changelog entries from the actual diff. -4. Commit and push the `docs` submodule. -5. Commit the parent repository update if the submodule pointer changed. -6. Create and push the annotated tag. -7. Create GitHub and Gitee Release page entries for the tag. -8. Verify both remote tags and both Release pages. - -Do not skip the repository inspection step. Release notes must come from the real diff between tags, not from guesswork. +2. Reconcile `dev` and `main`. +3. Inspect the repository and determine the comparison range. +4. Draft the `CHANGELOG.md` entry from the actual diff. +5. Run the verification suite. +6. Commit and push the changelog. +7. Create and push the annotated tag. +8. Create the GitHub Release page for the tag. +9. Verify the production image build and the remote tag. + +Do not skip the repository inspection step. Release notes must come from the real +diff between tags, not from guesswork. ## Validate The Version -- Accept only tags that match `^v\d+\.\d+\.\d+$`. -- Reject date-style tags such as `v20260414`. +- Accept only tags that match `^v\d+\.\d+\.\d+-crove\.\d+$`. +- Reject bare `vX.Y.Z` and reject date-style tags such as `v20260414`. - Confirm the target tag does not already exist locally or on any configured remote. -- Prefer the latest reachable semver tag as the previous release tag. -- If no earlier semver tag exists, fall back to the latest reachable tag of any format and state that fallback in the changelog drafting notes. +- Prefer the latest reachable `-crove.N` tag as the previous release tag. -Use the helper script first: +**The `-crove.N` suffix is load-bearing, not cosmetic.** +`.github/workflows/sync-upstream.yml` lines 79-88 skip the entire upstream sync +when this fork already holds a tag whose name matches the upstream target tag, and +line 121 pushes upstream's tag into the fork afterwards. A bare `v1.7.0` here +would therefore silently disable every future upstream sync the moment upstream +released `v1.7.0`. Upstream owns the plain `vX.Y.Z` namespace; the fork must not +allocate from it. ```bash python3 .codex/skills/release-version/scripts/collect_release_context.py \ --repo . \ - --tag v1.2.3 + --tag v1.7.1-crove.1 ``` -If the caller already specifies the previous tag, pass it explicitly: +Pass `--previous-tag` explicitly when the caller already knows the baseline. -```bash -python3 .codex/skills/release-version/scripts/collect_release_context.py \ - --repo . \ - --tag v1.2.3 \ - --previous-tag v1.2.2 -``` +## Reconcile dev And main -If the repository-local helper is unavailable, fall back to `~/.codex/skills/release-version/scripts/collect_release_context.py`. +`dev` is the integration branch: `sync-upstream.yml` merges upstream tags into it +and pushes it directly, and `deploy-beta.yml` builds `:beta` from every push to +it. `main` is the default branch and is what Dependabot scans. The two drift. -## Inspect The Repository - -- Check `git status --short` in the parent repo. -- Check `git -C docs status --short` in the `docs` submodule. -- Read the JSON output of `collect_release_context.py`. -- Use the commit list, changed files, and insertions/deletions to decide what is user-visible. -- Prioritize behavior changes, new features, fixes, migrations, API changes, configuration changes, and documentation changes that matter to adopters. -- Ignore pure formatting churn unless it changes usage. - -If the working tree contains unrelated changes that would be risky to include in the release, stop and ask the user before proceeding. - -## Draft The Changelog +Before tagging, check both directions: -Update these files: - -- `docs/zh/docs/changelog.md` -- `docs/en/docs/changelog.md` +```bash +git fetch origin +git rev-list --count origin/main..dev +git rev-list --count dev..origin/main +``` -Prepend a new entry using this exact structure: +If the second count is non-zero, **merge `origin/main` into `dev` first** and only +then tag. Tagging `dev` while `main` holds commits `dev` lacks ships a release +that silently regresses whatever is on `main`. This is not hypothetical: the +`web/pnpm-workspace.yaml` fix and the dependency overrides that drove Dependabot +from 162 alerts to 34 existed only on `main`, and merging `dev` over `main` +without reconciling first would have reverted them. -```md -## ${tag} (${yyyy-MM-dd}) +Expect a `web/pnpm-lock.yaml` conflict when this happens. Resolve it toward +whichever side was generated together with the current `web/pnpm-workspace.yaml`, +then prove the choice instead of arguing it: -### 更新内容 +```bash +cd web && pnpm install --frozen-lockfile +``` -${content} +That is the exact command CI and the Docker build run. `Lockfile is up to date` +plus exit 0 settles it. -### 发布地址 +After reconciling, bring `main` up to `dev` so the default branch and the release +tag point at the same commit: -- Github: -- Gitee: +```bash +git push origin dev:main +git branch -f main dev ``` -For the English file, keep the same links and heading level, but translate the section heading and content naturally: +## Inspect The Repository -```md -## ${tag} (${yyyy-MM-dd}) +- Check `git status --short`. If the working tree holds unrelated changes, stop + and ask before proceeding - a release commit must not sweep them up. +- Read the JSON output of `collect_release_context.py`. +- Use the commit list, changed files, and insertions/deletions to decide what is + user-visible. +- Prioritize behavior changes, new features, fixes, migrations, API changes, + configuration changes, and documentation changes that matter to adopters. +- Ignore pure formatting churn unless it changes usage. -### Updates +## Draft The Changelog -${content} +Update `CHANGELOG.md` at the repository root. It is English-only and follows Keep +a Changelog. Prepend the new entry directly above the previous one: -### Release Links +```md +## [1.7.1-crove.1] - YYYY-MM-DD -- Github: -- Gitee: +### Security +### Added +### Changed +### Fixed +### Known issues ``` +Omit any section that has nothing in it. Keep `### Known issues` even when it is +uncomfortable: it is where regressions this release deliberately did not fix get +recorded, and it is the section adopters most need. + Changelog writing rules: - Write concise, user-facing summaries instead of raw commit subjects. -- Keep Chinese and English entries semantically aligned. -- Prefer 3-6 bullets unless the release is extremely small. -- Group related changes into a single bullet when that reads better. - Mention compatibility-sensitive changes explicitly. -- If the comparison baseline is a non-semver fallback tag, note that in your private reasoning, not in the public changelog unless the user asks for it. - -## Commit And Push The Docs Submodule - -After editing the changelog files: - -1. Run `git -C docs status --short`. -2. Review the diff with `git -C docs diff -- zh/docs/changelog.md en/docs/changelog.md`. -3. Commit inside the `docs` submodule with a focused message such as `docs: update changelog for v1.2.3`. -4. Push the `docs` submodule commit to its remote branch. - -Branch rule: - -- If `docs` is on a local branch, push that branch. -- If `docs` is detached, push `HEAD` to `origin/main` unless the repository clearly uses another default branch. +- Every claim must trace to a commit, a file, or a command you actually ran. +- State what a fix does *not* cover when the fix is partial. Marking a half-fixed + issue as fixed is worse than leaving it listed. -## Commit The Parent Repository +## Verify Before Tagging -If the `docs` submodule pointer changed in the parent repository, commit it before tagging. Otherwise the release tag will not reference the new changelog revision. - -Recommended flow: +Run these and report real output. Do not claim a check you did not run. ```bash -git status --short -git add docs -git commit -m "chore: update docs submodule for v1.2.3" +go build -tags dev ./... +go vet -tags dev ./... +go test -count=1 -tags dev ./internal/services/... ./internal/repositories/... \ + ./internal/pkg/... ./internal/oidcclient/... ./internal/migration/... \ + ./internal/builders/... ./internal/bootstrap/... ./internal/handlers/... +cd web && pnpm install --frozen-lockfile +cd web && pnpm typecheck +cd web && node --test "**/*.test.mjs" +git diff --check ``` -Only include unrelated parent-repo changes if the user explicitly wants them in the release commit. - -## Create And Push The Tag +`-tags dev` is required: without it the `//go:embed all:out` directive in +`web/embed.go` fails on a tree that has no `web/out`. `pnpm lint` currently +reports pre-existing `react-hooks` errors (PROC-16) and is wired into CI as +non-blocking, so treat *new* lint errors as the signal rather than the absolute +count. -Create an annotated tag after the repository state is ready: +## Commit And Push The Changelog ```bash -git tag -a v1.2.3 -m "Release v1.2.3" +git add CHANGELOG.md +git commit -m "docs(changelog): add the vX.Y.Z-crove.N entry" +git push origin dev +git push origin dev:main ``` -Push the commit branch first if needed, then push the tag to every configured remote that should publish releases: - -```bash -git push github HEAD -git push origin HEAD -git push github v1.2.3 -git push origin v1.2.3 -``` +Stage only the changelog. Never `git add -A` in this repository - other sessions +frequently hold unrelated work in the same tree. -Adjust the branch name if `HEAD` is not tracking the intended release branch. - -## Create GitHub And Gitee Releases - -Pushing tags is not enough. The release is incomplete until both Release pages exist: - -- GitHub: `https://github.com/huabeitech/agent-desk/releases/tag/${tag}` -- Gitee: `https://gitee.com/huabeitech/agent-desk/releases/tag/${tag}` - -Use the same concise release notes derived from the changelog. Prefer a bilingual body with Chinese first and English second. - -Required credentials: - -- GitHub: `GITHUB_TOKEN` or `GH_TOKEN` with access to `huabeitech/agent-desk` and permission to create releases. For a fine-grained PAT, use an organization-allowed lifetime and grant the repository at least `Contents: Read and write` plus `Metadata: Read`. -- Gitee: `GITEE_ACCESS_TOKEN` or `GITEE_TOKEN` with release write access to `huabeitech/agent-desk`. - -Never print tokens in command output or final responses. If the user pastes a token into the conversation, use it only for the requested release operation and recommend rotation after use. - -Build the release body from the new changelog entry, for example: +## Create And Push The Tag ```bash -mkdir -p /tmp/agent-desk-release -awk 'BEGIN{p=0} /^## v1\.2\.3 /{p=1; next} /^## v[0-9]/{if(p) exit} p{print}' \ - docs/zh/docs/changelog.md | sed '/^### 发布地址/,$d' > /tmp/agent-desk-release/v1.2.3-zh.md -awk 'BEGIN{p=0} /^## v1\.2\.3 /{p=1; next} /^## v[0-9]/{if(p) exit} p{print}' \ - docs/en/docs/changelog.md | sed '/^### Release Links/,$d' > /tmp/agent-desk-release/v1.2.3-en.md -{ - printf '## 更新内容\n\n' - sed '1,/^### 更新内容$/d' /tmp/agent-desk-release/v1.2.3-zh.md - printf '\n## Updates\n\n' - sed '1,/^### Updates$/d' /tmp/agent-desk-release/v1.2.3-en.md -} > /tmp/agent-desk-release/v1.2.3-release-body.md +git tag -a v1.7.1-crove.1 -m "Release v1.7.1-crove.1" +git push origin v1.7.1-crove.1 ``` -Create the GitHub Release: +Tag the reconciled commit, not whatever `HEAD` happens to be. Pushing the tag +triggers `deploy-prod.yml` through its `push: tags: v*` rule. -```bash -token="${GITHUB_TOKEN:-$GH_TOKEN}" -curl -sS -o /tmp/github_release_v1.2.3.json -w '%{http_code}' \ - -X POST https://api.github.com/repos/huabeitech/agent-desk/releases \ - -H "Authorization: Bearer ${token}" \ - -H 'Accept: application/vnd.github+json' \ - -H 'X-GitHub-Api-Version: 2022-11-28' \ - -H 'Content-Type: application/json' \ - -d @<(jq -n --rawfile body /tmp/agent-desk-release/v1.2.3-release-body.md \ - '{tag_name:"v1.2.3", target_commitish:"main", name:"v1.2.3", body:$body, draft:false, prerelease:false}') -``` +## Create The GitHub Release -Create the Gitee Release: +Pushing the tag builds the image but publishes nothing human-readable. Create the +Release page on `DOS/Crove-Desk`: ```bash -token="${GITEE_ACCESS_TOKEN:-$GITEE_TOKEN}" -curl -sS -o /tmp/gitee_release_v1.2.3.json -w '%{http_code}' \ - -X POST https://gitee.com/api/v5/repos/huabeitech/agent-desk/releases \ - -H 'Content-Type: application/json' \ - -d @<(jq -n --rawfile body /tmp/agent-desk-release/v1.2.3-release-body.md --arg token "${token}" \ - '{access_token:$token, tag_name:"v1.2.3", target_commitish:"main", name:"v1.2.3", body:$body, prerelease:false}') +gh release create v1.7.1-crove.1 \ + --repo DOS/Crove-Desk \ + --title "v1.7.1-crove.1" \ + --notes-file ``` -If creation returns `422`/already exists, fetch the existing release and verify it references the target tag before treating it as complete. If GitHub returns `Resource not accessible by personal access token`, inspect the API message and ask for a token that satisfies the organization policy and repository permissions. +Build the notes body from the new `CHANGELOG.md` entry. If the first attempt +fails, re-run against the existing release with `gh release edit` rather than +creating a second tag. + +There is no Gitee mirror for this fork. Do not attempt to publish there. ## Final Verification -- Confirm `git rev-parse v1.2.3^{tag}` succeeds. -- Confirm `git ls-remote --tags github v1.2.3` and `git ls-remote --tags origin v1.2.3` show the new tag. -- Confirm `curl -sS -o /tmp/github_release_verify.json -w '%{http_code}' https://api.github.com/repos/huabeitech/agent-desk/releases/tags/v1.2.3` returns `200`. -- Confirm `curl -sS -o /tmp/gitee_release_verify.json -w '%{http_code}' https://gitee.com/api/v5/repos/huabeitech/agent-desk/releases/tags/v1.2.3` returns `200`. -- Confirm the `docs` submodule remote contains the changelog commit. -- Confirm both parent and `docs` working trees are clean. -- Summarize the previous tag used for comparison, the files updated, the commit hashes created, the remotes pushed, and the GitHub/Gitee Release URLs. +- `git rev-parse v1.7.1-crove.1^{tag}` succeeds. +- `git ls-remote --tags origin v1.7.1-crove.1` shows the tag. +- `git rev-parse dev origin/dev main origin/main v1.7.1-crove.1^{commit}` all + report the same commit. +- The production build finished: + `gh run list --repo DOS/Crove-Desk --limit 3 --json displayTitle,workflowName,status,conclusion` +- The image tags were actually published. Read them from the build log rather + than assuming them from the workflow config: + `gh run view --repo DOS/Crove-Desk --log --job ` filtered for + `dos/crove-desk:`. Expect `latest`, `X.Y.Z-crove.N` and `vX.Y.Z-crove.N` on one + shared digest, and confirm `org.opencontainers.image.revision` equals the + tagged commit. +- `gh release view v1.7.1-crove.1 --repo DOS/Crove-Desk` resolves. + +## What This Does Not Do + +Neither `deploy-prod.yml` nor `deploy-beta.yml` deploys anything. Both only build +an OCI image and push it to `ghcr.io/dos/crove-desk`. The operator still has to +pull the new image on the host and restart the service. Say so explicitly in the +handoff - a green workflow is not a live release. + +## Completion Checklist + +- Tag matches `^v\d+\.\d+\.\d+-crove\.\d+$` and exists on no other remote +- `dev` and `main` reconciled, both pointing at the tagged commit +- `CHANGELOG.md` entry derived from the real diff, Known issues included +- Verification suite run, with real output reported +- Annotated tag pushed and `deploy-prod.yml` succeeded +- Published image digest and `org.opencontainers.image.revision` confirmed +- GitHub Release page created on `DOS/Crove-Desk` +- Operator reminded that pulling the image on the host is still manual diff --git a/.codex/skills/release-version/agents/openai.yaml b/.codex/skills/release-version/agents/openai.yaml index 7732fdfb..64915950 100644 --- a/.codex/skills/release-version/agents/openai.yaml +++ b/.codex/skills/release-version/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "Release Version" - short_description: "Create semver tags and changelog drafts" - default_prompt: "Use $release-version to create a new vx.y.z release, update changelogs, and push docs plus tags." + short_description: "Create -crove.N tags and CHANGELOG.md drafts" + default_prompt: "Use $release-version to create a new vX.Y.Z-crove.N release, reconcile dev with main, update CHANGELOG.md, push the tag, and publish the GitHub Release." diff --git a/.codex/skills/release-version/references/changelog-style.md b/.codex/skills/release-version/references/changelog-style.md index 3376da66..164d6283 100644 --- a/.codex/skills/release-version/references/changelog-style.md +++ b/.codex/skills/release-version/references/changelog-style.md @@ -1,15 +1,38 @@ # Changelog Style -Use this guide when drafting `docs/zh/changelog.md` and `docs/en/changelog.md`. +Use this guide when drafting the new entry in `CHANGELOG.md` at the repository +root. + +The fork keeps a single English changelog in Keep a Changelog format. The +bilingual `docs/zh/docs/changelog.md` and `docs/en/docs/changelog.md` files this +guide used to point at belonged to the upstream `docs` submodule, which was +removed in upstream PR #35 and no longer exists here. ## Goal Turn Git history into short release notes that explain what changed for adopters. +## Structure + +```md +## [X.Y.Z-crove.N] - YYYY-MM-DD + +### Security +### Added +### Changed +### Fixed +### Known issues +``` + +Omit empty sections. Never omit `Known issues` when something is knowingly +unfixed - that section is the one adopters rely on most, and a release that hides +its own gaps gets distrusted. + ## Keep - New user-facing features. - Bug fixes with clear impact. +- Security fixes, stated as what an attacker could do before and cannot now. - Breaking or compatibility-sensitive changes. - API, configuration, deployment, model, workflow, or schema changes that affect usage. - Important documentation updates when they unlock new workflows. @@ -19,43 +42,55 @@ Turn Git history into short release notes that explain what changed for adopters - Pure refactors with no visible effect. - Formatting-only changes. - Internal rename churn. -- Mechanical dependency updates unless they fix a real issue. +- Mechanical dependency updates unless they fix a real issue. A dependency sweep + is one bullet with the alert count, not one bullet per package. -## Chinese Style +## Style -- Use concise bullets. +- Use concise bullets in the imperative or past tense, consistently within an entry. - Prefer product or workflow language over commit jargon. - Start with the effect, then mention the area if needed. - Keep terms consistent across bullets. +- Name the file, symbol, or endpoint when that is what an adopter would search for. +- Do not copy commit subjects. They describe the patch; the changelog describes + the consequence. Example: ```md -- 优化会话列表查询与筛选逻辑,减少后台定位问题时的人工排查成本。 -- 修复消息发送链路中的异常处理,避免部分失败场景下页面状态不同步。 +- Improved conversation list querying and filtering so operators can locate + problem cases faster. +- Fixed error handling in the message send flow to prevent UI state from drifting + after partial failures. ``` -## English Style +## Partial Fixes -- Mirror the Chinese meaning instead of translating word by word. -- Use direct release-note phrasing. -- Prefer active wording and concrete impact. +When a fix closes only part of a reported issue, say which part and why the rest +is open. "Fixed X" on a half-fixed X is worse than no entry, because it removes +the issue from everyone's tracking without removing it from the product. Example: ```md -- Improved conversation list querying and filtering so operators can locate problem cases faster. -- Fixed error handling in the message send flow to prevent UI state from drifting after partial failures. +- Closed the privilege-escalation path in password reset: a non-super-admin can no + longer reset a super_admin's password. The endpoint still returns the new + plaintext password in the response body, because the dashboard dialog depends on + it; replacing that needs an email-based flow first. ``` ## Grouping Heuristics - Merge multiple commits into one bullet when they deliver one outcome. - Separate bullets when the audience or impact differs. -- Keep both language versions aligned in bullet count when possible. +- Keep related security items together under `### Security` even when they landed + in different commits. ## Before Finalizing - Re-check that every bullet is supported by the diff. -- Remove statements that depend on assumptions you cannot verify from code, tests, or commits. +- Remove statements that depend on assumptions you cannot verify from code, tests, + or commits. +- Verify version numbers, alert counts, and commit hashes you cite are the real + ones. Wrong numbers in a changelog outlive the release. - Keep the notes short enough to scan in under a minute. diff --git a/.codex/skills/release-version/scripts/collect_release_context.py b/.codex/skills/release-version/scripts/collect_release_context.py index 9d958692..6a435c38 100755 --- a/.codex/skills/release-version/scripts/collect_release_context.py +++ b/.codex/skills/release-version/scripts/collect_release_context.py @@ -10,7 +10,12 @@ import sys from pathlib import Path -SEMVER_TAG_RE = re.compile(r"^v(\d+)\.(\d+)\.(\d+)$") +SEMVER_TAG_RE = re.compile(r"^v(\d+)\.(\d+)\.(\d+)(?:-crove\.(\d+))?$") + +# Only the fork's own tags are valid release targets. SEMVER_TAG_RE stays +# permissive on purpose: it also has to read the bare vX.Y.Z and vYYYYMMDD tags +# inherited from upstream so a baseline can be chosen at all. +FORK_RELEASE_TAG_RE = re.compile(r"^v\d+\.\d+\.\d+-crove\.\d+$") def run_git( @@ -37,11 +42,21 @@ def run_git( return result.stdout.strip() -def parse_semver(tag: str) -> tuple[int, int, int] | None: +def parse_semver(tag: str) -> tuple[int, int, int, int] | None: + """Parse a release tag into a sortable tuple. + + The fourth element is the fork counter from the ``-crove.N`` suffix, treated + as 0 when absent. Strict semver would rank ``1.7.0-crove.1`` below ``1.7.0`` + as a prerelease; this repository never mints a bare ``vX.Y.Z`` tag, because + that namespace belongs to upstream and a collision disables + sync-upstream.yml, so ordering the counter above the plain release is the + behaviour that actually matters when picking a baseline. + """ match = SEMVER_TAG_RE.match(tag) if not match: return None - return tuple(int(part) for part in match.groups()) + major, minor, patch, crove = match.groups() + return (int(major), int(minor), int(patch), int(crove) if crove else 0) def list_reachable_tags(repo: Path) -> list[str]: @@ -50,7 +65,7 @@ def list_reachable_tags(repo: Path) -> list[str]: def choose_previous_tag(repo: Path, tags: list[str], target_tag: str | None) -> tuple[str | None, str | None]: - semver_tags: list[tuple[tuple[int, int, int], str]] = [] + semver_tags: list[tuple[tuple[int, int, int, int], str]] = [] other_tags: list[str] = [] target_semver = parse_semver(target_tag) if target_tag else None @@ -154,7 +169,7 @@ def get_numstat(repo: Path, rev_range: str) -> dict[str, int]: def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--repo", default=".", help="Repository root. Defaults to current directory.") - parser.add_argument("--tag", help="Target release tag to validate, for example v1.2.3.") + parser.add_argument("--tag", help="Target release tag to validate, for example v1.7.1-crove.1.") parser.add_argument("--previous-tag", help="Explicit comparison baseline.") return parser @@ -167,8 +182,16 @@ def main() -> int: print(json.dumps({"error": f"not a git repository: {repo_path}"})) return 1 - if args.tag and parse_semver(args.tag) is None: - print(json.dumps({"error": f"invalid tag format: {args.tag}", "expected": "vx.y.z"})) + if args.tag and not FORK_RELEASE_TAG_RE.match(args.tag): + print(json.dumps({ + "error": f"invalid release tag format: {args.tag}", + "expected": "vx.y.z-crove.n", + "reason": ( + "a bare vX.Y.Z tag allocates from the upstream namespace, and " + "sync-upstream.yml skips a sync entirely when this fork already " + "holds a tag of that name" + ), + })) return 1 try: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 355b99b9..3c3c3d34 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,18 @@ name: CI on: + # deploy-beta.yml builds and publishes an image on every push to dev, so CI has + # to run on push too. Without this a direct push shipped a beta image that no + # test had ever seen, because pull_request was the only trigger. + push: + branches: + - main + - dev + paths-ignore: + - "**.md" + - "docs/**" + - "screenshots/**" + - ".gitignore" pull_request: branches: - main @@ -30,9 +42,13 @@ jobs: go-version: "1.26" cache: true + - name: Run Go Vet + run: | + go vet -tags dev ./... + - name: Run Go Tests run: | - go test -v -tags dev ./internal/services/... ./internal/repositories/... ./internal/pkg/... ./internal/oidcclient/... ./internal/migration/... + go test -v -tags dev ./internal/services/... ./internal/repositories/... ./internal/pkg/... ./internal/oidcclient/... ./internal/migration/... ./internal/builders/... ./internal/bootstrap/... ./internal/handlers/... frontend-typecheck: name: Frontend Typecheck @@ -60,3 +76,15 @@ jobs: - name: Run Typecheck run: | cd web && pnpm typecheck + + - name: Run Node Tests + run: | + cd web && node --test "**/*.test.mjs" + + # Reported but not enforced: the tree currently carries six pre-existing + # react-hooks errors (tracked as PROC-16). Making this blocking before they + # are fixed would turn every unrelated PR red. + - name: Run Lint + continue-on-error: true + run: | + cd web && pnpm lint