diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 58f064374..0036ad4dc 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -53,6 +53,9 @@ jobs: - uses: actions/checkout@v4 with: ref: ${{ steps.source.outputs.commit }} + # Full history so build.rs can measure the distance from the + # release tag; a shallow clone would label the build `+g`. + fetch-depth: 0 - name: Validate release version env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9264aa5a5..0e659b1b8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,6 +39,9 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ inputs.commit || github.sha }} + # Full history so build.rs can measure the distance from the + # release tag; a shallow clone would label the build `+g`. + fetch-depth: 0 - name: Validate package versions id: version @@ -127,6 +130,9 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ inputs.commit || github.sha }} + # Full history so build.rs can measure the distance from the + # release tag; a shallow clone would label the build `+g`. + fetch-depth: 0 - name: Validate package versions id: version @@ -194,6 +200,9 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ inputs.commit || github.sha }} + # Full history so build.rs can measure the distance from the + # release tag; a shallow clone would label the build `+g`. + fetch-depth: 0 - name: Validate package versions id: version @@ -315,6 +324,9 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ inputs.commit || github.sha }} + # Full history so build.rs can measure the distance from the + # release tag; a shallow clone would label the build `+g`. + fetch-depth: 0 - name: Validate package versions id: version diff --git a/build.rs b/build.rs index fd50c581c..0a3d19be1 100644 --- a/build.rs +++ b/build.rs @@ -14,32 +14,78 @@ fn main() { version.clone() }; println!("cargo:rustc-env=OCS_APP_VERSION={app_version}"); + // Rerun on commit and branch switch only. Watching `.git/index` as well + // would keep the dirty flag fresh, but Cargo recompiles the crate every + // time this script reruns, and `git status` rewrites the index. println!("cargo:rerun-if-changed=.git/HEAD"); if let Ok(head) = std::fs::read_to_string(".git/HEAD") { if let Some(reference) = head.trim().strip_prefix("ref: ") { println!("cargo:rerun-if-changed=.git/{reference}"); } } - let revision = std::process::Command::new("git") - .args(["rev-parse", "--short=12", "HEAD"]) - .output() - .ok() - .filter(|output| output.status.success()) - .and_then(|output| String::from_utf8(output.stdout).ok()) - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) + let revision = git_output(&["rev-parse", "--short=12", "HEAD"]) .unwrap_or_else(|| "unknown".to_string()); + // diff-index exits 1 for differences and 128 when there is no repository, + // so only a real difference marks the build dirty. let dirty = std::process::Command::new("git") .args(["diff-index", "--quiet", "HEAD", "--"]) .status() .ok() - .is_some_and(|status| !status.success()); + .is_some_and(|status| status.code() == Some(1)); + let commit = revision.clone(); let revision = if dirty { format!("{revision}-dirty") } else { revision }; println!("cargo:rustc-env=OCS_GIT_REV={revision}"); + + // Builds from the main branch report how far past the release tag they + // are. Release tags are `v` + the display version (scripts/release.py), + // so counting first-parent commits from that tag ties the distance to + // the version the app already claims. Shallow clones and source + // tarballs have no tag to count from and fall back to the hash alone. + let tag_distance = git_output(&[ + "rev-list", + "--count", + "--first-parent", + &format!("v{app_version}..HEAD"), + ]) + .and_then(|value| value.parse::().ok()); + println!( + "cargo:rustc-env=OCS_TAG_DISTANCE={}", + tag_distance.map_or_else(|| "unknown".to_string(), |n| n.to_string()) + ); + let commit_date = git_output(&["log", "-1", "--format=%cs", "HEAD"]) + .unwrap_or_else(|| "unknown".to_string()); + println!("cargo:rustc-env=OCS_COMMIT_DATE={commit_date}"); + + // Semver-style build metadata: empty for a clean build on the release + // tag, `+N.g` for N first-parent commits past it, `+g` when + // the distance is unknown, and `.dirty` appended for uncommitted changes. + let short_hash = (commit != "unknown").then(|| commit[..commit.len().min(8)].to_string()); + let mut metadata_parts: Vec = Vec::new(); + match (tag_distance, &short_hash) { + (Some(0), _) => {} + (Some(n), Some(hash)) => { + metadata_parts.push(n.to_string()); + metadata_parts.push(format!("g{hash}")); + } + (Some(n), None) => metadata_parts.push(n.to_string()), + (None, Some(hash)) => metadata_parts.push(format!("g{hash}")), + (None, None) => {} + } + if dirty { + metadata_parts.push("dirty".to_string()); + } + let build_metadata = if metadata_parts.is_empty() { + String::new() + } else { + format!("+{}", metadata_parts.join(".")) + }; + println!("cargo:rustc-env=OCS_BUILD_METADATA={build_metadata}"); + let full_version = format!("{app_version}{build_metadata}"); + println!("cargo:rustc-env=OCS_FULL_VERSION={full_version}"); let profile = std::env::var("PROFILE").unwrap_or_else(|_| "unknown".to_string()); println!("cargo:rustc-env=OCS_BUILD_PROFILE={profile}"); if std::env::var("TARGET").ok().as_deref() == Some("x86_64-pc-windows-msvc") @@ -73,7 +119,7 @@ fn main() { if Path::new("packaging/windows/AppIcon.ico").exists() { let mut res = winresource::WindowsResource::new(); res.set_icon("packaging/windows/AppIcon.ico"); - res.set("ProductVersion", &app_version); + res.set("ProductVersion", &full_version); res.set("FileVersion", &app_version); if let Err(e) = res.compile() { println!("cargo:warning=failed to embed Windows icon: {e}"); @@ -81,3 +127,16 @@ fn main() { } } } + +/// Runs `git` with `args` and returns trimmed stdout, or `None` when git is +/// missing, fails (no repository, unknown tag), or prints nothing. +fn git_output(args: &[&str]) -> Option { + std::process::Command::new("git") + .args(args) + .output() + .ok() + .filter(|output| output.status.success()) + .and_then(|output| String::from_utf8(output.stdout).ok()) + .map(|value| value.trim().to_string()) + .filter(|value| !value.is_empty()) +} diff --git a/docs/releases.md b/docs/releases.md index d08139d6a..c9940c696 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -18,6 +18,15 @@ download for the installed platform. The app shows `2026.35`, Cargo and macOS use `2026.35.0`, and MSI uses `26.35.0`. The main window title is `Open CAD Studio 2026.35 - Drawing.dwg`. +Builds that are not a clean checkout of the release tag carry build metadata: +`2026.35+194.gef189d77` means 194 first-parent commits past `v2026.35` at +commit `ef189d77`, and `.dirty` is appended when the tree has uncommitted +changes. `build.rs` derives this from git at compile time (`OCS_FULL_VERSION`); +without a tag to count from (shallow clone, source tarball) only the hash is +shown. The full form appears in About, **Copy Info**, `REPORT`, `--version` +and recovery reports. The window title, update check and User-Agent keep the +plain `2026.35`. + To preview release notes, manually run **Weekly release** on `main` with **publish** unchecked. Check **publish** to release immediately. Rerunning within the same week reuses the original tag and commit. To rebuild just a diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index 86443f3b9..63fca8084 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -4621,6 +4621,8 @@ plugins = .no-releases = لا توجد إصدارات ui = + .build = البناء + .commit-date = تاريخ الإيداع .count-steps-available = تتوفر __ocs_arg_count__ خطوات .label-history = سجل __ocs_arg_label__ .n-vp = __ocs_arg_n__ منفذ عرض @@ -4720,6 +4722,7 @@ ui = .quick-select-menu-period-title-case = تحديد سريع... .reading-file = يجري قراءة الملف… .redo = إعادة + .release = إصدار .repeat-last = تكرار __ocs_arg_last__ .replace-all = استبدال الكل .replace-with = الاستبدال بـ: diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index 3be5e6825..bb0452993 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -4609,6 +4609,8 @@ plugins = .no-releases = няма версии ui = + .build = Компилация + .commit-date = Дата на комита .count-steps-available = налични __ocs_arg_count__ стъпки .label-history = история на __ocs_arg_label__ .n-vp = __ocs_arg_n__ VP @@ -4708,6 +4710,7 @@ ui = .quick-select-menu-period-title-case = Бърз избор... .reading-file = Четене на файл… .redo = Повтори + .release = Издание .repeat-last = Повтори __ocs_arg_last__ .replace-all = Замени всички .replace-with = Замени с: diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index b885713ea..8cdbf27b7 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -4606,6 +4606,8 @@ plugins = .no-releases = žádná vydání ui = + .build = Sestavení + .commit-date = Datum commitu .count-steps-available = K dispozici jsou kroky __ocs_arg_count__ .label-history = Historie __ocs_arg_label__ .n-vp = __ocs_arg_n__ VP @@ -4705,6 +4707,7 @@ ui = .quick-select-menu-period-title-case = Rychlý výběr... .reading-file = Čtení souboru… .redo = Znovu + .release = Vydání .repeat-last = Opakujte __ocs_arg_last__ .replace-all = Nahradit vše .replace-with = Nahradit za: diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index 867d917d8..f4074b56d 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -4607,6 +4607,8 @@ plugins = .no-releases = Keine Freigaben ui = + .build = Build + .commit-date = Commit-Datum .count-steps-available = __ocs_arg_count__ Schritte verfügbar .label-history = __ocs_arg_label__ Verlauf .n-vp = __ocs_arg_n__ Ansichtsfenster @@ -4706,6 +4708,7 @@ ui = .quick-select-menu-period-title-case = Schnellauswahl… .reading-file = Lesedatei... .redo = Wiederholen + .release = Release .repeat-last = Wiederholen __ocs_arg_last__ .replace-all = Ersetzen Sie alle .replace-with = Ersetzen Sie durch: diff --git a/locales/el-GR/opencadstudio.ftl b/locales/el-GR/opencadstudio.ftl index d7fa6130e..918b7a467 100644 --- a/locales/el-GR/opencadstudio.ftl +++ b/locales/el-GR/opencadstudio.ftl @@ -4609,6 +4609,8 @@ plugins = .no-releases = καμία έκδοση ui = + .build = Build + .commit-date = Ημερομηνία commit .count-steps-available = __ocs_arg_count__ διαθέσιμα βήματα .label-history = Ιστορικό: __ocs_arg_label__ .n-vp = __ocs_arg_n__ ΘΠ @@ -4708,6 +4710,7 @@ ui = .quick-select-menu-period-title-case = Γρήγορη επιλογή... .reading-file = Ανάγνωση αρχείου… .redo = Επανάληψη + .release = Έκδοση .repeat-last = Επανάληψη __ocs_arg_last__ .replace-all = Αντικατάσταση όλων .replace-with = Αντικατάσταση με: diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index 7538de696..c92d76100 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -4610,6 +4610,8 @@ plugins = .no-releases = no releases ui = + .build = Build + .commit-date = Commit date .count-steps-available = __ocs_arg_count__ steps available .label-history = __ocs_arg_label__ history .n-vp = __ocs_arg_n__ VP @@ -4709,6 +4711,7 @@ ui = .quick-select-menu-period-title-case = Quick Select... .reading-file = Reading file… .redo = Redo + .release = Release .repeat-last = Repeat __ocs_arg_last__ .replace-all = Replace All .replace-with = Replace with: diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index 5bea5595a..ffa389345 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -4609,6 +4609,8 @@ plugins = .no-releases = sin versiones ui = + .build = Compilación + .commit-date = Fecha del commit .count-steps-available = __ocs_arg_count__ pasos disponibles .label-history = Historial de __ocs_arg_label__ .n-vp = __ocs_arg_n__ VG @@ -4708,6 +4710,7 @@ ui = .quick-select-menu-period-title-case = Selección rápida... .reading-file = Leyendo archivo… .redo = Rehacer + .release = Versión oficial .repeat-last = Repetir __ocs_arg_last__ .replace-all = Reemplazar todo .replace-with = Reemplazar por: diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index 6272243c7..f10fb4256 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -4608,6 +4608,8 @@ plugins = .no-releases = ei julkaisuja ui = + .build = Koontiversio + .commit-date = Commitin päivämäärä .count-steps-available = __ocs_arg_count__ portaat saatavilla .label-history = __ocs_arg_label__ historia .n-vp = __ocs_arg_n__ VP @@ -4705,6 +4707,7 @@ ui = .quick-select-menu-period-title-case = Pikavalinta... .reading-file = Luetaan tiedostoa… .redo = Toista + .release = Julkaisu .repeat-last = Toista __ocs_arg_last__ .replace-all = Korvaa kaikki .replace-with = Korvaa seuraavalla: diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index b4626a974..435643477 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -4609,6 +4609,8 @@ plugins = .no-releases = pas de libération ui = + .build = Build + .commit-date = Date du commit .count-steps-available = __ocs_arg_count__ étapes disponibles .label-history = __ocs_arg_label__ historique .n-vp = __ocs_arg_n__ fenêtre(s) @@ -4707,6 +4709,7 @@ ui = .quick-select-menu-period-title-case = Sélection rapide... .reading-file = Lecture du fichier... .redo = Refaire + .release = Version officielle .repeat-last = Répéter __ocs_arg_last__ .replace-all = Remplacer tout .replace-with = Remplacer par : diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index c4f8573bb..86831c91d 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -4597,6 +4597,8 @@ plugins = .no-releases = कोई विज्ञप्ति नहीं ui = + .build = बिल्ड + .commit-date = कमिट तिथि .count-steps-available = __ocs_arg_count__ उपलब्ध चरणों .label-history = __ocs_arg_label__ इतिहास .n-vp = __ocs_arg_n__ व्यूपोर्ट @@ -4696,6 +4698,7 @@ ui = .quick-select-menu-period-title-case = त्वरित चयन... .reading-file = फ़ाइल पढ़ना... .redo = रेडो + .release = रिलीज़ .repeat-last = दोहराएँ __ocs_arg_last__ .replace-all = सबको बदल दें .replace-with = इसके साथ बदलें: diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index 1d7ef1906..aa77346b3 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -4608,6 +4608,8 @@ plugins = .no-releases = nincs kiadás ui = + .build = Build + .commit-date = Commit dátuma .count-steps-available = __ocs_arg_count__ lépcsők állnak rendelkezésre .label-history = __ocs_arg_label__ története .n-vp = __ocs_arg_n__ VP @@ -4707,6 +4709,7 @@ ui = .quick-select-menu-period-title-case = Gyors kiválasztás... .reading-file = Fájl olvasása… .redo = Újra + .release = Kiadás .repeat-last = Ismételje meg a __ocs_arg_last__ .replace-all = Cserélje ki az összeset .replace-with = Csere erre: diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index cb9a7e25d..172ba8b13 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -4608,6 +4608,8 @@ plugins = .no-releases = nessuna release ui = + .build = Build + .commit-date = Data del commit .count-steps-available = __ocs_arg_count__ gradini disponibili .label-history = Storia di __ocs_arg_label__ .n-vp = __ocs_arg_n__ VP @@ -4707,6 +4709,7 @@ ui = .quick-select-menu-period-title-case = Selezione rapida... .reading-file = Lettura del file… .redo = Rifare + .release = Release .repeat-last = Ripeti __ocs_arg_last__ .replace-all = Sostituisci tutto .replace-with = Sostituisci con: diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index 0a06b2921..6bcf2e6fa 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -4597,6 +4597,8 @@ plugins = .no-releases = リリースなし ui = + .build = ビルド + .commit-date = コミット日 .count-steps-available = __ocs_arg_count__ ステップ利用可能 .label-history = __ocs_arg_label__ の履歴 .n-vp = __ocs_arg_n__ 個の VP @@ -4696,6 +4698,7 @@ ui = .quick-select-menu-period-title-case = クイック選択... .reading-file = ファイルを読み込み中… .redo = やり直し + .release = リリース .repeat-last = __ocs_arg_last__ を繰り返し .replace-all = すべて置換 .replace-with = 置換後: diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index 7ca820393..f0bd4b96b 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -4608,6 +4608,8 @@ plugins = .no-releases = 릴리스 없음 ui = + .build = 빌드 + .commit-date = 커밋 날짜 .count-steps-available = __ocs_arg_count__ 단계 사용 가능 .label-history = __ocs_arg_label__ 역사 .n-vp = __ocs_arg_n__ VP @@ -4707,6 +4709,7 @@ ui = .quick-select-menu-period-title-case = 빠른 선택... .reading-file = 파일을 읽는 중… .redo = 다시 실행 + .release = 릴리스 .repeat-last = __ocs_arg_last__ 반복 .replace-all = 모두 바꾸기 .replace-with = 다음으로 교체: diff --git a/locales/nl-NL/opencadstudio.ftl b/locales/nl-NL/opencadstudio.ftl index a0d35b8a4..239f0f69b 100644 --- a/locales/nl-NL/opencadstudio.ftl +++ b/locales/nl-NL/opencadstudio.ftl @@ -4607,6 +4607,8 @@ plugins = .no-releases = geen releases ui = + .build = Build + .commit-date = Commitdatum .count-steps-available = __ocs_arg_count__ stappen beschikbaar .label-history = __ocs_arg_label__ geschiedenis .n-vp = __ocs_arg_n__ weergavevenster @@ -4706,6 +4708,7 @@ ui = .quick-select-menu-period-title-case = Snel selecteren... .reading-file = Bestand wordt gelezen... .redo = Opnieuw + .release = Release .repeat-last = __ocs_arg_last__ herhalen .replace-all = Alles vervangen .replace-with = Vervangen door: diff --git a/locales/pl-PL/opencadstudio.ftl b/locales/pl-PL/opencadstudio.ftl index 0f2548b49..6cfacf03e 100644 --- a/locales/pl-PL/opencadstudio.ftl +++ b/locales/pl-PL/opencadstudio.ftl @@ -4608,6 +4608,8 @@ plugins = .no-releases = żadnych wydań ui = + .build = Kompilacja + .commit-date = Data commita .count-steps-available = Dostępne stopnie __ocs_arg_count__ .label-history = Historia __ocs_arg_label__ .n-vp = __ocs_arg_n__ VP @@ -4707,6 +4709,7 @@ ui = .quick-select-menu-period-title-case = Szybki wybór... .reading-file = Czytanie pliku… .redo = Powtórz + .release = Wydanie .repeat-last = Powtórz __ocs_arg_last__ .replace-all = Zamień wszystko .replace-with = Zamień na: diff --git a/locales/pt-BR/opencadstudio.ftl b/locales/pt-BR/opencadstudio.ftl index a54b10ee4..893a614cc 100644 --- a/locales/pt-BR/opencadstudio.ftl +++ b/locales/pt-BR/opencadstudio.ftl @@ -4609,6 +4609,8 @@ plugins = .no-releases = sem versões ui = + .build = Build + .commit-date = Data do commit .count-steps-available = __ocs_arg_count__ etapas disponíveis .label-history = Histórico de __ocs_arg_label__ .n-vp = __ocs_arg_n__ VG @@ -4708,6 +4710,7 @@ ui = .quick-select-menu-period-title-case = Seleção rápida... .reading-file = Lendo arquivo… .redo = Refazer + .release = Versão oficial .repeat-last = Repetir __ocs_arg_last__ .replace-all = Substituir tudo .replace-with = Substituir por: diff --git a/locales/ru-RU/opencadstudio.ftl b/locales/ru-RU/opencadstudio.ftl index 2eba43cc8..42ba729ea 100644 --- a/locales/ru-RU/opencadstudio.ftl +++ b/locales/ru-RU/opencadstudio.ftl @@ -4610,6 +4610,8 @@ plugins = .no-releases = без релизов ui = + .build = Сборка + .commit-date = Дата коммита .count-steps-available = __ocs_arg_count__ доступные шаги .label-history = __ocs_arg_label__ История .n-vp = __ocs_arg_n__ вид. экранов @@ -4709,6 +4711,7 @@ ui = .quick-select-menu-period-title-case = Быстрый выбор... .reading-file = Чтение файла... .redo = Повторить + .release = Релиз .repeat-last = Повторить __ocs_arg_last__ .replace-all = Заменить все .replace-with = Заменить на: diff --git a/locales/tr-TR/opencadstudio.ftl b/locales/tr-TR/opencadstudio.ftl index 027a179cc..156fdbc27 100644 --- a/locales/tr-TR/opencadstudio.ftl +++ b/locales/tr-TR/opencadstudio.ftl @@ -4546,6 +4546,8 @@ plugins = .no-releases = sürüm yok ui = + .build = Derleme + .commit-date = Commit tarihi .count-steps-available = __ocs_arg_count__ adım kullanılabilir .label-history = __ocs_arg_label__ geçmişi .n-vp = __ocs_arg_n__ görünüm alanı @@ -4635,6 +4637,7 @@ ui = .quick-select-menu-period-title-case = Hızlı Seçim... .reading-file = Dosya okunuyor… .redo = Yinele + .release = Sürüm .repeat-last = __ocs_arg_last__ komutunu yinele .replace-all = Tümünü Değiştir .replace-with = Şununla değiştir: diff --git a/locales/zh-CN/opencadstudio.ftl b/locales/zh-CN/opencadstudio.ftl index 028a2101d..b7adc94f2 100644 --- a/locales/zh-CN/opencadstudio.ftl +++ b/locales/zh-CN/opencadstudio.ftl @@ -4592,6 +4592,8 @@ plugins = .no-releases = 无发布版本 ui = + .build = 构建 + .commit-date = 提交日期 .count-steps-available = 可用 __ocs_arg_count__ 步 .label-history = __ocs_arg_label__ 历史 .n-vp = __ocs_arg_n__ 个视口 @@ -4691,6 +4693,7 @@ ui = .quick-select-menu-period-title-case = 快速选择... .reading-file = 正在读取文件… .redo = 重做 + .release = 正式版 .repeat-last = 重复 __ocs_arg_last__ .replace-all = 全部替换 .replace-with = 替换为: diff --git a/locales/zh-TW/opencadstudio.ftl b/locales/zh-TW/opencadstudio.ftl index 04c488dfc..ba8125188 100644 --- a/locales/zh-TW/opencadstudio.ftl +++ b/locales/zh-TW/opencadstudio.ftl @@ -4604,6 +4604,8 @@ plugins = .no-releases = 無發行版本 ui = + .build = 組建 + .commit-date = 提交日期 .count-steps-available = 可用 __ocs_arg_count__ 步 .label-history = __ocs_arg_label__ 歷史 .n-vp = __ocs_arg_n__ 個視口 @@ -4703,6 +4705,7 @@ ui = .quick-select-menu-period-title-case = 快速選擇... .reading-file = 正在讀取檔案… .redo = 重做 + .release = 正式版 .repeat-last = 重復 __ocs_arg_last__ .replace-all = 全部替換 .replace-with = 替換為: diff --git a/src/app/commands/view.rs b/src/app/commands/view.rs index c43f1983a..bcc808668 100644 --- a/src/app/commands/view.rs +++ b/src/app/commands/view.rs @@ -262,8 +262,11 @@ impl OpenCADStudio { // reports arrive with the basics already filled in. let body = format!( "\n\n\n\ - ---\n- Open CAD Studio: v{}\n- Platform: {}\n", - env!("OCS_APP_VERSION"), + ---\n- Open CAD Studio: v{}\n- Revision: {} ({}, {})\n- Platform: {}\n", + env!("OCS_FULL_VERSION"), + env!("OCS_GIT_REV"), + env!("OCS_COMMIT_DATE"), + env!("OCS_BUILD_PROFILE"), crate::sys::platform_info(), ); let url = format!( diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 259609c43..7faf35edb 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -6949,8 +6949,12 @@ impl OpenCADStudio { Message::AboutCopyInfo => { let info = format!( - "Open CAD Studio v{}\nOS: {}\nArch: {}", - env!("OCS_APP_VERSION"), + "Open CAD Studio v{}\nRevision: {}\nCommit date: {}\nProfile: {}\nFeatures: {}\nOS: {}\nArch: {}", + env!("OCS_FULL_VERSION"), + env!("OCS_GIT_REV"), + env!("OCS_COMMIT_DATE"), + env!("OCS_BUILD_PROFILE"), + env!("OCS_BUILD_FEATURES"), crate::ui::window::about::platform_name(), crate::ui::window::about::architecture_name(), ); @@ -7168,7 +7172,7 @@ impl OpenCADStudio { if let Some(error) = &self.plugin_registry_error { return iced::clipboard::write(format!( "Open CAD Studio v{}\nOS: {}\nArchitecture: {}\nRegistry: {}\nError: {}", - env!("OCS_APP_VERSION"), + env!("OCS_FULL_VERSION"), std::env::consts::OS, std::env::consts::ARCH, crate::plugin::marketplace::REGISTRY_URL, diff --git a/src/cli.rs b/src/cli.rs index 3312b1575..881932bfe 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -20,7 +20,14 @@ use clap::Parser; #[derive(Parser, Debug, Default)] #[command( name = "OpenCADStudio", - version, + version = env!("OCS_FULL_VERSION"), + long_version = concat!( + env!("OCS_FULL_VERSION"), + "\nrevision: ", env!("OCS_GIT_REV"), + "\ncommit date: ", env!("OCS_COMMIT_DATE"), + "\nprofile: ", env!("OCS_BUILD_PROFILE"), + "\nfeatures: ", env!("OCS_BUILD_FEATURES"), + ), about = crate::t!("Open CAD Studio — 2D/3D CAD editor").into_owned(), long_about = None, )] diff --git a/src/io/recovery.rs b/src/io/recovery.rs index 3daac49ad..cbea1a3e4 100644 --- a/src/io/recovery.rs +++ b/src/io/recovery.rs @@ -255,6 +255,7 @@ impl RecoveryReport { lines.push(format!("Report schema: {}", REPORT_SCHEMA_VERSION)); lines.push(format!("Report ID: {}", self.report_id)); lines.push(format!("Application version: {}", env!("OCS_APP_VERSION"))); + lines.push(format!("Application build: {}", env!("OCS_FULL_VERSION"))); lines.push(format!("Application revision: {}", env!("OCS_GIT_REV"))); lines.push(format!("Build profile: {}", env!("OCS_BUILD_PROFILE"))); lines.push(format!("Build features: {}", env!("OCS_BUILD_FEATURES"))); diff --git a/src/locale_catalog.rs b/src/locale_catalog.rs index f5fdc2646..e5ca34ddb 100644 --- a/src/locale_catalog.rs +++ b/src/locale_catalog.rs @@ -481,6 +481,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static "Bring to Front" => Some(("ui", "bring-to-front")), "Browse, install, and manage add-ons. Select one to view its README." => Some(("plugins", "browse-install-and-manage-add-ons-select-one-t")), "Browser storage" => Some(("options", "browser-storage")), + "Build" => Some(("ui", "build")), "Building scene caches…" => Some(("ui", "building-scene-caches")), "Bump Map" => Some(("properties", "bump-map")), "By annotation scale" => Some(("styles", "by-annotation-scale")), @@ -645,6 +646,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static "Command Aliases" => Some(("command", "command-aliases")), "Command cancelled because the active drawing space changed." => Some(("common", "command-cancelled-because-the-active-drawing-s")), "Command history navigation" => Some(("common", "command-history-navigation")), + "Commit date" => Some(("ui", "commit-date")), "Command:" => Some(("common", "command-label-title-case")), "Commands: %{cmds}" => Some(("plugins", "commands-cmds")), "Compact" => Some(("ui", "compact")), @@ -2234,6 +2236,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static "Refraction Map" => Some(("properties", "refraction-map")), "Region" => Some(("properties", "region")), "Registered Class Entity" => Some(("properties", "registered-class-entity")), + "Release" => Some(("ui", "release")), "Reload failed: {error}" => Some(("file", "reload-failed-error")), "Reload from Disk" => Some(("modal", "reload-from-disk")), "Remote Text" => Some(("properties", "remote-text")), diff --git a/src/ui/window/about.rs b/src/ui/window/about.rs index 81ccc2921..388f0e295 100644 --- a/src/ui/window/about.rs +++ b/src/ui/window/about.rs @@ -73,10 +73,23 @@ pub(crate) fn architecture_name() -> &'static str { } } +/// Build card text: the metadata suffix without its leading `+` +/// (`194.gef189d77`, `gef189d77.dirty`), or "Release" for a clean build on +/// the release tag. +fn build_label() -> std::borrow::Cow<'static, str> { + match env!("OCS_BUILD_METADATA").strip_prefix('+') { + Some(suffix) => std::borrow::Cow::Borrowed(suffix), + None => t!("Release"), + } +} + pub fn view_window( sizing: crate::ui::modal::ModalSizing, ) -> Element<'static, Message> { - let version = format!("v{}", env!("OCS_APP_VERSION")); + // The hero carries the full build identity (`2026.36+194.gef189d77`); + // the cards split it into the release version and the build details. + let version = format!("v{}", env!("OCS_FULL_VERSION")); + let build = build_label(); let content_width = if matches!(sizing.width, Length::Fill) { Fill } else { @@ -113,13 +126,21 @@ pub fn view_window( .style(surface_style); let metadata = row![ - info_card(t!("Version"), version, card_width), + info_card(t!("Version"), env!("OCS_APP_VERSION"), card_width), info_card(t!("Platform"), platform_name(), card_width), info_card(t!("Arch"), architecture_name(), card_width), ] .spacing(8) .width(content_width); + let build_info = row![ + info_card(t!("Build"), build, card_width), + info_card(t!("Commit date"), env!("OCS_COMMIT_DATE"), card_width), + info_card(t!("Profile"), env!("OCS_BUILD_PROFILE"), card_width), + ] + .spacing(8) + .width(content_width); + let copy = button(text(t!("Copy Info")).size(11)) .on_press(Message::AboutCopyInfo) .style(button::primary) @@ -129,6 +150,7 @@ pub fn view_window( column![ hero, metadata, + build_info, row![Space::new().width(content_width), copy] .width(sizing.width) .align_y(iced::Center),