Release Policy

source-repo release는 package artifact, Go module tag, container image를 분리해 관리합니다. 목적은 어떤 source revision이 어떤 artifact와 image로 나갔는지 추적 가능하게 만드는 것입니다.

PR, main, release tag에서 무엇을 build/push/publish할지는 CI/CD가 기준입니다. 이 문서는 그 정책 중 release tag와 approval 이후의 package, image, firmware handoff를 자세히 설명합니다.

1. Versioning

이 repo는 package별 independent versioning이 아니라 root release tag 기반 lockstep versioning을 채택합니다. Root release tag는 deployable app image, package artifact, firmware artifact, contract, release evidence를 함께 묶는 system release marker입니다.

v0.1.0

Monorepo release에는 일반적으로 두 가지 버전 전략이 있습니다.

Strategy 설명 잘 맞는 경우 Tradeoff
Lockstep/fixed versioning 하나의 root release tag가 여러 package, image, firmware artifact를 같은 version으로 묶습니다. 제품, platform, SaMD system처럼 여러 artifact가 같은 검증 baseline으로 배포되어야 할 때 변경되지 않은 package도 새 release version을 받을 수 있습니다.
Independent versioning package마다 변경 여부와 SemVer impact에 따라 version을 따로 올립니다. 독립 library가 많고 소비자가 package별 API compatibility를 기준으로 upgrade할 때 release evidence, compatibility matrix, CD handoff가 복잡해집니다.

Lockstep release에서는 v0.1.0 하나가 아래 artifact set을 함께 설명합니다.

@tirosh/patient-monitor-ui-core@0.1.0
patient-risk-python-core==0.1.0
patient-signal-c-core/0.1.0
ghcr.io/tirosh-chain/guide/patient-risk-web:v0.1.0
wearable-firmware/0.1.0/...

Independent release에서는 같은 source repo 안에서도 package마다 version이 다를 수 있습니다.

@tirosh/patient-monitor-ui-core@0.3.1
patient-risk-python-core==0.1.4
patient-signal-c-core/0.2.0

이 repo는 SaMD delivery guide이므로 lockstep versioning을 기본 정책으로 둡니다. vX.Y.Z는 개별 package API version만 뜻하지 않고, 같은 source revision에서 검증된 package, image, firmware, contract, release evidence의 system baseline을 뜻합니다. Release review와 rollback도 이 baseline을 기준으로 합니다.

npm, Python, Rust package는 각 생태계의 package metadata version과 root release tag가 일치해야 합니다. 개별 package가 독립 제품으로 소비되는 repo에서는 Changesets, release-please, cargo-release 같은 도구로 independent versioning을 운영할 수 있습니다. 이 repo에서는 SaMD release evidence와 CD handoff의 추적성을 우선해 root release tag를 기준으로 package metadata version을 맞춥니다.

Branch build와 수동 dev publish는 release tag를 만들지 않습니다. CI는 Git tag와 commit distance를 읽어 dev version을 계산합니다. SemVer 생태계는 0.2.0-dev.37처럼 prerelease version을 쓰고, Python은 PEP 440에 맞춰 0.2.0.dev37을 씁니다. Container image와 firmware artifact는 dev-37-g<sha>처럼 source commit이 보이는 tag/path를 사용합니다.

Go module은 module path와 VCS tag를 기준으로 publish합니다. 이 샘플처럼 Go module이 하위 디렉터리에 있으면 tag는 module directory prefix를 포함합니다.

source-repo/packages/patient-risk-go-core/v0.1.0

1-1. Ecosystem Version Commands

Version을 적용할 때는 각 생태계의 표준 도구를 우선 사용합니다.

