| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Shared CI/CD workflows and scripts for the Alchemy projects:
Bump versions across a set of publishable workspace packages, commit + tag, publish to npm with OIDC trusted publishing in dependency-graph order (waves), then cut a GitHub Release and notify Discord.
Single version input describes what to publish; the channel is inferred from its shape:
| Input | Channel | Result |
|---|---|---|
| "" (empty) | beta | Next beta from npm (auto-increment) |
| patch/minor/major | release | Bump current max stable |
| 1.2.3 | release | Explicit stable version |
| beta / beta.N | beta | Next or forced beta on <current-version> |
| alpha / alpha.N | alpha | Same for alpha |
| rc / rc.N | rc | Release candidate on <current-version> |
| <tag-name> | tag | Version becomes 0.0.0-<tag-name>; no git commit, tag, or GitHub Release |
Tag releases always use 0.0.0-<name> — semver-shaped tag specs like 2.0.0-experimental are rejected. Pass just the name.
force-latest (boolean input) publishes under npm’s latest dist-tag whatever the channel — use it to promote a release candidate to latest. If a version is already on the registry it moves the existing latest tag onto it instead of skipping.
Build modes. build-mode: up-front (default) builds every package once at the start of the bump job and ships lib//bin/ as part of the bump-files artifact — fastest when the whole workspace fits on one runner. build-mode: per-package skips that, and each publish job builds its own package inside the package's dir before packing. Use per-package when the full build doesn't fit on a single runner or when you want to route specific packages to bigger runners (see runner: below).
Per-package config (in the packages: JSON array):
| Field | Default | Purpose |
|---|---|---|
| dir | — | Workspace path (e.g. packages/aws) |
| name | — | npm package name (used for the workspace-dep graph) |
| runner | ubuntu-latest | GitHub runner used for that package's publish job |
Publish order is derived automatically from each package.json's workspace:* deps that point at other publishables. A wave is the set of packages that don't depend on each other — those publish in parallel. Wave N+1 starts after every wave ≤ N has succeeded. The array order in packages: doesn't matter.
For alchemy-effect (alchemy ← better-auth, pr-package) the DAG resolves to:
wave 1: alchemy wave 2: @alchemy.run/better-auth, @alchemy.run/pr-package (parallel)
For distilled (everything depends on @distilled.cloud/core):
wave 1: @distilled.cloud/core wave 2: @distilled.cloud/aws, …/cloudflare, …/neon, … (parallel)
The npm release workflow pre-declares four wave jobs and skips the empty ones (cloudflare-tools is the deepest consumer today at four: rolldown-plugin → runtime/framework-core → vite-plugin + frameworks → astro/waku). Deeper npm release DAGs require adding matching jobs.
Channels:
# consumer .github/workflows/release.yml
name: Release NPM Package
on:
workflow_dispatch:
inputs:
channel:
type: choice
default: beta
options: [release, beta, alpha, rc, tag]
spec:
type: string
jobs:
release:
uses: alchemy-run/actions/.github/workflows/release.yml@main
with:
version: ${{ inputs.version }}
repo: alchemy-run/alchemy-effect
current-version: "2.0.0"
packages: |
[
{ "dir": "packages/alchemy", "name": "alchemy" },
{ "dir": "packages/better-auth", "name": "@alchemy.run/better-auth" },
{ "dir": "packages/pr-package", "name": "@alchemy.run/pr-package" }
]
secrets: inheritReusable workflows automatically load their actions and scripts from the exact commit GitHub resolved for the workflow reference. This keeps branch, tag, and SHA callers self-contained. The optional actions-ref input exists only for testing a different scripts revision explicitly.
Publishes per-package tarballs to a pr-package service (default pkg.ing) on every push-to-main and PR sync. A separate optional action leaves a sticky PR comment with URLs pinned to the head commit.
Partial publication. Only packages whose own dir or a transitive workspace dependency's dir changed are repacked — the graph is derived from each package.json's workspace:* dependencies, so a touch to core/ republishes every leaf that depends on it. A force-ci PR label overrides and republishes everything; touching bun.lock, root package.json, or the consumer workflow also republishes everything. The caller may build a wider set before invoking the action.
On pull_request events the action sends Alchemy-Pull-Request: owner/repo#n with the tags so pkg.ing can renew TTL while the PR is open. Teardown (delete preview tags + comment) is the consumer workflow's job on closed — this action does not run on close.
# consumer .github/workflows/pr-package.yml
name: pr-package
on:
push:
branches: [main]
pull_request:
# Tags remain valid after a PR closes, so no `closed` event is needed.
types: [opened, synchronize, reopened, labeled]
jobs:
pr-package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: alchemy-run/actions/actions/setup@main
- uses: actions/cache@v5
with:
path: .turbo
key: turbo-${{ runner.os }}-${{ github.repository_id }}-${{ github.sha }}-${{ github.run_attempt }}
restore-keys: |
turbo-${{ runner.os }}-${{ github.repository_id }}-${{ github.sha }}-
turbo-${{ runner.os }}-${{ github.repository_id }}-
- run: bun run build
- id: publish
uses: alchemy-run/actions/actions/pr-package@main
with:
packages: |
[
{ "dir": "packages/alchemy", "name": "alchemy", "group": "Alchemy" },
{ "dir": "packages/better-auth", "name": "@alchemy.run/better-auth", "group": "Alchemy" },
{ "dir": "packages/pr-package", "name": "@alchemy.run/pr-package", "group": "Alchemy" }
]
pr-package-token: ${{ secrets.PR_PACKAGE_TOKEN }}
- if: github.event_name == 'pull_request'
id: bot-token
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.ALCHEMY_VERSION_BOT_ID }}
private-key: ${{ secrets.ALCHEMY_VERSION_BOT_PRIVATE_KEY }}
- if: github.event_name == 'pull_request'
uses: alchemy-run/actions/actions/pr-package-comment@main
with:
plan: ${{ steps.publish.outputs.plan }}
token: ${{ steps.bot-token.outputs.token }}Per-package config (all optional except dir and name):
| Field | Default | Meaning |
|---|---|---|
| dir | — | Workspace path (e.g. packages/aws) |
| name | — | npm package name used to match workspace dependencies |
| group | Packages | Heading used to group install commands in the PR comment |
| project | = name | Project name in the pr-package upload URL |
| install | = project | Path used in generated package URLs |
| submodule | false | Resolve this package's commit from its Git submodule |
Action inputs include pr-package-host (upload target, default pkg.ing), install-host (CDN host for package URLs; defaults to pr-package-host), and force-ci-label (default force-ci). The caller owns checkout, tool setup, dependency installation, building, runner selection, and whether to invoke the separate pr-package-comment action.
All selected packages pack and publish in one job on one runner. The action rewrites configured workspace dependencies to deterministic full-commit URLs, addresses each archive by (package, sha256), and uploads it only when that exact content is absent. It points every package's full commit first, then exposes short commit, branch, and PR aliases to the backing archive. The action also adds grouped install tables to the GitHub Actions run summary. It does not build packages, use package matrices, publish waves, workflow artifacts, or per-package runner overrides.
Packages use the action's workflow event commit by default. Set submodule: true on a package inside a checked-out Git submodule to use that submodule's checked-out commit for dependency URLs and install commands.
Optionally creates or updates the grouped install table from the publish action's plan output. The caller supplies the token and decides when the comment should run.
| Secret | Used by | Notes |
|---|---|---|
| ALCHEMY_VERSION_BOT_ID | release + pr-package | GitHub App id for the bot that commits/tags |
| ALCHEMY_VERSION_BOT_PRIVATE_KEY | release + pr-package | GitHub App private key |
| PR_PACKAGE_TOKEN | pr-package | Bearer token for the pkg.ing service |
| DISCORD_WEBHOOK_URL | release (optional) | Skip Discord post if unset |
| NPM_TOKEN | release (optional) | Only for dist-tag moves under force-latest |
| GITHUB_TOKEN | release | Provided automatically |
npm publishes use OIDC trusted publishing — no NPM_TOKEN required for the publish itself, but each package must have a Trusted Publisher configured against this repo on the npm side.
OIDC does not cover npm dist-tag add (npm/cli#8547), which force-latest needs to move the channel tag (e.g. next) alongside latest. Set NPM_TOKEN to a granular access token with read/write access to the published packages to enable that; without it the secondary tag move is skipped with a workflow warning that includes the manual command.
All scripts live under scripts/release/ and are invoked from the shared workflows and actions. They run in the consumer repo's working directory; the public action inputs are mapped to their internal environment variables:
| Env var | Meaning |
|---|---|
| ALCHEMY_PUBLISHABLE_DIRS | JSON array of package dirs (e.g. ["packages/alchemy"]) |
| ALCHEMY_PUBLISHABLE_NAMES | JSON array of npm names (parallel to dirs) |
| ALCHEMY_CURRENT_VERSION | Anchor for prerelease bumps (e.g. "2.0.0") |
| ALCHEMY_REPO | <owner>/<repo> for changelog/release-note URLs |
.github/workflows/ release.yml # reusable workflow (workflow_call) actions/ setup/action.yml # setup-node + setup-bun + cache + bun install pr-package/action.yml # plan + pack + rewrite + upload pr-package-comment/action.yml # optional grouped install comment scripts/release/ bump.ts publish-package.ts pack-pr-packages.ts release-notes.ts github-release.ts discord-notify.ts discord-body.ts render.ts
| Back | FazBrowse Home | New Git URL |