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
- آخر نشاط في المصدر
- ٢٤ سبتمبر ٢٠٢٦ في ١٣:٤٠
- لغة SKILL.md المكتشفة
- الإنجليزية
- النجوم
- ١٣٬٠٠٥
- التفرعات
- ١٬٢٩٩
خيارات التثبيت
يُحدَّد 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