릴리스 주기 런북
로컬 게이트에서 서명된 GitHub Release를 거쳐 npm 게시에 이르는 완전한 릴리스 워크플로입니다.
이 런북은 apothem의 vX.Y.Z 릴리스를 깨끗한 작업 트리에서 검증된 배포
표면까지 이끕니다. 이 절차는 gh, git, 그리고 릴리스 등급 셸을 다루는
메인테이너를 위해 작성되었으며, 검증된 Release 페이지, 기록된 CHANGELOG,
그리고 사용자 대상의 각 표면이 정식 설치 명령에 응답하는지 확인하는 설치 경로
스모크 테스트로 끝납니다.
릴리스 모델은 추가형입니다. 각 릴리스는 main에 커밋 하나와 서명된 주석
vX.Y.Z 태그 하나를 추가합니다. 어떤 것도 강제 푸시되지 않으며, 이전의 태그,
GitHub Release, npm 버전은 어느 것도 제거되지 않습니다. 각 릴리스는 자체 태그, 자체 GitHub Release, 자체 npm 버전을 유지하며, 모두 게시된 상태로 남습니다. npm 버전은 변경할 수 없고(게시된
name@version은 절대 교체할 수 없음), 고정된 설치는 태그에 의존하기
때문입니다.
태그 푸시는 세 워크플로를 시작합니다. .github/workflows/release.yml은 전체
아티팩트 세트, 즉 sdist, wheel, CycloneDX SBOM, Sigstore cosign 번들, SLSA-3
출처, 플랫폼 런타임 아카이브와 함께 GitHub Release를 빌드하고, 서명하고,
증명하고, 게시합니다. publish-npm.yml은 npm Trusted Publishing을 통해
@ahmed-g-gad/apothem을 npmjs.com에 게시하고, publish-vscode.yml은 VS Code 확장을
패키징하여 Visual Studio Marketplace에 게시합니다. 버전이 매겨진 CHANGELOG
섹션은 릴리스 커밋에 안착하며, 운영자는 두 워크플로가 모두 끝난 후 설치 경로
스모크 테스트를 실행합니다.
1. 전제 조건
메인테이너는 태그하기 전에 다음을 확인합니다.
-
작업 트리. 깨끗합니다(
git status에 보류 중인 항목이 없음). 릴리스를 위한 모든 기능 작업이main에 있습니다. -
CI 녹색. 최신
main에서gh run list --branch=main --limit=1 --json conclusion --jq '.[0].conclusion'이success를 반환합니다. -
도구.
ahmed-g-gad에 대해 인증된gh(GitHub CLI) 버전 2.40 이상, GPG 서명 하위 키가 로드된git2.40 이상이 있고,@ahmed-g-gad/apothem에 대한 npm 신뢰할 수 있는 게시자 바인딩이 활성 상태입니다 (게시자 계정 설정 런북에 따라 검증). -
버전 앵커 정렬.
pyproject.toml에 선언된 버전 문자열이v접두사를 뺀 계획된 태그와 일치합니다(§2.1이 다른 모든 앵커와 함께 이 값을 설정함). 검증:grep '^version' pyproject.toml예상 출력(
vX.Y.Z릴리스의 경우, 플레이스홀더는 진행 중인 릴리스 태그를 나타냄):version = "X.Y.Z" -
CHANGELOG 항목 스테이징. 릴리스 항목이 Keep-a-Changelog 헤딩 형식으로
## [Unreleased]아래에 있습니다. §2.1이 태그를 안착시키는 커밋에서 이 항목들을 날짜가 표기된 버전 섹션으로 바꿉니다.
2. 태그 지정과 트리거
2.1 릴리스 커밋 작성
버전은 열두 개가 넘는 파일에 들어 있으므로 손으로 편집하지 마세요. 명령 하나가 그 모든 파일을 함께 옮깁니다.
git checkout main
git pull --ff-only origin main
python scripts/release/bump_version.py X.Y.Zbump_version.py는 모든 버전 앵커를 함께 업데이트합니다.
-
모든 패키징, 확장, 플러그인, 마켓플레이스 매니페스트와 인용 파일(버전과 릴리스 날짜). 전체 목록:
pyproject.toml package.json site/package.json site/package-lock.json (the project entries only) vscode-extension/package.json gemini-extension.json qwen-extension.json .claude-plugin/marketplace.json (top level and metadata) plugins/apothem/plugin.json plugins/apothem/.codex-plugin/plugin.json CITATION.cff -
CHANGELOG.md:[Unreleased]항목이 날짜가 표기된## [X.Y.Z]섹션이 되고,[Unreleased]는vX.Y.Z부터 비교하며,[X.Y.Z]링크가 추가됩니다. -
SECURITY.md: 새 마이너 버전에는 지원되는X.Y.x행이 추가됩니다. 이전 마이너 버전을 목록에 남길지는 직접 결정합니다. 스크립트가 알림을 출력합니다.
그런 다음 스크립트는 파생 트리, 즉 Claude Code 플러그인 패키지, 동작 diff
골든, 문서 레퍼런스 페이지와 변경 로그 페이지를 다시 생성합니다. 변경 사항을
먼저 나열하려면 --dry-run을, 릴리스 날짜를 설정하려면 --date YYYY-MM-DD를(기본값: UTC 기준 오늘), 재생성을 건너뛰려면 --no-regen을
전달합니다. 스크립트는 형식이 잘못된 버전이나 다운그레이드를 거부하며, 현재
버전으로 다시 실행하면 어긋난 앵커만 다시 정렬합니다.
diff를 검토한 다음 커밋합니다.
git add -A
git commit -S -m "chore(release): cut vX.Y.Z"
git push origin main-S 플래그는 GPG 키로 커밋에 서명합니다. 푸시는 표준 CI 매트릭스(lint, test,
build)를 트리거합니다. 태그하기 전에 녹색이 될 때까지 기다리세요.
§2.4의 준비 상태 검사는 릴리스 커밋에서 CodeQL과 Conformity도 녹색이어야 합니다. 두 워크플로는 각자의 경로가 바뀔 때만 실행되는데, 릴리스 커밋은 보통 그 경로 중 어느 것도 바꾸지 않습니다. 그래서 릴리스 커밋이 main의 최신 커밋(HEAD)이 되면 두 워크플로를 시작하고, 각 실행이 그 커밋을 가리키는지 확인하세요:
gh workflow run codeql.yml --ref main
gh workflow run conformity.yml --ref main
gh run list --workflow codeql.yml --commit "$(git rev-parse HEAD)" --limit 1
gh run list --workflow conformity.yml --commit "$(git rev-parse HEAD)" --limit 1태그하기 전에 두 워크플로가 모두 성공할 때까지 기다리고, 릴리스가 완료될 때까지 main에 다른 것을 푸시하지 마세요. 나중의 푸시는 main이 릴리스 커밋에서 벗어나게 하며, CI는 같은 브랜치에서 아직 진행 중인 실행을 취소합니다.
2.2 태그 서명과 푸시
git tag -a -s vX.Y.Z -m "vX.Y.Z"
git push origin vX.Y.Z-a -s 조합은 GPG로 서명된 주석 태그를 생성합니다. 푸시는
.github/workflows/release.yml을 트리거하며, 이 워크플로는 빌드하고,
서명하고, 증명하고, 게시합니다.
2.3 릴리스 워크플로 완료 확인
gh run watch $(gh run list --workflow=release.yml --limit=1 --json databaseId --jq '.[0].databaseId')워크플로는 빌드(sdist + wheel, 그다음 이 배포판들의 CycloneDX SBOM을 그 옆에
기록), 키리스 cosign 서명, SLSA-3 출처 생성과 검증, 플랫폼 아카이브
빌드(darwin / linux / windows), 집계된 SHA256SUMS 매니페스트를 사용한
아카이브 서명, 그리고 최종 GitHub Release 게시를 실행합니다. 메인테이너는
publish GitHub Release 작업 로그에서 자산 업로드 확인 메시지를 훑어봅니다.
publish GitHub Release 작업은 메인테이너의 승인이 필요한 release 환경에서 실행되므로, 앞선 작업들이 통과하면 실행이 그 자리에서 대기합니다. 실행 페이지의 Review deployments(배포 검토)에서 승인하거나, 셸에서 승인하세요:
run_id=$(gh run list --workflow=release.yml --limit=1 --json databaseId --jq '.[0].databaseId')
env_id=$(gh api repos/ahmed-g-gad/apothem/environments/release --jq .id)
gh api -X POST "repos/ahmed-g-gad/apothem/actions/runs/${run_id}/pending_deployments" \
-F "environment_ids[]=${env_id}" -f state=approved -f comment="vX.Y.Z"태그 푸시는 npm 워크플로와 VS Code 확장 워크플로도 시작합니다. 두 워크플로의 게시 작업도 같은 승인을 기다리며, 그 승인은 §2.4에서 다룹니다.
2.4 npm 게시 확인
태그 푸시는 release.yml과 나란히 publish-npm.yml도 실행합니다. npm 워크플로는 릴리스 워크플로를 기다리지 않으며, release 환경 승인이 게이트입니다. §2.3의 릴리스 워크플로가 끝나면 준비 상태를 확인하세요. 이 검사는 릴리스, 워크플로 실행, 게시된 SBOM을 읽으므로, 먼저 두 가지 입력을 준비하세요:
GH_TOKEN. 스크립트는gh에 이미 로그인되어 있더라도 이 값이 없으면 실행을 거부합니다.ahmed-g-gad/apothem으로 범위를 제한한 세분화된 개인 액세스 토큰을 사용하고, Actions: Read(워크플로 실행)와 Contents: Read(릴리스)를 부여하세요. 이 검사는 아무것도 쓰지 않으므로 쓰기 권한은 필요하지 않습니다.gh auth token으로 얻은 토큰은 로그인의 모든 범위를 가지므로, 세분화된 토큰을 권장합니다.- 게시된 SBOM. 검사는 릴리스가 첨부한 CycloneDX 파일
dist/release-assets/sbom.cdx.json을 읽습니다. 로컬 빌드 대신 릴리스가 게시한 사본을 다운로드하세요.
read -rs GH_TOKEN && export GH_TOKEN
mkdir -p dist/release-assets
gh release download vX.Y.Z --repo ahmed-g-gad/apothem \
--pattern sbom.cdx.json --dir dist/release-assets --clobber
bash scripts/release/check-release-ready.sh vX.Y.Z검사는 릴리스가 초안이거나 프리릴리스인 경우, 태그 커밋에 필요한 워크플로가 녹색이 아닌 경우, SBOM이 없는 경우, 다른 버전을 기술하는 경우, 벤더링된 패키지를 빠뜨린 경우에 실패합니다. 실패 메시지는 원인을 알려 줍니다.
준비 상태 검사가 통과하면, §2.3과 같은 방식으로 대기 중인 Publish npm 실행을 승인하세요(실행 ID는 gh run list --workflow=publish-npm.yml에서 가져옵니다). 워크플로는 npm Trusted Publishing(OIDC)을 통해 @ahmed-g-gad/[email protected]를 npmjs.com에 게시하며, 저장소에는 레지스트리 토큰이 저장되지 않습니다. 대기 중인 Publish VS Code Extension 실행도 승인하세요. 이 워크플로는 apothem.vsix를 패키징하고 Visual Studio Marketplace에 ahmed-g-gad.apothem-vscode로 게시합니다. 게시 단계는 저장소 변수 VSCODE_MARKETPLACE_READY가 true이고 자격 증명 하나가 구성되어 있을 때에만 실행됩니다. 그 자격 증명은 변수 VSCE_AUTH=oidc(Marketplace Trusted Publishing, 저장된 시크릿 없음), 변수 VSCE_AUTH=entra, AZURE_CLIENT_ID, AZURE_TENANT_ID(수명이 짧은 Microsoft Entra ID 토큰, 저장된 시크릿 없음), 또는 VSCE_PAT 시크릿입니다. 어느 것도 없으면 실행은 확장을 패키징하기만 하고 게시는 건너뜁니다. 자격 증명은 재시도가 아니라 스위치입니다. 한 경로에서 실패한 게시가 스스로 다른 경로로 넘어가지는 않습니다. 각 경로의 설정은 아래의 Trusted Publishing을 사용한 Marketplace 게시(권장)와 Microsoft Entra ID를 사용한 Marketplace 게시에 있습니다.
실패한 실행 후 다시 게시하려면 같은 태그에 대해 워크플로를 디스패치합니다
(gh workflow run publish-npm.yml --field tag=vX.Y.Z). npm 버전은 변경할 수
없기 때문에, 버전이 이미 레지스트리에 있으면 워크플로는 게시를 건너뜁니다.
Trusted Publishing을 사용한 Marketplace 게시
Trusted Publishing을 사용하면 Marketplace가 이 저장소의 GitHub OIDC 토큰을
직접 받아들입니다. vsce publish --oidc는 게시 작업의 토큰을 수명이 짧은
Marketplace 자격 증명으로 교환합니다. 저장된 시크릿도 Azure 리소스도 없으므로,
이 경로를 권장합니다. 아래의 Entra 경로와 VSCE_PAT 시크릿은 운영자가 선택하는
대안으로 남습니다.
-
이를 지원하는 vsce 릴리스를 사용합니다. 이 경로에서 게시 작업은 게시하기 전에
tools/vsce에 잠긴 vsce 릴리스를 확인하고, 잠금이 더 오래된 경우 최소 릴리스를 명시하는 오류와 함께 중단됩니다. Dependabot이 그 잠금 파일에 대한 vsce 업데이트를 제안하므로, 전환하기 전에 최소 릴리스에 도달하는 업데이트를 병합하십시오. -
정책을 만듭니다. 게시자의 Owner로 로그인하여
https://marketplace.visualstudio.com/manage/publishers/ahmed-g-gad를 열고, Trusted Publishing 아래에 정책을 추가합니다.- 정책 이름:
apothem-release같은 임의의 이름 - 저장소 소유자:
Ahmed-G-Gad - 저장소:
apothem - 워크플로:
publish-vscode.yml - 환경:
release
필드 이름은 VS Code 게시 가이드의 GitHub Actions와 Trusted Publishing으로 게시하기 섹션에 나열된 이름입니다 (
https://code.visualstudio.com/api/working-with-extensions/publishing-extension). 확인되지 않음: 모든 게시자에게 아직 Trusted Publishing 탭이 보이는지 여부. 보이지 않는다면 Entra 경로를 사용하십시오. - 정책 이름:
-
경로를 전환합니다.
VSCODE_MARKETPLACE_READY를true로 유지하고 선택자를 설정합니다. 다음 태그 푸시가 이 경로로 게시하므로, 대기 중인 태그가 없을 때 설정하십시오.gh variable set VSCE_AUTH --body oidc -
게시합니다. 다음 태그 푸시나 기존 태그에 대한 디스패치가 Trusted Publishing으로 게시합니다. 이 경로에는 드라이 런이 없습니다. 첫 게시가 곧 검사이며,
dry_run을 지정한 디스패치는 아무것도 검증되지 않았다고 경고만 합니다. -
실패하면 대체 경로로 돌아갑니다. 선택자를 삭제하고 같은 태그에 대해 다시 디스패치합니다.
VSCE_PAT시크릿이 설정되어 있으면 토큰 경로가 게시합니다. 실패한 게시는 아무것도 게시하지 않았으므로, 같은 태그를 다시 디스패치할 수 있습니다.gh variable delete VSCE_AUTH
Microsoft Entra ID를 사용한 Marketplace 게시
Entra 경로는 저장된 개인 액세스 토큰을 수명이 짧은 토큰으로 대체합니다.
publish-vscode.yml이 GitHub에 OIDC 토큰을 요청하면, Azure/login 액션이
페더레이션 자격 증명을 통해 그 토큰을 Entra 로그인으로 교환하고,
vsce publish --azure-credential이 그 결과로 얻은 토큰으로 게시합니다. 클라이언트
시크릿은 존재하지 않습니다. VSCE_PAT 시크릿은 운영자가 선택하는 대체 수단으로
남으며, 워크플로가 스스로 그것으로 전환하는 일은 결코 없습니다.
tools/vsce에 고정된 vsce 릴리스는 이 경로를 지원합니다. 이 경로에는 Entra
테넌트, 애플리케이션 등록, 게시자 멤버십이 필요하며, 위의 Trusted Publishing은
이것들이 필요하지 않습니다. 게시자가 Trusted Publishing을 사용할 수 없을 때 이
경로를 사용하십시오.
-
전제 조건을 확인합니다. 게시자
ahmed-g-gad를 소유한 계정 (https://marketplace.visualstudio.com/manage/publishers/ahmed-g-gad의 Members 탭)과 애플리케이션을 등록할 수 있는 Entra 테넌트를 찾습니다. 확인되지 않음: 게시자 소유자의 테넌트가 애플리케이션의 테넌트와 일치해야 하는지 여부, 그리고 Azure DevOps 조직Ahmed-G-Gad가 Entra에 연결되어 있어야 하는지 여부 (dev.azure.com/Ahmed-G-Gad, Organization settings, Microsoft Entra). 둘 중 어느 것도 밝히는 출처가 없습니다. 5단계나 6단계가 실패하면 이것부터 확인하십시오. -
애플리케이션을 등록합니다.
https://entra.microsoft.com에서 App registrations를 연 다음 New registration을 엽니다. 이름을apothem-vscode-publish로 지정하고, 단일 테넌트를 선택하며, 리디렉션 URI는 비워 둡니다. Overview에서 Application (client) ID와 Directory (tenant) ID를 복사합니다. 클라이언트 시크릿은 만들지 않고 API 권한도 부여하지 않습니다. 이 단계에는 애플리케이션 소유자 권한이나 Application Administrator 같은 Entra 역할이 필요합니다. -
페더레이션 자격 증명을 추가합니다. 해당 애플리케이션에서 Certificates & secrets, Federated credentials, Add credential을 열고, 시나리오 "GitHub Actions deploying Azure resources"를 선택합니다. 조직
Ahmed-G-Gad, 저장소apothem, 엔터티 유형Environment, 환경 이름release, 자격 증명 이름apothem-release를 입력합니다. 생성된 필드는 다음과 같아야 합니다.issuer: https://token.actions.githubusercontent.com subject: repo:Ahmed-G-Gad/apothem:environment:release audience: api://AzureADTokenExchange일치 검사는 정확하며 대소문자를 구분합니다. 이후 로그인이 AADSTS700213이나 AADSTS7002138로 실패하면, 자격 증명을
Azure/login이 작업 로그에 기록하는subject claim줄과 비교하십시오. 생성 직후의 AADSTS70021은 자격 증명이 아직 전파되지 않았다는 뜻입니다. 몇 분 기다린 후 다시 시도하십시오. -
저장소 변수를 설정합니다.
VSCODE_MARKETPLACE_READY가true인 동안에는 세 변수가 모두 설정되는 즉시 Entra 경로가 활성화되므로, 승인을 기다리는 릴리스 태그가 없을 때 설정하십시오.VSCODE_MARKETPLACE_READY=true가 없으면 아무것도 로그인하지 않으며 실행은 게시를 건너뜁니다.gh variable set AZURE_CLIENT_ID --body "<application-client-id>" gh variable set AZURE_TENANT_ID --body "<directory-tenant-id>" gh variable set VSCE_AUTH --body entraVSCODE_MARKETPLACE_READY와VSCE_PAT는 그대로 두십시오.VSCE_AUTH가entra인데AZURE_CLIENT_ID나AZURE_TENANT_ID가 비어 있으면, 워크플로는VSCE_PAT의 설정 여부와 관계없이 경고를 기록하고 Entra 경로를 사용하지 않습니다. 그런 다음VSCE_PAT시크릿이 설정되어 있으면 그것을 사용합니다. 시크릿이 없으면 아무것도 게시하지 않으면서도 실행은 녹색으로 끝나므로, 경고를 찾아보십시오. -
드라이 런을 실행합니다. 기존 릴리스 태그에 대해
dry_run을 설정하고 워크플로를 디스패치합니다. Entra 경로에서는 로그인하고, Azure DevOps 프로필을 출력하며, 게시하지 않고 게시자 접근 권한을 검증합니다. 토큰 경로에는 드라이 런이 없습니다. 로그에Marketplace credential mode: pat가 보이면, 실행은 경고와 함께 녹색으로 끝나며 아무것도 검증하지 않은 것입니다. 대기 중인release환경 실행을 승인하십시오.gh workflow run publish-vscode.yml --ref main -f tag=vX.Y.Z -f dry_run=true예상 결과:
Marketplace credential mode: entra, 3단계의 subject와 같은subject claim줄, 그리고 첫 번째 드라이 런 단계가 출력한 프로필id와displayName. 검증 단계는 6단계 전까지 403으로 실패하는 것이 예상된 동작입니다. 확인되지 않음: Azure 구독 없이 로그인해도 Azure CLI를 통해 Marketplace용 토큰을 얻을 수 있는지 여부. 이 실행이 그에 대한 첫 시험입니다. 여기서 실패하면, 대신 구독에 사용자 할당 관리 ID를 등록하십시오. 이것이 Microsoft가 문서화한 경로입니다. -
애플리케이션을 게시자에 추가합니다. 1단계의 Members 탭에서 게시자 Owner로 로그인하고, 5단계의 프로필
id를 사용하여 역할 Contributor로 멤버를 추가합니다. 확인되지 않음: 대화 상자의 필드 레이블, 그리고 애플리케이션이 Azure DevOps 조직의 멤버가 되기 전에 대화 상자가 그 id를 받아들이는지 여부. Azure DevOps는 일반적으로 애플리케이션 ID를 조직별로 추가합니다. 대화 상자가 id를 거부하면, 서비스 주체를 조직Ahmed-G-Gad에 추가하고(Organization settings, Users, Enterprise application Object ID 사용) 다시 시도하십시오. 역시 확인되지 않음: 해당 ID가 처음으로 Azure DevOps를 호출하기 전에 프로필이 존재하는지 여부. -
드라이 런을 반복합니다. 이제 검증 단계는 "verification succeeded for the publisher"와 함께 성공합니다. Entra 토큰이 같은 경로를 거치기 때문에, 그 메시지에는 여전히 Personal Access Token이라고 표시됩니다. 이 검사는 게시 권한이 아니라 게시자 멤버십(Creator, Owner, Contributor 또는 Reader)을 증명합니다. 확인되지 않음: 업데이트를 게시하는 데 Contributor로 충분한지 여부. Microsoft의 역할 설명은 충분하다고 하지만 그 역할로 게시한 사례를 보여 주는 출처는 없으므로, 첫 실제 게시가 그 증거가 됩니다.
-
릴리스를 게시합니다. §2.2와 같이 태그하고 실행을 승인합니다.
Publish to Visual Studio Marketplace (Entra ID)단계가 실행되고, 그 환경 헤더에는AZURE_TENANT_ID만 나열되며, 토큰 대체 단계는 건너뛰어지고, vsce가DONE Published ahmed-g-gad.apothem-vscode를 출력합니다. Marketplace 갤러리에는 약 4분 후에 새 버전이 나열됩니다. -
토큰을 어떻게 할지 결정합니다. 첫 Entra 게시가 성공할 때까지
VSCE_PAT를 유지하고, 대체 수단이 필요 없다면 그다음에 시크릿을 삭제하십시오. 시크릿이 없어지면, 삭제되었거나 비어 있는 Entra 변수는 게시 없이 실행을 녹색으로 남깁니다.VSCE_AUTH가 여전히entra이면 경고와 함께, 그렇지 않으면 건너뛰기 알림만 남깁니다. 각 태그 후에 실행의 주석을 읽으십시오. 전역 개인 액세스 토큰이 폐지된 이후에도 대체 수단을 유지하려면, 사용자 지정 범위 Marketplace Manage와 허용할 수 있는 가장 짧은 만료 기간으로Ahmed-G-Gad에 대한 조직 범위 토큰을 만들고VSCE_PAT로 저장하십시오. 확인되지 않음: 이 단계의 포털 레이블은 1차 출처가 아니라 기억에 기반한 것입니다. Microsoft는 조직 범위 토큰을 임시 해결책이라고 부르며 이를 더 이상 허용하지 않을 수 있습니다. 또한 Microsoft의 페이지들은 전역 토큰의 폐지 날짜에 대해 서로 다르게 기술합니다. -
토큰으로 돌아갑니다. 선택자를 삭제하고 다시 디스패치한 다음 실행을 승인합니다. 실패한 Entra 게시는 아무것도 게시하지 않았으므로 같은 태그를 다시 디스패치할 수 있습니다. 다만 안착한 버전은 두 번 게시할 수 없으므로, 먼저 Marketplace 페이지를 확인하십시오.
gh variable delete VSCE_AUTH gh workflow run publish-vscode.yml --ref main -f tag=vX.Y.Z
Entra 게시가 실패하면 단계 로그를 읽으십시오.
- "Can not acquire a Microsoft Entra ID access token"으로 시작하는 메시지는
로그인이나 자격 증명 체인이 실패했다는 뜻입니다. 먼저
Sign in to Microsoft Entra ID단계와 페더레이션 자격 증명을 확인하십시오. 로그인이 녹색이면, 워크플로에서 vsce 단계의env에서AZURE_TENANT_ID를 제거하십시오. GitHub 자체의 확장 릴리스 워크플로는 이를 내보내지 않습니다. - "InvalidAccessException"이나 "not allowed"는 토큰의 ID가 게시자 멤버가 아니라는
뜻입니다. 드라이 런의 프로필
id를 Members 탭과 비교하십시오. - 드라이 런 검증 단계의 403은 애플리케이션에 아직 역할이 없다는 뜻입니다.
이 경로에는 세 가지 제한이 적용됩니다.
id-token: write는 게시 작업 전체에 부여되므로, 토큰 모드에서도 그 작업의 모든 단계가 OIDC 토큰을 요청할 수 있습니다. 토큰의 subject는 검토자의 승인이 필요한release환경에 바인딩됩니다.release환경에는 배포 브랜치 또는 태그 정책이 없으므로, 메인테이너가 승인하는 모든 ref가 환경 subject 토큰을 얻을 수 있습니다. 이를v*.*.*태그로 제한하려면 먼저release.yml과publish-npm.yml이 무엇을 디스패치하는지 확인해야 합니다.- 저장소 이름 변경이나 이전은 OIDC subject 형식을 바꾸어 페더레이션 자격 증명을
조용히 깨뜨립니다. 이름을 변경한 후에는
Azure/login로그의 subject를 자격 증명에 다시 복사하십시오.
2.5 템플릿 다이제스트 고정값 갱신
태그가 생성된 후, tests/unit/test_template_digests.py의 _LATEST_RELEASE와
_LATEST_RELEASE_DIGESTS를 새 릴리스와, 그 릴리스가 배포한 각 일반 텍스트
템플릿의 SHA-256 다이제스트(git show vX.Y.Z:<template path> | sha256sum)로
설정합니다. 이 작업은 main에 대한 다음 변경에서 수행하고, 템플릿이 바뀐 경우
이전 다이제스트를 매니페스트 항목의 earlier_digests에 추가하십시오.
release-preflight CI 작업은 모든 태그를 체크아웃하므로, 고정값이 갱신될 때까지
태그 이름과 함께 실패합니다.
3. 배포 표면
각 표면에는 "릴리스가 여기에 안착했는가?"에 답하는 검증 명령이 있습니다.
| # | 표면 | 아티팩트 | 검증 |
|---|---|---|---|
| 1 | GitHub Release(Python 배포판) | apothem-X.Y.Z.tar.gz(sdist) + apothem-X.Y.Z-py3-none-any.whl | gh release view vX.Y.Z --json assets --jq '.assets[].name'에 두 파일명이 모두 포함된다 |
| 2 | GitHub Release(SBOM) | sbom.cdx.json + sbom.cdx.json.cosign.bundle | 자산 목록에 둘 다 포함된다. SBOM의 metadata.component.purl은 pkg:pypi/[email protected]이고, 그 구성 요소에는 src/apothem/_vendor/vendor.txt의 모든 패키지가 포함된다 |
| 3 | GitHub Release(서명) | sdist, wheel, SBOM, VS Code 확장, 플랫폼 아카이브, SHA256SUMS마다 하나씩 있는 <asset>.cosign.bundle(이전 릴리스에서는 아카이브와 SHA256SUMS 번들의 이름이 <asset>.sig) | §5.5의 정확한 신원으로 실행한 cosign verify-blob이 0으로 종료한다 |
| 4 | GitHub Release(출처) | provenance.intoto.jsonl | bash scripts/release/verify-provenance.sh <download-dir>이 0으로 종료한다(§5.5) |
| 5 | GitHub Release(플랫폼 아카이브) | apothem-vX.Y.Z-darwin.tar.gz, apothem-vX.Y.Z-linux.tar.gz, apothem-vX.Y.Z-windows.zip | 자산 목록에 모든 아카이브와 SHA256SUMS가 포함된다. darwin 아카이브와 linux 아카이브는 두 이름 아래의 같은 바이트이므로(런타임은 플랫폼 중립적이며, --platform은 tar.gz와 zip 중 하나만 고름), 두 아카이브의 SHA256SUMS 줄에는 같은 다이제스트가 두 번 나타난다 |
| 6 | GitHub Release(VS Code 확장) | apothem.vsix + apothem.vsix.cosign.bundle | 자산 목록에 둘 다 포함된다. code --install-extension apothem.vsix로 설치된다. 릴리스 워크플로가 확장을 패키징하기 전의 릴리스에는 둘 다 없다 |
| 7 | npmjs.com | @ahmed-g-gad/[email protected] | npm view @ahmed-g-gad/apothem version이 X.Y.Z를 반환한다. npx @ahmed-g-gad/[email protected] --version이 Apothem, version X.Y.Z를 출력한다 |
| 8 | Pages(스크립트 설치 프로그램) | 문서 사이트의 install.sh / install.ps1 | curl -fsSL https://apothem.ahmedgad.com/install.sh | head -n 1이 스크립트 헤더를 반환한다 |
| 9 | Visual Studio Marketplace | ahmed-g-gad.apothem-vscode 버전 X.Y.Z | 확장의 Marketplace 페이지에 X.Y.Z가 나열된다. code --install-extension ahmed-g-gad.apothem-vscode로 설치된다 |
GitHub Release는 정식 아티팩트 표면이며, npm 패키지와 스크립트 설치 프로그램은 그 위에 얹힌 설치 경로입니다. npm 게시는 같은 태그 푸시에서 릴리스 워크플로와 나란히 실행됩니다.
4. CHANGELOG 규율
CHANGELOG.md는 Keep-a-Changelog 관례에 따라 릴리스마다 섹션 하나를 담습니다.
릴리스 커밋의 CHANGELOG 편집은 다음을 수행합니다.
- 버전 섹션이
## [X.Y.Z] - YYYY-MM-DD헤딩을 사용하는지 확인합니다(하이픈 사용.bump_version.py가 이 헤딩을 기록하고,extract_release_notes.py는## [X.Y.Z]접두사를 매칭함). - 표준 헤딩, 즉 Added, Changed, Deprecated, Removed, Fixed, Security가 해당하는 곳에 사용되는지 확인합니다. 빈 헤딩은 렌더링된 파일에서 제외됩니다.
- 구현이 아니라 역량을 담습니다. 도메인 언어만 사용하고, 플랜 내부 참조, 커밋 해시, 플랜이나 페이즈 식별자는 넣지 않습니다.
메인테이너의 검토를 통과한 CHANGELOG 섹션은 공지 내러티브를 위한 릴리스 노트 앵커가 됩니다.
5. 설치 경로 스모크 테스트
두 단계가 모두 완료된 후, 메인테이너는 스모크 테스트를 실행합니다. 스모크 테스트는 사용자 대상 설치 명령이 응답하는지에 대한 정식 검증입니다.
5.1 Claude Code 플러그인
/plugin marketplace add ahmed-g-gad/apothem
/plugin install apothem@apothem예상 결과: 플러그인이 설치되고 Claude Code 안에서 apothem 명령을 사용할 수
있습니다.
5.2 Node.js (npx)
npx @ahmed-g-gad/[email protected] --version예상 출력: Apothem, version X.Y.Z.
5.3 원샷 스크립트 설치 프로그램(POSIX)
설치 프로그램은 apothem 런처를 ~/.local/bin에 기록하거나, APOTHEM_BIN_DIR 변수가 설정된 경우 그 변수가 지정한 디렉터리에 기록합니다. 아래의 검사를 하려면 그 디렉터리가 PATH에 있어야 합니다. 그렇지 않으면 설치 프로그램이 추가할 export PATH 줄을 출력합니다. 그 디렉터리가 PATH에 들어 있기 전까지는 셸이 apothem을 찾지 못하고 검사가 실패합니다. 설치 프로그램이 런처를 배치할 수 없으면 경고를 출력하며, export PATH 줄은 출력하지 않습니다. 메인테이너 서명 키를 아직 보유하지 않은 호스트에서는 첫 줄이 키를 가져오며, 키 없이는 설치 프로그램이 중단됩니다.
curl -fsSL https://github.com/ahmed-g-gad.gpg | gpg --import
curl -fsSL https://apothem.ahmedgad.com/install.sh | sh
apothem --version예상 출력: Apothem, version X.Y.Z.
5.4 원샷 스크립트 설치 프로그램(Windows)
첫 줄은 Git 자체의 bash를 통해 메인테이너 서명 키를 가져오며, 그 셸의 gpg 키링이 바로 git verify-tag가 읽는 키링입니다.
& "$env:ProgramFiles\Git\bin\bash.exe" -c 'curl -fsSL https://github.com/ahmed-g-gad.gpg | gpg --import'
irm https://apothem.ahmedgad.com/install.ps1 | iex
apothem --version예상 출력: Apothem, version X.Y.Z.
5.5 아티팩트 검증(공급망 증거)
모든 자산을 빈 디렉터리에 다운로드한 다음 검증합니다. 인증서 신원은 바로 이
태그 시점의 릴리스 워크플로입니다. 신원 검사는 대소문자를 구분하며, 인증서에는
GitHub가 보고하는 대로의 소유자 Ahmed-G-Gad가 담겨 있으므로 소문자 소유자는
실패합니다.
mkdir release-vX.Y.Z && cd release-vX.Y.Z
gh release download vX.Y.Z --repo ahmed-g-gad/apothem
cosign verify-blob \
--bundle apothem-X.Y.Z-py3-none-any.whl.cosign.bundle \
--certificate-identity https://github.com/Ahmed-G-Gad/apothem/.github/workflows/release.yml@refs/tags/vX.Y.Z \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
apothem-X.Y.Z-py3-none-any.whl
slsa-verifier verify-artifact \
--provenance-path provenance.intoto.jsonl \
--source-uri github.com/Ahmed-G-Gad/apothem \
--source-tag vX.Y.Z \
apothem-X.Y.Z-py3-none-any.whl apothem-X.Y.Z.tar.gz
bash ../scripts/release/verify-provenance.sh .예상 결과: Verified OK, PASSED: SLSA verification passed, 그리고
verify-provenance: chain OK (3 of 3 layers verified). 스크립트는 한 번의
실행으로 SHA256SUMS, 모든 서명 번들, 출처를 점검하며, wheel 이름에서 태그를
도출합니다(--tag vX.Y.Z로 재정의 가능).
스모크 테스트는 표면마다 한 줄의 PASS / FAIL 행을 생성합니다. 그 행들을 메인테이너의 릴리스 로그에 기록하세요.
6. 장애 복구
릴리스 워크플로에는 릴리스 엔진과 npm 게시자에 대해 문서화된 장애 모드가 있습니다.
| 장애 | 진단 | 복구 |
|---|---|---|
| 태그 푸시 거부(서명 실패) | git push origin vX.Y.Z가 GPG 관련 메시지와 함께 error: failed to push some refs를 반환한다 | GPG 하위 키가 로드되었는지 검증한다(gpg --list-secret-keys --keyid-format=long). 키링을 고친 후 로컬에서 -s로 다시 태그한다. 다시 푸시한다 |
릴리스 빌드가 Check the plugin version changed with the plugin에서 멈춘다 | 빌드 작업 로그에 플러그인과 이전 태그가 명시된다. 어떤 자산도 빌드되거나 게시되지 않았다 | 이전 릴리스 이후 플러그인 패키지는 변경되었지만 그 버전은 변경되지 않았다. 태그를 삭제하고(git push --delete origin vX.Y.Z, git tag -d vX.Y.Z), python scripts/release/bump_version.py X.Y.Z를 실행하고, 커밋한 다음, 태그에 다시 서명하고 푸시한다(§2.2). 같은 점검이 모든 풀 리퀘스트에서 경고 주석으로 실행되므로, 이 문제는 보통 태그하기 전에 드러난다 |
release.yml 작업이 단일 단계에서 실패한다 | gh run view <run-id> --log가 실패한 작업(build(SBOM도 함께 기록) / sign / provenance / archive / publish)의 이름을 알려 준다 | gh run rerun <run-id> --failed로 실패한 작업만 다시 실행한다. 실패가 반복되면 다시 태그하기 전에 작업의 입력을 그 출처에서 고친다 |
| SLSA 출처 검증 실패 | verify SLSA provenance artifact 작업이 0이 아닌 코드로 종료한다 | 출처 아티팩트에 주체 다이제스트 불일치가 있는지 검사한다. 같은 태그에서 다시 빌드한다. 출처는 정확한 아티팩트 다이제스트에 바인딩되므로, 아티팩트를 어떤 식으로든 다시 생성하려면 워크플로 전체를 다시 실행해야 한다 |
| npmjs.com 게시 거부 | npm publish 중에 npm 작업이 0이 아닌 코드로 종료한다. PUT 중의 레지스트리 404는 인가 / 신뢰할 수 있는 게시자 불일치이며, 패키지 코드가 없다는 증거가 아니다 | @ahmed-g-gad/apothem에 대한 npm Trusted Publishing이 소유자 ahmed-g-gad, 저장소 apothem, 워크플로 publish-npm.yml, 허용된 액션 npm publish를 지정하는지 검증한다. 작업이 워크플로에 고정된 Node 버전과 npm CLI OIDC 최저 버전으로 실행되는지 확인한다 |
| 태그는 보이지만 자산이 없음 | gh release view vX.Y.Z가 스텁 릴리스를 반환한다 | 릴리스 워크플로의 게시 작업을 다시 실행한다. 이 작업은 기존 릴리스를 편집하고 모든 자산을 다시 업로드한다. 태그에서 나온 어떤 것도 사용자에게 도달하지 않은 경우(자산 없음, npm 버전 없음)에만 삭제하고 다시 태그한다. 그렇지 않으면 다음 패치 릴리스로 앞으로 고친다(릴리스 복구 런북 §2) |
복구 주기가 한 번의 진단 패스 안에 표면을 안착시키지 못하면, 그 표면은 열린 이슈 링크와 함께 릴리스 노트에 알려진 장애로 기록됩니다. 망가진 표면이 패치 릴리스에서 고쳐지는 동안 다운스트림 사용자는 작동하는 표면으로 안내됩니다.
7. 상호 참조
- 게시자 설정. 게시자 계정 설정 런북은 npm 신뢰할 수 있는 게시자 바인딩과 이 런북이 전제하는 GitHub 측 전제 조건을 마련합니다.
- 복구 흐름. 릴리스 복구 런북은 같은 추가형 모델을 따릅니다. 이 런북은 실패한 태그나 게시자를 단계별로 복구하며, 수정을 새 패치 버전으로 릴리스하는 방식으로 롤백하고, 게시된 태그, 릴리스, npm 버전을 삭제하거나 다른 대상을 가리키게 하는 방식은 절대 쓰지 않습니다.
- Pages 활성화.
publish-static-site.yml워크플로는 프로젝트 웹사이트를 검증하고 사용자 지정 도메인에 배포합니다. Pages 활성화 런북 (site/content/docs/runbooks/pages-enablement.mdx)은 애초에 사용자 지정 도메인을 마련한 상위 절차입니다. - 버전 관리 정책.
site/content/docs/governance/release-engineering-policy.mdx는 이 런북이 준수하는 SemVer + 서명 + 지원 중단 정책을 담고 있습니다.