BuildSpace
GitHub Actions
Rust
TypeScript
NPM
Crates.io
빠르고 신속하게 배포하는 팀을 위한 재사용 가능한 GitHub Actions 블록 및 워크플로우
BuildSpace는 세 가지 계층의 CI/CD 자동화 기능을 제공합니다:
- 워크플로우(Workflows): 플러그 앤 플레이(plug-and-play) 배포 파이프라인입니다. 워크플로우를 리포지토리에 지정하고 몇 가지 시크릿을 제공하기만 하면 됩니다. 대부분의 팀은 이것만 필요합니다.
- 블록(Blocks): 설정(setup), 빌드(builds), 패키징(packaging), 게시(publication)를 결합하는 호환성 레시피입니다. 기존에 존재하는 입력 및 출력 이름이 유지됩니다.
- 프리미티브(Primitives): 릴리스 정책이나 툴체인 설정이 없는 단일 작업(single-operation) 액션입니다. 블록의 다른 동작을 채택하지 않고 파이프라인을 구성하는 데 사용하십시오.
목차
목차
빠른 시작 (Quick Start)
어떤 워크플로우가 필요할까요?
| 제가 가진 것이... | 이 워크플로우를 사용하세요 | 트리거 |
|---|
| Kargo 프로모션을 위한 컨테이너 이미지 | publish-image | CI 이후 main 또는 보호된 핫픽스 브랜치에 푸시 |
| ... | | |
패키지의 경우, pack-script 계약을 포함하여 Package Stage and Promote를 따르십시오. Swift 실행 파일 및 라이브러리의 경우 Swift Stage and Promote를 따르십시오. 각 가이드에는 스테이징 및 프로모션 호출자, 필요한 권한, 승인 구성이 포함되어 있습니다.
빌드를 스테이징하려면 main으로 병합(Merge)하십시오. 이후 승인 후 저장된 빌드를 프로모션합니다. 이 두 단계 모두 PR 릴리스 레이블이나 AI API 키를 사용하지 않습니다. 프로덕션 후보는 스테이징 과정에서 준비되므로, 프로모션 시 재빌드되지 않습니다.
기존 직접 배포 호출자 (Existing direct-release callers)
아래 워크플로우들은 호환성을 위해 계속 사용할 수 있습니다. 이들은 여전히 기존 레이블 규칙을 사용하므로, 릴리스 경로를 마이그레이션하기 전에 호출자의 레이블을 제거하지 마십시오. 규칙 및 호출자 인벤토리에서 남아있는 소비자(consumer)와 제거 조건을 추적합니다.
Package stage/promote는 독립적인 Go/Rust 바이너리 릴리스나 crates.io 게시를 대체하지 않습니다. Swift stage/promote는 Makefile dylibs 또는 payload 전용 패키지를 빌드하지 않습니다. 해당 호출자는 폐지되기 전에 대체품이 필요합니다.
README 최신화 확인 (README Update-To-Date Check)
모든 리포지토리의 .github/workflows/ci.yaml에 추가하세요:
이 확인 절차는 릴리스나 프로모션과 독립적으로 풀 리퀘스트(pull requests)에서 실행됩니다.
BuildSpace 고정(Pinning)
@main을 사용하는 예시는 BuildSpace의 변경 사항이 병합되는 순간마다 이를 반영합니다. 대신, stage 및 promote 예시가 하는 것처럼 main 브랜치의 검토된 커밋을 고정하세요:
uses: photon-hq/buildspace/.github/workflows/package-stage.yml@<reviewed-commit-sha>
현재 커밋은 git ls-remote https://github.com/photon-hq/buildspace refs/heads/main으로 가져올 수 있습니다. 고정(pin)을 이동하는 것은 사용자의 리포지토리 내 풀 리퀘스트이며, 따라서 BuildSpace의 모든 변경 사항은 그곳에서 검토되고, stage 워크플로우의 드라이 런(dry run)이 배포되기 전에 이를 테스트합니다. BuildSpace는 자체적인 릴리스나 태그가 없습니다: main으로 병합하는 것이 곧 릴리스입니다. 이전의 v1.x 태그와 릴리스는 삭제되었기 때문에, 이에 대한 참조는 더 이상 해결되지 않습니다. 대신 커밋을 고정하세요.
레거시(Legacy) 직접 릴리스 래퍼들은 여전히 @main에 있는 중첩 블록을 참조합니다.
레거시 래퍼만 고정한다고 해서 해당 의존성이 동결되는 것은 아닙니다. 소비자(사용자)들이 마이그레이션할 때까지 호환성 블록들을 유지하세요.
전제 조건 (Prerequisites)
필수 시크릿(Required Secrets)
Package 및 Swift stage/promote 워크플로우는 AI API 키를 사용하지 않습니다. AI 검사 및 레거시 직접 릴리스 워크플로우는 OPENAI_API_KEY를 사용하며, update-docs와 update-skills는 ANTHROPIC_API_KEY를 사용합니다. 워크플로우가 필요한 시크릿만 추가하세요:
이러한 시크릿을 리포지토리의 Settings > Secrets and variables > Actions에 추가하거나, 호출하는 리포지토리에 접근 권한이 부여된 조직(organization) 시크릿을 구성하십시오. 필요한 시크릿은 명시적으로 전달해야 합니다. GitHub는 호출자(caller)와 재사용 가능한 워크플로우(reusable workflow)가 동일한 조직 또는 엔터프라이즈 내에 있을 때만 시크릿 상속(secret inheritance)를 지원합니다. 이 경계를 벗어난 호출자는 아래에 표시된 것처럼 명시적인 시크릿 매핑을 사용해야 합니다.
GitHub Packages 게시(publishing)는 별도의 저장된 시크릿이 필요하지 않습니다. 이는 호출자 리포지토리의 단기 GITHUB_TOKEN을 사용합니다. publish-github-packages가 활성화될 때 재사용 가능한 워크플로우 작업에 packages: write 권한을 부여하십시오.
릴리스 정책 (Release policy)
스테이징/프로모트(stage/promote) 호출자의 경우, main 브랜치가 스테이징 환경입니다. 프로덕션(Production)은 승인된 스테이징 빌드의 저장된 후보(stored candidate)를 받습니다. release 및 prerelease PR 레이블은 이 워크플로우에서 필요하지 않거나 참조되지 않습니다.
레이블 권한 부여는 호환성 규칙(compatibility rules)에만 남아 있습니다. 이러한 규칙은 워크플로우마다 다르므로, 나머지 호출자를 위해 온전히 유지되어야 합니다. 사용하지 않는 PR 레이블을 제거할 때 GitHub prerelease 메타데이터, 레지스트리 채널 또는 프로덕션 의존성 검사를 제거하지 마십시오.
권한 (Permissions)
릴리스를 생성하거나 커밋을 푸시하는 워크플로우에는 contents: write가 필요합니다. 최신 버전 계산은 PR 제목과 설명을 읽어야 하므로 pull-requests: read가 필요하며, 레거시 레이블 검사에도 이 권한이 필요합니다. 댓글을 게시하는 워크플로우는 pull-requests: write가 필요합니다. GitHub Packages 게시에는 packages: write가 필요합니다. 아래의 각 워크플로우 섹션은 필요한 권한 목록을 제공합니다.
워크플로우 (Workflows)
바로 사용할 수 있는 릴리스 파이프라인입니다. 각 워크플로우는 내부적으로 더 낮은 수준의 블록(blocks)을 구성합니다. 사용자 지정 파이프라인을 구축하는 경우가 아니라면 개별 블록에 대해 알 필요가 없습니다.
불변 이미지 게시자 (Immutable Image Publisher)
.github/workflows/publish-image.yml은 호출자의 정확한 커밋을 한 번 빌드하고 증명(attest)하거나, 기존의 증명을 검증한 후 이미지를 재사용합니다. 다른 환경으로 승격시키는 작업은 이 워크플로우를 호출하지 않습니다.
필수 소스 CI(Continuous Integration) 이후 작업으로, 검토된 커밋에 고정하여 호출합니다:
publish:
needs: [test]
uses: photon-hq/buildspace/.github/workflows/publish-image.yml@<reviewed-commit-sha>
...
- ECR은
main-* 및 hotfix-* 태그에 대해 불변성(immutability)을 강제해야 합니다. Photon의 기존 Terraform은 정확한 플로팅 main 태그만 제외하는 IMMUTABLE_WITH_EXCLUSION을 사용하며, 이 예외를 절대 main* 또는 hotfix-*로 확대하지 마십시오. 이 레지스트리 설정은 조회와 푸시 사이에 다른 작성자가 태그를 대체하는 것을 방지합니다. 게시자를 도입하기 전에 이를 프로비저닝하십시오.
- 소스 저장소는 테스트, hotfix 브랜치/검토 정책 및 프로덕션 종속성 검사 기능을 자체적으로 보유해야 합니다. main/hotfix 태그는 정확한 이벤트 커밋을 식별해야 합니다.
- 선택적 입력(optional inputs)은 컨텍스트, 플랫폼, 러너(runner), 환경, 리전 및 캐시되지 않은 빌드 단계를 구성합니다. 다른 모든 동작은 공유됩니다. 매트릭스(matrix)를 사용하여 여러 독립적인 이미지를 호출할 수 있습니다.
build-args는 비(非)비밀 빌드 인자(build arguments)를 추가합니다. GIT_SHA는 항상 이벤트 커밋이며 대체될 수 없습니다.
submodules는 이미지 소스가 고정된 서브모듈에 있는 저장소의 경우 checkout 과정으로 전달됩니다.
- 동일한 소유자의 비공개(private) 저장소 내 서브모듈의 경우, 또한
submodule-repositories를 설정하고 app-id와 app-private-key를 전달해야 합니다. 작업 토큰은 호출하는 저장소만 읽기 때문에, checkout 과정은 호출하는 저장소 및 나열된 저장소에 제한된 GitHub App 토큰을 사용합니다. 이 앱은 모든 저장소에 설치되어야 합니다.
토큰은 체크아웃에 영구 저장되지 않으며, prepare 또는 빌드로 전달되지 않습니다.
-
prepare는 호출자(caller)가 체크아웃에서 스크립트를 실행하며, 이 스크립트는 레포지토리가 생성하는 빌드 컨텍스트(예: 서브모듈의 패치된 사본)를 대상으로 합니다. 이는 Dockerfile과 마찬가지로 호출자의 검토된 커밋으로부터 신뢰받는 코드이며 샌드박스가 아닙니다. AWS 자격 증명이 구성되기 전에 실행되며, 이 과정에서 작업 토큰(job token) 및 OIDC 요청 자격 증명은 초기화됩니다.
-
모든 이미지는 org.opencontainers.image.created 필드를 포함하며, 이는 이벤트 커밋의 작성자 시간(UTC 기준)으로 설정됩니다. 이 필드가 없으면 이미지는 기본 이미지의 레이블을 상속받거나, 완전히 캐시된 빌드에서 이전 이미지와 공유하는 최신 레이어의 빌드 시간으로 대체됩니다. 생성 시간 순서로 이미지를 정렬하는 파이프라인은 소스 순서를 따르며, 오래된 커밋을 재실행한다고 해서 가장 최신 이미지가 되는 것은 아닙니다.
-
이미지는 다이제스트(digest)로 푸시되고, 증명(attested)된 후, 비로소 태그가 지정됩니다. 따라서 태그는 항상 그 증명이 없으면 존재할 수 없습니다. 태그로 이미지를 검색하는 파이프라인은 증명되지 않은 이미지를 가져올 수 없으며, 증명에 실패한 실행은 다음 시도가 걸려 넘어질 만한 것을 남기지 않습니다. 태그는 예상되는 다이제스트와 함께 동일한 매니페스트 바이트를 다시 넣어 추가되므로, 레지스트리는 다른 매니페스트에 대한 태그를 거부합니다. 게시(publishing) 역할은 ecr:BatchGetImage뿐만 아니라 ecr:PutImage도 필요로 합니다.
-
호출자의 레포지토리 식별자와 구성된 환경은 AWS OIDC 주체(subject)로 유지됩니다. 재사용 가능한 워크플로우가 증명 서명자(attestation signer) 역할을 합니다: photon-hq/buildspace/.github/workflows/publish-image.yml. 검증자는 이 서명자 외에도 호출 레포지토리, 소스 참조(source ref), 그리고 소스 SHA를 확인해야 합니다.
-
legacy-signer-workflow는 검토된 이전 버전의 기존 이미지를 재사용할 수 있게 허용합니다. 이 워크플로우는 이미지를 덮어쓰거나 증명(re-attest)하지 않습니다. 누락된 증명이나 ECR 접근 오류가 발생하면 작업이 실패합니다.
-
빌드 중에 가져오는 private Git 의존성을 위해 app-token-repositories를 설정하고 app-id와 app-private-key를 전달해야 합니다. 이 워크플로우는 해당 리포지토리 읽기 권한으로 제한된 GitHub App 토큰을 생성하여, app-token-secret (기본값: github_token)이라는 이름의 BuildKit secret으로 마운트합니다. 호출자(caller)가 생성한 토큰은 전달될 수 없습니다. 비밀 정보(secret)를 포함하는 job 출력물은 삭제됩니다.
-
Dockerfile이 필요로 하는 BuildKit secret만 제공하세요. 이 워크플로우는 digest를 반환합니다. 매트릭스 빌드(matrix builds)의 경우, 결합된 매트릭스 출력물이 아닌 작업별(per-job) 결과를 사용해야 합니다.
로컬 검증(Local validation): actionlint .github/workflows/publish-image.yml 및 python3 -m unittest discover -s test (PyYAML 6.0.3 필요).