Skip to main content

release-version

Steps to cut a VNote version release (e.g. v4.3.0) — bump the version with scripts/update_version.py, refresh and fill zh_CN/ja translations via lupdate, write the changes.md changelog, and trigger the CI [Release] build. Use when asked to "release", "cut a release", "bump the version", or "prepare vX.Y.Z".

ソース情報

リポジトリ
vnotex/vnote
ソースの最終更新活動
2026年9月24日 13:40
検出された SKILL.md の言語
英語
スター
12,987
フォーク
1,296

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
release-version
description
Steps to cut a VNote version release (e.g. v4.3.0) — bump the version with scripts/update_version.py, refresh and fill zh_CN/ja translations via lupdate, write the changes.md changelog, and trigger the CI [Release] build. Use when asked to "release", "cut a release", "bump the version", or "prepare vX.Y.Z".
# VNote Version Release End-to-end checklist for releasing a new VNote version. Replace `X.Y.Z` with the target version (e.g. `4.3.0`) throughout. ## 0. Preconditions - You are on `master` (releases are published from `master`), working tree clean except for the release changes. - Decide `X.Y.Z`. Confirm the previous tag with `git tag | Sort-Object -Descending`. - Submodules are already pinned to their intended commits (see root `AGENTS.md` § Submodule Push Discipline — push submodules BEFORE the parent). ## 1. Bump the version (use the script — do NOT hand-edit) ```pwsh python scripts/update_version.py X.Y.Z ``` `scripts/update_version.py` is the single source of truth. It updates: - `CMakeLists.txt` — `project(... VERSION X.Y.Z ...)` - `.github/workflows/ci-win.yml`, `ci-linux.yml`, `ci-macos.yml`, `ci-linux-tsan.yml` — `VNOTE_VER: X.Y.Z` - `src/data/core/Info.plist` — short (`X.Y`) and full (`X.Y.Z`, `X.Y.Z.1`) strings - `src/core/configmgr2.cpp` — `ConfigMgr2::c_version{X, Y, Z}` - `src/data/core/fun.vnote.app.VNote.metainfo.xml` — prepends a dated `<release>` entry Note: `ci-linux-tsan.yml` IS in the script's file list (`scripts/update_version.py:21`) and the `[Release] VNote 4.3.0` commit `a282ef07` bumped it too, so keep the script's change. Its `VNOTE_VER` is only a build sanity value and does not publish anything — it just tracks the release version. Verify: `git diff --stat` should show exactly the files listed above. ## 2. Update translations (extract, then fill, both locales) The two maintained catalogs are `src/data/core/translations/vnote_zh_CN.ts` (Simplified Chinese) and `vnote_ja.ts` (Japanese). ### 2a. Extract new/changed strings with lupdate `lupdate` ships with Qt, and the installed Qt version changes over time — do NOT hardcode a path (`C:\Qt\6.9.3\...` was already gone by 4.3.1). Resolve it first: ```pwsh $lupdate = (Get-Command lupdate -ErrorAction SilentlyContinue).Source if (-not $lupdate) { $lupdate = (Get-ChildItem C:\Qt -Recurse -Filter lupdate.exe -ErrorAction SilentlyContinue | Where-Object FullName -like '*msvc*' | Sort-Object FullName -Descending | Select-Object -First 1).FullName } $lupdate # e.g. C:\Qt\6.10.3\msvc2022_64\bin\lupdate.exe ``` ```pwsh & $lupdate -no-obsolete -locations relative src ` -ts src/data/core/translations/vnote_zh_CN.ts src/data/core/translations/vnote_ja.ts ``` - `-no-obsolete` drops entries no longer in the source (keeps the catalog lean). - Harmless `pdf.js` JS parse errors are expected — lupdate still finishes. - The summary reports "N new" strings; those become `type="unfinished"`. ### 2b. Fill in the unfinished translations Every `type="unfinished"` entry must be translated for BOTH locales. Count them: ```pwsh (Select-String -Path "src/data/core/translations/vnote_zh_CN.ts" -Pattern 'type="unfinished"').Count (Select-String -Path "src/data/core/translations/vnote_ja.ts" -Pattern 'type="unfinished"').Count ``` Extract the source strings needing translation, then for each `<message>` whose `<translation type="unfinished">` is empty, provide the localized text and drop the `type="unfinished"` attribute (`<translation>...</translation>`). Practical approach: script it. Build a `source -> translation` map per locale and rewrite each unfinished `<message>` block, escaping `&`/`<`/`>` in the output and preserving `%1`/`%2` placeholders, `&`-accelerators (e.g. `&View` -> `查看(&V)` / `表示(&V)`), and literal newlines. Re-run the count above; both must reach `0`. Two traps when scripting the rewrite: - **Anchor the `<source>` capture.** A pattern like `<source>(.*?)</source>` with `(?s)` lets the group swallow whole `</message>` blocks to reach a *later* unfinished `<translation>`, silently deleting every message in between (a whole `<context>` disappeared this way during 4.3.1). Forbid the closing tag inside the group and stop the gap before the next message: `<source>((?:(?!</source>).)*)</source>((?:(?!</message>|<source>).)*?)<translation type="unfinished">` - **Numerus entries** are `<translation type="unfinished"><numerusform></numerusform></translation>`. Fill the `<numerusform>` rather than replacing the element body, so the plural structure and indentation survive. Then VERIFY against the pre-edit file — an over-matching regex leaves the counts looking fine while having eaten unrelated entries: ```pwsh foreach ($l in 'zh_CN','ja') { git show "HEAD:src/data/core/translations/vnote_$l.ts" > "$env:TEMP\old_$l.ts" $o = [xml](Get-Content -Raw "$env:TEMP\old_$l.ts") $n = [xml](Get-Content -Raw "src/data/core/translations/vnote_$l.ts") # NOTE: PowerShell's XML adapter returns a bare string for <translation> once # the type attribute is gone, so .InnerText is $null — normalize first. function Tr($m) { $t = $m.translation; if ($null -eq $t) { '' } elseif ($t -is [string]) { $t } else { $t.InnerText } } $old = @{}; foreach ($c in $o.TS.context) { foreach ($m in $c.message) { $old["$($c.name)|$($m.source)"] = (Tr $m) } } $changed = 0; $bad = 0 foreach ($c in $n.TS.context) { foreach ($m in $c.message) { $k = "$($c.name)|$($m.source)"; $tr = (Tr $m) if ($old.ContainsKey($k)) { if ($old[$k] -ne $tr) { $changed++; "CHANGED $k" }; continue } $ps = @([regex]::Matches($m.source, '%\d|%n') | ForEach-Object { $_.Value } | Sort-Object -Unique) $pt = @([regex]::Matches($tr, '%\d|%n') | ForEach-Object { $_.Value } | Sort-Object -Unique) if (($ps -join ',') -ne ($pt -join ',')) { $bad++; "PLACEHOLDER $k" } } } "$l changed_existing=$changed placeholder_mismatch=$bad" } ``` `changed_existing` MUST be 0 (only lupdate's own obsolete removals may drop keys — cross-check them against `git log` / the source tree) and `placeholder_mismatch` MUST be 0. Finally, compile both catalogs; each must report `0 unfinished`: ```pwsh foreach ($l in 'zh_CN','ja') { & ($lupdate -replace 'lupdate\.exe$','lrelease.exe') "src/data/core/translations/vnote_$l.ts" -qm "$env:TEMP\$l.qm" } Remove-Item "$env:TEMP\zh_CN.qm","$env:TEMP\ja.qm" ``` The `.qm` binaries are generated at build time by the `lrelease` CMake target (see `src/CMakeLists.txt`), so you do NOT commit `.qm` files. ## 3. Write the changelog Prepend a new `## vX.Y.Z` section at the TOP of `changes.md` (right under the `# Changes` header, above the previous version). - Summarize `git log <prev-tag>..HEAD --oneline` grouped by theme (Editor, Export, Tasks, Fixes, Security, Translations, …), matching the style of existing entries. - Lead with a one-line summary sentence "… on top of VNote <prev>:". - If an `## Unreleased` section exists, FOLD it into the new `## vX.Y.Z` section (it is not a separate release) rather than leaving both. - Always end with a **Translations** bullet noting zh_CN + ja were updated. ## 4. Review Use the read-only `checkpoint-reviewer` agent for both required checkpoints: once after the release plan is ready, before edits, and again after implementation and verification, before committing. Resolve blockers and investigate concerns; the primary owns validation, so tell the reviewer to skip builds and tests. ## 5. Commit and trigger the release CI publishes a (draft) GitHub release from `master` ONLY when the head commit message starts with `[Release]` (see the `Release` job in each `ci-*.yml`; condition: `github.ref == 'refs/heads/master' && startsWith(head_commit.message, '[Release]')`). It creates tag `vX.Y.Z` and uploads the platform artifacts. Release commits skip test compilation, registration checks, test runs and failure reruns on Linux, macOS and Windows; the test-only TSan job is skipped entirely. Packaging, signing and package-integrity checks still run. Normal pushes, pull requests and manual runs retain testing (Windows Qt 5 remains packaging-only). A green release run is not test-suite evidence; use a non-release run for that. - Commit message MUST start with `[Release]`, e.g. `[Release] VNote X.Y.Z`. - Follow repo `AGENTS.md` rule 13 for author/commit date (night-time), and only commit when the user explicitly asks. - If submodule pointers moved, push submodules first, then the parent (rule + root `AGENTS.md` § Submodule Push Discipline). ## 6. Once CI is green, assemble the draft release The `[Release]` commit makes CI create a **draft** GitHub release for tag `vX.Y.Z`. Wait until ALL platform jobs are green, then make sure the draft carries the five platform packages before publishing. The `ncipollo/release-action` step in each `ci-*.yml` uploads its own platform's asset directly, but confirm all five are present (and if any job's upload was skipped/failed, download that job's build artifact and attach it manually). The five required platform packages (additional installers/update assets may also be present): | Platform | Artifact file | Produced by | |----------|---------------|-------------| | Linux | `VNote-X.Y.Z-linux-x64.AppImage` | `ci-linux.yml` | | macOS 13+ (Qt 6.10.3) | `VNote-X.Y.Z-mac-universal.dmg` | `ci-macos.yml` (suffix `""`) | | macOS 12.1+ (Qt 6.9.3) | `VNote-X.Y.Z-mac-universal-macos12.dmg` | `ci-macos.yml` (suffix `-macos12`) | | Win64 (Qt 6) | `VNote-X.Y.Z-win64.zip` | `ci-win.yml` (suffix `""`) | | Windows 7 (Qt 5.15) | `VNote-X.Y.Z-win64-windows7.zip` | `ci-win.yml` (suffix `-windows7`) | Both macOS packages are universal (`x86_64` + `arm64`) and built on `macos-latest`. The `-macos12` suffix describes the deployment target, not the runner OS; its minimum is 12.1, matching VNote's existing deployment floor. Keep each variant's CMake deployment target and bundle `LSMinimumSystemVersion` aligned. Verify the compatibility package launches and renders Markdown preview on Intel macOS 12.7.6 before claiming Monterey compatibility; a newer CI runner cannot establish that. Continuous builds retain both `VNote-continuous-mac-universal.dmg` and `VNote-continuous-mac-universal-macos12.dmg` with independent pruning. Watch the runs and confirm the draft, using `gh`: ```pwsh # Watch the release-triggering runs on master until they finish. gh run list --branch master --limit 8 gh run watch <run-id> # Inspect the draft release and its currently-attached assets. gh release view vX.Y.Z ``` If an asset is missing, download it from the corresponding workflow run and upload it to the draft: ```pwsh # Download the build artifact(s) from a finished run into ./_artifacts. gh run download <run-id> -D _artifacts # Attach a missing asset to the draft release (repeat per file). gh release upload vX.Y.Z "_artifacts\<path>\VNote-X.Y.Z-...zip" --clobber ``` ### Set the release description from `changes.md` The draft's body is driven from `## vX.Y.Z` section of `changes.md` (the same changelog written in step 3) focusing on user experience changes. CI creates the release with an empty body, so set it. ```pwsh # Extract the ## vX.Y.Z section into a temp notes file... $ver = "X.Y.Z" $md = Get-Content changes.md -Raw $sec = [regex]::Match($md, "(?ms)^## v$([regex]::Escape($ver))\b.*?(?=^## |\z)").Value.TrimEnd() Set-Content -Path notes.md -Value $sec -NoNewline -Encoding utf8 # ...and apply it as the draft's description after rewriting it to focus on user experience changes. gh release edit vX.Y.Z --notes-file notes.md ``` Drop the leading `## vX.Y.Z` line if you prefer the version to appear only as the release title; keep the bullet body either way. Verify with `gh release view vX.Y.Z`. `ncipollo/release-action` REPLACES the body and the name on every update unless told not to, and all five platform jobs target the same draft with `allowUpdates: true`. Before 4.4.2 that silently blanked the notes: whichever job finished last wiped them, `gh release view` then showed an empty body, and the Gitee mirror — which copies the GitHub body — fell back to the bare tag name. The three `ci-*.yml` Release steps now pass `omitBodyDuringUpdate: true` and `omitNameDuringUpdate: true`, so notes set at any point after the release exists survive. If you ever see the body empty again, check those inputs are still there before re-typing the notes. ### Publish Only when the draft `vX.Y.Z` release shows all **5** platform packages (linux / macos / macos12 / win64 / windows7) AND its body matches the `changes.md` section do you publish it: ```pwsh gh release edit vX.Y.Z --draft=false ```
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る