Files
dify/.github/workflows/cli-release.yml
GareArc d84fad76ed feat(cli): trigger-agnostic difyctl release pipeline + tokenless installers
- 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.
2026-06-03 19:23:55 -07:00

158 lines
5.6 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: CLI Release
on:
workflow_dispatch:
workflow_call:
repository_dispatch:
types: [difyctl-release]
push:
tags:
- 'difyctl-v*'
concurrency:
group: difyctl-release
cancel-in-progress: false
jobs:
validate:
name: validate manifest + idempotency guard
runs-on: depot-ubuntu-24.04
# Releases may be cut from any branch (tag pushes and dispatches alike); the
# tag-vs-manifest guard below still prevents a dangling/mismatched tag.
if: github.repository == 'langgenius/dify'
permissions:
contents: read
defaults:
run:
shell: bash
working-directory: ./cli
outputs:
version: ${{ steps.manifest.outputs.version }}
channel: ${{ steps.manifest.outputs.channel }}
minDify: ${{ steps.manifest.outputs.minDify }}
maxDify: ${{ steps.manifest.outputs.maxDify }}
already_exists: ${{ steps.guard.outputs.already_exists }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Read cli/package.json
id: manifest
run: |
version=$(node -p "require('./package.json').version")
channel=$(node -p "require('./package.json').difyctl.channel")
minDify=$(node -p "require('./package.json').difyctl.compat.minDify")
maxDify=$(node -p "require('./package.json').difyctl.compat.maxDify")
{
echo "version=$version"
echo "channel=$channel"
echo "minDify=$minDify"
echo "maxDify=$maxDify"
} >> "$GITHUB_OUTPUT"
- name: Validate manifest
run: scripts/release-validate-manifest.sh
- name: Tag matches manifest version
# On a tag push the version is still sourced from cli/package.json; the
# pushed tag must match it, else the tag would dangle with no release.
if: github.ref_type == 'tag'
run: |
expected="difyctl-v${{ steps.manifest.outputs.version }}"
if [ "$GITHUB_REF_NAME" != "$expected" ]; then
echo "::error::pushed tag ${GITHUB_REF_NAME} does not match manifest version (${expected}); bump cli/package.json or push the matching tag"
exit 1
fi
- name: Idempotency guard
id: guard
env:
GH_TOKEN: ${{ github.token }}
run: |
tag="difyctl-v${{ steps.manifest.outputs.version }}"
if gh release view "$tag" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
echo "already_exists=true" >> "$GITHUB_OUTPUT"
echo "::notice::release ${tag} already exists; release job will skip"
else
echo "already_exists=false" >> "$GITHUB_OUTPUT"
fi
release:
name: build standalone binaries (all targets)
needs: validate
if: needs.validate.outputs.already_exists == 'false'
runs-on: depot-ubuntu-24.04
permissions:
contents: write
defaults:
run:
shell: bash
working-directory: ./cli
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
fetch-depth: 1
- name: Setup web environment
uses: ./.github/actions/setup-web
- name: Setup Bun
uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2.0.2
with:
bun-version-file: cli/.bun-version
- name: Install cross-arch native prebuilds
# Re-installs node_modules with every @napi-rs/keyring platform variant
# so `bun build --compile` can embed the right .node into each target.
working-directory: ./
run: NPM_CONFIG_USERCONFIG="$PWD/cli/scripts/cross-arch.npmrc" pnpm install --frozen-lockfile
- name: Compile standalone binaries (all targets)
env:
CLI_VERSION: ${{ needs.validate.outputs.version }}
DIFYCTL_CHANNEL: ${{ needs.validate.outputs.channel }}
DIFYCTL_MIN_DIFY: ${{ needs.validate.outputs.minDify }}
DIFYCTL_MAX_DIFY: ${{ needs.validate.outputs.maxDify }}
run: |
DIFYCTL_COMMIT="$(git rev-parse HEAD)" \
DIFYCTL_BUILD_DATE="$(git log -1 --format=%cI HEAD)" \
pnpm build:bin
- name: Generate sha256 checksum file
env:
CLI_VERSION: ${{ needs.validate.outputs.version }}
run: scripts/release-write-checksums.sh
- name: Publish GitHub Release
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2
with:
tag_name: difyctl-v${{ needs.validate.outputs.version }}
name: difyctl ${{ needs.validate.outputs.version }}
prerelease: ${{ needs.validate.outputs.channel != 'stable' }}
generate_release_notes: false
fail_on_unmatched_files: true
body: |
difyctl ${{ needs.validate.outputs.version }} (${{ needs.validate.outputs.channel }})
**Compatible with Dify** ${{ needs.validate.outputs.minDify }} ${{ needs.validate.outputs.maxDify }}
### Install
macOS / Linux:
```sh
curl -fsSL https://raw.githubusercontent.com/langgenius/dify/main/cli/scripts/install-cli.sh | sh
```
Windows (PowerShell):
```powershell
irm https://raw.githubusercontent.com/langgenius/dify/main/cli/scripts/install.ps1 | iex
```
[Changes in `cli/`](https://github.com/langgenius/dify/commits/difyctl-v${{ needs.validate.outputs.version }}/cli)
files: |
cli/dist/bin/difyctl-v*