BuildSpace Release Workflows Skill
Use this skill to set up or debug BuildSpace-powered release automation in repositories that use reusable GitHub Actions workflows.
What BuildSpace Provides
BuildSpace has two layers:
- Workflows: full release pipelines under
.github/workflows/*.
- Blocks: reusable composite actions under
.github/blocks/* for custom pipelines.
Default recommendation: use a prebuilt workflow unless the user explicitly needs custom behavior.
Workflow Selection
Pick exactly one primary workflow based on project type:
| Project type |
Workflow file |
Trigger |
| Rust binary/library |
rust-service-release.yaml |
PR label release |
| TypeScript/JavaScript single package |
typescript-service-release.yaml |
PR label release |
| TypeScript monorepo (multiple packages) |
typescript-monorepo-release.yaml |
PR label release |
| Go binary |
go-service-release.yaml |
PR label release |
Swift macOS .pkg (with compiled binary) |
swift-release.yml |
PR label release |
macOS .pkg without binary (payload/scripts only) |
pkg-release.yml |
PR label release |
macOS .pkg PR build (payload/scripts only) |
pkg-release-pr.yml |
Every PR commit |
Swift macOS .pkg PR build previews |
swift-pkg-pr.yml |
Every PR commit |
| macOS dylib release (Xcode workspace) |
dylib-release.yml |
PR label release |
| macOS dylib release (Makefile) |
makefile-dylib-release.yml |
PR label release |
| Generic release (version + GitHub Release only) |
release.yaml |
PR label release |
| README freshness check on PRs |
check-readme.yaml |
Every PR |
| Skills documentation freshness check on PRs |
check-skills.yaml |
Every PR |
Required Inputs, Secrets, and Permissions
Always verify these before writing YAML:
- Inputs: service/package names, paths, build command, package lists, Homebrew tap info, Jamf config.
- Secrets:
- Always required for AI features:
OPENAI_API_KEY
- npm publishing:
NPM_TOKEN
- crates publishing:
CARGO_REGISTRY_TOKEN
- Swift compile-time env vars:
SECRET_ENV_VARS
- Jamf upload:
JAMF_CLIENT_ID + JAMF_CLIENT_SECRET
- Protected-branch pushes or Homebrew tap updates:
APP_ID + APP_PRIVATE_KEY
- Skills documentation check (private repos):
SKILLS_REPO_TOKEN
- Note:
DEVELOPER_ID_INSTALLER_NAME is deprecated and ignored — packages are always unsigned.
- Permissions:
- Release/version bump jobs need
contents: write
- Label checks need
pull-requests: read
- PR commenting needs
pull-requests: write
Release Trigger Rules (Important)
BuildSpace is label-gated by default.
- Standard release label:
release
- Optional prerelease label:
prerelease
- No label usually means no release job.
Behavior nuance to keep accurate:
typescript-monorepo-release supports prerelease path directly.
typescript-service-release, rust-service-release, and go-service-release gate release jobs on release (or forced input), and treat prerelease as flavor once release is active.
swift-release, pkg-release, dylib-release, and makefile-dylib-release check only release.
release.yaml (generic) checks release label and supports a release boolean input to force.
Implementation Procedure
When asked to set up BuildSpace in a repo:
- Detect repo type (Rust, TS single, TS monorepo, Go, Swift, macOS pkg, dylib).
- Confirm publish targets (GitHub only, npm, crates, Jamf, Homebrew tap).
- Create a caller workflow in the user repo (
.github/workflows/release.yaml or ci.yaml) with uses: photon-hq/buildspace/...@v1.
- Wire
with: inputs and secrets: exactly for that workflow.
- Add recommended permissions block.
- Add
dry-run: true for first validation run unless the user requests immediate publish.
- Explain how to trigger (
release label + merge path, or forced release: true).
- For monorepos, validate
packages JSON and dependency order behavior.
- For Homebrew tap updates, wire
tap-repo and tap-formula inputs plus APP_ID/APP_PRIVATE_KEY secrets.
- For Jamf uploads, wire
jamf-url and Jamf secrets.
Workflow Reference
rust-service-release.yaml
Complete release pipeline for Rust services: label check, AI version + release notes, cross-build (Linux x64, macOS ARM64, Windows x64), sync workspace crate versions, publish crates to crates.io, create GitHub Release with binaries, optionally update Homebrew tap.
Inputs:
| Input |
Type |
Required |
Default |
Description |
service-name |
string |
Yes |
— |
Display name for the service |
binary-name |
string |
Yes |
— |
Name of the binary from Cargo.toml |
binary-path |
string |
No |
"" |
Path to crate directory (e.g., crates/client) |
crates |
string |
No |
[] |
JSON array of crate paths to publish in dependency order |
build-env |
string |
No |
"" |
Compile-time env vars (e.g., BASE_URL=https://...) |
labels-to-check |
string |
No |
["release", "prerelease"] |
PR labels that trigger releases |
prerelease |
boolean |
No |
false |
Force prerelease (adds -rc.N suffix) |
release |
boolean |
No |
false |
Force release (bypasses label check) |
dry-run |
boolean |
No |
false |
Test without actually publishing |
tap-repo |
string |
No |
"" |
Homebrew tap repository (e.g., photon-hq/homebrew-photon). Empty to skip. |
tap-formula |
string |
No |
"" |
Formula name in tap (e.g., jamf-package-updater). Required if tap-repo is set. |
Secrets:
| Secret |
Required |
Description |
OPENAI_API_KEY |
Yes |
For AI-powered versioning and release notes |
CARGO_REGISTRY_TOKEN |
No |
crates.io API token (required for publishing) |
APP_ID |
No |
GitHub App ID (for protected branches and Homebrew tap updates) |
APP_PRIVATE_KEY |
No |
GitHub App private key |
typescript-service-release.yaml
Complete release pipeline for a single TypeScript/JavaScript package: label check, AI version + release notes, bump package.json, create GitHub Release, publish to npm.
Inputs:
| Input |
Type |
Required |
Default |
Description |
service-name |
string |
Yes |
— |
Display name for the service |
bun-version |
string |
No |
latest |
Bun version to use |
npm-tag |
string |
No |
latest |
npm dist-tag (e.g., latest, beta, next) |
no-npm-publish |
boolean |
No |
false |
Skip npm publishing (GitHub Release only) |
working-directory |
string |
No |
. |
Directory containing package.json |
build-command |
string |
No |
bun run build |
Build command to run |
labels-to-check |
string |
No |
["release", "prerelease"] |
PR labels that trigger releases |
prerelease |
boolean |
No |
false |
Force prerelease |
release |
boolean |
No |
false |
Force release (bypasses label check) |
dry-run |
boolean |
No |
false |
Test without actually publishing |
Secrets:
| Secret |
Required |
Description |
OPENAI_API_KEY |
Yes |
For AI-powered versioning and release notes |
NPM_TOKEN |
No |
npm auth token (required for publishing) |
typescript-monorepo-release.yaml
Complete release pipeline for TypeScript monorepos with independently-versioned packages. Detects changed packages, topologically sorts by dependency order, single AI call for all versions/notes, bumps each package.json, creates GitHub Release with release/YYYY-MM-DD.N tag, publishes to npm in order.
Inputs:
| Input |
Type |
Required |
Default |
Description |
service-name |
string |
Yes |
— |
Display name for the monorepo |
packages |
string |
Yes |
— |
JSON array: [{"name":"pkg","path":"packages/pkg"}] |
bun-version |
string |
No |
latest |
Bun version to use |
npm-tag |
string |
No |
latest |
npm dist-tag |
build-command |
string |
No |
bun run build |
Per-package build command (ignored if root-build-command is set) |
root-build-command |
string |
No |
"" |
Build once at repo root (e.g., turbo build) |
include-dependents |
boolean |
No |
false |
Also release downstream dependents |
labels-to-check |
string |
No |
["release", "prerelease"] |
PR labels that trigger releases |
prerelease |
boolean |
No |
false |
Force prerelease |
release |
boolean |
No |
false |
Force release |
dry-run |
boolean |
No |
false |
Test without actually publishing |
Secrets:
| Secret |
Required |
Description |
OPENAI_API_KEY |
Yes |
For AI-powered versioning and release notes |
NPM_TOKEN |
Yes |
npm authentication token |
APP_ID |
No |
GitHub App ID (for pushing to protected branches) |
APP_PRIVATE_KEY |
No |
GitHub App private key |
go-service-release.yaml
Complete release pipeline for Go binaries: label check, AI version + release notes, cross-compile for macOS (ARM64) and Linux (AMD64), create GitHub Release with binaries, optionally update Homebrew tap.
Inputs:
| Input |
Type |
Required |
Default |
Description |
service-name |
string |
Yes |
— |
Display name for the service |
binary-name |
string |
Yes |
— |
Output binary name |
go-version |
string |
No |
stable |
Go version to use |
build-flags |
string |
No |
"" |
Additional go build flags |
ldflags |
string |
No |
-s -w |
Linker flags |
labels-to-check |
string |
No |
["release", "prerelease"] |
PR labels that trigger releases |
prerelease |
boolean |
No |
false |
Force prerelease |
release |
boolean |
No |
false |
Force release |
tap-repo |
string |
No |
"" |
Homebrew tap repository. Empty to skip. |
tap-formula |
string |
No |
"" |
Formula name in tap. Required if tap-repo is set. |
Secrets:
| Secret |
Required |
Description |
OPENAI_API_KEY |
Yes |
For AI-powered versioning and release notes |
APP_ID |
No |
GitHub App ID (for Homebrew tap updates) |
APP_PRIVATE_KEY |
No |
GitHub App private key |
swift-release.yml
Complete release pipeline for macOS .pkg distribution with a compiled Swift binary: label check, AI version + release notes, Swift build, .pkg creation (unsigned), GitHub Release, optional Jamf upload.
Inputs:
| Input |
Type |
Required |
Default |
Description |
package-name |
string |
Yes |
— |
Name of the Swift binary / package |
identifier |
string |
Yes |
— |
Package identifier (e.g., com.example.mytool) |
scripts-path |
string |
No |
"" |
Path to scripts directory with preinstall/postinstall scripts |
payload-path |
string |
No |
"" |
Path to additional payload directory (mirrors install root) |
jamf-url |
string |
No |
"" |
Jamf Pro instance URL (leave empty to skip) |
jamf-package-priority |
string |
No |
"" |
Package priority in Jamf Pro |
jamf-package-name |
string |
No |
"" |
Package name to match in Jamf Pro |
Secrets:
| Secret |
Required |
Description |
OPENAI_API_KEY |
Yes |
For AI-powered versioning and release notes |
SECRET_ENV_VARS |
No |
Compile-time env vars written to .env |
JAMF_CLIENT_ID |
No |
Jamf Pro API client ID |
JAMF_CLIENT_SECRET |
No |
Jamf Pro API client secret |
pkg-release.yml
Release pipeline for macOS .pkg that does not contain a compiled binary. Packages payload files and scripts into a .pkg, creates GitHub Release, optionally uploads to Jamf. Use instead of swift-release when packages only deliver configuration files, LaunchDaemons, scripts, or other non-binary payload.
Inputs:
| Input |
Type |
Required |
Default |
Description |
package-name |
string |
Yes |
— |
Name of the package |
identifier |
string |
Yes |
— |
Package identifier (e.g., com.example.my-config) |
scripts-path |
string |
No |
"" |
Path to scripts directory with preinstall/postinstall scripts |
payload-path |
string |
No |
"" |
Path to payload directory (mirrors install root) |
jamf-url |
string |
No |
"" |
Jamf Pro instance URL (leave empty to skip) |
jamf-package-priority |
string |
No |
"" |
Package priority in Jamf Pro |
jamf-package-name |
string |
No |
"" |
Package name to match in Jamf Pro |
Secrets:
| Secret |
Required |
Description |
OPENAI_API_KEY |
Yes |
For AI-powered versioning and release notes |
JAMF_CLIENT_ID |
No |
Jamf Pro API client ID |
JAMF_CLIENT_SECRET |
No |
Jamf Pro API client secret |
pkg-release-pr.yml
Builds a macOS .pkg (without a compiled binary) on every PR commit and reports status in the PR as a living comment. Same experience as swift-pkg-pr but for payload/scripts-only packages. Stale in-progress builds are automatically cancelled on new commits.
Inputs:
| Input |
Type |
Required |
Default |
Description |
package-name |
string |
Yes |
— |
Name of the package |
identifier |
string |
Yes |
— |
Package identifier (e.g., com.example.my-config) |
scripts-path |
string |
No |
"" |
Path to scripts directory with preinstall/postinstall scripts |
payload-path |
string |
No |
"" |
Path to payload directory (mirrors install root) |
swift-pkg-pr.yml
Builds a macOS .pkg (with Swift binary) on every PR commit, posts/updates a single PR comment with build status, uploads artifact (7-day retention). Stale in-progress builds are automatically cancelled on new commits.
Inputs:
| Input |
Type |
Required |
Default |
Description |
package-name |
string |
Yes |
— |
Name of the Swift binary / package |
identifier |
string |
Yes |
— |
Package identifier (e.g., com.example.mytool) |
scripts-path |
string |
No |
"" |
Path to scripts directory with preinstall/postinstall scripts |
Secrets:
| Secret |
Required |
Description |
SECRET_ENV_VARS |
No |
Compile-time env vars written to .env |
dylib-release.yml
Release pipeline for macOS dynamic libraries built from an Xcode workspace with CocoaPods. Label check, AI version + release notes, install CocoaPods, build dylib (arm64e), create GitHub Release with .dylib artifact.
Inputs:
| Input |
Type |
Required |
Default |
Description |
workspace |
string |
Yes |
— |
Path to .xcworkspace |
scheme |
string |
Yes |
— |
Xcode scheme to build |
dylib-name |
string |
Yes |
— |
Name of the output dylib (e.g., BlueBubblesHelper) |
project-directory |
string |
Yes |
— |
Directory containing the Podfile |
Secrets:
| Secret |
Required |
Description |
OPENAI_API_KEY |
Yes |
For AI-powered versioning and release notes |
makefile-dylib-release.yml
Release pipeline for macOS dynamic libraries built from a Makefile. Label check, AI version + release notes, make release, create GitHub Release with .dylib artifact.
Inputs:
| Input |
Type |
Required |
Default |
Description |
dylib-name |
string |
Yes |
— |
Name of the output dylib (e.g., imessage-helper) |
Secrets:
| Secret |
Required |
Description |
OPENAI_API_KEY |
Yes |
For AI-powered versioning and release notes |
release.yaml
Generic release pipeline for projects that only need AI-powered versioning and a GitHub Release (no build step, no package publishing). Useful for configuration repos, documentation repos, or any project that just needs tagged releases.
Inputs:
| Input |
Type |
Required |
Default |
Description |
service-name |
string |
Yes |
— |
Display name for the service |
labels-to-check |
string |
No |
["release"] |
JSON array of PR labels to check |
release |
boolean |
No |
false |
Force release (overrides label) |
Secrets:
| Secret |
Required |
Description |
OPENAI_API_KEY |
Yes |
For AI-powered versioning and release notes |
check-readme.yaml
Runs on every PR to check if README.md is up to date with the changes. Uses AI to analyze changed files against the README. Posts/removes PR comments automatically.
Inputs:
| Input |
Type |
Required |
Default |
Description |
blocking |
boolean |
No |
false |
If true, fail the workflow when README is outdated. If false, only post a warning comment. |
Secrets:
| Secret |
Required |
Description |
OPENAI_API_KEY |
Yes |
For AI-powered README analysis |
check-skills.yaml
Runs on every PR to check if skills documentation (in a separate skills repo) is up to date with the changes. Uses AI to analyze changed files against skill SKILL.md files. Posts/removes PR comments automatically.
Inputs:
| Input |
Type |
Required |
Default |
Description |
fail_on_missing_skills |
boolean |
No |
false |
If true, fail workflow when skills are outdated. If false, only post a warning comment. |
skills-repo |
string |
No |
photon-hq/skills |
Skills repository to check against (owner/repo format) |
Secrets:
| Secret |
Required |
Description |
OPENAI_API_KEY |
Yes |
For AI-powered skills analysis |
SKILLS_REPO_TOKEN |
No |
GitHub token for skills repo (defaults to GITHUB_TOKEN; use PAT for private repos) |
Monorepo-Specific Guidance
For typescript-monorepo-release:
packages input must be a JSON array:
[{"name":"pkg","path":"packages/pkg"}]
- Changed package detection is diffed from last release tag.
- Packages are topologically sorted using local dependency edges.
- Optional
include-dependents: true pulls downstream packages into release.
root-build-command takes precedence over per-package build-command.
- Pre-release npm tag is automatically
beta when prerelease is active.
- Uses
release/YYYY-MM-DD.N date-based tags since there's no single version.
Rust/Go Notes
- Rust workflow can:
- cross-build artifacts for Linux x64, macOS ARM64, Windows x64
- sync workspace versions across all crates
- publish crates in provided order
- optionally update Homebrew tap (
tap-repo + tap-formula, requires APP_ID + APP_PRIVATE_KEY)
- Go workflow:
- cross-builds for macOS ARM64 and Linux AMD64
- can also update Homebrew tap (
tap-repo + tap-formula, requires APP_ID + APP_PRIVATE_KEY)
Swift / macOS .pkg Notes
swift-release builds the Swift binary, creates an unsigned .pkg, creates GitHub Release, and optionally uploads to Jamf.
pkg-release is for .pkg packages that contain no compiled binary — only payload files and/or scripts.
swift-pkg-pr and pkg-release-pr are the PR preview counterparts (build on every commit, comment on PR).
- Packages are always unsigned (
DEVELOPER_ID_INSTALLER_NAME is deprecated and ignored).
- Compile-time env vars are passed via
SECRET_ENV_VARS secret (written to .env).
- Jamf upload is optional — set
jamf-url input and provide JAMF_CLIENT_ID + JAMF_CLIENT_SECRET secrets.
Dylib Notes
dylib-release.yml is for projects using an Xcode workspace with CocoaPods. Builds arm64e dylib.
makefile-dylib-release.yml is for projects using a Makefile. Runs make release and embeds version.
- Both create a GitHub Release with the
.dylib as an artifact.
README Check Guidance
Use check-readme.yaml for PR docs drift detection:
blocking: false (default): warning comment only.
blocking: true: fails job when README appears stale.
- Requires
OPENAI_API_KEY and PR write permission for comments.
Skills Check Guidance
Use check-skills.yaml for skills documentation drift detection:
fail_on_missing_skills: false (default): warning comment only.
fail_on_missing_skills: true: fails job when skills appear outdated.
- Requires
OPENAI_API_KEY and PR write permission for comments.
- For private skills repos, provide
SKILLS_REPO_TOKEN (PAT with repo access).
Blocks Reference
All blocks live under .github/blocks/. Workflows compose these internally — use them directly only for custom pipelines.
Version & Release Info
| Block |
Path |
Purpose |
determine-publish-version |
.github/blocks/determine-publish-version/action.yaml |
AI-powered next semantic version (standalone, no release notes) |
generate-release-info |
.github/blocks/generate-release-info/action.yaml |
AI-powered version + release notes in one step |
create-github-release |
.github/blocks/create-github-release/action.yaml |
Create GitHub Release with optional artifact attachments |
Label & PR Interaction
| Block |
Path |
Purpose |
check-pr-label |
.github/blocks/check-pr-label/action.yaml |
Check PR labels to decide if release should trigger |
comment-on-pr |
.github/blocks/comment-on-pr/action.yaml |
Post or update a single PR comment (idempotent via comment-key) |
check-readme |
.github/blocks/check-readme/action.yaml |
AI-powered README freshness check |
check-skills |
.github/blocks/check-skills/action.yaml |
AI-powered skills documentation freshness check |
Build Blocks
| Block |
Path |
Purpose |
rust-build |
.github/blocks/rust-build/action.yaml |
Build Rust binary for a target triple |
go-build |
.github/blocks/go-build/action.yaml |
Build Go binary for a target OS/arch |
typescript-build |
.github/blocks/typescript-build/action.yaml |
Build TypeScript project using Bun |
swift-build |
.github/blocks/swift-build/action.yml |
Build Swift binary (SPM, supports .env injection) |
swift-pkg |
.github/blocks/swift-pkg/action.yml |
Create macOS .pkg from binary and/or payload/scripts |
build-dylib |
.github/blocks/build-dylib/action.yml |
Build macOS dylib from Xcode workspace (arm64e) |
build-makefile-dylib |
.github/blocks/build-makefile-dylib/action.yml |
Build macOS dylib from Makefile |
install-cocoapods |
.github/blocks/install-cocoapods/action.yml |
Cache and install CocoaPods dependencies |
Publish Blocks
| Block |
Path |
Purpose |
publish-npm |
.github/blocks/publish-npm/action.yaml |
Publish single package to npm |
publish-npm-packages |
.github/blocks/publish-npm-packages/action.yaml |
Publish multiple monorepo packages to npm in dependency order |
publish-crates |
.github/blocks/publish-crates/action.yaml |
Publish workspace crates to crates.io in order |
bump-npm-version |
.github/blocks/bump-npm-version/action.yaml |
Bump version in package.json and push |
bump-monorepo-versions |
.github/blocks/bump-monorepo-versions/action.yaml |
AI version bump for all changed monorepo packages |
sync-crates-version |
.github/blocks/sync-crates-version/action.yaml |
Set a single version across all Rust workspace crates |
update-tap |
.github/blocks/update-tap/action.yaml |
Update Homebrew tap formula (auto-calculates SHA256 for npm/Go/prebuilt) |
detect-changed-packages |
.github/blocks/detect-changed-packages/action.yaml |
Detect changed monorepo packages and return in topological order |
Troubleshooting Checklist
If release did not run:
- Confirm PR had expected label before merge.
- Confirm workflow permissions include required scopes.
- Confirm secrets exist at repository level.
- Confirm caller workflow points to correct reusable workflow path.
- For monorepos, confirm
packages JSON is valid and paths exist.
- For publish failures, test with
dry-run: true and verify auth token scopes.
- For Homebrew tap updates, confirm
APP_ID + APP_PRIVATE_KEY secrets are set and the GitHub App has push access to the tap repo.
- For Jamf uploads, confirm
JAMF_CLIENT_ID + JAMF_CLIENT_SECRET are set and jamf-url is a valid Jamf Pro URL.
- For dylib builds, confirm Xcode workspace/scheme or Makefile exists and produces expected output.
Output Format for Agent Responses
When generating BuildSpace setup instructions, respond with:
- Chosen workflow and why.
- Copy-ready YAML for caller workflow.
- Secrets to add (list exactly which ones and where to get them).
- How to trigger and verify.
- First-run safe mode (
dry-run) recommendation.
Keep recommendations concrete and default to the smallest working setup.
1---2name: buildspace-ci-cd3description: Configure and troubleshoot BuildSpace reusable GitHub Actions workflows and blocks for automated releases. Covers Rust, TypeScript (single package and monorepo), Go, Swift package, macOS .pkg (binary and payload-only), dylib (Xcode and Makefile), generic release, AI-based versioning/release notes, README freshness checks, skills documentation checks, labels, permissions, secrets, Homebrew tap updates, Jamf uploads, dry-run testing, and custom block composition. Use when users mention BuildSpace, release automation, reusable workflows, GitHub Actions CI/CD, or publishing to npm/crates/Homebrew/Jamf. Keywords: buildspace, ci/cd, github actions, release automation, reusable workflows, npm, crates, homebrew, rust, typescript, go, swift, monorepo, dylib, macOS, pkg, jamf, skills.4license: MIT5---6
7# BuildSpace Release Workflows Skill
8
9Use this skill to set up or debug BuildSpace-powered release automation in repositories that use reusable GitHub Actions workflows.
10
11## What BuildSpace Provides
12
13BuildSpace has two layers:
14
15- **Workflows**: full release pipelines under `.github/workflows/*`.
16- **Blocks**: reusable composite actions under `.github/blocks/*` for custom pipelines.
17
18Default recommendation: use a prebuilt workflow unless the user explicitly needs custom behavior.
19
20## Workflow Selection
21
22Pick exactly one primary workflow based on project type:
23
24| Project type | Workflow file | Trigger |
25|---|---|---|
26| Rust binary/library | `rust-service-release.yaml` | PR label `release` |
27| TypeScript/JavaScript single package | `typescript-service-release.yaml` | PR label `release` |
28| TypeScript monorepo (multiple packages) | `typescript-monorepo-release.yaml` | PR label `release` |
29| Go binary | `go-service-release.yaml` | PR label `release` |
30| Swift macOS `.pkg` (with compiled binary) | `swift-release.yml` | PR label `release` |
31| macOS `.pkg` without binary (payload/scripts only) | `pkg-release.yml` | PR label `release` |
32| macOS `.pkg` PR build (payload/scripts only) | `pkg-release-pr.yml` | Every PR commit |
33| Swift macOS `.pkg` PR build previews | `swift-pkg-pr.yml` | Every PR commit |
34| macOS dylib release (Xcode workspace) | `dylib-release.yml` | PR label `release` |
35| macOS dylib release (Makefile) | `makefile-dylib-release.yml` | PR label `release` |
36| Generic release (version + GitHub Release only) | `release.yaml` | PR label `release` |
37| README freshness check on PRs | `check-readme.yaml` | Every PR |
38| Skills documentation freshness check on PRs | `check-skills.yaml` | Every PR |
39
40## Required Inputs, Secrets, and Permissions
41
42Always verify these before writing YAML:
43
441. **Inputs**: service/package names, paths, build command, package lists, Homebrew tap info, Jamf config.
452. **Secrets**:
46 - Always required for AI features: `OPENAI_API_KEY`
47 - npm publishing: `NPM_TOKEN`
48 - crates publishing: `CARGO_REGISTRY_TOKEN`
49 - Swift compile-time env vars: `SECRET_ENV_VARS`
50 - Jamf upload: `JAMF_CLIENT_ID` + `JAMF_CLIENT_SECRET`
51 - Protected-branch pushes or Homebrew tap updates: `APP_ID` + `APP_PRIVATE_KEY`
52 - Skills documentation check (private repos): `SKILLS_REPO_TOKEN`
53 - Note: `DEVELOPER_ID_INSTALLER_NAME` is **deprecated and ignored** — packages are always unsigned.
543. **Permissions**:
55 - Release/version bump jobs need `contents: write`
56 - Label checks need `pull-requests: read`
57 - PR commenting needs `pull-requests: write`
58
59## Release Trigger Rules (Important)
60
61BuildSpace is label-gated by default.
62
63- Standard release label: `release`
64- Optional prerelease label: `prerelease`
65- No label usually means no release job.
66
67Behavior nuance to keep accurate:
68
69- `typescript-monorepo-release` supports prerelease path directly.
70- `typescript-service-release`, `rust-service-release`, and `go-service-release` gate release jobs on `release` (or forced input), and treat prerelease as flavor once release is active.
71- `swift-release`, `pkg-release`, `dylib-release`, and `makefile-dylib-release` check only `release`.
72- `release.yaml` (generic) checks `release` label and supports a `release` boolean input to force.
73
74## Implementation Procedure
75
76When asked to set up BuildSpace in a repo:
77
781. Detect repo type (Rust, TS single, TS monorepo, Go, Swift, macOS pkg, dylib).
792. Confirm publish targets (GitHub only, npm, crates, Jamf, Homebrew tap).
803. Create a caller workflow in the user repo (`.github/workflows/release.yaml` or `ci.yaml`) with `uses: photon-hq/buildspace/...@v1`.
814. Wire `with:` inputs and `secrets:` exactly for that workflow.
825. Add recommended permissions block.
836. Add `dry-run: true` for first validation run unless the user requests immediate publish.
847. Explain how to trigger (`release` label + merge path, or forced `release: true`).
858. For monorepos, validate `packages` JSON and dependency order behavior.
869. For Homebrew tap updates, wire `tap-repo` and `tap-formula` inputs plus `APP_ID`/`APP_PRIVATE_KEY` secrets.
8710. For Jamf uploads, wire `jamf-url` and Jamf secrets.
88
89## Workflow Reference
90
91### rust-service-release.yaml
92
93Complete release pipeline for Rust services: label check, AI version + release notes, cross-build (Linux x64, macOS ARM64, Windows x64), sync workspace crate versions, publish crates to crates.io, create GitHub Release with binaries, optionally update Homebrew tap.
94
95**Inputs:**
96
97| Input | Type | Required | Default | Description |
98|---|---|---|---|---|
99| `service-name` | string | Yes | — | Display name for the service |
100| `binary-name` | string | Yes | — | Name of the binary from `Cargo.toml` |
101| `binary-path` | string | No | `""` | Path to crate directory (e.g., `crates/client`) |
102| `crates` | string | No | `[]` | JSON array of crate paths to publish in dependency order |
103| `build-env` | string | No | `""` | Compile-time env vars (e.g., `BASE_URL=https://...`) |
104| `labels-to-check` | string | No | `["release", "prerelease"]` | PR labels that trigger releases |
105| `prerelease` | boolean | No | `false` | Force prerelease (adds `-rc.N` suffix) |
106| `release` | boolean | No | `false` | Force release (bypasses label check) |
107| `dry-run` | boolean | No | `false` | Test without actually publishing |
108| `tap-repo` | string | No | `""` | Homebrew tap repository (e.g., `photon-hq/homebrew-photon`). Empty to skip. |
109| `tap-formula` | string | No | `""` | Formula name in tap (e.g., `jamf-package-updater`). Required if `tap-repo` is set. |
110
111**Secrets:**
112
113| Secret | Required | Description |
114|---|---|---|
115| `OPENAI_API_KEY` | Yes | For AI-powered versioning and release notes |
116| `CARGO_REGISTRY_TOKEN` | No | crates.io API token (required for publishing) |
117| `APP_ID` | No | GitHub App ID (for protected branches and Homebrew tap updates) |
118| `APP_PRIVATE_KEY` | No | GitHub App private key |
119
120### typescript-service-release.yaml
121
122Complete release pipeline for a single TypeScript/JavaScript package: label check, AI version + release notes, bump `package.json`, create GitHub Release, publish to npm.
123
124**Inputs:**
125
126| Input | Type | Required | Default | Description |
127|---|---|---|---|---|
128| `service-name` | string | Yes | — | Display name for the service |
129| `bun-version` | string | No | `latest` | Bun version to use |
130| `npm-tag` | string | No | `latest` | npm dist-tag (e.g., `latest`, `beta`, `next`) |
131| `no-npm-publish` | boolean | No | `false` | Skip npm publishing (GitHub Release only) |
132| `working-directory` | string | No | `.` | Directory containing `package.json` |
133| `build-command` | string | No | `bun run build` | Build command to run |
134| `labels-to-check` | string | No | `["release", "prerelease"]` | PR labels that trigger releases |
135| `prerelease` | boolean | No | `false` | Force prerelease |
136| `release` | boolean | No | `false` | Force release (bypasses label check) |
137| `dry-run` | boolean | No | `false` | Test without actually publishing |
138
139**Secrets:**
140
141| Secret | Required | Description |
142|---|---|---|
143| `OPENAI_API_KEY` | Yes | For AI-powered versioning and release notes |
144| `NPM_TOKEN` | No | npm auth token (required for publishing) |
145
146### typescript-monorepo-release.yaml
147
148Complete release pipeline for TypeScript monorepos with independently-versioned packages. Detects changed packages, topologically sorts by dependency order, single AI call for all versions/notes, bumps each `package.json`, creates GitHub Release with `release/YYYY-MM-DD.N` tag, publishes to npm in order.
149
150**Inputs:**
151
152| Input | Type | Required | Default | Description |
153|---|---|---|---|---|
154| `service-name` | string | Yes | — | Display name for the monorepo |
155| `packages` | string | Yes | — | JSON array: `[{"name":"pkg","path":"packages/pkg"}]` |
156| `bun-version` | string | No | `latest` | Bun version to use |
157| `npm-tag` | string | No | `latest` | npm dist-tag |
158| `build-command` | string | No | `bun run build` | Per-package build command (ignored if `root-build-command` is set) |
159| `root-build-command` | string | No | `""` | Build once at repo root (e.g., `turbo build`) |
160| `include-dependents` | boolean | No | `false` | Also release downstream dependents |
161| `labels-to-check` | string | No | `["release", "prerelease"]` | PR labels that trigger releases |
162| `prerelease` | boolean | No | `false` | Force prerelease |
163| `release` | boolean | No | `false` | Force release |
164| `dry-run` | boolean | No | `false` | Test without actually publishing |
165
166**Secrets:**
167
168| Secret | Required | Description |
169|---|---|---|
170| `OPENAI_API_KEY` | Yes | For AI-powered versioning and release notes |
171| `NPM_TOKEN` | Yes | npm authentication token |
172| `APP_ID` | No | GitHub App ID (for pushing to protected branches) |
173| `APP_PRIVATE_KEY` | No | GitHub App private key |
174
175### go-service-release.yaml
176
177Complete release pipeline for Go binaries: label check, AI version + release notes, cross-compile for macOS (ARM64) and Linux (AMD64), create GitHub Release with binaries, optionally update Homebrew tap.
178
179**Inputs:**
180
181| Input | Type | Required | Default | Description |
182|---|---|---|---|---|
183| `service-name` | string | Yes | — | Display name for the service |
184| `binary-name` | string | Yes | — | Output binary name |
185| `go-version` | string | No | `stable` | Go version to use |
186| `build-flags` | string | No | `""` | Additional `go build` flags |
187| `ldflags` | string | No | `-s -w` | Linker flags |
188| `labels-to-check` | string | No | `["release", "prerelease"]` | PR labels that trigger releases |
189| `prerelease` | boolean | No | `false` | Force prerelease |
190| `release` | boolean | No | `false` | Force release |
191| `tap-repo` | string | No | `""` | Homebrew tap repository. Empty to skip. |
192| `tap-formula` | string | No | `""` | Formula name in tap. Required if `tap-repo` is set. |
193
194**Secrets:**
195
196| Secret | Required | Description |
197|---|---|---|
198| `OPENAI_API_KEY` | Yes | For AI-powered versioning and release notes |
199| `APP_ID` | No | GitHub App ID (for Homebrew tap updates) |
200| `APP_PRIVATE_KEY` | No | GitHub App private key |
201
202### swift-release.yml
203
204Complete release pipeline for macOS `.pkg` distribution with a compiled Swift binary: label check, AI version + release notes, Swift build, `.pkg` creation (unsigned), GitHub Release, optional Jamf upload.
205
206**Inputs:**
207
208| Input | Type | Required | Default | Description |
209|---|---|---|---|---|
210| `package-name` | string | Yes | — | Name of the Swift binary / package |
211| `identifier` | string | Yes | — | Package identifier (e.g., `com.example.mytool`) |
212| `scripts-path` | string | No | `""` | Path to scripts directory with preinstall/postinstall scripts |
213| `payload-path` | string | No | `""` | Path to additional payload directory (mirrors install root) |
214| `jamf-url` | string | No | `""` | Jamf Pro instance URL (leave empty to skip) |
215| `jamf-package-priority` | string | No | `""` | Package priority in Jamf Pro |
216| `jamf-package-name` | string | No | `""` | Package name to match in Jamf Pro |
217
218**Secrets:**
219
220| Secret | Required | Description |
221|---|---|---|
222| `OPENAI_API_KEY` | Yes | For AI-powered versioning and release notes |
223| `SECRET_ENV_VARS` | No | Compile-time env vars written to `.env` |
224| `JAMF_CLIENT_ID` | No | Jamf Pro API client ID |
225| `JAMF_CLIENT_SECRET` | No | Jamf Pro API client secret |
226
227### pkg-release.yml
228
229Release pipeline for macOS `.pkg` that does **not** contain a compiled binary. Packages payload files and scripts into a `.pkg`, creates GitHub Release, optionally uploads to Jamf. Use instead of `swift-release` when packages only deliver configuration files, LaunchDaemons, scripts, or other non-binary payload.
230
231**Inputs:**
232
233| Input | Type | Required | Default | Description |
234|---|---|---|---|---|
235| `package-name` | string | Yes | — | Name of the package |
236| `identifier` | string | Yes | — | Package identifier (e.g., `com.example.my-config`) |
237| `scripts-path` | string | No | `""` | Path to scripts directory with preinstall/postinstall scripts |
238| `payload-path` | string | No | `""` | Path to payload directory (mirrors install root) |
239| `jamf-url` | string | No | `""` | Jamf Pro instance URL (leave empty to skip) |
240| `jamf-package-priority` | string | No | `""` | Package priority in Jamf Pro |
241| `jamf-package-name` | string | No | `""` | Package name to match in Jamf Pro |
242
243**Secrets:**
244
245| Secret | Required | Description |
246|---|---|---|
247| `OPENAI_API_KEY` | Yes | For AI-powered versioning and release notes |
248| `JAMF_CLIENT_ID` | No | Jamf Pro API client ID |
249| `JAMF_CLIENT_SECRET` | No | Jamf Pro API client secret |
250
251### pkg-release-pr.yml
252
253Builds a macOS `.pkg` (without a compiled binary) on every PR commit and reports status in the PR as a living comment. Same experience as `swift-pkg-pr` but for payload/scripts-only packages. Stale in-progress builds are automatically cancelled on new commits.
254
255**Inputs:**
256
257| Input | Type | Required | Default | Description |
258|---|---|---|---|---|
259| `package-name` | string | Yes | — | Name of the package |
260| `identifier` | string | Yes | — | Package identifier (e.g., `com.example.my-config`) |
261| `scripts-path` | string | No | `""` | Path to scripts directory with preinstall/postinstall scripts |
262| `payload-path` | string | No | `""` | Path to payload directory (mirrors install root) |
263
264### swift-pkg-pr.yml
265
266Builds a macOS `.pkg` (with Swift binary) on every PR commit, posts/updates a single PR comment with build status, uploads artifact (7-day retention). Stale in-progress builds are automatically cancelled on new commits.
267
268**Inputs:**
269
270| Input | Type | Required | Default | Description |
271|---|---|---|---|---|
272| `package-name` | string | Yes | — | Name of the Swift binary / package |
273| `identifier` | string | Yes | — | Package identifier (e.g., `com.example.mytool`) |
274| `scripts-path` | string | No | `""` | Path to scripts directory with preinstall/postinstall scripts |
275
276**Secrets:**
277
278| Secret | Required | Description |
279|---|---|---|
280| `SECRET_ENV_VARS` | No | Compile-time env vars written to `.env` |
281
282### dylib-release.yml
283
284Release pipeline for macOS dynamic libraries built from an Xcode workspace with CocoaPods. Label check, AI version + release notes, install CocoaPods, build dylib (arm64e), create GitHub Release with `.dylib` artifact.
285
286**Inputs:**
287
288| Input | Type | Required | Default | Description |
289|---|---|---|---|---|
290| `workspace` | string | Yes | — | Path to `.xcworkspace` |
291| `scheme` | string | Yes | — | Xcode scheme to build |
292| `dylib-name` | string | Yes | — | Name of the output dylib (e.g., `BlueBubblesHelper`) |
293| `project-directory` | string | Yes | — | Directory containing the Podfile |
294
295**Secrets:**
296
297| Secret | Required | Description |
298|---|---|---|
299| `OPENAI_API_KEY` | Yes | For AI-powered versioning and release notes |
300
301### makefile-dylib-release.yml
302
303Release pipeline for macOS dynamic libraries built from a Makefile. Label check, AI version + release notes, `make release`, create GitHub Release with `.dylib` artifact.
304
305**Inputs:**
306
307| Input | Type | Required | Default | Description |
308|---|---|---|---|---|
309| `dylib-name` | string | Yes | — | Name of the output dylib (e.g., `imessage-helper`) |
310
311**Secrets:**
312
313| Secret | Required | Description |
314|---|---|---|
315| `OPENAI_API_KEY` | Yes | For AI-powered versioning and release notes |
316
317### release.yaml
318
319Generic release pipeline for projects that only need AI-powered versioning and a GitHub Release (no build step, no package publishing). Useful for configuration repos, documentation repos, or any project that just needs tagged releases.
320
321**Inputs:**
322
323| Input | Type | Required | Default | Description |
324|---|---|---|---|---|
325| `service-name` | string | Yes | — | Display name for the service |
326| `labels-to-check` | string | No | `["release"]` | JSON array of PR labels to check |
327| `release` | boolean | No | `false` | Force release (overrides label) |
328
329**Secrets:**
330
331| Secret | Required | Description |
332|---|---|---|
333| `OPENAI_API_KEY` | Yes | For AI-powered versioning and release notes |
334
335### check-readme.yaml
336
337Runs on every PR to check if `README.md` is up to date with the changes. Uses AI to analyze changed files against the README. Posts/removes PR comments automatically.
338
339**Inputs:**
340
341| Input | Type | Required | Default | Description |
342|---|---|---|---|---|
343| `blocking` | boolean | No | `false` | If `true`, fail the workflow when README is outdated. If `false`, only post a warning comment. |
344
345**Secrets:**
346
347| Secret | Required | Description |
348|---|---|---|
349| `OPENAI_API_KEY` | Yes | For AI-powered README analysis |
350
351### check-skills.yaml
352
353Runs on every PR to check if skills documentation (in a separate skills repo) is up to date with the changes. Uses AI to analyze changed files against skill SKILL.md files. Posts/removes PR comments automatically.
354
355**Inputs:**
356
357| Input | Type | Required | Default | Description |
358|---|---|---|---|---|
359| `fail_on_missing_skills` | boolean | No | `false` | If `true`, fail workflow when skills are outdated. If `false`, only post a warning comment. |
360| `skills-repo` | string | No | `photon-hq/skills` | Skills repository to check against (`owner/repo` format) |
361
362**Secrets:**
363
364| Secret | Required | Description |
365|---|---|---|
366| `OPENAI_API_KEY` | Yes | For AI-powered skills analysis |
367| `SKILLS_REPO_TOKEN` | No | GitHub token for skills repo (defaults to `GITHUB_TOKEN`; use PAT for private repos) |
368
369## Monorepo-Specific Guidance
370
371For `typescript-monorepo-release`:
372
373- `packages` input must be a JSON array:
374 `[{"name":"pkg","path":"packages/pkg"}]`
375- Changed package detection is diffed from last release tag.
376- Packages are topologically sorted using local dependency edges.
377- Optional `include-dependents: true` pulls downstream packages into release.
378- `root-build-command` takes precedence over per-package `build-command`.
379- Pre-release npm tag is automatically `beta` when prerelease is active.
380- Uses `release/YYYY-MM-DD.N` date-based tags since there's no single version.
381
382## Rust/Go Notes
383
384- Rust workflow can:
385 - cross-build artifacts for Linux x64, macOS ARM64, Windows x64
386 - sync workspace versions across all crates
387 - publish crates in provided order
388 - optionally update Homebrew tap (`tap-repo` + `tap-formula`, requires `APP_ID` + `APP_PRIVATE_KEY`)
389- Go workflow:
390 - cross-builds for macOS ARM64 and Linux AMD64
391 - can also update Homebrew tap (`tap-repo` + `tap-formula`, requires `APP_ID` + `APP_PRIVATE_KEY`)
392
393## Swift / macOS .pkg Notes
394
395- `swift-release` builds the Swift binary, creates an unsigned `.pkg`, creates GitHub Release, and optionally uploads to Jamf.
396- `pkg-release` is for `.pkg` packages that contain **no compiled binary** — only payload files and/or scripts.
397- `swift-pkg-pr` and `pkg-release-pr` are the PR preview counterparts (build on every commit, comment on PR).
398- Packages are **always unsigned** (`DEVELOPER_ID_INSTALLER_NAME` is deprecated and ignored).
399- Compile-time env vars are passed via `SECRET_ENV_VARS` secret (written to `.env`).
400- Jamf upload is optional — set `jamf-url` input and provide `JAMF_CLIENT_ID` + `JAMF_CLIENT_SECRET` secrets.
401
402## Dylib Notes
403
404- `dylib-release.yml` is for projects using an Xcode workspace with CocoaPods. Builds arm64e dylib.
405- `makefile-dylib-release.yml` is for projects using a Makefile. Runs `make release` and embeds version.
406- Both create a GitHub Release with the `.dylib` as an artifact.
407
408## README Check Guidance
409
410Use `check-readme.yaml` for PR docs drift detection:
411
412- `blocking: false` (default): warning comment only.
413- `blocking: true`: fails job when README appears stale.
414- Requires `OPENAI_API_KEY` and PR write permission for comments.
415
416## Skills Check Guidance
417
418Use `check-skills.yaml` for skills documentation drift detection:
419
420- `fail_on_missing_skills: false` (default): warning comment only.
421- `fail_on_missing_skills: true`: fails job when skills appear outdated.
422- Requires `OPENAI_API_KEY` and PR write permission for comments.
423- For private skills repos, provide `SKILLS_REPO_TOKEN` (PAT with repo access).
424
425## Blocks Reference
426
427All blocks live under `.github/blocks/`. Workflows compose these internally — use them directly only for custom pipelines.
428
429### Version & Release Info
430
431| Block | Path | Purpose |
432|---|---|---|
433| `determine-publish-version` | `.github/blocks/determine-publish-version/action.yaml` | AI-powered next semantic version (standalone, no release notes) |
434| `generate-release-info` | `.github/blocks/generate-release-info/action.yaml` | AI-powered version + release notes in one step |
435| `create-github-release` | `.github/blocks/create-github-release/action.yaml` | Create GitHub Release with optional artifact attachments |
436
437### Label & PR Interaction
438
439| Block | Path | Purpose |
440|---|---|---|
441| `check-pr-label` | `.github/blocks/check-pr-label/action.yaml` | Check PR labels to decide if release should trigger |
442| `comment-on-pr` | `.github/blocks/comment-on-pr/action.yaml` | Post or update a single PR comment (idempotent via `comment-key`) |
443| `check-readme` | `.github/blocks/check-readme/action.yaml` | AI-powered README freshness check |
444| `check-skills` | `.github/blocks/check-skills/action.yaml` | AI-powered skills documentation freshness check |
445
446### Build Blocks
447
448| Block | Path | Purpose |
449|---|---|---|
450| `rust-build` | `.github/blocks/rust-build/action.yaml` | Build Rust binary for a target triple |
451| `go-build` | `.github/blocks/go-build/action.yaml` | Build Go binary for a target OS/arch |
452| `typescript-build` | `.github/blocks/typescript-build/action.yaml` | Build TypeScript project using Bun |
453| `swift-build` | `.github/blocks/swift-build/action.yml` | Build Swift binary (SPM, supports `.env` injection) |
454| `swift-pkg` | `.github/blocks/swift-pkg/action.yml` | Create macOS `.pkg` from binary and/or payload/scripts |
455| `build-dylib` | `.github/blocks/build-dylib/action.yml` | Build macOS dylib from Xcode workspace (arm64e) |
456| `build-makefile-dylib` | `.github/blocks/build-makefile-dylib/action.yml` | Build macOS dylib from Makefile |
457| `install-cocoapods` | `.github/blocks/install-cocoapods/action.yml` | Cache and install CocoaPods dependencies |
458
459### Publish Blocks
460
461| Block | Path | Purpose |
462|---|---|---|
463| `publish-npm` | `.github/blocks/publish-npm/action.yaml` | Publish single package to npm |
464| `publish-npm-packages` | `.github/blocks/publish-npm-packages/action.yaml` | Publish multiple monorepo packages to npm in dependency order |
465| `publish-crates` | `.github/blocks/publish-crates/action.yaml` | Publish workspace crates to crates.io in order |
466| `bump-npm-version` | `.github/blocks/bump-npm-version/action.yaml` | Bump version in `package.json` and push |
467| `bump-monorepo-versions` | `.github/blocks/bump-monorepo-versions/action.yaml` | AI version bump for all changed monorepo packages |
468| `sync-crates-version` | `.github/blocks/sync-crates-version/action.yaml` | Set a single version across all Rust workspace crates |
469| `update-tap` | `.github/blocks/update-tap/action.yaml` | Update Homebrew tap formula (auto-calculates SHA256 for npm/Go/prebuilt) |
470| `detect-changed-packages` | `.github/blocks/detect-changed-packages/action.yaml` | Detect changed monorepo packages and return in topological order |
471
472## Troubleshooting Checklist
473
474If release did not run:
475
476- Confirm PR had expected label before merge.
477- Confirm workflow permissions include required scopes.
478- Confirm secrets exist at repository level.
479- Confirm caller workflow points to correct reusable workflow path.
480- For monorepos, confirm `packages` JSON is valid and paths exist.
481- For publish failures, test with `dry-run: true` and verify auth token scopes.
482- For Homebrew tap updates, confirm `APP_ID` + `APP_PRIVATE_KEY` secrets are set and the GitHub App has push access to the tap repo.
483- For Jamf uploads, confirm `JAMF_CLIENT_ID` + `JAMF_CLIENT_SECRET` are set and `jamf-url` is a valid Jamf Pro URL.
484- For dylib builds, confirm Xcode workspace/scheme or Makefile exists and produces expected output.
485
486## Output Format for Agent Responses
487
488When generating BuildSpace setup instructions, respond with:
489
4901. **Chosen workflow** and why.
4912. **Copy-ready YAML** for caller workflow.
4923. **Secrets to add** (list exactly which ones and where to get them).
4934. **How to trigger and verify**.
4945. **First-run safe mode** (`dry-run`) recommendation.
495
496Keep recommendations concrete and default to the smallest working setup.