feat(cli): difyctl release pipeline + tokenless installers#37036
Open
GareArc wants to merge 8 commits into
Open
feat(cli): difyctl release pipeline + tokenless installers#37036GareArc wants to merge 8 commits into
GareArc wants to merge 8 commits into
Conversation
…allers - cli-release.yml: workflow_dispatch/workflow_call/repository_dispatch/tag triggers; version sourced from cli/package.json (not the trigger). Split into validate + release jobs with an idempotency guard (gh release view) and a tag-vs-manifest match guard so a difyctl-v* tag push must equal the manifest version (prevents a dangling/mismatched tag). Releases may be cut from any branch. Concurrency serialized (fixed group, cancel-in-progress: false) so releases never double-publish. - install-cli.sh: tokenless resolution via git/matching-refs, channel-aware (stable/rc) selection, fail-closed sha256 verification; distinguishes a fetch failure (network / API rate limit) from "no release found". - install.ps1: Windows installer at parity with install-cli.sh. - install-cli.test.ts: resolver unit tests (rc ordering, channel filter). - .bun-version: pin Bun 1.3.11 for reproducible --compile builds.
Centralize release naming (tag, asset filenames, checksum file, target list, arch/os tokens) so producers derive from one source instead of duplicating literals across build, checksum, and workflow files. - cli/package.json: add difyctl.release block (tagPrefix, binName, checksumsSuffix, targets[id/bunTarget/exe]) as the naming data source. - cli/scripts/release-naming.mjs: new format source + validator; subcommands tag/asset/checksums/tag-prefix/targets/validate. - release-build.sh, release-write-checksums.sh: derive target list and asset names from release-naming.mjs; drop hardcoded maps and literals. - release-validate-manifest.sh: run release-naming.mjs validate, rejecting a malformed difyctl.release before any build derives from it. - cli-release.yml: emit tagPrefix from the manifest step; tag guard, idempotency check, tag_name, and files glob all derive from it. - install-cli.sh, install.ps1: standalone installers stay hardcoded by convention; add a comment pointing at the source.
Add a channel registry to release-naming.mjs as the single source for which
version forms are releasable and resolvable, and enforce it before any release
is cut.
- release-naming.mjs: CHANNELS dispatch table { name, prerelease, versionForm };
new channels/prerelease subcommands; validate now rejects any version that
does not match its declared channel form. Each versionForm is pinned to what
the installer channel filters accept (stable = no prerelease; rc = -rc.N with
nothing trailing), so a version that passes validate is guaranteed resolvable
at install time. Closes the silent-publish hole (e.g. channel=rc + 1.2.3-rc5
with no dot, a bare version on rc, or trailing +build on rc).
- release-validate-manifest.sh: drop the hardcoded version regex and rc|stable
case; release-naming.mjs validate is the single source for those rules.
- cli-release.yml: emit prerelease from the registry and use it for the release
prerelease flag instead of a hardcoded channel != 'stable'.
Extend by adding one CHANNELS entry; validity, version-form enforcement, and the
prerelease flag all derive from it.
Run scripts/release-validate-manifest.sh as an ubuntu-only step in cli-tests.yml (before pnpm ci) so a malformed version, channel, or difyctl.release block fails the PR instead of only the release. Same gate the release workflow uses; pnpm ci stays pure-node and portable.
Replace the per-field manifest plumbing in cli-release.yml with a single
generic emitter, so adding a release field no longer means editing the
workflow in multiple places.
- release-naming.mjs: add a github-env subcommand emitting every field CI needs
(version, channel, prerelease, minDify, maxDify, tagPrefix) as key=value lines.
- cli-release.yml: each job pipes `release-naming.mjs github-env` into
$GITHUB_ENV once and references ${{ env.<field> }} (or shell $field); drop the
per-field run reads, the enumerated job outputs (now just already_exists), and
all fromJSON use sites. Both jobs check out the same validated commit, so each
self-derives with no cross-job output to thread.
- release-write-checksums.sh: self-derive CLI_VERSION from package.json when
unset, mirroring release-build.sh, so the release job needs no version env.
Adding a release field now touches only githubEnv().
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reworks the
difyctlrelease pipeline and installers so binaries are published as public GitHub Releases and installed without authentication. Replaces the previous flow, which built per-run Actions artifacts and required a token to download them.Closes WTA-426
What changed
Release workflow (
.github/workflows/cli-release.yml)workflow_dispatch,workflow_call,repository_dispatch(difyctl-release), andpushof adifyctl-v*tag. The version is always sourced fromcli/package.json, never inferred from the trigger.validate→release.validatereads the manifest, checks the manifest schema, enforces that a pushed tag equalsdifyctl-v<version>, and runs an idempotency guard (gh release view) so an already-published version is skipped.releasebuilds all targets, generates checksums, and publishes the GitHub Release (creating thedifyctl-v<version>tag).cancel-in-progress: falseensures releases never run in parallel and an in-flight release is never cancelled mid-publish.Build tooling
cli/.bun-version(new). Pins Bun for reproduciblebun build --compileoutput across all targets.Before / after
GH_TOKENrequiredgh release viewguard, immutableinstall.ps1sha256(sh + ps1)Release flow
flowchart TD A[workflow_dispatch] --> V B[workflow_call] --> V C[repository_dispatch: difyctl-release] --> V D[push tag difyctl-v*] --> V subgraph validate V[read version from cli/package.json] --> G1[validate manifest] G1 --> G2{tag == difyctl-v<version>?} G2 -- no --> X[fail] G2 -- yes / non-tag --> G3{release already exists?} end G3 -- yes --> S[skip: immutable] G3 -- no --> R subgraph release R[build all targets] --> CK[generate sha256 checksums] CK --> P[publish GitHub Release + tag] endTargets:
linux-x64,linux-arm64,darwin-x64,darwin-arm64,windows-x64.exe, plus adifyctl-v<version>-checksums.txt.Install flow
flowchart TD I[install-cli.sh / install.ps1] --> Q[resolve highest tag via git/matching-refs] Q --> C{channel} C -- stable --> ST[highest non-prerelease] C -- rc --> RC[highest -rc.N] ST --> DL[download asset + checksums.txt] RC --> DL DL --> Vfy{sha256 matches?} Vfy -- no --> Fail[abort] Vfy -- yes --> Inst[install to PREFIX/bin/difyctl]Testing
vitest run cli/scripts/install-cli.test.ts— resolver unit tests pass (channel filter,rcordering).