Solidos Release Orchestrator - How To
The Solidos monorepo uses a centralized release orchestrator to publish all dependent packages at once.
solidos/ (main branch)
├── .github/workflows/
│ └── release.yml ← Manual GitHub Actions trigger
├── scripts/
│ └── release-orchestrator.js ← Does the actual work
├── release.config.json ← Lists repos to publish (stable mode)
├── release.config.test.json ← Lists repos to publish (test mode)
└── workspaces/
├── solid-panes/ ← Nothing special needed
├── folder-pane/ ← Nothing special needed
└── (other repos) ← Nothing special needed
Before the release workflow can publish stable versions from GitHub Actions:
- Create a fine-grained PAT for release automation.
- Grant it
Contents: Read and writeandMetadata: Readon the SolidOS repos to be released. - In
solidos/solidos→ Settings → Secrets and variables → Actions, add:
GIT_PUSH_TOKENNPM_TOKEN
- For protected branches on main: Use
mode=stable-publish(no extra config needed—it auto-merges PRs) - For unprotected main branch: Use
mode=stable(requires direct push access) - Run the
Solidos Releaseworkflow manually with:
mode=testfor prerelease publishingmode=stable-publishfor@latest(protected branch)mode=stablefor@latest(direct push)
Three execution modes:
| Scenario | Command | Where | Publishes? |
|---|---|---|---|
| Local Testing | node scripts/release-orchestrator.js --dry-run=true |
Your computer | ❌ No (prints what would happen) |
| Test Release | Manual trigger: mode=test | GitHub Actions | ✅ Yes (@test tag) |
| Stable Release (Direct Push) | Manual trigger: mode=stable | GitHub Actions | ✅ Yes (@latest tag) |
| Stable Release (Protected Branch) | Manual trigger: mode=stable-publish | GitHub Actions | ✅ Yes (@latest tag, auto-merges PR) |
Workflow (stable-publish mode with protected branches):
You click "Run workflow" → mode=stable-publish
↓ Step 1/3: Create Release PR
Creates release branch from dev → Merges dev into main → Pushes branch → Opens PR
↓ Step 2/3: Auto-Merge PR
Waits for CI checks to pass → Auto-merges PR with --squash → Fetches updated main
↓ Step 3/3: Publish
For each repo listed:
- npm install
- npm version (bump patch/minor/major)
- npm publish (@latest tag)
- git push + tags all at once
Old two-step flow (still supported for manual workflows):
- Step 1:
mode=stable-prepare-pr- creates PR, human reviews/merges - Step 2:
mode=stable-publish- requires manual merge first
New unified flow (recommended):
- Single
mode=stable-publish- does all three: create PR, auto-merge, publish
- Individual repos need nothing special — they just need
package.jsonand npm scripts - All repos already exist in GitHub — clone-missing is a fallback only
- Branches are configurable — typically main for stable, develop for test
- Both test and stable default to main — configure in release.config.json if you want different branches
- PR workflow is unaffected — each repo's ci.yml still runs for PRs
- Waiting PRs won't auto-publish — you manually trigger the release
- This repository uses a multi-repo release orchestrator to publish dependent packages.
- Publishing is only allowed in GitHub Actions; local runs must use --dry-run.
- Test publishes use a prerelease version and the npm dist-tag "test" so @latest is unaffected.
Files
- release.config.json: repo list and per-repo overrides.
- scripts/release-orchestrator.js: the release runner.
- .github/workflows/release.yml: GitHub Actions workflow.
Three Execution Scenarios
Scenario 1: Local Dry-Run (Testing)
cd d:\github\solidos
node scripts/release-orchestrator.js --mode test --dry-run=true- Runs on your computer
- Prints commands without executing them (prefixed with
[dry-run]) - Creates
release-summary.jsonlocally - No publishing occurs
- Use case: See exactly what would happen before running for real
Scenario 2: GitHub Test Release
- Click Actions → "Solidos Release" → Run workflow
- Inputs: mode=test, dry_run=false
- Publishes to npm with
@testtag - Does NOT create git tags
- Results in GitHub Actions logs and artifacts
- Always publishes (no skip logic)
- Use case: Pre-release versions for testing from dev branch
Scenario 3: GitHub Stable Release (Protected Branches)
- Click Actions → "Solidos Release" → Run workflow
- Inputs: mode=stable-publish, dry_run=false
- Single command that does everything:
- Creates release branch from dev
- Opens PR to main
- Waits for CI checks to pass
- Auto-merges PR (squash merge)
- Publishes all packages to npm with
@latesttag - Pushes git tags to main
- Eliminates manual PR merge step
- Perfect for: Organizations with branch protection rules on
main - Use case: Automated stable releases without human intervention on PR merge
Scenario 4: GitHub Stable Release (Direct Push)
- Click Actions → "Solidos Release" → Run workflow
- Inputs: mode=stable, dry_run=false
- Automatically merges origin/dev → main if dev has new commits
- Publishes to npm with
@latesttag - Creates git tags and pushes to GitHub
- Results in GitHub Actions logs and artifacts
- Skips if dev has no new commits (unless --branch=main specified)
- WARNING: Requires write access to protected branches, may fail with 403
- Use case: Repositories without branch protection on
main
Local dry-run
- Show the exact commands without running them: node scripts/release-orchestrator.js --mode test --dry-run
- Override the branch: node scripts/release-orchestrator.js --mode test --dry-run --branch develop
- Force stable publish regardless of changes: node scripts/release-orchestrator.js --mode stable --branch main
- Dry-run allows untracked files (ignored for convenience)
CI runs (GitHub Actions)
-
Trigger workflow "Solidos Release" with inputs:
- mode: test or stable
- config: release.config.json (or another config file)
- dry_run: true or false
- clone_missing: true or false
- branch: optional branch name override
Fine-Grained PAT for Git Push (GIT_PUSH_TOKEN)
- Stable mode needs to push commits/tags to multiple SolidOS repos.
GITHUB_TOKENis often limited to the current repository.- Configure
GIT_PUSH_TOKENas a repository secret insolidos/solidos.
Recommended setup (fine-grained PAT):
- Create a new fine-grained PAT in GitHub (do not reuse a broad personal token).
- Resource owner:
SolidOSorganization. - Repository access:
- Prefer "Only select repositories" and include repos listed in
release.config.json. - "All repositories" also works if needed.
- Permissions:
Contents:Read and write(required for clone/push/tags)Metadata:Read(required)Actions:Read(optional)
- If branch protection blocks direct push to
main, allow this token/account to bypass rules, or switch to a PR-based merge flow. - Save token as secret:
- Repo:
solidos/solidos - Secret name:
GIT_PUSH_TOKEN
Security notes:
- Do not use high-risk scopes like
delete_repoordelete:packages. - Use a dedicated token for release automation.
- Rotate regularly and immediately on suspected exposure.
Command-line Options
- --mode: test or stable (default: stable)
- --config: path to config file (default: release.config.json)
- --dry-run: true or false (default: false)
- --clone-missing: true or false (default: false)
- --branch: override branch for all repos (optional)
- Also disables skip logic in stable mode (forces publish)
- --summary-path: path to output summary file (default: release-summary.json)
Branch Configuration
By default, both test and stable modes use the main branch. To use different branches:
Example release.config.json with separate branches:
{
"defaultBranch": "main",
"modes": [
{
"name": "test",
"branch": "dev",
"versionBump": "prerelease",
"preid": "test",
"npmTag": "test"
},
{
"name": "stable",
"branch": "main",
"versionBump": "patch",
"npmTag": "latest"
}
],
"repos": [
{
"name": "solid-panes",
"path": "./workspaces/solid-panes",
"afterInstall": [
"npm install profile-pane"
]
}
]
}Behavior:
- Test mode:
- Pulls from
devbranch - Always publishes (no skip)
- afterInstall
npm install profile-panebecomesnpm install profile-pane@test || npm install profile-pane@latest
- Pulls from
- Stable mode:
- Pulls from
mainbranch - Auto-merges origin/dev if it has new commits
- Skips if no changes (unless --branch=main specified)
- Pulls from
Publish modes
- test:
- Runs on: dev branch (or configured branch)
- npm version prerelease --preid test
- npm publish --tag test --ignore-scripts
- does NOT create git tags or push
- Always publishes (no skip check)
- If the prerelease version already exists, it auto-bumps again before publishing
- Temporarily disables preversion/version/postversion scripts during version bump
- afterInstall commands use @test tag with @latest fallback
- stable:
- Runs on: main branch (or configured branch)
- Checks if origin/dev has commits that main doesn't
- If yes: auto-merges origin/dev → main (may fail on conflicts)
- npm version patch (or configured bump)
- npm publish (latest) with --ignore-scripts
- creates git tags and pushes by default
- Skips if no changes (unless --branch explicitly specified)
- afterInstall commands use @latest tag
Multiple configs
- Create additional config files (for example):
- release.config.json
- release.config.test.json
- release.config.hotfix.json
- Use with: --config release.config.test.json
Skip logic
- Test mode: Always publishes (no skip logic)
- Stable mode:
- Compares origin/dev vs main to detect unpublished changes
- If origin/dev has commits that main doesn't: merges and publishes
- If no changes: skips publishing
- Override:
--branch=mainforces publish regardless of changes - Merge happens automatically before publish (fails if conflicts)
- Merge commit includes
[skip ci]to prevent redundant ci.yml runs
Summary output
- A summary is printed at the end and written to release-summary.json.
- Override with: --summary-path path/to/summary.json
npm install with dist-tags
- Test mode: afterInstall commands automatically inject @test tags
- Example:
npm install solid-uibecomesnpm install solid-ui@test - Fallback: If @test doesn't exist, tries @latest automatically
- Command:
npm install solid-ui@test || npm install solid-ui@latest
- Example:
- Stable mode: afterInstall commands use @latest tags (default npm behavior)
- Manual install:
npm install <pkg>@testto get test versions
Config options (release.config.json)
- defaultBranch: branch name used if repo does not override.
- skipIfNoDiff: skip publish if no diff vs origin.
- defaultInstall / defaultTest / defaultBuild: commands for all repos.
- modes: publish behavior for "stable" and "test".
- repos: per repo configuration:
- name (required)
- path (required)
- repo (optional, needed for CI clone)
- branch (optional override)
- install/test/build (optional overrides)
- before*/after* arrays for custom steps
Notes
- CI uses npm OIDC with NODE_AUTH_TOKEN set in GitHub Actions.
- CI uses
GIT_PUSH_TOKEN(preferred) or falls back toGITHUB_TOKENfor git clone/push authentication. - If a repo requires special install steps, add them under afterInstall in the config.
Individual Repos and Their CI Workflows
Each workspace repo (solid-panes, folder-pane, etc.) has its own ci.yml workflow for PR testing. This is independent of the release orchestrator:
Per-repo ci.yml (solid-panes/.github/workflows/ci.yml):
- Triggers on: Push to main/develop, Pull requests
- Runs: Tests, linting, building
- Purpose: Verify code quality
- Used by: PRs waiting to be merged
- Does NOT publish to npm
Central release.yml (solidos/.github/workflows/release.yml):
- Triggers on: Manual button click only
- Runs: release-orchestrator.js
- Purpose: Publish all repos to npm at once
- Used by: Release maintainers
- Does NOT run automatically on PRs or merges
Summary:
┌─ PR Created
│ └─ ci.yml runs in each repo → blocks merge if tests fail
│
└─ PR Merged to main/develop
└─ ci.yml finishes
└─ [waiting for manual release trigger]
└─ You click "Run workflow" in Actions
└─ release.yml runs → publishes to npm
└─ (stable mode) auto-merges dev→main with [skip ci]
└─ pushes version tags and commits
└─ ci.yml does NOT run (prevented by [skip ci])
Important Notes:
- Waiting PRs are NOT automatically published. You must manually trigger the release after merging.
- When stable mode merges dev→main automatically, it uses
[skip ci]in the commit message to prevent redundant ci.yml runs in individual repos. - Tests/builds already ran in the release orchestrator, so skipping ci.yml avoids duplicate work.