Skip to content

릴리즈: 0.0.12 버전 정정 #14

릴리즈: 0.0.12 버전 정정

릴리즈: 0.0.12 버전 정정 #14

Workflow file for this run

name: Product Release
on:
push:
tags:
- "v*"
- "!v*-launcher"
env:
PYTHON_VERSION: "3.12"
PYTHON_RUNTIME_VERSION: "3.12.12"
PYTHON_RUNTIME_ASSET: python-runtime-win-x64.zip
RELEASE_MANIFEST_ASSET: release-manifest.json
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
build:
runs-on: ubuntu-latest
outputs:
package_version: ${{ steps.package.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
- name: Set up Python
run: uv python install ${{ env.PYTHON_VERSION }}
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
cache-dependency-path: editor/package-lock.json
- name: Build editor
working-directory: editor
run: |
npm ci
npm run build
- name: Verify packaged editor build
run: |
test -f src/codaro/webBuild/index.html
test -d src/codaro/webBuild/_app
- name: Run tests
run: uv run --extra dev pytest tests/ -v --tb=short
- name: Stage base curriculum into package
run: |
rm -rf src/codaro/curricula
cp -r curricula src/codaro/curricula
test -f src/codaro/curricula/python/__init__.py
- name: Build wheel
run: uv build
- name: Read package version
id: package
run: |
python - <<'PY' >> "$GITHUB_OUTPUT"
import tomllib
with open("pyproject.toml", "rb") as handle:
version = tomllib.load(handle)["project"]["version"]
print(f"version={version}")
PY
- name: Verify tag matches package version
run: |
if [ "${GITHUB_REF_NAME}" != "v${{ steps.package.outputs.version }}" ]; then
echo "tag ${GITHUB_REF_NAME} does not match package version v${{ steps.package.outputs.version }}" >&2
exit 1
fi
- name: Prepare package release assets
run: mkdir -p release-assets
- name: Generate package SPDX SBOM
uses: anchore/sbom-action@v0
with:
path: .
format: spdx-json
output-file: release-assets/codaro.spdx.json
- name: Verify wheel includes editor frontend and base curriculum
run: |
python - <<'PY'
from pathlib import Path
import zipfile
wheel = next(Path("dist").glob("codaro-*.whl"))
with zipfile.ZipFile(wheel) as archive:
names = set(archive.namelist())
if "codaro/webBuild/index.html" not in names:
raise SystemExit("codaro/webBuild/index.html missing from wheel")
if not any(name.startswith("codaro/webBuild/_app/") for name in names):
raise SystemExit("codaro/webBuild/_app/ assets missing from wheel")
if "codaro/curricula/python/__init__.py" not in names:
raise SystemExit("codaro/curricula/python/__init__.py missing from wheel")
lessonCount = sum(
1 for name in names
if name.startswith("codaro/curricula/python/") and name.endswith(".yaml")
)
if lessonCount == 0:
raise SystemExit("codaro/curricula/python/*.yaml lessons missing from wheel")
print(f"verified packaged editor frontend and {lessonCount} curriculum lessons in {wheel}")
PY
- name: Upload dist artifact
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
- name: Upload package release assets
uses: actions/upload-artifact@v4
with:
name: package-release-assets
path: release-assets/
windows-release-assets:
runs-on: windows-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install managed Python runtime
shell: pwsh
run: uv python install $env:PYTHON_RUNTIME_VERSION
- name: Package managed Python runtime
shell: pwsh
run: |
$python = (uv python find $env:PYTHON_RUNTIME_VERSION).Trim()
$runtimeRoot = Split-Path -Parent $python
$stage = Join-Path $env:RUNNER_TEMP "codaro-python-runtime"
$pythonStage = Join-Path $stage "python"
if (Test-Path $stage) {
Remove-Item -Recurse -Force $stage
}
New-Item -ItemType Directory -Force -Path $pythonStage | Out-Null
Copy-Item -Path (Join-Path $runtimeRoot "*") -Destination $pythonStage -Recurse -Force
# codaro 백엔드 런타임 의존성 + 커리큘럼 라이브러리를 런타임에 사전 설치한다. launcher provision은
# codaro wheel을 --no-deps로(오프라인) 설치하므로, 런타임이 의존성을 제공하지 않으면 (a) 백엔드가
# fastapi/pydantic 부재로 기동 실패하고, (b) 커리큘럼 레슨이 pandas/numpy/matplotlib 부재로 셀 실행
# 시 ModuleNotFoundError를 낸다. 셀은 백엔드 인터프리터에서 in-process로 실행되므로 레슨 라이브러리도
# 이 런타임에 있어야 한다. uv.lock 고정 버전을 curriculum extra 포함으로 export 해 설치한다.
$runtimePython = Join-Path $pythonStage "python.exe"
& $runtimePython -m ensurepip --upgrade | Out-Null
$depsFile = Join-Path $env:RUNNER_TEMP "codaro-backend-deps.txt"
uv export --no-dev --extra curriculum --no-emit-project --no-hashes --output-file $depsFile
if ($LASTEXITCODE -ne 0) { throw "uv export of backend + curriculum dependencies failed" }
& $runtimePython -m pip install --break-system-packages --disable-pip-version-check --no-warn-script-location -r $depsFile
if ($LASTEXITCODE -ne 0) { throw "failed to pre-install backend + curriculum dependencies into runtime" }
& $runtimePython -c "import fastapi, pydantic, uvicorn, yaml, requests; import pandas, numpy, matplotlib, sklearn, openpyxl; print('runtime backend + curriculum deps verified')"
if ($LASTEXITCODE -ne 0) { throw "runtime backend + curriculum dependency verification failed" }
New-Item -ItemType Directory -Force -Path "launcher/target/release" | Out-Null
$runtimeArchive = "launcher/target/release/$($env:PYTHON_RUNTIME_ASSET)"
Compress-Archive -Path (Join-Path $stage "python") -DestinationPath $runtimeArchive -Force
$hash = (Get-FileHash $runtimeArchive -Algorithm SHA256).Hash.ToLower()
"$hash $($env:PYTHON_RUNTIME_ASSET)" | Out-File -FilePath "$runtimeArchive.sha256" -Encoding utf8
- name: Build launcher release binary
working-directory: launcher/codaro-launcher
run: cargo build --release
- name: Rename launcher binary
shell: pwsh
run: |
Copy-Item "launcher/target/release/codaro-launcher.exe" "launcher/target/release/Codaro.exe"
- name: Compute launcher SHA256
shell: pwsh
run: |
$hash = (Get-FileHash "launcher/target/release/Codaro.exe" -Algorithm SHA256).Hash.ToLower()
"$hash Codaro.exe" | Out-File -FilePath "launcher/target/release/Codaro.exe.sha256" -Encoding utf8
- name: Generate launcher SPDX SBOM
uses: anchore/sbom-action@v0
with:
path: launcher/codaro-launcher
format: spdx-json
output-file: launcher/target/release/Codaro.exe.spdx.json
- name: Upload Windows release assets
uses: actions/upload-artifact@v4
with:
name: windows-release-assets
path: |
launcher/target/release/Codaro.exe
launcher/target/release/Codaro.exe.sha256
launcher/target/release/Codaro.exe.spdx.json
launcher/target/release/${{ env.PYTHON_RUNTIME_ASSET }}
launcher/target/release/${{ env.PYTHON_RUNTIME_ASSET }}.sha256
release:
needs: [build, windows-release-assets]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Download Windows release assets
uses: actions/download-artifact@v4
with:
name: windows-release-assets
path: release-assets/
- name: Download package release assets
uses: actions/download-artifact@v4
with:
name: package-release-assets
path: release-assets/
- name: Download backend dist
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Prepare backend wheel release asset
id: backend_wheel
shell: bash
run: |
set -euo pipefail
wheel_count="$(find dist -maxdepth 1 -name 'codaro-*.whl' | wc -l | tr -d ' ')"
if [ "$wheel_count" != "1" ]; then
echo "expected exactly one codaro wheel in dist, found $wheel_count" >&2
find dist -maxdepth 1 -type f -print >&2
exit 1
fi
wheel_path="$(find dist -maxdepth 1 -name 'codaro-*.whl' -print -quit)"
wheel_name="$(basename "$wheel_path")"
wheel_sha="$(sha256sum "$wheel_path" | awk '{print $1}')"
cp "$wheel_path" "release-assets/$wheel_name"
printf '%s %s\n' "$wheel_sha" "$wheel_name" > "release-assets/$wheel_name.sha256"
echo "name=$wheel_name" >> "$GITHUB_OUTPUT"
echo "sha256=$wheel_sha" >> "$GITHUB_OUTPUT"
- name: Build release manifest
run: |
python docs/skills/ops/tools/buildReleaseManifest.py \
--tag "${GITHUB_REF_NAME}" \
--repo "${GITHUB_REPOSITORY}" \
--package-version "${{ needs.build.outputs.package_version }}" \
--backend-wheel-url "https://github.com/${GITHUB_REPOSITORY}/releases/download/${GITHUB_REF_NAME}/${{ steps.backend_wheel.outputs.name }}" \
--backend-sha256 "${{ steps.backend_wheel.outputs.sha256 }}" \
--python-runtime-version "${PYTHON_RUNTIME_VERSION}" \
--python-runtime-asset-name "${PYTHON_RUNTIME_ASSET}" \
--python-runtime-archive "release-assets/${PYTHON_RUNTIME_ASSET}" \
--output "release-assets/${RELEASE_MANIFEST_ASSET}"
- name: Generate release notes from changelog
shell: bash
run: |
set -euo pipefail
python docs/skills/ops/tools/extractChangelogSection.py \
--version "${{ needs.build.outputs.package_version }}" \
--output release-assets/release-notes.md
cat >> release-assets/release-notes.md <<'EOF'
---
Download `Codaro.exe`, verify `Codaro.exe.sha256`, then run the launcher. The release also includes `release-manifest.json`, the exact `codaro` backend wheel, a managed Windows Python runtime archive, checksums, and SPDX SBOM assets. The manifest pins the wheel asset and sha256 from this GitHub Release.
EOF
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
target_commitish: ${{ github.sha }}
name: Codaro ${{ github.ref_name }}
fail_on_unmatched_files: true
body_path: release-assets/release-notes.md
files: |
release-assets/Codaro.exe
release-assets/Codaro.exe.sha256
release-assets/Codaro.exe.spdx.json
release-assets/python-runtime-win-x64.zip
release-assets/python-runtime-win-x64.zip.sha256
release-assets/codaro-*.whl
release-assets/codaro-*.whl.sha256
release-assets/release-manifest.json
release-assets/codaro.spdx.json
generate_release_notes: true