Ecosystem Command Notes
npm npm version <version> --no-git-tag-version package metadata만 바꾸고 Git tag는 release process가 소유합니다.
Python uv version --package <name> <version> --frozen 또는 uv version --project <path> <version> --frozen PEP 440 version을 적용하고 lockfile 자동 변경은 막습니다. Workspace package는 --package, standalone package는 --project를 사용합니다.
Rust cargo set-version -p <crate> <version> cargo-edit가 Cargo manifest 갱신을 맡습니다.
Go git tag <module-path>/vX.Y.Z Go는 module version을 manifest가 아니라 VCS tag로 표현합니다.
C conan create <path> --version <version> recipe version override로 package revision을 만듭니다.

2. Package Publish

언어별 source-repo-<language>.yml workflow는 package unit/integration test, app validation, image build를 순서대로 수행합니다. Nexus publish는 root release tag 또는 명시적인 manual publish에서만 수행하고, nexus-release environment approval을 거칩니다.

main merge에서 package version을 계산하거나 artifact build를 검증할 수는 있지만, 그것이 곧 package publish를 뜻하지는 않습니다. Dev package publish는 실제 consumer가 release 전 package를 받아야 할 때만 별도 dev channel 또는 dev repository 기준을 따릅니다.

test -> build -> artifact upload -> environment approval -> Nexus publish

Nexus Rust Python package publish 대상은 아래와 같습니다.

Ecosystem Publish mechanism
npm npm publish to npm guide hosted repository
Python uv publish to PyPI guide hosted repository. Pure Python package와 PyO3/maturin Rust Python wheel은 artifact를 분리합니다.
Rust cargo publish --registry tirosh-nexus
Go VCS module tag, Nexus Go proxy download/cache
C conan create --version, conan upload to Conan guide hosted repository
Firmware .elf, .hex, .bin, .map, release manifest upload to the raw-embedded-guide-hosted repository

2-1. Package Artifact 위치

각 생태계의 artifact 위치는 review와 troubleshooting을 위해 명시합니다. CI에서 upload/publish가 실패했을 때 어떤 파일이 만들어졌는지 먼저 확인할 수 있어야 합니다.

