Files
dify/cli/scripts/release-naming.test.ts
GareArc f1c32e1bc8 refactor(cli): split release scripts into pure rules and CLI shells
The release script tests could only observe behaviour through a process
exit code, because die() called process.exit from inside otherwise-pure
functions. That forced every test to spawn a subprocess, which forced
its input to come from cli/package.json, which is why editing that file
kept breaking tests — most recently #39658, and next the difyctl version
bump that cli-release.yml requires on every release.

Move the decisions into lib/release-rules.mjs and lib/edge-manifest.mjs,
which take every input as an argument and return values instead of
exiting. release-naming.mjs and release-r2-edge.mjs keep argv parsing,
manifest reading, stdout and exit codes, and nothing else.

Tests now split by what they ask. Logic is unit-tested against literal
inputs, the shells are tested for plumbing only, and release-config.test.ts
is the single place that reads the real manifest — asserting it is
internally consistent, never what it currently contains. Editing
cli/package.json no longer breaks a logic test; a malformed config still
fails. Verified by mutating each field in both directions.

Three intentional behaviour changes:

- compat-check compares the numeric A.B.C core only, ignoring prerelease
  and build suffixes, so Dify 1.16.0-rc1 now satisfies a 1.16.0 window.
  This matches the shipped runtime check in src/version/compat.ts, which
  already stripped suffixes; the release gate was the one disagreeing.
  Removes the hand-rolled prerelease ordering entirely.
- validate now rejects a missing or inverted compat window. It previously
  passed such a config while github-env emitted minDify=undefined.
- release-r2-edge reports a bad channel version under its own name rather
  than release-naming's, since it no longer borrows that script's die().

release-config.test.ts also pins the install scripts to the naming config.
They hardcode artifact names by necessity — they run standalone on a user
machine with no repo and no node — so changing tagPrefix or checksumsSuffix
now fails until all four installers are updated to match, instead of
silently publishing names no installer looks for.
2026-07-27 19:13:42 -07:00

78 lines
2.6 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { main } from './release-naming.mjs'
describe('release-naming argument handling', () => {
it.each([
[['tag'], 'version argument is required'],
[['asset'], 'version argument is required'],
[['checksums'], 'version argument is required'],
[['compat-check'], 'version argument is required'],
[['validate-version'], 'version argument is required'],
[['prerelease'], 'channel argument is required'],
[['edge-version'], 'git short sha'],
[['bogus'], 'unknown subcommand'],
[[], 'unknown subcommand'],
])('rejects %j', (args, message) => {
expect(() => main(args)).toThrow(message)
})
it('rejects an unknown target id', () => {
expect(() => main(['asset', '9.9.9', 'solaris-sparc'])).toThrow('unknown target id')
})
})
describe('release-naming output shape', () => {
it('emits every key CI reads from github-env', () => {
const out = main(['github-env'])
for (const key of [
'version',
'channel',
'prerelease',
'minDify',
'maxDify',
'tagPrefix',
'difyctlTag',
])
expect(out).toMatch(new RegExp(`^${key}=.+$`, 'm'))
expect(out).not.toMatch(/=undefined$/m)
})
it('emits difyctlTag as tagPrefix immediately followed by version', () => {
const env = Object.fromEntries(
main(['github-env'])
.split('\n')
.filter(Boolean)
.map((line: string) => line.split(/=(.*)/s).slice(0, 2)),
)
expect(env.difyctlTag).toBe(`${env.tagPrefix}${env.version}`)
})
it('lists one channel per line, including edge', () => {
const out = main(['channels'])
expect(out.trim().split('\n').length).toBeGreaterThan(1)
expect(out).toMatch(/^edge$/m)
})
it('emits targets as bunTarget<TAB>id<TAB>0|1 (release-build.sh parses this)', () => {
for (const line of main(['targets']).trim().split('\n'))
expect(line).toMatch(/^\S+\t\S+\t[01]$/)
})
it('reports prerelease as a boolean per channel', () => {
expect(main(['prerelease', 'stable']).trim()).toBe('false')
expect(main(['prerelease', 'alpha']).trim()).toBe('true')
expect(() => main(['prerelease', 'nightly'])).toThrow('unknown channel')
})
it('derives an edge version from the packaged version and a sha', () => {
expect(main(['edge-version', '2fd7b82']).trim()).toMatch(/-edge\.2fd7b82$/)
})
it('validates a version against a channel form', () => {
expect(main(['validate-version', '0.1.0-edge.2fd7b82', 'edge'])).toContain('valid')
expect(() => main(['validate-version', '0.1.0-rc.1', 'edge'])).toThrow(
'does not match the edge channel form',
)
})
})