diff --git a/c/firmware-info/tests/test_identity_archive.py b/c/firmware-info/tests/test_identity_archive.py index 06ea5ec..322b863 100644 --- a/c/firmware-info/tests/test_identity_archive.py +++ b/c/firmware-info/tests/test_identity_archive.py @@ -26,7 +26,7 @@ class ArchiveTests(unittest.TestCase): metadata.write_text(json.dumps(self.metadata()), encoding="utf-8") result = archive_build(image, metadata, root / "out") folder = Path(result["directory"]) - self.assertEqual(folder.name, "a1b2c3d4_2026-10-08_12-30-00_UTC") + self.assertEqual(folder.name, "a1b2c3d4_2026-10-08_15-30-00_UTC+3") self.assertEqual((folder / image.name).read_bytes(), payload) self.assertEqual(image.read_bytes(), payload) self.assertEqual(json.loads((folder / "firmware_identity.json").read_text()), self.metadata()) @@ -60,7 +60,7 @@ class ArchiveTests(unittest.TestCase): result = archive_build(image, metadata, root / "local", export=root / "exported") copied = Path(result["exported_directory"]) self.assertEqual([p.name for p in (root / "exported").iterdir()], - ["a1b2c3d4_2026-10-08_12-30-00_UTC"]) + ["a1b2c3d4_2026-10-08_15-30-00_UTC+3"]) self.assertEqual((copied / image.name).read_bytes(), image.read_bytes()) self.assertTrue((copied / "firmware_identity.json").is_file()) self.assertTrue((copied / (image.name + ".sha256")).is_file()) @@ -83,15 +83,19 @@ class ArchiveTests(unittest.TestCase): self.assertEqual((old / "keep.txt").read_text(), "keep") with self.assertRaises(ValueError): export_build(folder, folder / "recursive", image.name) - def test_offset_normalizes_to_utc_and_dirty_is_visible(self): + def test_offset_normalizes_to_utc_plus_three_and_dirty_is_visible(self): metadata = self.metadata("2026-10-08T15:30:00+03:00") metadata["source"]["dirty"] = True - self.assertEqual(build_folder_name(metadata), "a1b2c3d4+_2026-10-08_12-30-00_UTC") + self.assertEqual(build_folder_name(metadata), "a1b2c3d4+_2026-10-08_15-30-00_UTC+3") + def test_folder_date_rolls_over_at_utc_plus_three_midnight(self): + metadata = self.metadata("2026-10-08T22:30:00+00:00") + self.assertEqual(build_folder_name(metadata), "a1b2c3d4_2026-10-09_01-30-00_UTC+3") + self.assertEqual(metadata["built_at_utc"], "2026-10-08T22:30:00+00:00") def test_archive_without_git_has_explicit_placeholder(self): metadata = self.metadata() metadata["source"]["commit"] = None - self.assertEqual(build_folder_name(metadata), "NOGIT000_2026-10-08_12-30-00_UTC") + self.assertEqual(build_folder_name(metadata), "NOGIT000_2026-10-08_15-30-00_UTC+3") def test_invalid_provenance_cannot_be_a_folder_name(self): metadata = self.metadata() diff --git a/c/firmware-info/tools/identity.py b/c/firmware-info/tools/identity.py index a3e42bd..725d4ea 100644 --- a/c/firmware-info/tools/identity.py +++ b/c/firmware-info/tools/identity.py @@ -7,7 +7,7 @@ protocol encoding lives here: the generated header contains scalar constants. from __future__ import annotations import argparse -from datetime import datetime, timezone +from datetime import datetime, timedelta, timezone import hashlib import json import os @@ -108,7 +108,7 @@ def verify(image: Path, manifest: Path) -> dict: def build_folder_name(metadata: dict) -> str: - """Use the firmware commit and pre-build UTC time, never post-build time.""" + """Use the firmware commit and pre-build time rendered in UTC+3.""" source = metadata.get("source") or {} dependency = metadata.get("templates") or {} commit = source.get("commit") @@ -128,8 +128,8 @@ def build_folder_name(metadata: dict) -> str: stamp = datetime.fromisoformat(stamp_text.replace("Z", "+00:00")) if stamp.tzinfo is None: raise ValueError("pre-build timestamp must include a timezone") - stamp = stamp.astimezone(timezone.utc) - return identity + "_" + stamp.strftime("%Y-%m-%d_%H-%M-%S_UTC") + stamp = stamp.astimezone(timezone(timedelta(hours=3))) + return identity + "_" + stamp.strftime("%Y-%m-%d_%H-%M-%S_UTC+3") def export_build(folder: Path, export: Path, image_name: str) -> Path: diff --git a/tools/firmware-build/README.md b/tools/firmware-build/README.md index 8d83e37..af68a0f 100644 --- a/tools/firmware-build/README.md +++ b/tools/firmware-build/README.md @@ -54,7 +54,7 @@ Debug/ MyFirmware_BUILDID.bin MyFirmware_BUILDID.bin.manifest.json MyFirmware_BUILDID.bin.sha256 - HASH_YYYY-MM-DD_HH-MM-SS_UTC/ + HASH_YYYY-MM-DD_HH-MM-SS_UTC+3/ MyFirmware.hex MyFirmware_BUILDID.bin MyFirmware_BUILDID.bin.manifest.json @@ -62,7 +62,7 @@ Debug/ firmware_identity.json ``` -Postcompile автоматически создаёт папку `HASH_YYYY-MM-DD_HH-MM-SS_UTC` для каждой сборки. HASH — первые 8 символов commit прошивки из pre-build JSON; при незакоммиченных изменениях добавляется `+`. Дата и время берутся из `built_at_utc`, в UTC. Если commit исходников неизвестен, используется build ID или NOGIT000. Например: `f5c3654e+_2026-10-08_12-30-00_UTC`. Сохраняются точные байты образа, manifest, SHA-256 и firmware_identity.json; в CCS также HEX и map. Повтор postcompile с теми же метаданными обновляет ту же папку. Файлы непосредственно в firmware сохраняются, поэтому существующие команды копирования продолжают работать. +Postcompile автоматически создаёт папку `HASH_YYYY-MM-DD_HH-MM-SS_UTC+3` для каждой сборки. HASH — первые 8 символов commit прошивки из pre-build JSON; при незакоммиченных изменениях добавляется `+`. Дата и время берутся из `built_at_utc` и переводятся в UTC+3 для имени папки. В JSON и заголовке сохраняется UTC. Если commit исходников неизвестен, используется build ID или NOGIT000. Например: `f5c3654e+_2026-10-08_15-30-00_UTC+3`. Сохраняются точные байты образа, manifest, SHA-256 и firmware_identity.json; в CCS также HEX и map. Повтор postcompile с теми же метаданными обновляет ту же папку. Файлы непосредственно в firmware сохраняются, поэтому существующие команды копирования продолжают работать. Добавьте generated и firmware в игнорируемые Git-каталоги. Обычно вся папка Debug уже игнорируется. Полный Rebuild обязателен для выпуска. BAT сами по себе не подтверждают, что OUT собран именно с этим заголовком. ### 4. Копирование папки текущей сборки в SETGUI @@ -79,7 +79,7 @@ cmd /c call "K:\git_project\newProject\newTEMPL\templates\tools\firmware-build\c ```text K:\git_project\newProject\SETGUI\..periph_28335\firmware\ - f5c3654e+_2026-10-08_12-30-00_UTC\ + f5c3654e+_2026-10-08_15-30-00_UTC+3\ MyFirmware.hex MyFirmware_BUILDID.bin MyFirmware_BUILDID.bin.manifest.json @@ -157,14 +157,14 @@ Firmware.hex Firmware.hex.manifest.json Firmware.hex.sha256 firmware/ - HASH_YYYY-MM-DD_HH-MM-SS_UTC/ + HASH_YYYY-MM-DD_HH-MM-SS_UTC+3/ Firmware.hex Firmware.hex.manifest.json Firmware.hex.sha256 firmware_identity.json ``` -Папка HASH_DATE создаётся в firmware рядом с исходным HEX/BIN. Для неё используются commit и UTC-время из precompile, как в CCS. При успехе Console содержит строки `[firmware-build] Folder`, `Image`, `Manifest`, `SHA-256`. Для готового BIN вместо `#H` передайте его фактический путь; конвертацию AXF в BIN нужно выполнить до postcompile. Сгенерированные файлы исключите через .gitignore. +Папка HASH_DATE создаётся в firmware рядом с исходным HEX/BIN. Для неё используются commit и время precompile, переведённое в UTC+3, как в CCS. При успехе Console содержит строки `[firmware-build] Folder`, `Image`, `Manifest`, `SHA-256`. Для готового BIN вместо `#H` передайте его фактический путь; конвертацию AXF в BIN нужно выполнить до postcompile. Сгенерированные файлы исключите через .gitignore. ## Совместимость со старыми проектами