Ecosystem Artifact
npm dist/npm/*.tgz
Python dist/python/*, dist/python-rust/*
Rust target/package/*.crate
C/Conan patient-signal-c-core/<version>
Go github.com/tirosh-chain/tirosh-dev-guide/source-repo/packages/patient-risk-go-core@vX.Y.Z
Firmware .elf, .hex, .bin, .map, .release-manifest.json

Go module은 VCS tag와 module proxy를 기준으로 배포합니다. 이 샘플의 Go module은 하위 디렉터리에 있으므로 source-repo/packages/patient-risk-go-core/v0.1.0처럼 module directory prefix를 포함한 tag를 push합니다. Tag가 push되면 Nexus Go proxy가 GOPROXY 경로에서 module을 받아 cache합니다.

Firmware release manifest는 firmware-release-manifest.v1 contract를 따릅니다. raw-embedded-guide-hosted repository에는 파일만 올리지 않고, 각 artifact의 kind, fileName, sizeBytes, sha256, source revision, firmware version을 함께 올려 release evidence를 추적합니다.

2-2. Platform-specific Artifact

Release review에서는 artifact가 platform-neutral인지 platform-specific인지 먼저 확인합니다. Platform-specific artifact는 파일 이름, package metadata, release manifest 중 하나로 target을 드러내야 합니다.

Artifact Platform contract Release 기준
npm package Platform-neutral Browser/runtime compatibility는 package test와 app smoke에서 검증합니다.
Pure Python package Platform-neutral wheel/sdist Python version range와 dependency lock을 검증합니다.
Rust Python wheel Linux architecture-specific linux/amd64manylinux_*_x86_64.whl, linux/arm64manylinux_*_aarch64.whl을 publish합니다.
Rust crate Source package Consumer가 target별로 compile하므로 crate publish 자체는 platform-neutral로 봅니다.
Go module Source module VCS tag와 module proxy를 기준으로 배포하고, binary artifact를 만들 때만 target matrix를 둡니다.
C/Conan package Profile-specific binary package OS, architecture, compiler, build type을 Conan profile로 고정합니다.
Firmware Board/toolchain-specific binary board, MCU, RTOS/bare-metal, toolchain version을 release manifest에 남깁니다.
Container image Docker platform-specific image variant linux/amd64, linux/arm64 image를 build/push하고 digest를 handoff합니다.

macOS나 developer workstation에서 만든 native wheel은 local smoke에는 쓸 수 있지만, Linux container release artifact로 publish하지 않습니다. Container image가 소비하는 Rust Python wheel은 Docker platform 기준으로 만든 Linux wheel이어야 합니다.

3. Container Publish

각 언어별 workflow는 package 검증 뒤 관련 app container build와 publish를 수행합니다.

  • image-build: pull request와 main push에서 image build 가능 여부를 검증합니다.
  • dev-image-publish: main merge에서 linux/amd64, linux/arm64 dev image를 dev, dev-<count>-g<sha>, sha-<sha> tag로 GHCR에 push합니다.
  • release-image-publish: root release tag 또는 수동 release workflow에서 GHCR에 release image를 push합니다.

Image repository 이름은 언어별 source repo CI workflow의 publish job이 소유합니다. CD repo는 release handoff에서 image repository와 tag 또는 digest를 받습니다.

Release publish job은 container-release environment approval을 거칩니다. BuildKit provenance와 SBOM attestation을 켜서 image와 source revision의 연결을 남깁니다. Main/dev image는 release artifact가 아니며 preview, smoke, troubleshooting을 위한 통합 검증 artifact로 봅니다.

4. Release Handoff

source repo release가 끝나면 CD repo로 넘겨야 하는 정보는 아래와 같습니다.

이 handoff는 단순히 image tag를 전달하는 일이 아닙니다. SaMD에서는 어떤 model/policy/code 변경이 어떤 test evidence와 release artifact로 이어졌는지 설명 가능해야 합니다.

  • image repository와 immutable tag 또는 digest
  • package version
  • runtime config 변경
  • health endpoint 또는 port 변경
  • migration 필요 여부
  • SaMD risk/control 영향 여부

CD repo는 이 정보를 받아 environment overlay의 image tag 또는 digest를 갱신합니다.

4-1. Handoff 예시

Images:
ghcr.io/tirosh-chain/guide/dev-guide-docs:v0.1.0
ghcr.io/tirosh-chain/guide/patient-risk-web:v0.1.0
ghcr.io/tirosh-chain/guide/patient-risk-python-api:v0.1.0
ghcr.io/tirosh-chain/guide/risk-monitor-operator:v0.1.0

Packages:
@tirosh/patient-monitor-ui-core@0.1.0
@tirosh/patient-monitor-react-components@0.1.0
patient-risk-python-core==0.1.0
patient-signal-python-rust==0.1.0
patient-signal-rust-core=0.1.0
patient-signal-c-core/0.1.0

Firmware:
wearable-firmware/0.1.0/wearable_firmware.elf
wearable-firmware/0.1.0/wearable-firmware.hex
wearable-firmware/0.1.0/wearable-firmware.bin
wearable-firmware/0.1.0/wearable-firmware.map
wearable-firmware/0.1.0/wearable-firmware.release-manifest.json

4-2. Release Review 기준

Release review에서는 아래를 함께 확인합니다.

Area Check
Change set release에 포함된 PR 목록, risk-based human review 필요 항목, self-review 상태를 확인합니다.
Version package/image/firmware version이 같은 source revision에서 계산되었는지 확인합니다.
Tests unit, integration, app smoke, demo smoke가 통과했는지 확인합니다.
Supply chain SBOM/provenance, dependency update, vulnerability scan 상태를 확인합니다.
SaMD impact requirement/risk control/test evidence 연결이 깨지지 않았는지 확인합니다.
Runtime contract port, health endpoint, required env, migration 여부가 CD repo에 전달되었는지 확인합니다.