Reusable GitHub Actions blocks and workflows for fast shipping teams
BuildSpace gives you two layers of CI/CD automation:
- Workflows: plug-and-play release pipelines. Point a workflow at your repo, provide a few secrets, and you're done. Most teams only need this.
- Blocks: the composable actions that workflows are built from. Use them to assemble custom pipelines when the prebuilt workflows don't fit.
- Quick Start
- Prerequisites
- Workflows
- Rust Service Release
- TypeScript Service Release
- TypeScript Monorepo Release
- Go Binary Release
- Swift Release
- Package Release
- Package PR Build
- Swift Package PR Build
- Check README
- Blocks (Composite Actions)
- check-pr-label
- check-readme
- generate-release-info
- determine-publish-version
- create-github-release
- detect-changed-packages
- bump-monorepo-versions
- publish-npm-packages
- rust-build
- typescript-build
- sync-crates-version
- publish-crates
- publish-npm
- comment-on-pr
- Architecture
- Contributing
- License
| I have a... | Use this workflow | Trigger |
|---|---|---|
| Single Rust binary or library | rust-service-release |
PR label release |
| Single TypeScript / JavaScript package | typescript-service-release |
PR label release |
| TypeScript monorepo (multiple packages) | typescript-monorepo-release |
PR label release |
| Go binary | go-binary-release |
PR label release |
Swift macOS .pkg (release) |
swift-release |
PR label release |
macOS .pkg without binary (payload/scripts only) |
pkg-release |
PR label release |
macOS .pkg PR build (no binary) |
pkg-release-pr |
Every PR commit |
Swift macOS .pkg (PR build previews) |
swift-pkg-pr |
Every PR commit |
| Any project — check if README is current | check-readme |
Every PR |
Create .github/workflows/release.yaml:
name: Release
on:
push:
branches: [main]
jobs:
release:
uses: photon-hq/buildspace/.github/workflows/rust-service-release.yaml@main
permissions:
contents: write
pull-requests: read
with:
service-name: my-service
binary-name: my-binary
crates: '["crates/shared", "crates/client"]'
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}Create .github/workflows/release.yaml:
name: Release
on:
push:
branches: [main]
jobs:
release:
uses: photon-hq/buildspace/.github/workflows/typescript-service-release.yaml@main
permissions:
contents: write
pull-requests: read
with:
service-name: my-package
build-command: "npm run build"
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}Create .github/workflows/release.yaml:
name: Release
on:
push:
branches: [main]
jobs:
release:
uses: photon-hq/buildspace/.github/workflows/typescript-monorepo-release.yaml@main
permissions:
contents: write
pull-requests: read
with:
service-name: photon-ts
packages: '[{"name":"photon","path":"packages/photon"},{"name":"@photon/openai-compatible","path":"packages/openai-compatible"}]'
root-build-command: "turbo build"
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}Add to any repo's .github/workflows/ci.yaml:
name: CI
on:
pull_request:
jobs:
check-readme:
uses: photon-hq/buildspace/.github/workflows/check-readme.yaml@main
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}Then add a release label to your PR, merge, and let BuildSpace do the rest.
All examples above use @main which tracks the latest changes. For production stability, pin to a specific release tag:
uses: photon-hq/buildspace/.github/workflows/rust-service-release.yaml@v1.2.3Available versions are listed on the GitHub Releases page. Buildspace versions itself using the same AI-powered release pipeline it provides to other repos.
Every workflow needs at least OPENAI_API_KEY for AI-powered versioning and release notes. Add additional secrets depending on where you publish:
| Secret | Needed for | Where to get it |
|---|---|---|
OPENAI_API_KEY |
All workflows | platform.openai.com/api-keys |
NPM_TOKEN |
TypeScript publishing | npmjs.com/settings/tokens |
CARGO_REGISTRY_TOKEN |
Rust crate publishing | crates.io/settings/tokens |
DEVELOPER_ID_INSTALLER_NAME |
Swift .pkg signing |
Apple Developer portal |
Add these in your repo's Settings > Secrets and variables > Actions.
Control releases by adding labels to your PR before merging:
| Label | Effect |
|---|---|
release |
Triggers a full release (GitHub Release + package publish) |
prerelease |
Creates a prerelease with -rc.N suffix and beta npm tag |
No label = no release PRs without labels merge without triggering any release jobs. You can customize your release label with the input 'labels-to-check' in the 'release.yml'.
Workflows that create releases or push commits need contents: write. Workflows that read PR labels need pull-requests: read. Workflows that post comments need pull-requests: write. Each workflow section below lists the exact permissions required.
Ready-to-use release pipelines. Each workflow composes the lower-level blocks internally - you don't need to know about individual blocks unless you're building a custom pipeline.
File: .github/workflows/rust-service-release.yaml
Complete release pipeline for Rust services: checks PR labels, generates version and release notes with AI, builds binaries for Linux (x86_64), macOS (ARM64), and Windows, syncs version across all workspace crates, publishes to crates.io, and creates a GitHub Release with attached binaries.
| 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 |
| 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) |
jobs:
release:
uses: photon-hq/buildspace/.github/workflows/rust-service-release.yaml@main
permissions:
contents: write
pull-requests: read
with:
service-name: enva
binary-name: enva
binary-path: crates/client
crates: '["crates/shared", "crates/client"]'
build-env: "API_URL=https://api.example.com"
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}File: .github/workflows/typescript-service-release.yaml
Complete release pipeline for a single TypeScript/JavaScript package: checks PR labels, generates version and release notes with AI, bumps package.json, creates a GitHub Release, and publishes to npm.
| 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 |
| Secret | Required | Description |
|---|---|---|
OPENAI_API_KEY |
Yes | For AI-powered versioning and release notes |
NPM_TOKEN |
No | npm auth token (required for publishing) |
jobs:
release:
uses: photon-hq/buildspace/.github/workflows/typescript-service-release.yaml@main
permissions:
contents: write
pull-requests: read
with:
service-name: notebooklm-kit
build-command: "npm run build"
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}File: .github/workflows/typescript-monorepo-release.yaml
Complete release pipeline for TypeScript/JavaScript monorepos with independently-versioned packages. Detects which packages changed since the last release, topologically sorts them by dependency order, uses a single AI call to determine all versions and generate combined release notes, bumps each package.json, creates a GitHub Release with a release/YYYY-MM-DD.N tag, and publishes all changed packages to npm in dependency order.
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
service-name |
string | Yes | — | Display name for the monorepo |
packages |
string | Yes | — | JSON array of packages: [{"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 of changed packages |
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 |
| 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 (for pushing to protected branches) |
jobs:
release:
uses: photon-hq/buildspace/.github/workflows/typescript-monorepo-release.yaml@main
permissions:
contents: write
pull-requests: read
with:
service-name: photon-ts
packages: |
[
{"name":"photon","path":"packages/photon"},
{"name":"@photon/openai-compatible","path":"packages/openai-compatible"},
{"name":"create-photon","path":"packages/create-photon"}
]
root-build-command: "turbo build"
include-dependents: true
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}PR merged with "release" label
│
▼
┌─────────────┐ ┌───────────────────┐ ┌─────────────────┐
│ Check Labels │────▶│ Detect Changed │────▶│ Bump Versions │
│ │ │ Packages (topo- │ │ (single AI call │
│ │ │ sorted by deps) │ │ for all pkgs) │
└─────────────┘ └───────────────────┘ └────────┬────────┘
│
┌────────┴────────┐
│ │
▼ ▼
┌──────────────┐ ┌─────────────┐
│GitHub Release │ │ npm Publish │
│(combined tag) │ │ (in order) │
└──────────────┘ └─────────────┘
File: .github/workflows/go-service-release.yaml
Complete release pipeline for Go binaries: checks PR labels, generates version and release notes with AI, cross-compiles for Linux (x64/ARM64), macOS (ARM64), and Windows (x64), and creates a GitHub Release with attached binaries.
| 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 (bypasses label check) |
| Secret | Required | Description |
|---|---|---|
OPENAI_API_KEY |
Yes | For AI-powered versioning and release notes |
jobs:
release:
uses: photon-hq/buildspace/.github/workflows/go-service-release.yaml@main
permissions:
contents: write
pull-requests: read
with:
service-name: my-go-tool
binary-name: my-go-tool
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}File: .github/workflows/swift-release.yml
Complete release pipeline for macOS .pkg distribution packages: checks PR labels, generates version and release notes with AI, builds the Swift binary, creates a signed .pkg, creates a GitHub Release, and optionally uploads to Jamf Pro.
| 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 |
jamf-url |
string | No | "" |
Jamf Pro instance URL (leave empty to skip Jamf upload) |
jamf-package-priority |
string | No | "" |
Package priority in Jamf Pro |
jamf-package-name |
string | No | "" |
Package name to match in Jamf Pro |
| Secret | Required | Description |
|---|---|---|
OPENAI_API_KEY |
Yes | For AI-powered versioning and release notes |
DEVELOPER_ID_INSTALLER_NAME |
Yes | Developer ID Installer certificate name |
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 |
jobs:
release:
uses: photon-hq/buildspace/.github/workflows/swift-release.yml@main
with:
package-name: my-tool
identifier: com.example.my-tool
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
DEVELOPER_ID_INSTALLER_NAME: ${{ secrets.DEVELOPER_ID_INSTALLER_NAME }}File: .github/workflows/pkg-release.yml
Release pipeline for macOS .pkg distribution packages that don't contain a compiled binary. Packages payload files and scripts into a signed .pkg, creates a GitHub Release, and optionally uploads to Jamf Pro. Use this instead of swift-release when your package only delivers configuration files, LaunchDaemons, scripts, or other non-binary payload.
| 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 whose contents mirror the install root |
jamf-url |
string | No | "" |
Jamf Pro instance URL (leave empty to skip Jamf upload) |
jamf-package-priority |
string | No | "" |
Package priority in Jamf Pro |
jamf-package-name |
string | No | "" |
Package name to match in Jamf Pro |
| Secret | Required | Description |
|---|---|---|
OPENAI_API_KEY |
Yes | For AI-powered versioning and release notes |
DEVELOPER_ID_INSTALLER_NAME |
Yes | Developer ID Installer certificate name |
JAMF_CLIENT_ID |
No | Jamf Pro API client ID |
JAMF_CLIENT_SECRET |
No | Jamf Pro API client secret |
jobs:
release:
uses: photon-hq/buildspace/.github/workflows/pkg-release.yml@main
with:
package-name: my-config-pkg
identifier: com.example.my-config
payload-path: payload
scripts-path: scripts
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
DEVELOPER_ID_INSTALLER_NAME: ${{ secrets.DEVELOPER_ID_INSTALLER_NAME }}File: .github/workflows/pkg-release-pr.yml
Builds a macOS .pkg (without compiling a binary) on every PR commit and reports status directly in the PR as a living comment. Same PR experience as swift-pkg-pr but for payload/scripts-only packages. If a build is already running when a new commit is pushed, the old run is automatically cancelled.
| 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 whose contents mirror the install root |
| Secret | Required | Description |
|---|---|---|
DEVELOPER_ID_INSTALLER_NAME |
Yes | Developer ID Installer certificate name |
# .github/workflows/pr.yml
name: PR
on:
pull_request:
jobs:
pkg:
uses: photon-hq/buildspace/.github/workflows/pkg-release-pr.yml@main
with:
package-name: my-config-pkg
identifier: com.example.my-config
payload-path: payload
scripts-path: scripts
secrets:
DEVELOPER_ID_INSTALLER_NAME: ${{ secrets.DEVELOPER_ID_INSTALLER_NAME }}File: .github/workflows/swift-pkg-pr.yml
Builds a macOS .pkg on every PR commit and reports status directly in the PR as a living comment (updated in place, not spammy). If a build is already running when a new commit is pushed, the old run is automatically cancelled.
- Posts a "Building..." comment on the PR (or updates the existing one)
- Builds the Swift binary, creates a
.pkgversioned aspr.<PR#>.<run#> - Uploads the
.pkgas an Actions artifact (7-day retention) - Updates the PR comment to success (with artifact link) or failure (with log link)
| 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 |
| Secret | Required | Description |
|---|---|---|
DEVELOPER_ID_INSTALLER_NAME |
Yes | Developer ID Installer certificate name |
SECRET_ENV_VARS |
No | Compile-time env vars written to .env |
# .github/workflows/pr.yml
name: PR
on:
pull_request:
jobs:
swift-pkg:
uses: photon-hq/buildspace/.github/workflows/swift-pkg-pr.yml@main
with:
package-name: my-tool
identifier: com.example.my-tool
secrets:
DEVELOPER_ID_INSTALLER_NAME: ${{ secrets.DEVELOPER_ID_INSTALLER_NAME }}File: .github/workflows/check-readme.yaml
Runs on every PR to verify that README.md is up to date with the changes being introduced. Uses AI to read the README and the changed files, then determines whether the documentation needs updating. Posts a PR comment explaining what's missing when the README is stale, and automatically removes the comment when the README is brought up to date.
Flags changes to public APIs, features, configuration, install steps, and environment variables. Ignores internal refactors, test-only changes, CI tweaks, and bug fixes that don't affect documented behavior.
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
blocking |
boolean | No | false |
If true, fail the workflow when the README is outdated. If false, only post a warning comment. |
| Secret | Required | Description |
|---|---|---|
OPENAI_API_KEY |
Yes | For AI-powered README analysis |
name: CI
on:
pull_request:
jobs:
check-readme:
uses: photon-hq/buildspace/.github/workflows/check-readme.yaml@main
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}jobs:
check-readme:
uses: photon-hq/buildspace/.github/workflows/check-readme.yaml@main
with:
blocking: true
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}To enforce this as a required check, also add
check-readmeas a required status check in your branch protection rules (Settings > Branches > Branch protection rule).
rust-service-release, typescript-service-release, go-binary-release, and swift-release all share the same initial steps then diverge for language-specific publishing:
╔═══════════════════════════════════════════════════╗
║ SHARED STEPS (all workflows) ║
╠═══════════════════════════════════════════════════╣
║ ║
║ ┌─────────────┐ ┌───────────────┐ ║
║ │ PR Merged │────▶│ Check Labels │ ║
║ │ with label │ │ (release?) │ ║
║ └─────────────┘ └───────────────┘ ║
║ │ ║
║ ▼ ║
║ ┌──────────────────┐ ║
║ │ Generate Version │ ║
║ │ + Release Notes │ ║
║ │ (AI-powered) │ ║
║ └──────────────────┘ ║
║ │ ║
╚══════════════════════════════╪════════════════════╝
│
┌─────────────────────────────────────┴─────────────────────────────────────┐
│ │
▼ ▼
╔════════════════════════════════════════╗ ╔════════════════════════════════════════╗
║ rust-service-release ║ ║ typescript-service-release ║
╠════════════════════════════════════════╣ ╠════════════════════════════════════════╣
║ ║ ║ ║
║ ┌───────────────┐ ┌───────────────┐ ║ ║ ┌─────────────────┐ ║
║ │ Build Binaries│ │ Sync Versions │ ║ ║ │ Bump Version │ ║
║ │ (Linux/macOS/ │ │ (all crates) │ ║ ║ │ (package.json) │ ║
║ │ Windows) │ └───────────────┘ ║ ║ └─────────────────┘ ║
║ └───────────────┘ │ ║ ║ │ ║
║ │ ▼ ║ ║ ┌─────────┴─────────┐ ║
║ │ ┌─────────────────┐ ║ ║ │ │ ║
║ │ │ Publish Crates │ ║ ║ ▼ ▼ ║
║ │ │ (crates.io) │ ║ ║ ┌──────────────┐ ┌─────────────┐ ║
║ │ └─────────────────┘ ║ ║ │GitHub Release│ │ npm Publish │ ║
║ │ │ ║ ║ └──────────────┘ └─────────────┘ ║
║ └────────┬────────┘ ║ ║ ║
║ ▼ ║ ╚════════════════════════════════════════╝
║ ┌───────────────────┐ ║
║ │ GitHub Release │ ║
║ │ (with binaries) │ ║
║ └───────────────────┘ ║
║ ║
╚════════════════════════════════════════╝
typescript-monorepo-release extends the single-package pattern to handle multiple independently-versioned packages:
- Change detection — only packages with file changes (and optionally their dependents) are released
- Topological ordering — packages are processed in dependency order so downstream consumers see new versions of their dependencies
- Single AI call — one prompt contains all packages and their scoped commits, producing versions and notes in one shot
- Date-based tags — since there's no single version, releases use
release/YYYY-MM-DD.Ntags workspace:*protocol — left untouched; Bun/npm resolves these to real versions at pack-time
Individual building blocks that the workflows above are assembled from. Use these directly when you need a custom pipeline.
Expand all blocks
Path: .github/blocks/check-pr-label/action.yaml
Decides whether a PR should trigger a release based on its labels. Works on both PR events and push events (looks up the merged PR).
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
labels |
string | Yes | — | JSON array of labels to check (e.g., ["release", "prerelease"]) |
default-on-push |
string | No | "" |
Comma-separated labels to default to true on direct push |
| Output | Type | Description |
|---|---|---|
labels |
JSON | Object with boolean results for each label (e.g., {"release": true, "prerelease": false}) |
- uses: photon-hq/buildspace/.github/blocks/check-pr-label@main
id: labels
with:
labels: '["release", "prerelease"]'
- if: fromJSON(steps.labels.outputs.labels).release
run: echo "Release label found!"Path: .github/blocks/check-readme/action.yaml
Uses AI to read the project's README.md alongside the PR's changed files and determine whether the documentation needs updating. Returns a boolean verdict and a one-sentence explanation. This is the block that powers the Check README workflow.
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
openai-api-key |
secret | Yes | — | OpenAI API key |
| Output | Type | Description |
|---|---|---|
up-to-date |
boolean | true if the README appears current, false if it likely needs changes |
explanation |
string | One-sentence AI explanation of the verdict |
- uses: actions/checkout@v5
- uses: photon-hq/buildspace/.github/blocks/check-readme@main
id: readme
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
- if: steps.readme.outputs.up-to-date == 'false'
run: echo "README needs updating — ${{ steps.readme.outputs.explanation }}"Path: .github/blocks/generate-release-info/action.yaml
Generates a semantic version number and AI-written release notes by analyzing commit history since the last release. Combines determine-publish-version with release note generation in one step.
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
service-name |
string | Yes | — | Service name (used in release notes) |
prerelease |
boolean | No | false |
Append -rc.N suffix to version |
openai-api-key |
secret | Yes | — | OpenAI API key |
| Output | Type | Description |
|---|---|---|
version |
string | Determined version (e.g., 1.2.3 or 1.2.3-rc.5) |
release_notes |
string | AI-generated release notes in markdown |
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: photon-hq/buildspace/.github/blocks/generate-release-info@main
id: info
with:
service-name: my-service
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
- run: |
echo "Version: ${{ steps.info.outputs.version }}"
echo "Notes: ${{ steps.info.outputs.release_notes }}"Path: .github/blocks/determine-publish-version/action.yaml
Standalone action for determining the next semantic version using AI analysis of commits. Lighter-weight alternative to generate-release-info when you don't need release notes.
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
prerelease |
boolean | No | false |
Append -rc.N suffix to version |
openai-api-key |
secret | Yes | — | OpenAI API key |
| Output | Type | Description |
|---|---|---|
version |
string | Determined version (e.g., 1.2.3) |
previous-version |
string | Previous version before this release |
- uses: photon-hq/buildspace/.github/blocks/determine-publish-version@main
id: version
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
- run: echo "Bumping from ${{ steps.version.outputs.previous-version }} to ${{ steps.version.outputs.version }}"Path: .github/blocks/create-github-release/action.yaml
Creates a GitHub Release with optional artifact attachments.
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
version |
string | Yes | — | Version number (e.g., 1.2.3) |
title |
string | Yes | — | Release title |
notes |
string | No | "" |
Release notes in markdown |
prerelease |
boolean | No | false |
Mark as prerelease |
draft |
boolean | No | false |
Create as draft |
tag-prefix |
string | No | v |
Prefix for git tag (e.g., v1.2.3) |
artifact-pattern |
string | No | "" |
Pattern to match artifacts to attach |
| Output | Type | Description |
|---|---|---|
url |
string | URL of the created release |
tag |
string | Created tag name (e.g., v1.2.3) |
- uses: photon-hq/buildspace/.github/blocks/create-github-release@main
with:
version: "1.2.3"
title: "My Service v1.2.3"
notes: |
## What's New
- Added awesome feature
artifact-pattern: "my-binary-*"Path: .github/blocks/detect-changed-packages/action.yaml
Detects which monorepo packages have changed since the last GitHub Release and returns them in topological (dependency) order. Optionally includes downstream dependents.
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
packages |
string | Yes | — | JSON array: [{"name":"pkg","path":"packages/pkg"}] |
include-dependents |
boolean | No | false |
Also include packages that depend on changed packages |
| Output | Type | Description |
|---|---|---|
changed |
JSON | Array of changed packages in topological order |
has-changes |
boolean | Whether any packages have changes |
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: photon-hq/buildspace/.github/blocks/detect-changed-packages@main
id: detect
with:
packages: '[{"name":"photon","path":"packages/photon"},{"name":"@photon/openai","path":"packages/openai"}]'
include-dependents: true
- if: steps.detect.outputs.has-changes == 'true'
run: echo "Changed packages: ${{ steps.detect.outputs.changed }}"Path: .github/blocks/bump-monorepo-versions/action.yaml
Determines versions for all changed monorepo packages using a single AI call, bumps each package.json, commits, and pushes.
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
changed-packages |
string | Yes | — | JSON array from detect-changed-packages output |
service-name |
string | Yes | — | Service name for commit messages |
prerelease |
boolean | No | false |
Append -rc.N suffix to versions |
openai-api-key |
secret | Yes | — | OpenAI API key |
github-token |
secret | Yes | — | GitHub token (fallback) |
app-id |
secret | No | "" |
GitHub App ID (for protected branches) |
app-private-key |
secret | No | "" |
GitHub App private key |
| Output | Type | Description |
|---|---|---|
versions |
JSON | Object mapping package names to new versions |
release-notes |
string | Combined AI-generated release notes |
- uses: photon-hq/buildspace/.github/blocks/bump-monorepo-versions@main
id: bump
with:
changed-packages: ${{ steps.detect.outputs.changed }}
service-name: photon-ts
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
github-token: ${{ github.token }}Path: .github/blocks/publish-npm-packages/action.yaml
Builds and publishes multiple monorepo packages to npm in dependency order. Supports both per-package builds and a single root build command.
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
changed-packages |
string | Yes | — | JSON array of packages in topological order |
bun-version |
string | No | latest |
Bun version |
node-version |
string | No | 20 |
Node.js version |
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) |
dry-run |
boolean | No | false |
Run npm publish --dry-run |
npm-token |
secret | Yes | — | npm authentication token |
- uses: photon-hq/buildspace/.github/blocks/publish-npm-packages@main
with:
changed-packages: ${{ steps.detect.outputs.changed }}
root-build-command: "turbo build"
npm-token: ${{ secrets.NPM_TOKEN }}Path: .github/blocks/rust-build/action.yaml
Builds a Rust binary for a specific target platform.
| Target | OS |
|---|---|
x86_64-unknown-linux-gnu |
Linux (x64) |
aarch64-apple-darwin |
macOS (ARM64) |
x86_64-pc-windows-msvc |
Windows (x64) |
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
binary-name |
string | Yes | — | Name of the binary (from Cargo.toml) |
binary-path |
string | No | "" |
Path to crate directory |
target |
string | Yes | — | Target triple |
build-env |
string | No | "" |
Compile-time env vars for .env file |
| Output | Type | Description |
|---|---|---|
artifact-name |
string | Name of the built artifact |
artifact-path |
string | Path to the artifact file |
- uses: photon-hq/buildspace/.github/blocks/rust-build@main
with:
binary-name: my-cli
target: x86_64-unknown-linux-gnuPath: .github/blocks/typescript-build/action.yaml
Builds a TypeScript project using Bun.
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
bun-version |
string | No | latest |
Bun version |
working-directory |
string | No | . |
Directory containing package.json |
build-command |
string | No | bun run build |
Build command |
- uses: photon-hq/buildspace/.github/blocks/typescript-build@main
with:
build-command: "npm run build"Path: .github/blocks/sync-crates-version/action.yaml
Sets a single version across all workspace crates using cargo-edit, commits, and pushes.
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
version |
string | Yes | — | Version to set across all crates |
commit-changes |
boolean | No | true |
Whether to commit and push |
github-token |
secret | Yes | — | GitHub token for pushing |
| Output | Type | Description |
|---|---|---|
updated-crates |
JSON | Array of crate names that were updated |
- uses: photon-hq/buildspace/.github/blocks/sync-crates-version@main
with:
version: "1.2.3"
github-token: ${{ github.token }}Path: .github/blocks/publish-crates/action.yaml
Publishes workspace crates to crates.io in dependency order with retry logic and rate limit handling.
Order matters. List dependencies before dependents.
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
crates |
string | Yes | — | JSON array of crate paths in publish order |
dry-run |
boolean | No | false |
Run cargo publish --dry-run |
cargo-registry-token |
secret | Yes | — | crates.io API token |
- uses: photon-hq/buildspace/.github/blocks/publish-crates@main
with:
crates: '["crates/shared", "crates/client"]'
cargo-registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}Path: .github/blocks/publish-npm/action.yaml
Publishes a single package to npm (installs dependencies, builds, publishes).
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
bun-version |
string | No | latest |
Bun version |
node-version |
string | No | 20 |
Node.js version |
working-directory |
string | No | . |
Directory containing package.json |
build-command |
string | No | bun run build |
Build command |
tag |
string | No | latest |
npm dist-tag |
dry-run |
boolean | No | false |
Run npm publish --dry-run |
npm-token |
secret | Yes | — | npm authentication token |
- uses: photon-hq/buildspace/.github/blocks/publish-npm@main
with:
build-command: "npm run build"
npm-token: ${{ secrets.NPM_TOKEN }}Path: .github/blocks/comment-on-pr/action.yaml
Posts or updates a single comment on a pull request. When a comment-key is provided, subsequent calls with the same key update the existing comment instead of creating a new one.
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
message |
string | Yes | — | Comment body (markdown supported) |
comment-key |
string | No | "" |
Unique key to find and update an existing comment |
- uses: photon-hq/buildspace/.github/blocks/comment-on-pr@main
with:
comment-key: my-build-status
message: |
## Build Status
All checks passed for commit ${{ github.sha }}buildspace/
├── .github/
│ ├── blocks/ # Composite actions (building blocks)
│ │ ├── bump-monorepo-versions/ # AI version bump for monorepos
│ │ ├── check-pr-label/ # PR label detection
│ │ ├── check-readme/ # AI README freshness check
│ │ ├── comment-on-pr/ # Post/update PR comments
│ │ ├── create-github-release/ # GitHub Release creation
│ │ ├── detect-changed-packages/ # Monorepo change detection + topo-sort
│ │ ├── determine-publish-version/ # AI version detection (standalone)
│ │ ├── generate-release-info/ # AI version + release notes
│ │ ├── go-build/ # Go cross-compilation
│ │ ├── publish-crates/ # crates.io publishing
│ │ ├── publish-npm/ # npm publishing (single package)
│ │ ├── publish-npm-packages/ # npm publishing (monorepo, ordered)
│ │ ├── rust-build/ # Cross-platform Rust builds
│ │ ├── swift-build/ # Swift binary builds
│ │ ├── swift-pkg/ # macOS .pkg creation
│ │ ├── sync-crates-version/ # Workspace version sync
│ │ └── typescript-build/ # TypeScript builds
│ │
│ └── workflows/ # Reusable workflows (full pipelines)
│ ├── check-readme.yaml # AI README freshness check (PR)
│ ├── go-service-release.yaml # Complete Go release pipeline
│ ├── rust-service-release.yaml # Complete Rust release pipeline
│ ├── pkg-release.yml # .pkg release pipeline (no binary)
│ ├── pkg-release-pr.yml # .pkg PR build (no binary)
│ ├── self-release.yaml # Buildspace's own versioned releases
│ ├── swift-pkg-pr.yml # Swift .pkg build on every PR commit
│ ├── swift-release.yml # Swift .pkg release pipeline
│ ├── typescript-monorepo-release.yaml # Complete TS monorepo pipeline
│ └── typescript-service-release.yaml # Complete TS release pipeline
- Create a feature branch from
main - Make your changes
- Test locally or in a test repository
- Open a PR with a clear description
- Add the
releaselabel when ready to publish
To test without publishing, use dry-run: true:
with:
dry-run: trueOr test by pointing to your branch:
uses: photon-hq/buildspace/.github/workflows/rust-service-release.yaml@your-branchMIT © Photon