Automated NPM package versioning, building, and publishing with intelligent flow detection for NPM Registry and GitHub Packages.
- π Intelligent Flow Detection: Automatically determines build type based on GitHub context
- π¦ Dual Registry Support: Publish to NPM Registry and/or GitHub Packages
- π’ Monorepo Support: Process multiple packages independently with their own versions
- β¨ Auto-Scoping: Automatically scopes packages for GitHub Packages using repository owner
- π·οΈ Smart Versioning: SemVer versioning with pre-release tags
- π Security Scanning: Built-in npm audit integration
- π¬ PR Comments: Automatic installation instructions in pull requests
- π― Dist-tag Management: Non-latest tags for pre-releases to keep production clean
- π Zero Configuration: Works out of the box with sensible defaults
name: Build and Publish
on:
pull_request:
push:
branches:
- main
- dev
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- uses: wgtechlabs/package-build-flow-action@v1
with:
npm-token: ${{ secrets.NPM_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
registry: 'both'
# package-scope not needed - auto-scoped for GitHub Packages! β¨The action automatically detects the build flow based on GitHub context:
| Flow Type | Trigger | Version Format | NPM Tag | Description |
|---|---|---|---|---|
| release | GitHub Release (standard) | {tag} (with leading v stripped, e.g., v1.0.0 β 1.0.0) |
latest |
Production release from GitHub release event |
| release | GitHub Release (pre-release) | {tag} (with leading v stripped) |
{prerelease-id} (or prerelease if no identifier is detected) |
Pre-release from GitHub release event (e.g., beta, alpha) |
| pr | PR β dev branch | {base}-pr.{sha} |
pr |
Pre-release for testing PR changes |
| dev | PR devβmain OR push to dev | {base}-dev.{sha} |
dev |
Development version for integration testing |
| patch | PR β main (not from dev) | {base}-patch.{sha} |
patch |
Hotfix pre-release for testing |
| staging | Push to main | {base}-staging.{sha} |
staging |
Staging version for final validation before release |
| wip | Other branches | {base}-wip.{sha} |
wip |
Experimental build from feature branch |
Event: release
Release Type: Standard Release
Tag: v1.0.0
Version: 1.0.0
NPM Tag: latest
Event: release
Release Type: Pre-release
Tag: v1.0.0-beta.1
Version: 1.0.0-beta.1
NPM Tag: beta
Event: release
Release Type: Pre-release
Tag: v1.2.3-staging.abc1234
Version: 1.2.3-staging.abc1234
NPM Tag: staging
Warning
Avoid using staging as a prerelease identifier in GitHub Releases.
The staging npm dist-tag is automatically used by the push-to-main flow (e.g., 1.2.3-staging.abc1234). If you also create a GitHub Release with a staging prerelease identifier (e.g., v1.2.3-staging.abc1234), both flows will publish under the same staging dist-tag. This means whichever version is published last will overwrite the staging tag pointer, and running npm install mypackage@staging will resolve to that last-published version instead of the one you might expect.
This won't break anything β both versions remain available by their exact version number (e.g., npm install mypackage@1.2.3-staging.abc1234). It only affects which version the staging dist-tag shortcut points to. If you want to avoid this overlap, use a different prerelease identifier for your GitHub Releases that doesn't collide with any of the built-in flow tags (pr, dev, patch, staging, wip).
Event: pull_request
Base: dev
Head: feature/new-feature
Version: 1.2.3-pr.abc1234
Tag: pr
Event: push
Branch: dev
Version: 1.2.3-dev.abc1234
Tag: dev
Event: pull_request
Base: main
Head: dev
Version: 1.2.3-dev.abc1234
Tag: dev
Event: push
Branch: main
Version: 1.2.3-staging.abc1234
Tag: staging
Event: pull_request
Base: main
Head: hotfix/critical-bug
Version: 1.2.3-patch.abc1234
Tag: patch
| Input | Description | Default | Required |
|---|---|---|---|
registry |
Target registry: npm, github, or both |
both |
No |
npm-token |
NPM access token | - | If publishing to NPM |
npm-registry-url |
NPM registry URL | https://registry.npmjs.org |
No |
github-token |
GitHub token for GitHub Packages | ${{ github.token }} |
No |
github-registry-url |
GitHub Packages registry URL | https://npm.pkg.github.com |
No |
package-scope |
Package scope for GitHub Packages (e.g., @myorg). If not provided, uses the repository owner only when the package name in package.json is unscoped; if the package name is already scoped, its existing scope is kept |
- | No |
| Input | Description | Default | Required |
|---|---|---|---|
main-branch |
Name of main/production branch | main |
No |
dev-branch |
Name of development branch | dev |
No |
| Input | Description | Default | Required |
|---|---|---|---|
package-path |
Path to package.json | ./package.json |
No |
build-script |
NPM script to run before publishing | build |
No |
package-manager |
Package manager to use: npm, yarn, pnpm, bun, or auto (auto-detects from lockfile) |
auto |
No |
version-prefix |
Prefix for version tags | - | No |
| Input | Description | Default | Required |
|---|---|---|---|
audit-enabled |
Enable npm audit security scanning | true |
No |
audit-level |
Minimum severity level: critical, high, moderate, low |
high |
No |
fail-on-audit |
Fail build if vulnerabilities found | false |
No |
| Input | Description | Default | Required |
|---|---|---|---|
pr-comment-enabled |
Enable PR comments with installation instructions | true |
No |
pr-comment-template |
Custom PR comment template | - | No |
| Input | Description | Default | Required |
|---|---|---|---|
publish-enabled |
Enable publishing to registry | true |
No |
dry-run |
Perform dry run without publishing | false |
No |
access |
Package access level for scoped packages: public or restricted |
public |
No |
| Input | Description | Default | Required |
|---|---|---|---|
monorepo |
Enable monorepo mode | false |
No |
package-paths |
Comma-separated list of package.json paths (monorepo mode only). Takes priority over workspace-detection. Either this OR workspace-detection with valid workspaces field is required when monorepo is true. | - | Conditional* |
workspace-detection |
Auto-detect workspaces from the package.json resolved from package-path (default ./package.json). Reads its workspaces field and discovers all non-private packages. |
true |
No |
changed-only |
Only build/publish packages that changed relative to the event-specific git diff base (monorepo mode only). Uses git diff to detect changes. | true |
No |
dependency-order |
Build packages in dependency order using topological sort (monorepo mode only). Analyzes workspace dependencies and builds packages in the correct order. Set to false to use discovery order. |
true |
No |
*Required when monorepo: 'true' AND (workspace-detection: 'false' OR no workspaces field in the package.json resolved from package-path)
| Output | Description |
|---|---|
package-version |
Generated package version (single-package mode) |
registry-urls |
Installation commands for each registry (single-package mode) |
build-flow-type |
Detected flow type (pr, dev, patch, staging, wip) (single-package mode) |
short-sha |
Short commit SHA (single-package mode) |
npm-published |
Whether published to NPM (true/false) (single-package mode) |
github-published |
Whether published to GitHub Packages (true/false) (single-package mode) |
audit-completed |
Whether security audit completed (single-package mode) |
total-vulnerabilities |
Total vulnerabilities found (single-package mode) |
critical-vulnerabilities |
Critical vulnerabilities count (single-package mode) |
high-vulnerabilities |
High vulnerabilities count (single-package mode) |
build-results |
JSON array of per-package build results (monorepo mode only) |
discovered-packages |
JSON array of discovered packages with name, version, path, and dir (monorepo mode with workspace-detection only) |
package-count |
Number of discovered publishable packages (monorepo mode with workspace-detection only) |
changed-packages |
JSON array of packages with changes (monorepo mode with changed-only only) |
changed-count |
Number of changed packages (monorepo mode with changed-only only) |
When monorepo: 'true', the build-results output contains a JSON array:
[
{
"name": "@tinyclaw/core",
"version": "1.0.0-dev.abc1234",
"result": "success"
},
{
"name": "@tinyclaw/plugin-discord",
"version": "1.2.0-dev.abc1234",
"result": "success"
},
{
"name": "@tinyclaw/cli",
"version": "2.0.0-dev.abc1234",
"result": "failed",
"error": "Build failed"
}
]The action supports multiple package managers with automatic detection:
When package-manager: 'auto' (default), the action automatically selects the package manager based on lockfile presence:
- If
bun.lockbexists β Uses Bun (legacy binary format, Bun < v1.2) - If
bun.lockexists β Uses Bun (text-based format, Bun v1.2+) - If
pnpm-lock.yamlexists β Uses pnpm - If
yarn.lockexists β Uses Yarn - If
package-lock.jsonexists β Uses npm - Neither β Falls back to npm
This ensures lockfile consistency and preserves backward compatibility. The action checks for bun.lockb first to support legacy Bun projects, then checks for bun.lock for modern Bun v1.2+ projects. Note: If both bun.lockb and bun.lock exist in a project, bun.lockb takes precedence to ensure consistent behavior for projects in transition.
Explicitly specify the package manager:
- uses: wgtechlabs/package-build-flow-action@v1
with:
package-manager: 'pnpm' # or 'npm', 'yarn', 'bun'
npm-token: ${{ secrets.NPM_TOKEN }}For Bun-based projects, install both Node.js (for publishing) and Bun (for building):
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: wgtechlabs/package-build-flow-action@v1
with:
package-manager: 'bun' # or 'auto' to detect from bun.lockb or bun.lock
npm-token: ${{ secrets.NPM_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}For pnpm-based projects:
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- uses: wgtechlabs/package-build-flow-action@v1
with:
package-manager: 'pnpm' # or 'auto' to detect from pnpm-lock.yaml
npm-token: ${{ secrets.NPM_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}For Yarn-based projects:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'
- uses: wgtechlabs/package-build-flow-action@v1
with:
package-manager: 'yarn' # or 'auto' to detect from yarn.lock
npm-token: ${{ secrets.NPM_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}Note: This action always uses the npm CLI for the final publish step, regardless of the selected package manager. While Bun, pnpm, and Yarn can publish to the npm registry, this action standardizes on npm for publishing to ensure consistent behavior across environments.
- Create an NPM access token at https://www.npmjs.com/settings/tokens
- Add the token as a repository secret:
NPM_TOKEN - Configure the action:
- uses: wgtechlabs/package-build-flow-action@v1
with:
registry: 'npm'
npm-token: ${{ secrets.NPM_TOKEN }}When publishing scoped packages (e.g., @org/package-name) to NPM, the action defaults to --access public to avoid 402 payment errors. Scoped packages are private by default on npm, but most open-source packages should be public.
Default Behavior (Public Access):
- uses: wgtechlabs/package-build-flow-action@v1
with:
registry: 'npm'
npm-token: ${{ secrets.NPM_TOKEN }}
# access: 'public' is the default - scoped packages will be publicPrivate Package Publishing (Requires Paid NPM Plan):
If you have a paid NPM plan and want to publish private scoped packages:
- uses: wgtechlabs/package-build-flow-action@v1
with:
registry: 'npm'
npm-token: ${{ secrets.NPM_TOKEN }}
access: 'restricted' # Publishes as private packageAlternative: Use publishConfig in package.json
You can also set the access level in your package.json:
{
"name": "@myorg/my-package",
"publishConfig": {
"access": "public"
}
}The action's access input will override publishConfig if both are specified.
GitHub Packages requires all packages to be scoped (e.g., @owner/package-name). The action provides automatic scope detection to make this seamless:
The action automatically scopes your package using this priority order:
- Explicit scope from
package-scopeinput β Uses provided scope - Existing scope in package.json β Uses scope from package name
- Repository owner β Automatically uses
@{repository-owner}β¨
This means most users don't need to configure anything - the action will automatically scope packages using your repository owner!
| Your package.json | package-scope Input | GitHub Packages Publishes As |
|---|---|---|
"name": "mypackage" |
(empty) | @owner/mypackage β¨ Auto-scoped! |
"name": "mypackage" |
@custom |
@custom/mypackage |
"name": "@org/mypackage" |
(any) | @org/mypackage |
For most cases, you don't need to provide package-scope:
- uses: wgtechlabs/package-build-flow-action@v1
with:
registry: 'github'
github-token: ${{ secrets.GITHUB_TOKEN }}
# package-scope not needed - auto-scoped as @{owner}!If you want to use a different scope than the repository owner:
- uses: wgtechlabs/package-build-flow-action@v1
with:
registry: 'github'
github-token: ${{ secrets.GITHUB_TOKEN }}
package-scope: '@myorg'Publish to both NPM and GitHub Packages. GitHub Packages will use auto-scoping if needed:
- uses: wgtechlabs/package-build-flow-action@v1
with:
registry: 'both'
npm-token: ${{ secrets.NPM_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
# package-scope optional - auto-scoped for GitHub PackagesOr with custom scope:
- uses: wgtechlabs/package-build-flow-action@v1
with:
registry: 'both'
npm-token: ${{ secrets.NPM_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
package-scope: '@myorg' # Use custom scopeAll versions follow Semantic Versioning (SemVer) format: MAJOR.MINOR.PATCH[-prerelease]
- Base Version: Read from package.json
- Pre-release Suffix: Automatically added based on flow type
- Dist-tags: Used to hide pre-releases from
npm installdefaults
Pre-release versions use non-latest dist-tags to prevent accidental installation:
# Latest production version (no tag needed)
npm install mypackage
# Pre-release versions require explicit tag or version
npm install mypackage@dev
npm install mypackage@1.2.3-dev.abc1234
# Production releases use 'latest' tag (default)
npm install mypackage@latest- PR to dev: Uses base version with
-pr.{sha}suffix - Dev branch: Uses base version with
-dev.{sha}suffix - Patch PR: Uses base version with
-patch.{sha}suffix - Staging (main): Uses base version with
-staging.{sha}suffix
The action includes built-in npm audit integration:
- uses: wgtechlabs/package-build-flow-action@v1
with:
audit-enabled: 'true'
audit-level: 'high'
fail-on-audit: 'true'Audit results are:
- Displayed in action logs
- Included in PR comments
- Available as action outputs
- Optionally fail the build
Automatic PR comments include:
- π¦ Package information (name, version, dist-tag)
- π₯ Installation instructions for each registry
- π·οΈ Dist-tag shortcuts
- π Security audit results (if enabled)
- π Links to registry pages
Create a custom PR comment format:
- uses: wgtechlabs/package-build-flow-action@v1
with:
pr-comment-template: |
## Build Complete
Version: {PACKAGE_VERSION}
Flow: {BUILD_FLOW}
Install: {NPM_INSTALL}
{AUDIT_RESULTS}Template variables:
{BUILD_FLOW}: Flow type{PACKAGE_VERSION}: Generated version{NPM_INSTALL}: NPM install command{GITHUB_INSTALL}: GitHub Packages install command{AUDIT_RESULTS}: Security audit summary
name: Advanced Build and Publish
on:
pull_request:
branches: [main, dev]
push:
branches: [main, dev]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- uses: wgtechlabs/package-build-flow-action@v1
id: build
with:
# Registries
registry: 'both'
npm-token: ${{ secrets.NPM_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
package-scope: '@myorg'
# Branches
main-branch: 'main'
dev-branch: 'develop'
# Package
package-path: './package.json'
build-script: 'build'
# Security
audit-enabled: 'true'
audit-level: 'high'
fail-on-audit: 'false'
# PR Comments
pr-comment-enabled: 'true'
- name: Display Results
run: |
echo "Version: ${{ steps.build.outputs.package-version }}"
echo "Flow Type: ${{ steps.build.outputs.build-flow-type }}"
echo "NPM Published: ${{ steps.build.outputs.npm-published }}"
echo "GitHub Published: ${{ steps.build.outputs.github-published }}"The action automatically detects GitHub release events and publishes packages with the correct version from the release tag. No manual version updates needed!
name: Production Release
on:
release:
types: [published]
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
# No manual version update needed - automatically uses release tag!
- uses: wgtechlabs/package-build-flow-action@v1
with:
registry: 'both'
npm-token: ${{ secrets.NPM_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
package-scope: '@myorg'
audit-enabled: 'true'
fail-on-audit: 'true'How it works:
- Standard Release (v1.0.0): Publishes
1.0.0withlatestnpm tag - Pre-release (v1.0.0-beta.1): Publishes
1.0.0-beta.1withbetanpm tag - Staging Release (v1.2.3-staging.abc1234): Publishes
1.2.3-staging.abc1234withstagingnpm tag
Test the action without publishing:
- uses: wgtechlabs/package-build-flow-action@v1
with:
dry-run: 'true'
npm-token: ${{ secrets.NPM_TOKEN }}Process multiple packages in a monorepo with independent versioning.
The action can automatically discover packages from your workspace configuration:
name: Monorepo Build and Publish
on:
push:
branches: [main, dev]
pull_request:
branches: [main, dev]
jobs:
build-monorepo:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Build and Publish Multiple Packages
id: build
uses: wgtechlabs/package-build-flow-action@v2
with:
# Enable monorepo mode
monorepo: 'true'
# Auto-discover packages from workspaces (default: true)
# No package-paths needed!
# Registry configuration
registry: 'both'
npm-token: ${{ secrets.NPM_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Display Discovered Packages
run: |
echo "Discovered ${{ steps.build.outputs.package-count }} packages:"
echo '${{ steps.build.outputs.discovered-packages }}' | jq '.'
- name: Display Build Results
run: |
echo "Build Results:"
echo '${{ steps.build.outputs.build-results }}' | jq '.'Root package.json with workspaces:
{
"name": "my-monorepo",
"private": true,
"workspaces": [
"core",
"apps/*",
"plugins/*"
]
}The action will:
- Read the
workspacesfield from rootpackage.json - Resolve glob patterns like
apps/*andplugins/* - Skip packages with
"private": true - Process all discovered publishable packages
You can also explicitly specify packages (takes priority over workspace detection):
name: Monorepo Build and Publish
on:
push:
branches: [main, dev]
pull_request:
branches: [main, dev]
jobs:
build-monorepo:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Build and Publish Multiple Packages
id: build
uses: wgtechlabs/package-build-flow-action@v2
with:
# Enable monorepo mode
monorepo: 'true'
# List all packages (comma-separated)
package-paths: 'core/package.json,plugins/plugin-discord/package.json,apps/cli/package.json'
# Registry configuration
registry: 'both'
npm-token: ${{ secrets.NPM_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Display Build Results
run: |
echo "Build Results:"
echo '${{ steps.build.outputs.build-results }}' | jq '.'Key Features:
- Each package gets its own version based on its
package.json - Packages are processed sequentially
- If one package fails, remaining packages still attempt to build/publish
- Returns JSON array with per-package results
- Works with
dry-runandpublish-enabled: false - Supports both workspace auto-discovery and explicit package lists
- Automatically resolves
workspace:*protocol dependencies before publishing
By default, the action detects which packages changed and only builds/publishes those packages:
- name: Build and Publish Changed Packages
uses: wgtechlabs/package-build-flow-action@v2
with:
monorepo: 'true'
# changed-only: 'true' (default - only build changed packages)
npm-token: ${{ secrets.NPM_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}How it works:
- Pull Request: Compares against PR base branch to detect changed files
- Push: Compares against previous commit (
HEAD~1) - Release: Compares against previous git tag
- Maps changed files to their owning workspace package
- Root config files (
package.json,tsconfig.json, lockfiles) mark ALL packages as changed - Automatically handles shallow clones by fetching necessary history
- Falls back to building all packages if git diff fails (safe default)
Example output:
{
"changed-packages": [
{"name": "@tinyclaw/core", "path": "core/package.json"}
],
"changed-count": 1
}To disable change detection and always build all packages:
- name: Build All Packages
uses: wgtechlabs/package-build-flow-action@v2
with:
monorepo: 'true'
changed-only: 'false' # Disable change detection
npm-token: ${{ secrets.NPM_TOKEN }}Benefits:
- β‘ Faster CI/CD - skip unchanged packages
- π° Reduced CI costs - fewer builds
- π― No manual changesets needed - automatic detection
- π Safe fallbacks - builds all packages if detection fails
Example Output:
[
{
"name": "@tinyclaw/core",
"version": "1.0.0-dev.abc1234",
"result": "success"
},
{
"name": "@tinyclaw/plugin-discord",
"version": "1.2.0-dev.abc1234",
"result": "success"
}
]The action automatically orders packages based on their workspace dependencies using topological sort when workspace metadata is available. This ensures dependencies are built before their dependents.
Requirements:
- Requires
workspace-detection: 'true'(default) with valid workspace packages discovered - When using explicit
package-pathswithout workspace discovery, ordering is not available
Example:
- name: Build Packages in Dependency Order
uses: wgtechlabs/package-build-flow-action@v2
with:
monorepo: 'true'
# dependency-order: 'true' (default - build in dependency order)
npm-token: ${{ secrets.NPM_TOKEN }}How it works:
- Analyzes
dependencies,peerDependencies, anddevDependenciesin each package.json - Filters to only workspace-internal dependencies (ignores external npm packages)
- Performs topological sort using Kahn's algorithm
- Builds packages in the correct order (dependencies before dependents)
- Detects and reports circular dependencies with clear error messages
- Supports
workspace:*protocol and standard version ranges
Example dependency graph:
@tinyclaw/core β no workspace deps
@tinyclaw/plugin-discord β depends on @tinyclaw/core
@tinyclaw/plugin-slack β depends on @tinyclaw/core
tinyclaw (CLI) β depends on @tinyclaw/core, @tinyclaw/plugin-discord, @tinyclaw/plugin-slack
Build order output:
π Build order:
1. @tinyclaw/core (no workspace deps)
2. @tinyclaw/plugin-discord (depends on: @tinyclaw/core)
3. @tinyclaw/plugin-slack (depends on: @tinyclaw/core)
4. tinyclaw (depends on: @tinyclaw/core, @tinyclaw/plugin-discord, @tinyclaw/plugin-slack)
Circular dependency detection: If circular dependencies are found, the build fails with a clear error:
β Circular dependency detected among: @tinyclaw/plugin-a, @tinyclaw/plugin-b
Dependency graph for these packages:
@tinyclaw/plugin-a β @tinyclaw/plugin-b
@tinyclaw/plugin-b β @tinyclaw/plugin-a
To disable dependency ordering and use discovery order:
- name: Build in Discovery Order
uses: wgtechlabs/package-build-flow-action@v2
with:
monorepo: 'true'
dependency-order: 'false' # Disable dependency ordering
npm-token: ${{ secrets.NPM_TOKEN }}Benefits:
- π Correct build order - dependencies built before dependents
- π Reliable monorepo builds - no dependency resolution failures
- π Early error detection - circular dependencies caught immediately
- π Works with complex dependency graphs including diamond dependencies
The action automatically resolves workspace:* protocol dependencies to actual semver versions before publishing to npm. This ensures that published packages are installable from the registry, as workspace:* is not a valid semver range on npm.
Supported workspace protocols:
workspace:*β resolves to exact version (e.g.,1.2.3)workspace:^β resolves to caret range (e.g.,^1.2.3)workspace:~β resolves to tilde range (e.g.,~1.2.3)workspace:^1.0.0β strips prefix, keeps range (e.g.,^1.0.0)
Example package.json before resolution:
{
"name": "@myorg/app",
"version": "2.0.0",
"dependencies": {
"@myorg/core": "workspace:*",
"@myorg/utils": "workspace:^"
},
"peerDependencies": {
"@myorg/core": "workspace:~"
}
}After resolution (when @myorg/core is v1.2.3 and @myorg/utils is v3.4.5):
{
"name": "@myorg/app",
"version": "2.0.0",
"dependencies": {
"@myorg/core": "1.2.3",
"@myorg/utils": "^3.4.5"
},
"peerDependencies": {
"@myorg/core": "~1.2.3"
}
}How it works:
- Automatically detects workspace protocol dependencies in dependencies, devDependencies, and peerDependencies
- Looks up actual versions from discovered workspace packages
- Resolves versions before running
npm publish - Restores original
package.jsonafter publishing - Works with pnpm, Yarn Berry, and Bun workspace protocols
Benefits:
- β Published packages are installable from the registry
- π Automatic resolution - no manual version updates needed
- π‘οΈ Original workspace protocols preserved in source control
- π¦ Compatible with pnpm, Yarn Berry, and Bun workspaces
Discovered Packages Output:
[
{
"name": "@tinyclaw/core",
"version": "1.0.0",
"path": "core/package.json",
"dir": "core"
},
{
"name": "@tinyclaw/plugin-discord",
"version": "1.2.0",
"path": "plugins/plugin-discord/package.json",
"dir": "plugins/plugin-discord"
}
]Issue: Package doesn't appear on the registry
Solutions:
- Check
npm-tokenis valid and has publish permissions - Verify package name is not already taken
- For GitHub Packages, ensure package name is scoped
- Check action logs for error messages
Issue: 401 Unauthorized errors
Solutions:
- Verify tokens are correctly set in repository secrets
- For GitHub Packages, ensure
packages: writepermission - Check token hasn't expired
- For NPM, ensure token type is "Automation" or "Publish"
Issue: Cannot publish version that already exists
Solutions:
- The action generates unique versions with commit SHA
- If still failing, check if version was manually published
- Verify flow detection is working correctly
Issue: Package name must be scoped
Solutions:
- Set
package-scopeinput:@myorg - Or update package.json name to include scope:
@myorg/package-name - Ensure scope matches your GitHub organization
Issue: Build fails due to vulnerabilities
Solutions:
- Set
fail-on-audit: 'false'to continue despite vulnerabilities - Update dependencies to fix vulnerabilities
- Adjust
audit-levelto be less strict - Review audit output in action logs
Issue: No comments on pull requests
Solutions:
- Ensure
pull-requests: writepermission is granted - Check
pr-comment-enabledis set to'true' - Verify
github-tokenhas correct permissions - Only works on pull_request events
See the examples directory for complete workflow examples:
- basic-workflow.yml - Simple workflow example
- advanced-workflow.yml - Full-featured workflow
- release-workflow.yml - Production release workflow
- monorepo-workflow.yml - Monorepo multi-package workflow
Contributions are welcome! Please read our Contributing Guidelines before submitting a Pull Request.
This repository follows the Clean Commit convention with emoji-based commit messages. See CONTRIBUTING.md for details.
MIT License - see LICENSE file for details.
- π Documentation
- π Issue Tracker
- π¬ Discussions
- Container Build Flow Action - Similar action for container images
Made with β€οΈ by WG Technology Labs