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