@qlover/fe-release 4.3.1 → 5.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (7) hide show
  1. package/README.md +110 -119
  2. package/dist/cli.cjs +2550 -7401
  3. package/dist/cli.js +2597 -7472
  4. package/dist/index.cjs +2534 -5426
  5. package/dist/index.d.ts +1707 -1096
  6. package/dist/index.js +2552 -5474
  7. package/package.json +13 -10
package/dist/index.d.ts CHANGED
@@ -1,683 +1,1483 @@
1
- import { LifecycleExecutor, ExecutorContextInterface } from '@qlover/fe-corekit';
2
- import { ScriptPlugin, ScriptContext, ScriptPluginProps, ScriptSharedInterface, ScriptContextInterface, FeReleaseConfig, ShellInterface } from '@qlover/scripts-context';
1
+ import { ExecutorContextInterface, LifecycleExecutor } from '@qlover/fe-corekit';
2
+ import { ScriptPluginProps, ScriptPlugin, ScriptSharedInterface, ScriptContextInterface, ScriptContext, TemplateEngine, RenderFn, ShellInterface } from '@qlover/scripts-context';
3
3
  export { ScriptPlugin } from '@qlover/scripts-context';
4
4
  import { CommitField } from 'gitlog';
5
5
  import { LoggerInterface } from '@qlover/logger';
6
6
  import { OptionValues } from 'commander';
7
7
 
8
8
  /**
9
- * @module PluginTuple
10
- * @description Type-safe plugin tuple creation and handling
11
- *
12
- * This module provides utilities for creating and handling tuples that
13
- * represent plugin configurations. It ensures type safety when working
14
- * with plugin constructors and their parameters.
15
- *
16
- * Core Features:
17
- * - Type-safe plugin class handling
18
- * - Constructor parameter inference
19
- * - Plugin tuple creation
20
- *
21
- * @example Basic usage
22
- * ```typescript
23
- * class MyPlugin extends ScriptPlugin {
24
- * constructor(context: ScriptContext, config: { option: string }) {
25
- * super(context);
26
- * }
27
- * }
9
+ * @module WorkspaceInterface
10
+ * @description Core data model for a monorepo package in a release run
28
11
  *
29
- * const pluginTuple = tuple(MyPlugin, { option: 'value' });
30
- * // [MyPlugin, { option: 'value' }]
31
- * ```
12
+ * Represents one publishable workspace discovered by the {@link Workspaces}
13
+ * plugin and passed through {@link ChangesetVersion} and {@link Github}.
32
14
  *
33
- * @example Plugin name string
34
- * ```typescript
35
- * const pluginTuple = tuple('MyPlugin', { option: 'value' });
36
- * // ['MyPlugin', { option: 'value' }]
37
- * ```
15
+ * Typical lifecycle fields:
16
+ * - `version` / `newVersion` — before and after `changeset version`
17
+ * - `lastTag` — git baseline for changelog generation
18
+ * - `dependencyRelease` — internal dependent bumped only because a dependency changed
38
19
  */
39
20
 
21
+ interface WorkspaceInterface {
22
+ /**
23
+ * Package name from package.json
24
+ */
25
+ name: string;
26
+ /**
27
+ * Current version from package.json before bump
28
+ */
29
+ version: string;
30
+ /**
31
+ * Version after `changeset version`, read from package.json on disk.
32
+ *
33
+ * - Before bump: usually undefined
34
+ * - After bump: latest version on disk; may equal `version` if unchanged
35
+ */
36
+ newVersion?: string;
37
+ /**
38
+ * The relative path of the workspace
39
+ */
40
+ path: string;
41
+ /**
42
+ * The absolute path of the workspace
43
+ */
44
+ root: string;
45
+ /**
46
+ * The package.json of the workspace
47
+ */
48
+ packageJson: PackageJson;
49
+ /**
50
+ * Release tag name after version bump (for example `pkg@1.0.1`).
51
+ *
52
+ * Set by ChangesetVersion.mergeWorkspaces only when `newVersion` differs
53
+ * from `version`. Not available before `changeset version` completes.
54
+ */
55
+ tagName?: string;
56
+ /**
57
+ * Previous release tag used as the git changelog baseline
58
+ */
59
+ lastTag?: string;
60
+ /**
61
+ * The changelog of the workspace
62
+ *
63
+ */
64
+ changelog?: string;
65
+ /**
66
+ * Whether this workspace is an internal dependent bumped only because a
67
+ * dependency was released (not directly changed in git).
68
+ *
69
+ * Set by the Workspaces plugin when `includeDependencyReleases` is enabled.
70
+ * Processing rules depend on `changesetVersion.ignoreNonUpdatedPackages`:
71
+ *
72
+ * - `false`: included in changelog template flow and version bump logs
73
+ * - `true`: tracked for restore only; skipped in changelog generation
74
+ *
75
+ * @default false
76
+ */
77
+ dependencyRelease?: boolean;
78
+ /**
79
+ * Package name of the direct dependency that caused this `dependencyRelease`.
80
+ *
81
+ * Set by Workspaces when appending dependents. ChangesetVersion uses it after
82
+ * `changeset version` to fill `dependencyReleaseTemplate` with the source's
83
+ * real `newVersion`.
84
+ */
85
+ dependencyReleaseOf?: string;
86
+ }
87
+
40
88
  /**
41
- * Plugin class constructor type
42
- *
43
- * Represents a constructor for a class that extends ScriptPlugin.
44
- * Supports generic constructor arguments.
89
+ * Base configuration for Git-related plugins
45
90
  *
46
- * @template T - Array type for constructor arguments
91
+ * Extends ScriptPluginProps with generic options.
47
92
  *
48
93
  * @example
49
94
  * ```typescript
50
- * class MyPlugin extends ScriptPlugin {
51
- * constructor(context: ScriptContext, config: { option: string }) {
52
- * super(context);
53
- * }
54
- * }
55
- *
56
- * const PluginCtor: PluginClass = MyPlugin;
95
+ * const config: GitBaseProps = {
96
+ * timeout: 5000
97
+ * };
57
98
  * ```
58
99
  */
59
- type PluginClass<T extends unknown[] = any[]> = new (...args: T) => ScriptPlugin<ScriptContext<any>, ScriptPluginProps>;
100
+ interface GitBaseProps extends ScriptPluginProps {
101
+ /**
102
+ * Environment variable name for GitHub API token
103
+ * @deprecated This property is GitHub-specific, use a subclass if needed.
104
+ */
105
+ tokenRef?: string;
106
+ /**
107
+ * Timeout for API requests in milliseconds (generic)
108
+ */
109
+ timeout?: number;
110
+ }
111
+
60
112
  /**
61
- * Plugin constructor parameters type
113
+ * @module ReleaseFormatter
114
+ * @description Template-based formatting for release branches, commits, and PRs
62
115
  *
63
- * Extracts the constructor parameter types for a plugin class,
64
- * excluding the first parameter (context). Uses TypeScript's
65
- * conditional types and inference to extract parameter types.
116
+ * Centralizes string formatting for the GitHub release flow. Uses
117
+ * {@link TemplateEngine} from `@qlover/scripts-context` with ES6-style
118
+ * `${ path }` placeholders and variables from {@link BranchNameTplVars}.
66
119
  *
67
- * @template T - Plugin class type
120
+ * Responsibilities:
121
+ * - **Branch/tag names**: `getReleaseBranch()` from `branchName` / `releaseTagName` templates
122
+ * - **Commit message**: `getCommitMessage()` with optional `less` / `more` templates when
123
+ * workspace count exceeds 3
124
+ * - **PR content**: `getPRTitle()` and `getPRBody()` with single- vs multi-workspace changelog
125
+ * formatting via `batchPRBody`
68
126
  *
69
- * @example
70
- * ```typescript
71
- * class MyPlugin extends ScriptPlugin {
72
- * constructor(
73
- * context: ScriptContext,
74
- * config: { option: string },
75
- * extra: number
76
- * ) {
77
- * super(context);
78
- * }
79
- * }
127
+ * Defaults are sourced from `releaseJson.github` in {@link defaults}.
128
+ * {@link Github} constructs an instance and calls `setConfig()` in `onBefore`
129
+ * with runtime context (`repoName`, `releaseId`, `env`, etc.).
80
130
  *
81
- * // Type: [{ option: string }, number]
82
- * type Params = PluginConstructorParams<typeof MyPlugin>;
131
+ * @example Branch name template variables
132
+ * ```typescript
133
+ * // Template: release/${repoName}-${releaseId}
134
+ * // Variables: repoName, releaseId, timestamp, authorName, env, count, spaces
135
+ * formatter.getReleaseBranch(workspaces);
83
136
  * ```
84
- */
85
- type PluginConstructorParams<T extends PluginClass> = T extends new (first: any, ...args: infer P) => unknown ? P : never;
86
- /**
87
- * Plugin configuration tuple type
88
137
  *
89
- * Represents a tuple containing a plugin class (or name) and its
90
- * constructor arguments. Used for plugin registration and loading.
91
- *
92
- * @template T - Plugin class type
93
- *
94
- * @example
138
+ * @example Multi-workspace PR body
95
139
  * ```typescript
96
- * class MyPlugin extends ScriptPlugin {
97
- * constructor(context: ScriptContext, config: { option: string }) {
98
- * super(context);
99
- * }
100
- * }
101
- *
102
- * // Type: [typeof MyPlugin, { option: string }]
103
- * type Tuple = PluginTuple<typeof MyPlugin>;
104
- *
105
- * // Type: [string, { option: string }]
106
- * type StringTuple = PluginTuple<'MyPlugin'>;
140
+ * formatter.getPRBody(workspaces, releaseBranchResult, templateContext);
107
141
  * ```
108
142
  */
109
- type PluginTuple<T extends PluginClass> = [
110
- T | string,
111
- ...PluginConstructorParams<T>
112
- ];
143
+
144
+ interface ReleaseFormatterConfig {
145
+ /**
146
+ * Repository name
147
+ */
148
+ repoName?: string;
149
+ /**
150
+ * Author name
151
+ */
152
+ authorName?: string;
153
+ /**
154
+ * Release environment
155
+ */
156
+ env?: string;
157
+ /**
158
+ * Unique ID for the current release run
159
+ */
160
+ releaseId?: string;
161
+ /**
162
+ * The branch name for batch release
163
+ *
164
+ * Template variables: see {@link BranchNameTplVars}
165
+ *
166
+ * @default `release/${repoName}-${releaseId}`
167
+ */
168
+ branchName?: string;
169
+ /**
170
+ * The tag name for batch release
171
+ *
172
+ * Template variables: see {@link BranchNameTplVars}
173
+ *
174
+ * @default `release-tag-${count}-patch-${releaseId}`
175
+ */
176
+ releaseTagName?: string;
177
+ /**
178
+ *
179
+ * @default 'Release ${spaces}' */
180
+ releaseName?: string;
181
+ /**
182
+ * Commit message template used when creating the release branch
183
+ *
184
+ * When configured as an object, supports `less` and `more` templates:
185
+ * - `less`: used when workspace count is 3 or fewer
186
+ * - `more`: used when workspace count exceeds 3
187
+ *
188
+ * Supports conventional commit structure: subject, body, and footer.
189
+ *
190
+ * **Object form is experimental.**
191
+ *
192
+ * @example Conventional commit layout
193
+ * ```
194
+ * <type>(<scope>): <subject> <-- Header/Subject (required)
195
+ * <-- blank line
196
+ * <body> <-- detailed description (optional)
197
+ * <-- blank line
198
+ * <footer> <-- issue refs or BREAKING CHANGE (optional)
199
+ * ```
200
+ *
201
+ * @example Full template string
202
+ *
203
+ * ```
204
+ * \`\`\`
205
+ * chore(release): bump ${worksapce[0].name} to v${worksapce[0].newVersion} and others
206
+ * -
207
+ * \`\`\`
208
+ * ```
209
+ *
210
+ * By default, lists all package names and versions in the commit message.
211
+ *
212
+ * @default `'chore(release): ${spaces}'`
213
+ */
214
+ commitMessage?: string | {
215
+ less: string;
216
+ more: string;
217
+ };
218
+ /**
219
+ * Pull request title template
220
+ *
221
+ * @default {@link DEFAULT_PR_TITLE}
222
+ */
223
+ PRTitle?: string;
224
+ /**
225
+ * Pull request body template
226
+ *
227
+ * @default from release.json
228
+ */
229
+ PRBody?: string;
230
+ /**
231
+ * Template for each workspace section in a multi-workspace PR body
232
+ *
233
+ * @default from release.json
234
+ */
235
+ batchPRBody?: string;
236
+ }
237
+
113
238
  /**
114
- * Creates a type-safe plugin configuration tuple
115
- *
116
- * Helper function for creating tuples that represent plugin
117
- * configurations with proper type inference for constructor
118
- * arguments.
119
- *
120
- * @template T - Plugin class type
121
- * @param plugin - Plugin class or name
122
- * @param args - Plugin constructor arguments
123
- * @returns Plugin configuration tuple
124
- *
125
- * @example Class-based plugin
126
- * ```typescript
127
- * class MyPlugin extends ScriptPlugin {
128
- * constructor(
129
- * context: ScriptContext,
130
- * config: { option: string },
131
- * extra: number
132
- * ) {
133
- * super(context);
239
+ * @module Github
240
+ * @description GitHub changelog enrichment and release PR plugin
241
+ *
242
+ * Third plugin in the default release pipeline (after {@link Workspaces} and
243
+ * {@link ChangesetVersion}). Extends {@link GitBase} for git operations and
244
+ * delegates GitHub API calls to {@link GithubManager}.
245
+ *
246
+ * Pipeline phases:
247
+ * - **onBefore**: validate GitHub token; seed {@link ReleaseFormatter} context
248
+ * - **onExec**: enrich workspace changelogs with PR/commit links via {@link GithubChangelog}
249
+ * - **onSuccess**: create release branch, commit, push, and open PR (unless skipped)
250
+ *
251
+ * Release branch flow:
252
+ * 1. `ReleaseFormatter.getReleaseBranch()` — derive branch and tag names from templates
253
+ * 2. Create branch from `sourceBranch`, commit version/changelog changes, push
254
+ * 3. Open PR with formatted title/body and optional labels
255
+ * 4. Auto-merge when `autoMergeReleasePr` is enabled
256
+ *
257
+ * @example Skip PR creation (local dry-run)
258
+ * ```bash
259
+ * fe-release --github.skip-create-release-pr --dry-run
260
+ * ```
261
+ *
262
+ * @example fe-config label and merge settings
263
+ * ```json
264
+ * {
265
+ * "release": {
266
+ * "github": {
267
+ * "autoMergeReleasePr": false,
268
+ * "label": { "name": "CI-Release" }
269
+ * }
134
270
  * }
135
271
  * }
136
- *
137
- * const config = tuple(MyPlugin, { option: 'value' }, 42);
138
- * // [MyPlugin, { option: 'value' }, 42]
139
- * ```
140
- *
141
- * @example String-based plugin
142
- * ```typescript
143
- * const config = tuple('MyPlugin', { option: 'value' });
144
- * // ['MyPlugin', { option: 'value' }]
145
272
  * ```
146
273
  */
147
- declare function tuple<T extends PluginClass>(plugin: T | string, ...args: PluginConstructorParams<T>): PluginTuple<T>;
274
+
275
+ type GithubMode = 'createPR';
276
+ type GithubLabel = {
277
+ /**
278
+ * Hexadecimal color code for label appearance
279
+ *
280
+ * Color format: 6-character hex string without '#'
281
+ * Used for visual distinction in GitHub interface
282
+ * Supports standard web color codes
283
+ *
284
+ * @optional
285
+ * @default `'1A7F37'`
286
+ * @example Green color
287
+ * ```typescript
288
+ * color: '1A7F37'
289
+ * ```
290
+ *
291
+ * @example Blue color
292
+ * ```typescript
293
+ * color: '0366D6'
294
+ * ```
295
+ */
296
+ color?: string;
297
+ /**
298
+ * Descriptive text for label documentation
299
+ *
300
+ * Provides context about the label's purpose
301
+ * Used in GitHub label management interface
302
+ * Helps team members understand label usage
303
+ *
304
+ * @optional
305
+ * @default `'Release PR'`
306
+ * @example
307
+ * ```typescript
308
+ * description: 'Automated release pull request'
309
+ * ```
310
+ */
311
+ description?: string;
312
+ /**
313
+ * Label name for identification and display
314
+ *
315
+ * Used as the primary identifier for the label
316
+ * Displayed in GitHub PR interface
317
+ * Should be descriptive and consistent
318
+ *
319
+ * @optional
320
+ * @default `'CI-Release'`
321
+ * @example
322
+ * ```typescript
323
+ * name: 'release'
324
+ * ```
325
+ */
326
+ name?: string;
327
+ };
328
+ interface GithubProps extends ReleaseFormatterConfig, GitBaseProps {
329
+ /**
330
+ * Plugin work mode
331
+ *
332
+ * Currently only `createPR` is supported: enrich changelogs in `onExec`,
333
+ * then create release branch and PR in `onSuccess`.
334
+ *
335
+ * @default `'createPR'`
336
+ */
337
+ mode?: GithubMode;
338
+ /**
339
+ * PR auto-merge strategy for release pull requests
340
+ *
341
+ * Core concept:
342
+ * Defines the merge strategy used when automatically merging
343
+ * release pull requests, affecting commit history and
344
+ * repository structure.
345
+ *
346
+ * Merge strategies:
347
+ * - merge: Creates merge commit with branch history
348
+ * - squash: Combines all commits into single commit
349
+ * - rebase: Replays commits on target branch
350
+ *
351
+ * Strategy considerations:
352
+ * - merge: Preserves complete branch history
353
+ * - squash: Creates clean, linear history
354
+ * - rebase: Maintains chronological order
355
+ * - Affects commit message and history structure
356
+ * - Influences repository maintenance and debugging
357
+ *
358
+ * @optional
359
+ * @default `'squash'`
360
+ * @example Squash merge
361
+ * ```typescript
362
+ * const config: FeReleaseConfig = {
363
+ * autoMergeType: 'squash'
364
+ * };
365
+ * ```
366
+ *
367
+ * @example Preserve history
368
+ * ```typescript
369
+ * const config: FeReleaseConfig = {
370
+ * autoMergeType: 'merge'
371
+ * };
372
+ * ```
373
+ */
374
+ mergeType?: 'merge' | 'squash' | 'rebase';
375
+ /**
376
+ * Whether to skip this plugin
377
+ * @default false
378
+ */
379
+ skip?: boolean;
380
+ /** @default 'chore(tag): ${name} v${version}' */
381
+ commitMessage?: string;
382
+ /** @default [] */
383
+ commitArgs?: string[];
384
+ draft?: boolean;
385
+ preRelease?: boolean;
386
+ autoGenerate?: boolean;
387
+ makeLatest?: boolean | 'true' | 'false' | 'legacy';
388
+ releaseNotes?: string;
389
+ discussionCategoryName?: string;
390
+ /**
391
+ * Whether to auto-merge the created release PR
392
+ *
393
+ * @default false
394
+ */
395
+ autoMergeReleasePr?: boolean;
396
+ /** @default false */
397
+ pushChangeLabels?: boolean;
398
+ /**
399
+ * Skip creating the GitHub release pull request.
400
+ *
401
+ * When enabled, the release branch is still created and pushed,
402
+ * but no PR is opened via the GitHub API. Useful for local testing.
403
+ *
404
+ * CLI: `--github.skip-create-release-pr`
405
+ * fe-config: `release.github.skipCreateReleasePR`
406
+ *
407
+ * @default false
408
+ */
409
+ skipCreateReleasePr?: boolean;
410
+ /**
411
+ * Configuration for release pull request labels
412
+ *
413
+ * Core concept:
414
+ * Defines the label configuration for release pull requests,
415
+ * enabling automated categorization and visual identification
416
+ * of release-related PRs.
417
+ *
418
+ * Label features:
419
+ * - Automated label application
420
+ * - Customizable label appearance
421
+ * - Consistent release identification
422
+ * - Integration with GitHub labeling system
423
+ * - Support for custom label descriptions
424
+ *
425
+ * Label properties:
426
+ * - name: Label identifier and display name
427
+ * - color: Hexadecimal color code for visual distinction
428
+ * - description: Label description for documentation
429
+ *
430
+ * @optional
431
+ * @example Basic label configuration
432
+ * ```typescript
433
+ * const config: FeReleaseConfig = {
434
+ * label: {
435
+ * name: 'release',
436
+ * color: '1A7F37',
437
+ * description: 'Automated release PR'
438
+ * }
439
+ * };
440
+ * ```
441
+ *
442
+ * @example Custom label
443
+ * ```typescript
444
+ * const config: FeReleaseConfig = {
445
+ * label: {
446
+ * name: 'CI-Release',
447
+ * color: '0366D6',
448
+ * description: 'Release created by CI/CD'
449
+ * }
450
+ * };
451
+ * ```
452
+ */
453
+ label?: GithubLabel;
454
+ }
148
455
 
149
456
  /**
150
- * @module ReleaseTask
151
- * @description Task orchestration for release process
152
- *
153
- * This module provides the core task orchestration for the release process,
154
- * managing plugin loading, execution order, and context handling. It serves
155
- * as the main entry point for executing release operations.
457
+ * @module ChangeLog
458
+ * @description Core interfaces for changelog generation
156
459
  *
157
- * Core Features:
158
- * - Plugin management and execution
159
- * - Release context initialization
160
- * - Task execution control
161
- * - Environment-based control
460
+ * This module provides the core interfaces and types for generating
461
+ * changelogs from Git commit history. It includes types for commit
462
+ * parsing, formatting, and changelog generation.
162
463
  *
163
- * Default Plugins:
164
- * - Workspaces: Monorepo workspace management
165
- * - Changelog: Version and changelog management
166
- * - GithubPR: Pull request creation and management
464
+ * Core Components:
465
+ * - Commit data structures
466
+ * - Changelog formatting
467
+ * - Git log options
468
+ * - Changelog generation
167
469
  *
168
470
  * @example Basic usage
169
471
  * ```typescript
170
- * // Initialize and execute
171
- * const task = new ReleaseTask({
172
- * rootPath: '/path/to/project',
173
- * sourceBranch: 'main'
174
- * });
175
- *
176
- * await task.exec();
177
- * ```
178
- *
179
- * @example Custom plugins
180
- * ```typescript
181
- * import { tuple } from '@qlover/fe-release';
182
- *
183
- * // Add custom plugin
184
- * class CustomPlugin extends ScriptPlugin {
185
- * async onExec() {
186
- * // Custom release logic
472
+ * class MyChangeLog implements ChangeLogInterface {
473
+ * async getCommits(options?: GitChangelogOptions): Promise<CommitValue[]> {
474
+ * // Implementation
187
475
  * }
188
476
  * }
189
477
  *
190
- * const task = new ReleaseTask({}, new LifecycleExecutor<ReleaseContext>(), [
191
- * tuple(CustomPlugin, { option: 'value' })
192
- * ]);
193
- *
194
- * await task.exec();
195
- * ```
196
- *
197
- * @example Environment control
198
- * ```typescript
199
- * // Skip release
200
- * process.env.FE_RELEASE = 'false';
201
- *
202
- * const task = new ReleaseTask();
203
- * try {
204
- * await task.exec();
205
- * } catch (e) {
206
- * // Handle "Skip Release" error
478
+ * class MyFormatter implements ChangelogFormatter {
479
+ * format(commits: CommitValue[]): string[] {
480
+ * // Implementation
481
+ * }
207
482
  * }
208
483
  * ```
209
484
  */
210
485
 
211
486
  /**
212
- * Core task class for managing release operations
487
+ * Base commit type mapping Git commit fields
213
488
  *
214
- * Handles plugin orchestration, task execution, and context management
215
- * for the release process. Supports both built-in and custom plugins.
489
+ * Maps all available Git commit fields to optional string values.
490
+ * Uses the CommitField type from gitlog package to ensure type safety.
216
491
  *
217
- * Features:
218
- * - Plugin lifecycle management
219
- * - Task execution control
220
- * - Context initialization and access
221
- * - Environment-based control
492
+ * Available fields include:
493
+ * - hash: Full commit hash
494
+ * - abbrevHash: Abbreviated commit hash
495
+ * - subject: Commit message subject
496
+ * - authorName: Author's name
497
+ * - authorDate: Author date
498
+ * - And many more from gitlog.CommitField
222
499
  *
223
- * @example Basic initialization
500
+ * @example
224
501
  * ```typescript
225
- * const task = new ReleaseTask({
226
- * rootPath: '/path/to/project'
227
- * });
502
+ * const commit: BaseCommit = {
503
+ * hash: 'abc123def456',
504
+ * abbrevHash: 'abc123',
505
+ * subject: 'feat: new feature',
506
+ * authorName: 'John Doe',
507
+ * authorDate: '2023-01-01'
508
+ * };
228
509
  * ```
510
+ */
511
+ type BaseCommit = {
512
+ [key in CommitField]: string | undefined;
513
+ };
514
+ /**
515
+ * Configuration options for changelog generation
229
516
  *
230
- * @example Custom executor
231
- * ```typescript
232
- * const executor = new LifecycleExecutor<ReleaseContext>({
233
- * onError: (err) => console.error('Release failed:', err)
234
- * });
517
+ * Provides comprehensive options for controlling how changelogs
518
+ * are generated from Git history, including commit range selection,
519
+ * formatting, and filtering.
235
520
  *
236
- * const task = new ReleaseTask({}, executor);
521
+ * @example Basic usage
522
+ * ```typescript
523
+ * const options: GitChangelogOptions = {
524
+ * from: 'v1.0.0',
525
+ * to: 'v2.0.0',
526
+ * directory: 'packages/my-pkg',
527
+ * noMerges: true
528
+ * };
237
529
  * ```
238
530
  *
239
- * @example Custom plugins
531
+ * @example Custom formatting
240
532
  * ```typescript
241
- * const task = new ReleaseTask(
242
- * {}, // options
243
- * new LifecycleExecutor<ReleaseContext>(),
244
- * [
245
- * tuple(CustomPlugin, { config: 'value' }),
246
- * ...innerTuples // include default plugins
247
- * ]
248
- * );
533
+ * const options: GitChangelogOptions = {
534
+ * types: [
535
+ * { type: 'feat', section: '### Features' },
536
+ * { type: 'fix', section: '### Bug Fixes' }
537
+ * ],
538
+ * formatTemplate: '* ${commitlint.message} ${prLink}',
539
+ * commitBody: true
540
+ * };
249
541
  * ```
250
542
  */
251
- declare class ReleaseTask {
252
- private executor;
253
- private defaultTuples;
254
- /**
255
- * Release context instance
256
- * @protected
257
- */
258
- protected context: ReleaseContext;
543
+ interface GitChangelogOptions {
259
544
  /**
260
- * Creates a new ReleaseTask instance
261
- *
262
- * Initializes the release context and sets up plugin configuration.
263
- * Supports custom executors and plugin configurations.
545
+ * Starting tag or commit reference
264
546
  *
265
- * @param options - Release context configuration
266
- * @param executor - Custom async executor (optional)
267
- * @param defaultTuples - Plugin configuration tuples (optional)
547
+ * Defines the start point for collecting commits.
548
+ * Can be a tag name, commit hash, or branch name.
268
549
  *
269
550
  * @example
270
551
  * ```typescript
271
- * // Basic initialization
272
- * const task = new ReleaseTask({
273
- * rootPath: '/path/to/project',
274
- * sourceBranch: 'main'
275
- * });
276
- *
277
- * // With custom executor and plugins
278
- * const task = new ReleaseTask(
279
- * { rootPath: '/path/to/project' },
280
- * new LifecycleExecutor<ReleaseContext>(),
281
- * [tuple(CustomPlugin, { option: 'value' })]
282
- * );
552
+ * from: 'v1.0.0' // Start from v1.0.0 tag
553
+ * from: 'abc123' // Start from specific commit
283
554
  * ```
284
555
  */
285
- constructor(options?: Partial<ReleaseContextOptions>, executor?: LifecycleExecutor<ReleaseContext>, defaultTuples?: PluginTuple<PluginClass>[]);
556
+ from?: string;
286
557
  /**
287
- * Gets the current release context
558
+ * Ending tag or commit reference
288
559
  *
289
- * @returns Release context instance
560
+ * Defines the end point for collecting commits.
561
+ * Can be a tag name, commit hash, or branch name.
290
562
  *
291
563
  * @example
292
564
  * ```typescript
293
- * const task = new ReleaseTask();
294
- * const context = task.getContext();
295
- *
296
- * console.log(context.releaseEnv);
297
- * console.log(context.sourceBranch);
565
+ * to: 'v2.0.0' // End at v2.0.0 tag
566
+ * to: 'main' // End at main branch
298
567
  * ```
299
568
  */
300
- getContext(): ReleaseContext;
569
+ to?: string;
301
570
  /**
302
- * Loads and configures plugins for the release task
571
+ * Directory to collect commits from
303
572
  *
304
- * Combines default and external plugins, initializes them with
305
- * the current context, and configures special cases like the
306
- * Workspaces plugin.
573
+ * Limits commit collection to changes in specified directory.
574
+ * Useful for monorepo package-specific changelogs.
307
575
  *
308
- * Plugin Loading Process:
309
- * 1. Merge default and external plugins
310
- * 2. Initialize plugins with context
311
- * 3. Configure special plugins
312
- * 4. Add plugins to executor
576
+ * @example
577
+ * ```typescript
578
+ * directory: 'packages/my-pkg' // Only changes in this directory
579
+ * ```
580
+ */
581
+ directory?: string;
582
+ /**
583
+ * Git commit fields to include
313
584
  *
314
- * @param externalTuples - Additional plugin configurations
315
- * @returns Array of initialized plugins
585
+ * Specifies which Git commit fields to retrieve.
586
+ * @default ["abbrevHash", "hash", "subject", "authorName", "authorDate"]
316
587
  *
317
- * @example Basic usage
588
+ * @example
318
589
  * ```typescript
319
- * const task = new ReleaseTask();
320
- * const plugins = await task.usePlugins();
590
+ * fields: ['hash', 'subject', 'authorName']
321
591
  * ```
592
+ */
593
+ fields?: CommitField[];
594
+ /**
595
+ * Whether to exclude merge commits
322
596
  *
323
- * @example Custom plugins
324
- * ```typescript
325
- * const task = new ReleaseTask();
326
- * const plugins = await task.usePlugins([
327
- * tuple(CustomPlugin, { option: 'value' })
328
- * ]);
597
+ * When true, merge commits are filtered out from the changelog.
598
+ * @default true
599
+ *
600
+ * @example
601
+ * ```typescript
602
+ * noMerges: true // Exclude merge commits
603
+ * noMerges: false // Include merge commits
329
604
  * ```
330
605
  */
331
- usePlugins(externalTuples?: PluginTuple<PluginClass>[]): Promise<ScriptPlugin<ScriptContext<any>, ScriptPluginProps>[]>;
606
+ noMerges?: boolean;
332
607
  /**
333
- * Executes the release task
608
+ * Commit type configurations
334
609
  *
335
- * Internal method that runs the task through the executor.
336
- * Preserves the context through the execution chain.
610
+ * Defines how different commit types should be handled and
611
+ * formatted in the changelog.
337
612
  *
338
- * @returns Execution result
339
- * @internal
613
+ * @example
614
+ * ```typescript
615
+ * types: [
616
+ * { type: 'feat', section: '### Features' },
617
+ * { type: 'fix', section: '### Bug Fixes' },
618
+ * { type: 'chore', hidden: true } // Skip chore commits
619
+ * ]
620
+ * ```
340
621
  */
341
- run(): Promise<unknown>;
622
+ types?: {
623
+ type: string;
624
+ section?: string;
625
+ hidden?: boolean;
626
+ }[];
342
627
  /**
343
- * Main entry point for executing the release task
344
- *
345
- * Checks environment conditions, loads plugins, and executes
346
- * the release process. Supports additional plugin configuration
347
- * at execution time.
628
+ * Template for formatting commit entries
348
629
  *
349
- * Environment Control:
350
- * - Checks FE_RELEASE environment variable
351
- * - Skips release if FE_RELEASE=false
630
+ * Supports variables from CommitValue properties and adds:
631
+ * - ${scopeHeader}: Formatted scope
632
+ * - ${commitLink}: Commit hash link
633
+ * - ${prLink}: PR number link
352
634
  *
353
- * @param externalTuples - Additional plugin configurations
354
- * @returns Execution result
355
- * @throws Error if release is skipped via environment variable
635
+ * @default '\n- ${scopeHeader} ${commitlint.message} ${commitLink} ${prLink}'
356
636
  *
357
- * @example Basic execution
637
+ * @example
358
638
  * ```typescript
359
- * const task = new ReleaseTask();
360
- * await task.exec();
639
+ * formatTemplate: '* ${commitlint.message} (${commitLink})'
361
640
  * ```
641
+ */
642
+ formatTemplate?: string;
643
+ /**
644
+ * Whether to include commit message body
362
645
  *
363
- * @example With additional plugins
364
- * ```typescript
365
- * const task = new ReleaseTask();
366
- * await task.exec([
367
- * tuple(CustomPlugin, { option: 'value' })
368
- * ]);
369
- * ```
646
+ * When true, includes the full commit message body
647
+ * in the changelog entry.
370
648
  *
371
- * @example Environment control
372
- * ```typescript
373
- * // Skip release
374
- * process.env.FE_RELEASE = 'false';
649
+ * @since 2.3.0
650
+ * @default false
375
651
  *
376
- * const task = new ReleaseTask();
377
- * try {
378
- * await task.exec();
379
- * } catch (e) {
380
- * if (e.message === 'Skip Release') {
381
- * console.log('Release skipped via environment variable');
382
- * }
383
- * }
652
+ * @example
653
+ * ```typescript
654
+ * commitBody: true // Include full commit message
384
655
  * ```
385
656
  */
386
- exec(externalTuples?: PluginTuple<PluginClass>[]): Promise<unknown>;
387
- }
388
-
389
- type PackageJson$1 = Record<string, unknown>;
390
- interface WorkspacesProps extends ScriptPluginProps {
657
+ commitBody?: boolean;
391
658
  /**
392
- * Whether to skip workspaces
659
+ * Template for dependency release entries
393
660
  *
394
- * @default `false`
395
- */
396
- skip?: boolean;
397
- /**
398
- * Whether to skip checking the package.json file
661
+ * Used for formatting changelog entries related to dependency updates.
662
+ * Supports variables:
663
+ * - ${dep.name}: Dependency name
664
+ * - ${dep.oldVersion}: Previous version
665
+ * - ${dep.newVersion}: Updated version
399
666
  *
400
- * @default `false`
401
- */
402
- skipCheckPackage?: boolean;
403
- /**
404
- * The workspace to publish
405
- */
406
- workspace?: WorkspaceValue;
407
- /**
408
- * The workspaces to publish
409
- * @private
410
- */
411
- workspaces?: WorkspaceValue[];
412
- /**
413
- * The change labels
667
+ * @since 5.0.0
668
+ * @default '- Update dependency **${name}** from `${oldVersion}` to `${newVersion}`'
414
669
  *
415
- * from `changePackagesLabel`
670
+ * @example
671
+ * ```typescript
672
+ * dependencyReleaseTemplate: '- Bump **${dep.name}** from `${dep.oldVersion}` to `${dep.newVersion}`'
673
+ * ```
416
674
  */
417
- changeLabels?: string[];
675
+ dependencyReleaseTemplate?: string;
676
+ }
677
+ /**
678
+ * Raw commit message parsing result
679
+ *
680
+ * Represents a parsed conventional commit message with its
681
+ * component parts.
682
+ *
683
+ * @example
684
+ * ```typescript
685
+ * const tuple: CommitTuple = {
686
+ * raw: 'feat(api): add new endpoint\n\nDetails here',
687
+ * type: 'feat',
688
+ * scope: 'api',
689
+ * message: 'add new endpoint',
690
+ * body: 'Details here'
691
+ * };
692
+ * ```
693
+ */
694
+ interface CommitTuple {
695
+ /** Original commit message */
696
+ raw: string;
697
+ /** Commit type (e.g., 'feat', 'fix') */
698
+ type?: string;
699
+ /** Commit scope (e.g., 'api', 'core') */
700
+ scope?: string;
701
+ /** Main commit message */
702
+ message: string;
703
+ /** Optional commit body */
704
+ body?: string;
705
+ }
706
+ /**
707
+ * Parsed conventional commit data
708
+ *
709
+ * Represents a commit message parsed according to the
710
+ * conventional commit specification.
711
+ *
712
+ * Format: type(scope): message
713
+ *
714
+ * @example
715
+ * ```typescript
716
+ * const commit: Commitlint = {
717
+ * type: 'feat',
718
+ * scope: 'api',
719
+ * message: 'add new endpoint',
720
+ * body: 'Adds support for new API endpoint\n\nBREAKING CHANGE: API format changed'
721
+ * };
722
+ * ```
723
+ */
724
+ interface Commitlint {
725
+ /** Commit type (e.g., 'feat', 'fix') */
726
+ type?: string;
727
+ /** Commit scope (e.g., 'api', 'core') */
728
+ scope?: string;
729
+ /** Main commit message */
730
+ message: string;
418
731
  /**
419
- * The changed paths
420
- * @private
732
+ * Commit message body with title removed
733
+ * @since 2.3.0
421
734
  */
422
- changedPaths?: string[];
735
+ body?: string;
736
+ }
737
+ /**
738
+ * Complete commit information
739
+ *
740
+ * Combines Git commit data, parsed conventional commit info,
741
+ * and PR metadata into a single value object.
742
+ *
743
+ * @example
744
+ * ```typescript
745
+ * const commit: CommitValue = {
746
+ * base: {
747
+ * hash: 'abc123',
748
+ * subject: 'feat(api): new endpoint (#123)'
749
+ * },
750
+ * commitlint: {
751
+ * type: 'feat',
752
+ * scope: 'api',
753
+ * message: 'new endpoint'
754
+ * },
755
+ * commits: [],
756
+ * prNumber: '123'
757
+ * };
758
+ * ```
759
+ */
760
+ interface CommitValue {
761
+ /** Raw Git commit information */
762
+ base: BaseCommit;
763
+ /** Parsed conventional commit data */
764
+ commitlint: Commitlint;
765
+ /** Sub-commits (for merge commits) */
766
+ commits?: CommitValue[];
767
+ /** Associated pull request number */
768
+ prNumber?: string;
769
+ }
770
+ /**
771
+ * Interface for changelog formatting
772
+ *
773
+ * Defines the contract for classes that format commit data
774
+ * into changelog entries.
775
+ *
776
+ * @example
777
+ * ```typescript
778
+ * class MarkdownFormatter implements ChangelogFormatter {
779
+ * format(commits: CommitValue[]): string[] {
780
+ * return commits.map(commit =>
781
+ * `- ${commit.commitlint.message} (#${commit.prNumber})`
782
+ * );
783
+ * }
784
+ * }
785
+ * ```
786
+ */
787
+ interface ChangelogFormatter {
423
788
  /**
424
- * The packages
425
- * @private
789
+ * Formats commits into changelog entries
790
+ *
791
+ * @param commits - Array of commits to format
792
+ * @param options - Optional formatting options
793
+ * @returns Array of formatted changelog lines
426
794
  */
427
- packages?: string[];
795
+ format<Opt extends GitChangelogOptions>(commits: unknown[], options?: Opt): string[];
796
+ }
797
+ /**
798
+ * Interface for changelog generation
799
+ *
800
+ * Defines the contract for classes that generate changelogs
801
+ * from Git history.
802
+ *
803
+ * @example
804
+ * ```typescript
805
+ * class GitChangelog implements ChangeLogInterface {
806
+ * async getCommits(options?: GitChangelogOptions): Promise<CommitValue[]> {
807
+ * // Get commits from Git and parse them
808
+ * const commits = await gitlog(options);
809
+ * return commits.map(commit => ({
810
+ * base: commit,
811
+ * commitlint: parseCommit(commit.subject),
812
+ * commits: []
813
+ * }));
814
+ * }
815
+ * }
816
+ * ```
817
+ */
818
+ interface ChangeLogInterface {
428
819
  /**
429
- * All project packages mapping
430
- * @private
820
+ * Retrieves and parses Git commits
821
+ *
822
+ * @param options - Optional Git log options
823
+ * @returns Promise resolving to array of parsed commits
431
824
  */
432
- projectWorkspaces?: WorkspaceValue[];
825
+ getCommits(options?: GitChangelogOptions): Promise<CommitValue[]>;
433
826
  }
434
- interface WorkspaceValue {
435
- name: string;
436
- version: string;
827
+
828
+ /**
829
+ * @module ChangesetVersion
830
+ * @description Changelog generation and changeset version/publish plugin
831
+ *
832
+ * Second plugin in the default release pipeline (after {@link Workspaces},
833
+ * before {@link Github}). Bridges git-based changelog formatting with the
834
+ * Changesets CLI for monorepo version bumps.
835
+ *
836
+ * Pipeline phases:
837
+ * - **onBefore**: validate `.changeset` directory; validate `NPM_TOKEN` when mode includes publish
838
+ * - **onExec**: generate per-workspace git changelogs (skips `dependencyRelease`
839
+ * when `ignoreNonUpdatedPackages` is enabled)
840
+ * - **onSuccess**: run version and/or publish flow based on `mode`
841
+ *
842
+ * Version flow (`mode: 'version'` or first half of `'both'`):
843
+ * 1. Write `.changeset/*.md` files for directly changed packages only
844
+ * 2. Run `changeset version` (optionally with `changelog: false` when `onlyVersion`)
845
+ * 3. Optionally `git restore` dependency-release paths when `ignoreNonUpdatedPackages`
846
+ * 4. Sync workspace `newVersion` / `tagName` from disk via `mergeWorkspaces`
847
+ *
848
+ * @example Version-only release
849
+ * ```typescript
850
+ * // fe-config.json
851
+ * {
852
+ * "release": {
853
+ * "changesetVersion": {
854
+ * "mode": "version",
855
+ * "increment": "patch"
856
+ * }
857
+ * }
858
+ * }
859
+ * ```
860
+ *
861
+ * @example Ignore internal dependent bumps
862
+ * ```bash
863
+ * fe-release --changesetVersion.ignore-non-updated-packages
864
+ * ```
865
+ *
866
+ * @see {@link ChangesetVersionProps.ignoreNonUpdatedPackages} for dependency-release behavior
867
+ */
868
+
869
+ type ChangesetVersionMode = 'version' | 'publish' | 'both';
870
+ interface ChangesetVersionProps extends GitChangelogOptions, ScriptPluginProps {
437
871
  /**
438
- * The relative path of the workspace
872
+ * Work mode
873
+ *
874
+ * - `version`: generate git changelog, write changeset files, run `changeset version`
875
+ * - `publish`: run `changeset publish`
876
+ * - `both`: run `version` flow first, then `publish`
877
+ *
878
+ * @default 'version'
439
879
  */
440
- path: string;
880
+ mode?: ChangesetVersionMode;
441
881
  /**
442
- * The absolute path of the workspace
882
+ * Version increment type for generated changeset files
883
+ * @default 'patch'
443
884
  */
444
- root: string;
885
+ increment?: string;
445
886
  /**
446
- * The package.json of the workspace
887
+ * Whether to skip this plugin
888
+ * @default false
447
889
  */
448
- packageJson: PackageJson$1;
890
+ skip?: boolean;
449
891
  /**
450
- * The tag name of the workspace
451
- * @private
892
+ * Whether to skip generating changeset files (version mode only)
893
+ * @default false
452
894
  */
453
- tagName?: string;
895
+ skipChangeset?: boolean;
454
896
  /**
455
- * The last tag name of the workspace
456
- * @private
897
+ * Root directory of the changeset config
898
+ * @default '.changeset'
457
899
  */
458
- lastTag?: string;
900
+ changesetRoot?: string;
459
901
  /**
460
- * The changelog of the workspace
461
- * @private
902
+ * When true, only bump package.json versions via changesets;
903
+ * do not write changelog content into CHANGELOG.md
904
+ * @default false
462
905
  */
463
- changelog?: string;
464
- }
465
-
466
- type ReleaseParamsConfig = {
906
+ onlyVersion?: boolean;
467
907
  /**
468
- * Max number of workspaces to include in the release name
908
+ * Control how internal dependency bump side-effects are handled during release.
469
909
  *
470
- * @default 3
471
- */
472
- maxWorkspace?: number;
473
- /**
474
- * Multi-workspace separator
910
+ * ## Background
475
911
  *
476
- * @default '_'
477
- */
478
- multiWorkspaceSeparator?: string;
479
- /**
480
- * Workspace version separator
912
+ * When a source package changes, `changeset version` may also bump its internal
913
+ * dependents (for example, `fe-scripts` depends on `scripts-context`).
914
+ * Dependents are tracked as `dependencyRelease` workspaces
915
+ * (see `workspaces.includeDependencyReleases`).
481
916
  *
482
- * @default '@'
483
- */
484
- workspaceVersionSeparator?: string;
485
- /**
486
- * The branch name for batch release
917
+ * ## `false` (default) — keep dependent bumps
487
918
  *
488
- * @default `batch-${releaseName}-${length}-packages-${timestamp}`
489
- */
490
- batchBranchName?: string;
491
- /**
492
- * The tag name for batch release
919
+ * 1. **Workspaces**: append dependents and set `dependencyRelease: true`
920
+ * 2. **Changelog**: generate git changelog for changed packages; dependents use
921
+ * `dependencyReleaseTemplate` changelog
922
+ * 3. **Changeset files**: only created for directly changed packages
923
+ * 4. **`changeset version`**: bumps changed packages and dependents on disk
924
+ * 5. **Result**: `Updated workspaces` includes `(DEP)` entries; dependents may
925
+ * be published together
493
926
  *
494
- * @default `batch-${length}-packages-${timestamp}`
495
- */
496
- batchTagName?: string;
497
- /**
498
- * The PR title for batch release
927
+ * ## `true` — ignore dependent bumps (restore after version)
499
928
  *
500
- * default from feConfig.release.PRTitle
929
+ * 1. **Workspaces**: append dependents for restore targeting (`lastTag` is still resolved)
930
+ * 2. **Changelog / changeset**: skip processing for `dependencyRelease` workspaces
931
+ * 3. **`changeset version`**: runs as usual (changesets may still touch dependents)
932
+ * 4. **Restore**: `git restore` all `dependencyRelease` workspace paths
933
+ * 5. **Result**: only directly changed packages remain bumped; they are the only
934
+ * workspaces left for GitHub PR title/body/`release-tag-${count}-*` naming
501
935
  *
502
- * @default `Release ${env} ${pkgName} ${tagName}`
503
- */
504
- PRTitle?: string;
505
- /**
506
- * The PR body for batch release
936
+ * @see {@link shouldProcessWorkspace} for the per-workspace processing gate
937
+ *
938
+ * CLI: `--changesetVersion.ignore-non-updated-packages`
939
+ * Alias: `--changelog.ignore-non-updated-packages`
507
940
  *
508
- * default from feConfig.release.PRBody
941
+ * @default false
509
942
  */
510
- PRBody?: string;
511
- };
943
+ ignoreNonUpdatedPackages?: boolean;
944
+ }
945
+ /**
946
+ * Manages changelog generation, changeset file creation, and Changesets CLI execution.
947
+ *
948
+ * Coordinates with {@link Workspaces} for workspace discovery and
949
+ * `dependencyRelease` tagging. Downstream {@link Github} consumes enriched
950
+ * changelogs and bumped versions produced here.
951
+ */
952
+ declare class ChangesetVersion extends ScriptPlugin<ReleaseContext, ChangesetVersionProps> {
953
+ constructor(context: ReleaseContext, props?: ChangesetVersionProps);
954
+ get changesetRoot(): string;
955
+ get changesetConfigPath(): string;
956
+ protected get mode(): ChangesetVersionMode;
957
+ protected get ignoreNonUpdatedPackages(): boolean;
958
+ protected shouldProcessWorkspace(workspace: WorkspaceInterface): boolean;
959
+ protected getProcessableWorkspaces(workspaces: WorkspaceInterface[]): WorkspaceInterface[];
960
+ onBefore(): Promise<void>;
961
+ /**
962
+ * Ensure NPM_TOKEN is available and configured before changeset publish.
963
+ *
964
+ * Only required for `publish` / `both` modes. Version-only runs (release PR)
965
+ * do not need an npm auth token.
966
+ */
967
+ protected validateNpmToken(): Promise<void>;
968
+ onExec(_context: ReleaseContext): Promise<void>;
969
+ onSuccess(): Promise<void>;
970
+ protected logDryRun(message: string): void;
971
+ protected runVersionFlow(): Promise<void>;
972
+ protected runChangesetPublish(): Promise<void>;
973
+ protected syncWorkspaces(workspaces: WorkspaceInterface[]): void;
974
+ /**
975
+ * Rebuild `dependencyRelease` changelogs after source packages have `newVersion`.
976
+ *
977
+ * Workspaces appends dependents before `changeset version`, so the template can
978
+ * only use a provisional version. Once mergeWorkspaces reads bumped versions
979
+ * from disk, rewrite each dependent changelog with the real source bump.
980
+ */
981
+ protected refreshDependencyReleaseChangelogs(workspaces: WorkspaceInterface[]): WorkspaceInterface[];
982
+ mergeWorkspaces(workspaces: WorkspaceInterface[]): WorkspaceInterface[];
983
+ protected runChangesetVersion(onlyVersion?: boolean): Promise<void>;
984
+ restoreIgnorePackages(): Promise<void>;
985
+ generateChangelog(workspace: WorkspaceInterface): Promise<WorkspaceInterface>;
986
+ protected getChangelogWithGit(tagName: string, dir: string): Promise<string[]>;
987
+ protected generateTagName(workspace: WorkspaceInterface): string;
988
+ getIncrement(): string;
989
+ /**
990
+ * Labels that can override semver increment.
991
+ *
992
+ * Prefer explicit `workspaces.changeLabels`, then fall back to the PR labels
993
+ * from `GITHUB_EVENT_PATH` when running in GitHub Actions after a PR merge.
994
+ */
995
+ protected getIncrementLabels(): string[];
996
+ protected readGithubEventLabelNames(): string[];
997
+ generateChangesetFile(workspace: WorkspaceInterface): Promise<void>;
998
+ }
512
999
 
513
1000
  /**
514
- * Base configuration for Git-related plugins
1001
+ * @module ReleaseLabel
1002
+ * @description Release label management and file change detection
515
1003
  *
516
- * Extends ScriptPluginProps with GitHub-specific configuration
517
- * options for API access and timeouts.
1004
+ * This module provides utilities for managing release labels and detecting
1005
+ * which packages have changed based on file paths. It supports custom
1006
+ * comparison logic and label formatting.
518
1007
  *
519
- * @example
1008
+ * Core Features:
1009
+ * - File change detection
1010
+ * - Package path matching
1011
+ * - Label generation
1012
+ * - Custom comparison logic
1013
+ *
1014
+ * @example Basic usage
520
1015
  * ```typescript
521
- * const config: GitBaseProps = {
522
- * tokenRef: 'CUSTOM_TOKEN',
523
- * timeout: 5000
524
- * };
1016
+ * const label = new ReleaseLabel({
1017
+ * changePackagesLabel: 'changed:${name}',
1018
+ * packagesDirectories: ['packages/a', 'packages/b']
1019
+ * });
1020
+ *
1021
+ * // Find changed packages
1022
+ * const changed = label.pick(['packages/a/src/index.ts']);
1023
+ * // ['packages/a']
1024
+ *
1025
+ * // Generate labels
1026
+ * const labels = label.toChangeLabels(changed);
1027
+ * // ['changed:packages/a']
1028
+ * ```
1029
+ *
1030
+ * @example Custom comparison
1031
+ * ```typescript
1032
+ * const label = new ReleaseLabel({
1033
+ * changePackagesLabel: 'changed:${name}',
1034
+ * packagesDirectories: ['packages/a'],
1035
+ * compare: (file, pkg) => file.includes(pkg)
1036
+ * });
1037
+ *
1038
+ * const changed = label.pick(['src/packages/a/index.ts']);
1039
+ * // ['packages/a']
525
1040
  * ```
526
1041
  */
527
- interface GitBaseProps extends ScriptPluginProps {
1042
+ /**
1043
+ * Function type for custom file path comparison
1044
+ *
1045
+ * Used to determine if a changed file belongs to a package.
1046
+ * Default implementation checks if the file path starts with
1047
+ * the package path.
1048
+ *
1049
+ * @param changedFilePath - Path of the changed file
1050
+ * @param packagePath - Path of the package to check against
1051
+ * @returns True if the file belongs to the package
1052
+ */
1053
+ type ReleaseLabelCompare = (changedFilePath: string, packagePath: string) => boolean;
1054
+ interface ReleaseLabelOptions {
528
1055
  /**
529
- * Environment variable name for GitHub API token
530
- *
531
- * The value of this environment variable will be used
532
- * for GitHub API authentication.
533
- *
534
- * @default 'GITHUB_TOKEN'
535
- *
536
- * @example
537
- * ```typescript
538
- * process.env.CUSTOM_TOKEN = 'ghp_123...';
539
- * const config = { tokenRef: 'CUSTOM_TOKEN' };
540
- * ```
1056
+ * The change packages label
541
1057
  */
542
- tokenRef?: string;
1058
+ changePackagesLabel: string;
543
1059
  /**
544
- * Timeout for GitHub API requests in milliseconds
545
- *
546
- * Controls how long to wait for GitHub API responses
547
- * before timing out.
548
- *
549
- * @example
550
- * ```typescript
551
- * const config = { timeout: 5000 }; // 5 seconds
552
- * ```
1060
+ * The packages directories
553
1061
  */
554
- timeout?: number;
1062
+ packagesDirectories: string[];
1063
+ compare?: ReleaseLabelCompare;
555
1064
  }
556
-
557
1065
  /**
558
- * @module GithubPR
559
- * @description GitHub Pull Request and Release Management
1066
+ * Core class for managing release labels and change detection
560
1067
  *
561
- * This module provides functionality for managing GitHub pull requests
562
- * and releases as part of the release process. It handles PR creation,
563
- * release publishing, and changelog management.
1068
+ * Provides utilities for detecting changed packages and generating
1069
+ * appropriate labels. Supports custom comparison logic and label
1070
+ * formatting.
564
1071
  *
565
- * Core Features:
566
- * - Pull request creation and management
567
- * - Release publishing
568
- * - Changelog integration
569
- * - Tag management
570
- * - Label management
571
- * - Auto-merge support
1072
+ * Features:
1073
+ * - File change detection
1074
+ * - Package path matching
1075
+ * - Label generation
1076
+ * - Custom comparison logic
572
1077
  *
573
1078
  * @example Basic usage
574
1079
  * ```typescript
575
- * const plugin = new GithubPR(context, {
576
- * releasePR: true,
577
- * autoGenerate: true
1080
+ * const label = new ReleaseLabel({
1081
+ * changePackagesLabel: 'changed:${name}',
1082
+ * packagesDirectories: ['packages/a', 'packages/b']
578
1083
  * });
579
1084
  *
580
- * await plugin.exec();
1085
+ * // Find changed packages
1086
+ * const changed = label.pick(['packages/a/src/index.ts']);
1087
+ *
1088
+ * // Generate labels
1089
+ * const labels = label.toChangeLabels(changed);
581
1090
  * ```
582
1091
  *
583
- * @example Release publishing
1092
+ * @example Custom comparison
584
1093
  * ```typescript
585
- * const plugin = new GithubPR(context, {
586
- * releasePR: false,
587
- * makeLatest: true,
588
- * preRelease: false
1094
+ * const label = new ReleaseLabel({
1095
+ * changePackagesLabel: 'changed:${name}',
1096
+ * packagesDirectories: ['packages/a'],
1097
+ * compare: (file, pkg) => file.includes(pkg)
589
1098
  * });
590
1099
  *
591
- * await plugin.exec();
1100
+ * const changed = label.pick(['src/packages/a/index.ts']);
592
1101
  * ```
593
1102
  */
594
-
595
- interface GithubPRProps extends ReleaseParamsConfig, GitBaseProps {
1103
+ declare class ReleaseLabel {
1104
+ private readonly options;
1105
+ /**
1106
+ * Creates a new ReleaseLabel instance
1107
+ *
1108
+ * @param options - Configuration options for label management
1109
+ *
1110
+ * @example
1111
+ * ```typescript
1112
+ * const label = new ReleaseLabel({
1113
+ * // Label template with ${name} placeholder
1114
+ * changePackagesLabel: 'changed:${name}',
1115
+ *
1116
+ * // Package directories to monitor
1117
+ * packagesDirectories: ['packages/a', 'packages/b'],
1118
+ *
1119
+ * // Optional custom comparison logic
1120
+ * compare: (file, pkg) => file.includes(pkg)
1121
+ * });
1122
+ * ```
1123
+ */
1124
+ constructor(options: ReleaseLabelOptions);
1125
+ /**
1126
+ * Compares a changed file path against a package path
1127
+ *
1128
+ * Uses custom comparison function if provided, otherwise
1129
+ * checks if the file path starts with the package path.
1130
+ *
1131
+ * @param changedFilePath - Path of the changed file
1132
+ * @param packagePath - Path of the package to check against
1133
+ * @returns True if the file belongs to the package
1134
+ *
1135
+ * @example
1136
+ * ```typescript
1137
+ * // Default comparison
1138
+ * label.compare('packages/a/src/index.ts', 'packages/a');
1139
+ * // true
1140
+ *
1141
+ * // Custom comparison
1142
+ * const label = new ReleaseLabel({
1143
+ * ...options,
1144
+ * compare: (file, pkg) => file.includes(pkg)
1145
+ * });
1146
+ * label.compare('src/packages/a/index.ts', 'packages/a');
1147
+ * // true
1148
+ * ```
1149
+ */
1150
+ compare(changedFilePath: string, packagePath: string): boolean;
1151
+ /**
1152
+ * Generates a change label for a single package
1153
+ *
1154
+ * Replaces ${name} placeholder in the label template with
1155
+ * the package path.
1156
+ *
1157
+ * @param packagePath - Path of the package
1158
+ * @param label - Optional custom label template
1159
+ * @returns Formatted change label
1160
+ *
1161
+ * @example
1162
+ * ```typescript
1163
+ * // Default label template
1164
+ * label.toChangeLabel('packages/a');
1165
+ * // 'changed:packages/a'
1166
+ *
1167
+ * // Custom label template
1168
+ * label.toChangeLabel('packages/a', 'modified:${name}');
1169
+ * // 'modified:packages/a'
1170
+ * ```
1171
+ */
1172
+ toChangeLabel(packagePath: string, label?: string): string;
1173
+ /**
1174
+ * Generates change labels for multiple packages
1175
+ *
1176
+ * Maps each package path to a formatted change label.
1177
+ *
1178
+ * @param packages - Array of package paths
1179
+ * @param label - Optional custom label template
1180
+ * @returns Array of formatted change labels
1181
+ *
1182
+ * @example
1183
+ * ```typescript
1184
+ * // Default label template
1185
+ * label.toChangeLabels(['packages/a', 'packages/b']);
1186
+ * // ['changed:packages/a', 'changed:packages/b']
1187
+ *
1188
+ * // Custom label template
1189
+ * label.toChangeLabels(
1190
+ * ['packages/a', 'packages/b'],
1191
+ * 'modified:${name}'
1192
+ * );
1193
+ * // ['modified:packages/a', 'modified:packages/b']
1194
+ * ```
1195
+ */
1196
+ toChangeLabels(packages: string[], label?: string): string[];
596
1197
  /**
597
- * Whether to dry run the creation of the pull request
1198
+ * Identifies packages affected by changed files
1199
+ *
1200
+ * Checks each changed file against package paths to determine
1201
+ * which packages have been modified.
1202
+ *
1203
+ * @param changedFiles - Array or Set of changed file paths
1204
+ * @param packages - Optional array of package paths to check
1205
+ * @returns Array of affected package paths
1206
+ *
1207
+ * @example
1208
+ * ```typescript
1209
+ * // Check against default packages
1210
+ * label.pick(['packages/a/src/index.ts']);
1211
+ * // ['packages/a']
598
1212
  *
599
- * - create pr
600
- * - changeset publish
1213
+ * // Check specific packages
1214
+ * label.pick(
1215
+ * ['packages/a/index.ts', 'packages/b/test.ts'],
1216
+ * ['packages/a', 'packages/c']
1217
+ * );
1218
+ * // ['packages/a']
601
1219
  *
602
- * @default `false`
1220
+ * // Using Set of files
1221
+ * label.pick(new Set(['packages/a/index.ts']));
1222
+ * // ['packages/a']
1223
+ * ```
603
1224
  */
604
- dryRunCreatePR?: boolean;
1225
+ pick(changedFiles: Array<string> | Set<string>, packages?: string[]): string[];
1226
+ }
1227
+
1228
+ interface WorkspacesProps extends ScriptPluginProps {
605
1229
  /**
606
- * Whether to skip the release
1230
+ * Whether to skip checking the package.json file
607
1231
  *
608
1232
  * @default `false`
609
1233
  */
610
- skip?: boolean;
1234
+ skipCheckPackage?: boolean;
611
1235
  /**
612
- * Whether to publish a PR
613
- *
614
- * @default `false`
1236
+ * The workspaces to publish
1237
+ * @private
615
1238
  */
616
- releasePR?: boolean;
1239
+ workspaces?: WorkspaceInterface[];
617
1240
  /**
618
- * The commit message of the release
619
- *
620
- * support WorkspaceValue
1241
+ * The change labels
621
1242
  *
622
- * @default 'chore(tag): {{name}} v${version}'
1243
+ * from `changePackagesLabel`
623
1244
  */
624
- commitMessage?: string;
1245
+ changeLabels?: string[];
625
1246
  /**
626
- * The commit args of the release
627
- *
628
- * @default []
1247
+ * The changed paths
1248
+ * @private
629
1249
  */
630
- commitArgs?: string[];
1250
+ changedPaths?: string[];
631
1251
  /**
632
- * The release name of the release
633
- *
634
- * @default 'Release ${name} v${version}'
1252
+ * The packages
1253
+ * @private
635
1254
  */
636
- releaseName?: string;
1255
+ packages?: string[];
637
1256
  /**
638
- * Whether to create a draft release
639
- *
640
- * @default false
1257
+ * All project packages mapping
1258
+ * @private
641
1259
  */
642
- draft?: boolean;
1260
+ projectWorkspaces?: WorkspaceInterface[];
643
1261
  /**
644
- * Whether to create a pre-release
1262
+ * Template for generating release tag names after version bump
645
1263
  *
646
- * @default false
1264
+ * Template variables support {@link WorkspaceInterface} properties.
1265
+ *
1266
+ * @default `'${name}@${version}'`
647
1267
  */
648
- preRelease?: boolean;
1268
+ tagTemplate?: string;
649
1269
  /**
650
- * Whether to auto-generate the release notes
1270
+ * Glob-style pattern for matching historical release tags
651
1271
  *
652
- * @default false
1272
+ * @default `'${name}@*'`
653
1273
  */
654
- autoGenerate?: boolean;
1274
+ tagMatch?: string;
655
1275
  /**
656
- * Whether to make the latest release
1276
+ * Include internal dependents in the release workspace list.
1277
+ *
1278
+ * When enabled (default), packages that depend on a changed source are appended
1279
+ * with `dependencyRelease: true`. This list is used for:
1280
+ *
1281
+ * - changelog / version logging when `changesetVersion.ignoreNonUpdatedPackages`
1282
+ * is `false`
1283
+ * - `git restore` targeting when `changesetVersion.ignoreNonUpdatedPackages`
1284
+ * is `true`
657
1285
  *
658
1286
  * @default true
659
1287
  */
660
- makeLatest?: boolean | 'true' | 'false' | 'legacy';
1288
+ includeDependencyReleases?: boolean;
661
1289
  /**
662
- * The release notes of the release
1290
+ * Directories containing packages for monorepo releases
1291
+ *
1292
+ * Core concept:
1293
+ * Specifies the directories that contain packages for
1294
+ * monorepo release management, enabling selective
1295
+ * package discovery and release coordination.
1296
+ *
1297
+ * Directory patterns:
1298
+ * - Supports glob patterns for flexible matching
1299
+ * - Enables selective package inclusion
1300
+ * - Supports nested directory structures
1301
+ * - Facilitates monorepo organization
1302
+ * - Enables workspace-specific configurations
1303
+ *
1304
+ * Use cases:
1305
+ * - Monorepo package discovery
1306
+ * - Selective package releases
1307
+ * - Workspace-specific configurations
1308
+ * - Multi-package coordination
1309
+ * - Dependency-aware releases
1310
+ *
1311
+ * @optional
1312
+ * @default `[]`
1313
+ * @example Basic package directories
1314
+ * ```typescript
1315
+ * const config: FeReleaseConfig = {
1316
+ * packagesDirectories: ['packages/*']
1317
+ * };
1318
+ * ```
663
1319
  *
664
- * @default undefined
1320
+ * @example Multiple package directories
1321
+ * ```typescript
1322
+ * const config: FeReleaseConfig = {
1323
+ * packagesDirectories: ['packages/*', 'apps/*', 'libs/*']
1324
+ * };
1325
+ * ```
665
1326
  */
666
- releaseNotes?: string;
1327
+ packagesDirectories?: string[];
667
1328
  /**
668
- * The discussion category name of the release
1329
+ * Git ref used as the left side of `git diff <compareRef>...HEAD`
1330
+ * when detecting changed packages.
1331
+ *
1332
+ * Defaults to `origin/<sourceBranch>`, then falls back to the merged PR base
1333
+ * SHA from `GITHUB_EVENT_PATH` when running after a GitHub Actions PR merge
1334
+ * into the same branch (where `origin/<sourceBranch>...HEAD` is empty).
669
1335
  *
670
- * @default undefined
1336
+ * @optional
1337
+ * @example `'abc1234'` or `'origin/master'`
671
1338
  */
672
- discussionCategoryName?: string;
1339
+ compareRef?: string;
673
1340
  /**
674
- * Whether to push the changed labels to the release PR
1341
+ * Template for package change labels in monorepos
675
1342
  *
676
- * @default false
1343
+ * Core concept:
1344
+ * Defines the naming pattern for labels that identify
1345
+ * which packages have changed in monorepo releases,
1346
+ * enabling targeted review and deployment.
1347
+ *
1348
+ * Label usage:
1349
+ * - Applied to PRs when specific packages change
1350
+ * - Enables package-specific review processes
1351
+ * - Supports selective deployment strategies
1352
+ * - Improves monorepo change tracking
1353
+ * - Facilitates team collaboration and review
1354
+ *
1355
+ * Template variables:
1356
+ * - `${name}`: Package name for label identification
1357
+ *
1358
+ * @optional
1359
+ * @default `'changes:${name}'`
1360
+ * @example Basic change label
1361
+ * ```typescript
1362
+ * const config: FeReleaseConfig = {
1363
+ * changePackagesLabel: 'changes:${name}'
1364
+ * };
1365
+ * ```
1366
+ *
1367
+ * @example Custom change label
1368
+ * ```typescript
1369
+ * const config: FeReleaseConfig = {
1370
+ * changePackagesLabel: 'package:${name}'
1371
+ * };
1372
+ * ```
677
1373
  */
678
- pushChangeLabels?: boolean;
1374
+ changePackagesLabel?: string;
679
1375
  }
680
1376
 
1377
+ /**
1378
+ * @module FeReleaseDefaults
1379
+ * @description Internal constants for fe-release
1380
+ *
1381
+ * User-facing default configuration lives in `release.json` and is
1382
+ * injected into {@link ReleaseContext} at construction time.
1383
+ */
1384
+ /**
1385
+ * Default name for the release task context (fe-config.json key)
1386
+ */
1387
+ declare const defaultReleaaseName = "release";
1388
+ /**
1389
+ * Path to package manifest file
1390
+ */
1391
+ declare const MANIFEST_PATH = "package.json";
1392
+ /**
1393
+ * Template opening delimiter
1394
+ */
1395
+ declare const TEMPLATE_OPEN = "{{";
1396
+ declare const releaseJson: {
1397
+ readonly sourceBranch: "master";
1398
+ readonly releaseEnv: "development";
1399
+ readonly github: {
1400
+ readonly mode: "createPR";
1401
+ readonly mergeType: "squash";
1402
+ readonly autoMergeReleasePR: false;
1403
+ readonly pushChangeLabels: false;
1404
+ readonly skipCreateReleasePR: false;
1405
+ readonly PRTitle: "Release ${env} ${pkgName} ${tagName}";
1406
+ readonly PRBody: "## Changelog\n\n${changelog}";
1407
+ readonly batchPRBody: "\n## ${name} ${newVersion}\n${changelog}\n";
1408
+ readonly branchName: "release/${repoName}-${releaseId}";
1409
+ readonly releaseTagName: "release-tag-${count}-patch-${releaseId}";
1410
+ readonly commitMessage: "chore(release): ${spaces}";
1411
+ readonly label: {
1412
+ readonly name: "CI-Release";
1413
+ readonly color: "1A7F37";
1414
+ readonly description: "Release PR";
1415
+ };
1416
+ };
1417
+ readonly changesetVersion: {
1418
+ readonly mode: "version";
1419
+ readonly increment: "patch";
1420
+ readonly changesetRoot: ".changeset";
1421
+ readonly ignoreNonUpdatedPackages: false;
1422
+ readonly dependencyReleaseTemplate: "- Update dependency **${name}** from `${oldVersion}` to `${newVersion}`";
1423
+ readonly formatTemplate: "\n- ${scopeHeader} ${commitlint.message} ${commitLink} ${prLink}";
1424
+ readonly types: readonly [{
1425
+ readonly type: "feat";
1426
+ readonly section: "#### ✨ Features";
1427
+ readonly hidden: false;
1428
+ }, {
1429
+ readonly type: "fix";
1430
+ readonly section: "#### 🐞 Bug Fixes";
1431
+ readonly hidden: false;
1432
+ }, {
1433
+ readonly type: "chore";
1434
+ readonly section: "#### 🔧 Chores";
1435
+ readonly hidden: true;
1436
+ }, {
1437
+ readonly type: "docs";
1438
+ readonly section: "#### 📝 Documentation";
1439
+ readonly hidden: false;
1440
+ }, {
1441
+ readonly type: "refactor";
1442
+ readonly section: "#### ♻️ Refactors";
1443
+ readonly hidden: false;
1444
+ }, {
1445
+ readonly type: "perf";
1446
+ readonly section: "#### 🚀 Performance";
1447
+ readonly hidden: false;
1448
+ }, {
1449
+ readonly type: "test";
1450
+ readonly section: "#### 🚨 Tests";
1451
+ readonly hidden: true;
1452
+ }, {
1453
+ readonly type: "style";
1454
+ readonly section: "#### 🎨 Styles";
1455
+ readonly hidden: true;
1456
+ }, {
1457
+ readonly type: "ci";
1458
+ readonly section: "#### 🔄 CI";
1459
+ readonly hidden: true;
1460
+ }, {
1461
+ readonly type: "build";
1462
+ readonly section: "#### 🚧 Build";
1463
+ readonly hidden: false;
1464
+ }, {
1465
+ readonly type: "revert";
1466
+ readonly section: "#### ⏪ Reverts";
1467
+ readonly hidden: true;
1468
+ }, {
1469
+ readonly type: "release";
1470
+ readonly section: "#### 🔖 Releases";
1471
+ readonly hidden: true;
1472
+ }];
1473
+ };
1474
+ readonly workspaces: {
1475
+ readonly tagTemplate: "${name}@${version}";
1476
+ readonly tagMatch: "${name}@*";
1477
+ readonly includeDependencyReleases: true;
1478
+ };
1479
+ };
1480
+
681
1481
  /**
682
1482
  * @module FeReleaseTypes
683
1483
  * @description Type definitions for the fe-release framework
@@ -761,7 +1561,7 @@ type ReleaseReturnValue = {
761
1561
  * ```
762
1562
  */
763
1563
  type DeepPartial<T> = {
764
- [P in keyof T]?: DeepPartial<T[P]>;
1564
+ [P in keyof T]?: string extends keyof T[P] ? T[P] : T[P] extends object ? DeepPartial<T[P]> : T[P];
765
1565
  };
766
1566
  /**
767
1567
  * Configuration interface for release process
@@ -784,8 +1584,16 @@ type DeepPartial<T> = {
784
1584
  * ```
785
1585
  */
786
1586
  interface ReleaseConfig extends ScriptSharedInterface {
787
- githubPR?: GithubPRProps;
1587
+ changesetVersion?: ChangesetVersionProps;
1588
+ github?: GithubProps;
788
1589
  workspaces?: WorkspacesProps;
1590
+ /** Repository name without owner */
1591
+ repoName?: string;
1592
+ /** Repository owner / org / namespace */
1593
+ authorName?: string;
1594
+ releaseEnv?: string;
1595
+ currentBranch?: string;
1596
+ releaseId?: string;
789
1597
  }
790
1598
  /**
791
1599
  * Options interface for release context
@@ -858,27 +1666,190 @@ type PackageJson = Record<string, unknown>;
858
1666
  * adds template-specific properties. Includes deprecated fields
859
1667
  * with migration guidance.
860
1668
  *
861
- * @example
1669
+ * @example
1670
+ * ```typescript
1671
+ * const context: TemplateContext = {
1672
+ * publishPath: './dist',
1673
+ * env: 'production', // Deprecated
1674
+ * branch: 'main', // Deprecated
1675
+ * // ... other properties from ReleaseContextOptions
1676
+ * };
1677
+ * ```
1678
+ */
1679
+ interface TemplateContext extends ReleaseContextOptions$1, WorkspaceInterface {
1680
+ publishPath: string;
1681
+ /**
1682
+ * @deprecated use `releaseEnv` from `shared`
1683
+ */
1684
+ env: string;
1685
+ /**
1686
+ * @deprecated use `sourceBranch` from `shared`
1687
+ */
1688
+ branch: string;
1689
+ }
1690
+ interface ReleaseGlobalConfig {
1691
+ /**
1692
+ * The github PR of the project
1693
+ * @private
1694
+ */
1695
+ github?: GithubProps;
1696
+ /**
1697
+ * Changeset version/publish plugin options
1698
+ * @private
1699
+ */
1700
+ changesetVersion?: ChangesetVersionProps;
1701
+ /**
1702
+ * The workspaces of the project
1703
+ * @private
1704
+ */
1705
+ workspaces?: WorkspacesProps;
1706
+ }
1707
+ declare module '@qlover/scripts-context' {
1708
+ interface FeConfig {
1709
+ [defaultReleaaseName]?: ReleaseGlobalConfig;
1710
+ }
1711
+ }
1712
+
1713
+ /**
1714
+ * @module PluginTuple
1715
+ * @description Type-safe plugin tuple creation and handling
1716
+ *
1717
+ * This module provides utilities for creating and handling tuples that
1718
+ * represent plugin configurations. It ensures type safety when working
1719
+ * with plugin constructors and their parameters.
1720
+ *
1721
+ * Core Features:
1722
+ * - Type-safe plugin class handling
1723
+ * - Constructor parameter inference
1724
+ * - Plugin tuple creation
1725
+ *
1726
+ * @example Basic usage
1727
+ * ```typescript
1728
+ * class MyPlugin extends ScriptPlugin {
1729
+ * constructor(context: ScriptContext, config: { option: string }) {
1730
+ * super(context);
1731
+ * }
1732
+ * }
1733
+ *
1734
+ * const pluginTuple = tuple(MyPlugin, { option: 'value' });
1735
+ * // [MyPlugin, { option: 'value' }]
1736
+ * ```
1737
+ *
1738
+ * @example Plugin name string
1739
+ * ```typescript
1740
+ * const pluginTuple = tuple('MyPlugin', { option: 'value' });
1741
+ * // ['MyPlugin', { option: 'value' }]
1742
+ * ```
1743
+ */
1744
+
1745
+ /**
1746
+ * Plugin class constructor type
1747
+ *
1748
+ * Represents a constructor for a class that extends ScriptPlugin.
1749
+ * Supports generic constructor arguments.
1750
+ *
1751
+ * @template T - Array type for constructor arguments
1752
+ *
1753
+ * @example
1754
+ * ```typescript
1755
+ * class MyPlugin extends ScriptPlugin {
1756
+ * constructor(context: ScriptContext, config: { option: string }) {
1757
+ * super(context);
1758
+ * }
1759
+ * }
1760
+ *
1761
+ * const PluginCtor: PluginClass = MyPlugin;
1762
+ * ```
1763
+ */
1764
+ type PluginClass<T extends unknown[] = any[]> = new (...args: T) => ScriptPlugin<ScriptContext<any>, ScriptPluginProps>;
1765
+ /**
1766
+ * Plugin constructor parameters type
1767
+ *
1768
+ * Extracts the constructor parameter types for a plugin class,
1769
+ * excluding the first parameter (context). Uses TypeScript's
1770
+ * conditional types and inference to extract parameter types.
1771
+ *
1772
+ * @template T - Plugin class type
1773
+ *
1774
+ * @example
1775
+ * ```typescript
1776
+ * class MyPlugin extends ScriptPlugin {
1777
+ * constructor(
1778
+ * context: ScriptContext,
1779
+ * config: { option: string },
1780
+ * extra: number
1781
+ * ) {
1782
+ * super(context);
1783
+ * }
1784
+ * }
1785
+ *
1786
+ * // Type: [{ option: string }, number]
1787
+ * type Params = PluginConstructorParams<typeof MyPlugin>;
1788
+ * ```
1789
+ */
1790
+ type PluginConstructorParams<T extends PluginClass> = T extends new (first: any, ...args: infer P) => unknown ? P : never;
1791
+ /**
1792
+ * Plugin configuration tuple type
1793
+ *
1794
+ * Represents a tuple containing a plugin class (or name) and its
1795
+ * constructor arguments. Used for plugin registration and loading.
1796
+ *
1797
+ * @template T - Plugin class type
1798
+ *
1799
+ * @example
1800
+ * ```typescript
1801
+ * class MyPlugin extends ScriptPlugin {
1802
+ * constructor(context: ScriptContext, config: { option: string }) {
1803
+ * super(context);
1804
+ * }
1805
+ * }
1806
+ *
1807
+ * // Type: [typeof MyPlugin, { option: string }]
1808
+ * type Tuple = PluginTuple<typeof MyPlugin>;
1809
+ *
1810
+ * // Type: [string, { option: string }]
1811
+ * type StringTuple = PluginTuple<'MyPlugin'>;
1812
+ * ```
1813
+ */
1814
+ type PluginTuple<T extends PluginClass> = [
1815
+ T | string,
1816
+ ...PluginConstructorParams<T>
1817
+ ];
1818
+ /**
1819
+ * Creates a type-safe plugin configuration tuple
1820
+ *
1821
+ * Helper function for creating tuples that represent plugin
1822
+ * configurations with proper type inference for constructor
1823
+ * arguments.
1824
+ *
1825
+ * @template T - Plugin class type
1826
+ * @param plugin - Plugin class or name
1827
+ * @param args - Plugin constructor arguments
1828
+ * @returns Plugin configuration tuple
1829
+ *
1830
+ * @example Class-based plugin
1831
+ * ```typescript
1832
+ * class MyPlugin extends ScriptPlugin {
1833
+ * constructor(
1834
+ * context: ScriptContext,
1835
+ * config: { option: string },
1836
+ * extra: number
1837
+ * ) {
1838
+ * super(context);
1839
+ * }
1840
+ * }
1841
+ *
1842
+ * const config = tuple(MyPlugin, { option: 'value' }, 42);
1843
+ * // [MyPlugin, { option: 'value' }, 42]
1844
+ * ```
1845
+ *
1846
+ * @example String-based plugin
862
1847
  * ```typescript
863
- * const context: TemplateContext = {
864
- * publishPath: './dist',
865
- * env: 'production', // Deprecated
866
- * branch: 'main', // Deprecated
867
- * // ... other properties from ReleaseContextOptions
868
- * };
1848
+ * const config = tuple('MyPlugin', { option: 'value' });
1849
+ * // ['MyPlugin', { option: 'value' }]
869
1850
  * ```
870
1851
  */
871
- interface TemplateContext extends ReleaseContextOptions$1, WorkspaceValue {
872
- publishPath: string;
873
- /**
874
- * @deprecated use `releaseEnv` from `shared`
875
- */
876
- env: string;
877
- /**
878
- * @deprecated use `sourceBranch` from `shared`
879
- */
880
- branch: string;
881
- }
1852
+ declare function tuple<T extends PluginClass>(plugin: T | string, ...args: PluginConstructorParams<T>): PluginTuple<T>;
882
1853
 
883
1854
  /**
884
1855
  * @module ReleaseContext
@@ -933,17 +1904,7 @@ interface TemplateContext extends ReleaseContextOptions$1, WorkspaceValue {
933
1904
 
934
1905
  interface ReleaseContextOptions extends ScriptContextInterface<ReleaseContextConfig> {
935
1906
  }
936
- interface ReleaseContextConfig extends FeReleaseConfig, ScriptSharedInterface {
937
- /**
938
- * The github PR of the project
939
- * @private
940
- */
941
- githubPR?: GithubPRProps;
942
- /**
943
- * The workspaces of the project
944
- * @private
945
- */
946
- workspaces?: WorkspacesProps;
1907
+ interface ReleaseContextConfig extends ReleaseGlobalConfig, ScriptSharedInterface {
947
1908
  /**
948
1909
  * The environment of the project
949
1910
  *
@@ -969,6 +1930,12 @@ interface ReleaseContextConfig extends FeReleaseConfig, ScriptSharedInterface {
969
1930
  * The current branch of the project
970
1931
  */
971
1932
  currentBranch?: string;
1933
+ /**
1934
+ * Unique identifier for the current release run
1935
+ *
1936
+ * @private
1937
+ */
1938
+ releaseId?: string;
972
1939
  }
973
1940
  /**
974
1941
  * Core context class for release operations
@@ -1004,6 +1971,8 @@ interface ReleaseContextConfig extends FeReleaseConfig, ScriptSharedInterface {
1004
1971
  * ```
1005
1972
  */
1006
1973
  declare class ReleaseContext extends ScriptContext<ReleaseContextConfig> {
1974
+ protected templateEngine: TemplateEngine;
1975
+ protected compileMap: Map<string, RenderFn>;
1007
1976
  /**
1008
1977
  * Creates a new ReleaseContext instance
1009
1978
  *
@@ -1014,7 +1983,7 @@ declare class ReleaseContext extends ScriptContext<ReleaseContextConfig> {
1014
1983
  * - releaseEnv: Uses environment variables or 'development'
1015
1984
  *
1016
1985
  * Environment Variable Priority:
1017
- * - sourceBranch: FE_RELEASE_BRANCH > FE_RELEASE_SOURCE_BRANCH > DEFAULT_SOURCE_BRANCH
1986
+ * - sourceBranch: FE_RELEASE_BRANCH > FE_RELEASE_SOURCE_BRANCH > release.json
1018
1987
  * - releaseEnv: FE_RELEASE_ENV > NODE_ENV > 'development'
1019
1988
  *
1020
1989
  * @param name - Unique identifier for this release context
@@ -1066,6 +2035,10 @@ declare class ReleaseContext extends ScriptContext<ReleaseContextConfig> {
1066
2035
  * ```
1067
2036
  */
1068
2037
  get releaseEnv(): string;
2038
+ /**
2039
+ * Gets the unique identifier for the current release run
2040
+ */
2041
+ get releaseId(): string;
1069
2042
  /**
1070
2043
  * Gets all configured workspaces
1071
2044
  *
@@ -1077,19 +2050,7 @@ declare class ReleaseContext extends ScriptContext<ReleaseContextConfig> {
1077
2050
  * // [{ name: 'pkg-a', version: '1.0.0', ... }]
1078
2051
  * ```
1079
2052
  */
1080
- get workspaces(): WorkspaceValue[] | undefined;
1081
- /**
1082
- * Gets the current active workspace
1083
- *
1084
- * @returns Current workspace configuration or undefined
1085
- *
1086
- * @example
1087
- * ```typescript
1088
- * const current = context.workspace;
1089
- * // { name: 'pkg-a', version: '1.0.0', ... }
1090
- * ```
1091
- */
1092
- get workspace(): WorkspaceValue | undefined;
2053
+ get workspaces(): WorkspaceInterface[] | undefined;
1093
2054
  /**
1094
2055
  * Sets the workspace configurations
1095
2056
  *
@@ -1102,38 +2063,18 @@ declare class ReleaseContext extends ScriptContext<ReleaseContextConfig> {
1102
2063
  * context.setWorkspaces([{
1103
2064
  * name: 'pkg-a',
1104
2065
  * version: '1.0.0',
1105
- * path: 'packages/a'
2066
+ * path: 'packages/a',
2067
+ * lastTag: 'pkg-aV1.0.0'
1106
2068
  * }]);
1107
2069
  * ```
1108
2070
  */
1109
- setWorkspaces(workspaces: WorkspaceValue[]): void;
2071
+ setWorkspaces(workspaces: WorkspaceInterface[]): void;
1110
2072
  /**
1111
- * Gets package.json data for the current workspace
1112
- *
1113
- * Provides type-safe access to package.json fields with optional
1114
- * path and default value support.
1115
- *
1116
- * @param key - Optional dot-notation path to specific field
1117
- * @param defaultValue - Default value if field not found
1118
- * @returns Package data of type T
1119
- * @throws Error if package.json not found
1120
- *
1121
- * @example Basic usage
1122
- * ```typescript
1123
- * // Get entire package.json
1124
- * const pkg = context.getPkg();
1125
- *
1126
- * // Get specific field
1127
- * const version = context.getPkg<string>('version');
1128
- *
1129
- * // Get nested field with default
1130
- * const script = context.getPkg<string>(
1131
- * 'scripts.build',
1132
- * 'echo "No build script"'
1133
- * );
1134
- * ```
2073
+ * @deprecated use `getParameters` or use `context.parameters`(type safe)
2074
+ * @param key
2075
+ * @param defaultValue
1135
2076
  */
1136
- getPkg<T>(key?: string, defaultValue?: T): T;
2077
+ getOptions<T = unknown>(key?: keyof ReleaseContextConfig | (keyof ReleaseContextConfig)[], defaultValue?: T): T;
1137
2078
  /**
1138
2079
  * Generates template context for string interpolation
1139
2080
  *
@@ -1149,590 +2090,303 @@ declare class ReleaseContext extends ScriptContext<ReleaseContextConfig> {
1149
2090
  * // {
1150
2091
  * // publishPath: 'packages/my-pkg',
1151
2092
  * // env: 'production', // deprecated
1152
- * // branch: 'main', // deprecated
1153
- * // releaseEnv: 'production', // use this instead
1154
- * // sourceBranch: 'main', // use this instead
1155
- * // ...other options
1156
- * // }
1157
- * ```
1158
- */
1159
- getTemplateContext(): TemplateContext;
1160
- /**
1161
- * Executes changeset CLI commands
1162
- *
1163
- * Automatically detects and uses appropriate package manager
1164
- * (pnpm or npx) to run changeset commands.
1165
- *
1166
- * @param name - Changeset command name
1167
- * @param args - Optional command arguments
1168
- * @returns Command output
1169
- *
1170
- * @example Version bump
1171
- * ```typescript
1172
- * // Bump version with snapshot
1173
- * await context.runChangesetsCli('version', ['--snapshot', 'alpha']);
1174
- *
1175
- * // Create new changeset
1176
- * await context.runChangesetsCli('add');
1177
- *
1178
- * // Status check
1179
- * await context.runChangesetsCli('status');
1180
- * ```
1181
- */
1182
- runChangesetsCli(name: string, args?: string[]): Promise<string>;
1183
- }
1184
-
1185
- /**
1186
- * @module ReleaseLabel
1187
- * @description Release label management and file change detection
1188
- *
1189
- * This module provides utilities for managing release labels and detecting
1190
- * which packages have changed based on file paths. It supports custom
1191
- * comparison logic and label formatting.
1192
- *
1193
- * Core Features:
1194
- * - File change detection
1195
- * - Package path matching
1196
- * - Label generation
1197
- * - Custom comparison logic
1198
- *
1199
- * @example Basic usage
1200
- * ```typescript
1201
- * const label = new ReleaseLabel({
1202
- * changePackagesLabel: 'changed:${name}',
1203
- * packagesDirectories: ['packages/a', 'packages/b']
1204
- * });
1205
- *
1206
- * // Find changed packages
1207
- * const changed = label.pick(['packages/a/src/index.ts']);
1208
- * // ['packages/a']
1209
- *
1210
- * // Generate labels
1211
- * const labels = label.toChangeLabels(changed);
1212
- * // ['changed:packages/a']
1213
- * ```
1214
- *
1215
- * @example Custom comparison
1216
- * ```typescript
1217
- * const label = new ReleaseLabel({
1218
- * changePackagesLabel: 'changed:${name}',
1219
- * packagesDirectories: ['packages/a'],
1220
- * compare: (file, pkg) => file.includes(pkg)
1221
- * });
1222
- *
1223
- * const changed = label.pick(['src/packages/a/index.ts']);
1224
- * // ['packages/a']
1225
- * ```
1226
- */
1227
- /**
1228
- * Function type for custom file path comparison
1229
- *
1230
- * Used to determine if a changed file belongs to a package.
1231
- * Default implementation checks if the file path starts with
1232
- * the package path.
1233
- *
1234
- * @param changedFilePath - Path of the changed file
1235
- * @param packagePath - Path of the package to check against
1236
- * @returns True if the file belongs to the package
1237
- */
1238
- type ReleaseLabelCompare = (changedFilePath: string, packagePath: string) => boolean;
1239
- interface ReleaseLabelOptions {
1240
- /**
1241
- * The change packages label
1242
- */
1243
- changePackagesLabel: string;
1244
- /**
1245
- * The packages directories
1246
- */
1247
- packagesDirectories: string[];
1248
- compare?: ReleaseLabelCompare;
1249
- }
1250
- /**
1251
- * Core class for managing release labels and change detection
1252
- *
1253
- * Provides utilities for detecting changed packages and generating
1254
- * appropriate labels. Supports custom comparison logic and label
1255
- * formatting.
1256
- *
1257
- * Features:
1258
- * - File change detection
1259
- * - Package path matching
1260
- * - Label generation
1261
- * - Custom comparison logic
1262
- *
1263
- * @example Basic usage
1264
- * ```typescript
1265
- * const label = new ReleaseLabel({
1266
- * changePackagesLabel: 'changed:${name}',
1267
- * packagesDirectories: ['packages/a', 'packages/b']
1268
- * });
1269
- *
1270
- * // Find changed packages
1271
- * const changed = label.pick(['packages/a/src/index.ts']);
1272
- *
1273
- * // Generate labels
1274
- * const labels = label.toChangeLabels(changed);
1275
- * ```
1276
- *
1277
- * @example Custom comparison
1278
- * ```typescript
1279
- * const label = new ReleaseLabel({
1280
- * changePackagesLabel: 'changed:${name}',
1281
- * packagesDirectories: ['packages/a'],
1282
- * compare: (file, pkg) => file.includes(pkg)
1283
- * });
1284
- *
1285
- * const changed = label.pick(['src/packages/a/index.ts']);
1286
- * ```
1287
- */
1288
- declare class ReleaseLabel {
1289
- private readonly options;
1290
- /**
1291
- * Creates a new ReleaseLabel instance
1292
- *
1293
- * @param options - Configuration options for label management
1294
- *
1295
- * @example
1296
- * ```typescript
1297
- * const label = new ReleaseLabel({
1298
- * // Label template with ${name} placeholder
1299
- * changePackagesLabel: 'changed:${name}',
1300
- *
1301
- * // Package directories to monitor
1302
- * packagesDirectories: ['packages/a', 'packages/b'],
1303
- *
1304
- * // Optional custom comparison logic
1305
- * compare: (file, pkg) => file.includes(pkg)
1306
- * });
1307
- * ```
1308
- */
1309
- constructor(options: ReleaseLabelOptions);
1310
- /**
1311
- * Compares a changed file path against a package path
1312
- *
1313
- * Uses custom comparison function if provided, otherwise
1314
- * checks if the file path starts with the package path.
1315
- *
1316
- * @param changedFilePath - Path of the changed file
1317
- * @param packagePath - Path of the package to check against
1318
- * @returns True if the file belongs to the package
1319
- *
1320
- * @example
1321
- * ```typescript
1322
- * // Default comparison
1323
- * label.compare('packages/a/src/index.ts', 'packages/a');
1324
- * // true
1325
- *
1326
- * // Custom comparison
1327
- * const label = new ReleaseLabel({
1328
- * ...options,
1329
- * compare: (file, pkg) => file.includes(pkg)
1330
- * });
1331
- * label.compare('src/packages/a/index.ts', 'packages/a');
1332
- * // true
1333
- * ```
1334
- */
1335
- compare(changedFilePath: string, packagePath: string): boolean;
1336
- /**
1337
- * Generates a change label for a single package
1338
- *
1339
- * Replaces ${name} placeholder in the label template with
1340
- * the package path.
1341
- *
1342
- * @param packagePath - Path of the package
1343
- * @param label - Optional custom label template
1344
- * @returns Formatted change label
1345
- *
1346
- * @example
1347
- * ```typescript
1348
- * // Default label template
1349
- * label.toChangeLabel('packages/a');
1350
- * // 'changed:packages/a'
1351
- *
1352
- * // Custom label template
1353
- * label.toChangeLabel('packages/a', 'modified:${name}');
1354
- * // 'modified:packages/a'
2093
+ * // branch: 'main', // deprecated
2094
+ * // releaseEnv: 'production', // use this instead
2095
+ * // sourceBranch: 'main', // use this instead
2096
+ * // ...other options
2097
+ * // }
1355
2098
  * ```
1356
2099
  */
1357
- toChangeLabel(packagePath: string, label?: string): string;
2100
+ getTemplateContext(): TemplateContext;
1358
2101
  /**
1359
- * Generates change labels for multiple packages
2102
+ * Executes changeset CLI commands
1360
2103
  *
1361
- * Maps each package path to a formatted change label.
2104
+ * Automatically detects and uses appropriate package manager
2105
+ * (pnpm or npx) to run changeset commands.
1362
2106
  *
1363
- * @param packages - Array of package paths
1364
- * @param label - Optional custom label template
1365
- * @returns Array of formatted change labels
2107
+ * @param name - Changeset command name
2108
+ * @param args - Optional command arguments
2109
+ * @returns Command output
1366
2110
  *
1367
- * @example
2111
+ * @example Version bump
1368
2112
  * ```typescript
1369
- * // Default label template
1370
- * label.toChangeLabels(['packages/a', 'packages/b']);
1371
- * // ['changed:packages/a', 'changed:packages/b']
2113
+ * // Bump version with snapshot
2114
+ * await context.runChangesetsCli('version', ['--snapshot', 'alpha']);
1372
2115
  *
1373
- * // Custom label template
1374
- * label.toChangeLabels(
1375
- * ['packages/a', 'packages/b'],
1376
- * 'modified:${name}'
1377
- * );
1378
- * // ['modified:packages/a', 'modified:packages/b']
2116
+ * // Create new changeset
2117
+ * await context.runChangesetsCli('add');
2118
+ *
2119
+ * // Status check
2120
+ * await context.runChangesetsCli('status');
1379
2121
  * ```
1380
2122
  */
1381
- toChangeLabels(packages: string[], label?: string): string[];
2123
+ runChangesetsCli(name: string, args?: string[]): Promise<string>;
1382
2124
  /**
1383
- * Identifies packages affected by changed files
2125
+ * Gets the workspaces of the project
1384
2126
  *
1385
- * Checks each changed file against package paths to determine
1386
- * which packages have been modified.
2127
+ * If no workspaces are found, throws an error.
1387
2128
  *
1388
- * @param changedFiles - Array or Set of changed file paths
1389
- * @param packages - Optional array of package paths to check
1390
- * @returns Array of affected package paths
2129
+ * @throws Error if no workspaces are found
2130
+ * @returns The workspaces of the project
1391
2131
  *
1392
2132
  * @example
1393
2133
  * ```typescript
1394
- * // Check against default packages
1395
- * label.pick(['packages/a/src/index.ts']);
1396
- * // ['packages/a']
2134
+ * const workspaces = context.requireWorkspaces();
2135
+ * // [{ name: 'pkg-a', version: '1.0.0', ... }]
2136
+ * ```
2137
+ */
2138
+ requireWorkspaces(): WorkspaceInterface[];
2139
+ /**
2140
+ * Format a template with the given data
1397
2141
  *
1398
- * // Check specific packages
1399
- * label.pick(
1400
- * ['packages/a/index.ts', 'packages/b/test.ts'],
1401
- * ['packages/a', 'packages/c']
1402
- * );
1403
- * // ['packages/a']
2142
+ * The template will be compiled only once and cached for future use.
1404
2143
  *
1405
- * // Using Set of files
1406
- * label.pick(new Set(['packages/a/index.ts']));
1407
- * // ['packages/a']
1408
- * ```
2144
+ * @param template - The template to format
2145
+ * @param data - The data to format the template with
2146
+ * @returns The formatted template
1409
2147
  */
1410
- pick(changedFiles: Array<string> | Set<string>, packages?: string[]): string[];
2148
+ format(template: string, data: Record<string, any>): string;
2149
+ getTemplateEngine(): TemplateEngine;
1411
2150
  }
1412
2151
 
1413
2152
  /**
1414
- * @module ChangeLog
1415
- * @description Core interfaces for changelog generation
2153
+ * @module ReleaseTask
2154
+ * @description Task orchestration for release process
1416
2155
  *
1417
- * This module provides the core interfaces and types for generating
1418
- * changelogs from Git commit history. It includes types for commit
1419
- * parsing, formatting, and changelog generation.
2156
+ * This module provides the core task orchestration for the release process,
2157
+ * managing plugin loading, execution order, and context handling. It serves
2158
+ * as the main entry point for executing release operations.
1420
2159
  *
1421
- * Core Components:
1422
- * - Commit data structures
1423
- * - Changelog formatting
1424
- * - Git log options
1425
- * - Changelog generation
2160
+ * Core Features:
2161
+ * - Plugin management and execution
2162
+ * - Release context initialization
2163
+ * - Task execution control
2164
+ * - Environment-based control
2165
+ *
2166
+ * Default Plugins:
2167
+ * - Workspaces: Monorepo workspace management
2168
+ * - Changelog: Version and changelog management
2169
+ * - GithubPR: Pull request creation and management
1426
2170
  *
1427
2171
  * @example Basic usage
1428
2172
  * ```typescript
1429
- * class MyChangeLog implements ChangeLogInterface {
1430
- * async getCommits(options?: GitChangelogOptions): Promise<CommitValue[]> {
1431
- * // Implementation
2173
+ * // Initialize and execute
2174
+ * const task = new ReleaseTask({
2175
+ * rootPath: '/path/to/project',
2176
+ * sourceBranch: 'main'
2177
+ * });
2178
+ *
2179
+ * await task.exec();
2180
+ * ```
2181
+ *
2182
+ * @example Custom plugins
2183
+ * ```typescript
2184
+ * import { tuple } from '@qlover/fe-release';
2185
+ *
2186
+ * // Add custom plugin
2187
+ * class CustomPlugin extends ScriptPlugin {
2188
+ * async onExec() {
2189
+ * // Custom release logic
1432
2190
  * }
1433
2191
  * }
1434
2192
  *
1435
- * class MyFormatter implements ChangelogFormatter {
1436
- * format(commits: CommitValue[]): string[] {
1437
- * // Implementation
1438
- * }
2193
+ * const task = new ReleaseTask({}, new LifecycleExecutor<ReleaseContext>(), [
2194
+ * tuple(CustomPlugin, { option: 'value' })
2195
+ * ]);
2196
+ *
2197
+ * await task.exec();
2198
+ * ```
2199
+ *
2200
+ * @example Environment control
2201
+ * ```typescript
2202
+ * // Skip release
2203
+ * process.env.FE_RELEASE = 'false';
2204
+ *
2205
+ * const task = new ReleaseTask();
2206
+ * try {
2207
+ * await task.exec();
2208
+ * } catch (e) {
2209
+ * // Handle "Skip Release" error
1439
2210
  * }
1440
2211
  * ```
1441
2212
  */
1442
2213
 
1443
2214
  /**
1444
- * Base commit type mapping Git commit fields
2215
+ * Core task class for managing release operations
1445
2216
  *
1446
- * Maps all available Git commit fields to optional string values.
1447
- * Uses the CommitField type from gitlog package to ensure type safety.
2217
+ * Handles plugin orchestration, task execution, and context management
2218
+ * for the release process. Supports both built-in and custom plugins.
1448
2219
  *
1449
- * Available fields include:
1450
- * - hash: Full commit hash
1451
- * - abbrevHash: Abbreviated commit hash
1452
- * - subject: Commit message subject
1453
- * - authorName: Author's name
1454
- * - authorDate: Author date
1455
- * - And many more from gitlog.CommitField
2220
+ * Features:
2221
+ * - Plugin lifecycle management
2222
+ * - Task execution control
2223
+ * - Context initialization and access
2224
+ * - Environment-based control
1456
2225
  *
1457
- * @example
2226
+ * @example Basic initialization
1458
2227
  * ```typescript
1459
- * const commit: BaseCommit = {
1460
- * hash: 'abc123def456',
1461
- * abbrevHash: 'abc123',
1462
- * subject: 'feat: new feature',
1463
- * authorName: 'John Doe',
1464
- * authorDate: '2023-01-01'
1465
- * };
2228
+ * const task = new ReleaseTask({
2229
+ * rootPath: '/path/to/project'
2230
+ * });
1466
2231
  * ```
1467
- */
1468
- type BaseCommit = {
1469
- [key in CommitField]: string | undefined;
1470
- };
1471
- /**
1472
- * Configuration options for changelog generation
1473
- *
1474
- * Provides comprehensive options for controlling how changelogs
1475
- * are generated from Git history, including commit range selection,
1476
- * formatting, and filtering.
1477
2232
  *
1478
- * @example Basic usage
2233
+ * @example Custom executor
1479
2234
  * ```typescript
1480
- * const options: GitChangelogOptions = {
1481
- * from: 'v1.0.0',
1482
- * to: 'v2.0.0',
1483
- * directory: 'packages/my-pkg',
1484
- * noMerges: true
1485
- * };
2235
+ * const executor = new LifecycleExecutor<ReleaseContext>({
2236
+ * onError: (err) => console.error('Release failed:', err)
2237
+ * });
2238
+ *
2239
+ * const task = new ReleaseTask({}, executor);
1486
2240
  * ```
1487
2241
  *
1488
- * @example Custom formatting
2242
+ * @example Custom plugins
1489
2243
  * ```typescript
1490
- * const options: GitChangelogOptions = {
1491
- * types: [
1492
- * { type: 'feat', section: '### Features' },
1493
- * { type: 'fix', section: '### Bug Fixes' }
1494
- * ],
1495
- * formatTemplate: '* ${commitlint.message} ${prLink}',
1496
- * commitBody: true
1497
- * };
2244
+ * const task = new ReleaseTask(
2245
+ * {}, // options
2246
+ * new LifecycleExecutor<ReleaseContext>(),
2247
+ * [
2248
+ * tuple(CustomPlugin, { config: 'value' }),
2249
+ * ...innerTuples // include default plugins
2250
+ * ]
2251
+ * );
1498
2252
  * ```
1499
2253
  */
1500
- interface GitChangelogOptions {
2254
+ declare class ReleaseTask {
2255
+ protected executor: LifecycleExecutor<ReleaseContext>;
2256
+ protected defaultTuples: PluginTuple<PluginClass>[];
1501
2257
  /**
1502
- * Starting tag or commit reference
1503
- *
1504
- * Defines the start point for collecting commits.
1505
- * Can be a tag name, commit hash, or branch name.
1506
- *
1507
- * @example
1508
- * ```typescript
1509
- * from: 'v1.0.0' // Start from v1.0.0 tag
1510
- * from: 'abc123' // Start from specific commit
1511
- * ```
2258
+ * Release context instance
2259
+ * @protected
1512
2260
  */
1513
- from?: string;
2261
+ protected context: ReleaseContext;
1514
2262
  /**
1515
- * Ending tag or commit reference
1516
- *
1517
- * Defines the end point for collecting commits.
1518
- * Can be a tag name, commit hash, or branch name.
2263
+ * Creates a new ReleaseTask instance
1519
2264
  *
1520
- * @example
1521
- * ```typescript
1522
- * to: 'v2.0.0' // End at v2.0.0 tag
1523
- * to: 'main' // End at main branch
1524
- * ```
1525
- */
1526
- to?: string;
1527
- /**
1528
- * Directory to collect commits from
2265
+ * Initializes the release context and sets up plugin configuration.
2266
+ * Supports custom executors and plugin configurations.
1529
2267
  *
1530
- * Limits commit collection to changes in specified directory.
1531
- * Useful for monorepo package-specific changelogs.
2268
+ * @param options - Release context configuration
2269
+ * @param executor - Custom async executor (optional)
2270
+ * @param defaultTuples - Plugin configuration tuples (optional)
1532
2271
  *
1533
2272
  * @example
1534
2273
  * ```typescript
1535
- * directory: 'packages/my-pkg' // Only changes in this directory
2274
+ * // Basic initialization
2275
+ * const task = new ReleaseTask({
2276
+ * rootPath: '/path/to/project',
2277
+ * sourceBranch: 'main'
2278
+ * });
2279
+ *
2280
+ * // With custom executor and plugins
2281
+ * const task = new ReleaseTask(
2282
+ * { rootPath: '/path/to/project' },
2283
+ * new LifecycleExecutor<ReleaseContext>(),
2284
+ * [tuple(CustomPlugin, { option: 'value' })]
2285
+ * );
1536
2286
  * ```
1537
2287
  */
1538
- directory?: string;
2288
+ constructor(options?: Partial<ReleaseContextOptions>, executor?: LifecycleExecutor<ReleaseContext>, defaultTuples?: PluginTuple<PluginClass>[]);
1539
2289
  /**
1540
- * Git commit fields to include
2290
+ * Gets the current release context
1541
2291
  *
1542
- * Specifies which Git commit fields to retrieve.
1543
- * @default ["abbrevHash", "hash", "subject", "authorName", "authorDate"]
2292
+ * @returns Release context instance
1544
2293
  *
1545
2294
  * @example
1546
2295
  * ```typescript
1547
- * fields: ['hash', 'subject', 'authorName']
2296
+ * const task = new ReleaseTask();
2297
+ * const context = task.getContext();
2298
+ *
2299
+ * console.log(context.releaseEnv);
2300
+ * console.log(context.sourceBranch);
1548
2301
  * ```
1549
2302
  */
1550
- fields?: CommitField[];
2303
+ getContext(): ReleaseContext;
1551
2304
  /**
1552
- * Whether to exclude merge commits
2305
+ * Loads and configures plugins for the release task
1553
2306
  *
1554
- * When true, merge commits are filtered out from the changelog.
1555
- * @default true
2307
+ * Combines default and external plugins, initializes them with
2308
+ * the current context, and configures special cases like the
2309
+ * Workspaces plugin.
1556
2310
  *
1557
- * @example
2311
+ * Plugin Loading Process:
2312
+ * 1. Merge default and external plugins
2313
+ * 2. Initialize plugins with context
2314
+ * 3. Configure special plugins
2315
+ * 4. Add plugins to executor
2316
+ *
2317
+ * @param externalTuples - Additional plugin configurations
2318
+ * @returns Array of initialized plugins
2319
+ *
2320
+ * @example Basic usage
1558
2321
  * ```typescript
1559
- * noMerges: true // Exclude merge commits
1560
- * noMerges: false // Include merge commits
2322
+ * const task = new ReleaseTask();
2323
+ * const plugins = await task.usePlugins();
1561
2324
  * ```
1562
- */
1563
- noMerges?: boolean;
1564
- /**
1565
- * Commit type configurations
1566
- *
1567
- * Defines how different commit types should be handled and
1568
- * formatted in the changelog.
1569
2325
  *
1570
- * @example
2326
+ * @example Custom plugins
1571
2327
  * ```typescript
1572
- * types: [
1573
- * { type: 'feat', section: '### Features' },
1574
- * { type: 'fix', section: '### Bug Fixes' },
1575
- * { type: 'chore', hidden: true } // Skip chore commits
1576
- * ]
2328
+ * const task = new ReleaseTask();
2329
+ * const plugins = await task.usePlugins([
2330
+ * tuple(CustomPlugin, { option: 'value' })
2331
+ * ]);
1577
2332
  * ```
1578
2333
  */
1579
- types?: {
1580
- type: string;
1581
- section?: string;
1582
- hidden?: boolean;
1583
- }[];
2334
+ usePlugins(externalTuples?: PluginTuple<PluginClass>[]): Promise<ScriptPlugin<ScriptContext<any>, ScriptPluginProps>[]>;
1584
2335
  /**
1585
- * Template for formatting commit entries
1586
- *
1587
- * Supports variables from CommitValue properties and adds:
1588
- * - ${scopeHeader}: Formatted scope
1589
- * - ${commitLink}: Commit hash link
1590
- * - ${prLink}: PR number link
2336
+ * Executes the release task
1591
2337
  *
1592
- * @default '\n- ${scopeHeader} ${commitlint.message} ${commitLink} ${prLink}'
2338
+ * Internal method that runs the task through the executor.
2339
+ * Preserves the context through the execution chain.
1593
2340
  *
1594
- * @example
1595
- * ```typescript
1596
- * formatTemplate: '* ${commitlint.message} (${commitLink})'
1597
- * ```
2341
+ * @returns Execution result
2342
+ * @internal
1598
2343
  */
1599
- formatTemplate?: string;
2344
+ run(): Promise<unknown>;
1600
2345
  /**
1601
- * Whether to include commit message body
2346
+ * Main entry point for executing the release task
1602
2347
  *
1603
- * When true, includes the full commit message body
1604
- * in the changelog entry.
2348
+ * Checks environment conditions, loads plugins, and executes
2349
+ * the release process. Supports additional plugin configuration
2350
+ * at execution time.
1605
2351
  *
1606
- * @since 2.3.0
1607
- * @default false
2352
+ * Environment Control:
2353
+ * - Checks FE_RELEASE environment variable
2354
+ * - Skips release if FE_RELEASE=false
1608
2355
  *
1609
- * @example
2356
+ * @param externalTuples - Additional plugin configurations
2357
+ * @returns Execution result
2358
+ * @throws Error if release is skipped via environment variable
2359
+ *
2360
+ * @example Basic execution
1610
2361
  * ```typescript
1611
- * commitBody: true // Include full commit message
2362
+ * const task = new ReleaseTask();
2363
+ * await task.exec();
1612
2364
  * ```
1613
- */
1614
- commitBody?: boolean;
1615
- }
1616
- /**
1617
- * Parsed conventional commit data
1618
- *
1619
- * Represents a commit message parsed according to the
1620
- * conventional commit specification.
1621
- *
1622
- * Format: type(scope): message
1623
- *
1624
- * @example
1625
- * ```typescript
1626
- * const commit: Commitlint = {
1627
- * type: 'feat',
1628
- * scope: 'api',
1629
- * message: 'add new endpoint',
1630
- * body: 'Adds support for new API endpoint\n\nBREAKING CHANGE: API format changed'
1631
- * };
1632
- * ```
1633
- */
1634
- interface Commitlint {
1635
- /** Commit type (e.g., 'feat', 'fix') */
1636
- type?: string;
1637
- /** Commit scope (e.g., 'api', 'core') */
1638
- scope?: string;
1639
- /** Main commit message */
1640
- message: string;
1641
- /**
1642
- * Commit message body with title removed
1643
- * @since 2.3.0
1644
- */
1645
- body?: string;
1646
- }
1647
- /**
1648
- * Complete commit information
1649
- *
1650
- * Combines Git commit data, parsed conventional commit info,
1651
- * and PR metadata into a single value object.
1652
- *
1653
- * @example
1654
- * ```typescript
1655
- * const commit: CommitValue = {
1656
- * base: {
1657
- * hash: 'abc123',
1658
- * subject: 'feat(api): new endpoint (#123)'
1659
- * },
1660
- * commitlint: {
1661
- * type: 'feat',
1662
- * scope: 'api',
1663
- * message: 'new endpoint'
1664
- * },
1665
- * commits: [],
1666
- * prNumber: '123'
1667
- * };
1668
- * ```
1669
- */
1670
- interface CommitValue {
1671
- /** Raw Git commit information */
1672
- base: BaseCommit;
1673
- /** Parsed conventional commit data */
1674
- commitlint: Commitlint;
1675
- /** Sub-commits (for merge commits) */
1676
- commits: CommitValue[];
1677
- /** Associated pull request number */
1678
- prNumber?: string;
1679
- }
1680
- /**
1681
- * Interface for changelog formatting
1682
- *
1683
- * Defines the contract for classes that format commit data
1684
- * into changelog entries.
1685
- *
1686
- * @example
1687
- * ```typescript
1688
- * class MarkdownFormatter implements ChangelogFormatter {
1689
- * format(commits: CommitValue[]): string[] {
1690
- * return commits.map(commit =>
1691
- * `- ${commit.commitlint.message} (#${commit.prNumber})`
1692
- * );
1693
- * }
1694
- * }
1695
- * ```
1696
- */
1697
- interface ChangelogFormatter {
1698
- /**
1699
- * Formats commits into changelog entries
1700
2365
  *
1701
- * @param commits - Array of commits to format
1702
- * @param options - Optional formatting options
1703
- * @returns Array of formatted changelog lines
1704
- */
1705
- format<Opt extends GitChangelogOptions>(commits: unknown[], options?: Opt): string[];
1706
- }
1707
- /**
1708
- * Interface for changelog generation
1709
- *
1710
- * Defines the contract for classes that generate changelogs
1711
- * from Git history.
1712
- *
1713
- * @example
1714
- * ```typescript
1715
- * class GitChangelog implements ChangeLogInterface {
1716
- * async getCommits(options?: GitChangelogOptions): Promise<CommitValue[]> {
1717
- * // Get commits from Git and parse them
1718
- * const commits = await gitlog(options);
1719
- * return commits.map(commit => ({
1720
- * base: commit,
1721
- * commitlint: parseCommit(commit.subject),
1722
- * commits: []
1723
- * }));
1724
- * }
1725
- * }
1726
- * ```
1727
- */
1728
- interface ChangeLogInterface {
1729
- /**
1730
- * Retrieves and parses Git commits
2366
+ * @example With additional plugins
2367
+ * ```typescript
2368
+ * const task = new ReleaseTask();
2369
+ * await task.exec([
2370
+ * tuple(CustomPlugin, { option: 'value' })
2371
+ * ]);
2372
+ * ```
1731
2373
  *
1732
- * @param options - Optional Git log options
1733
- * @returns Promise resolving to array of parsed commits
2374
+ * @example Environment control
2375
+ * ```typescript
2376
+ * // Skip release
2377
+ * process.env.FE_RELEASE = 'false';
2378
+ *
2379
+ * const task = new ReleaseTask();
2380
+ * try {
2381
+ * await task.exec();
2382
+ * } catch (e) {
2383
+ * if (e.message === 'Skip Release') {
2384
+ * console.log('Release skipped via environment variable');
2385
+ * }
2386
+ * }
2387
+ * ```
1734
2388
  */
1735
- getCommits(options?: GitChangelogOptions): Promise<CommitValue[]>;
2389
+ exec(externalTuples?: PluginTuple<PluginClass>[]): Promise<unknown>;
1736
2390
  }
1737
2391
 
1738
2392
  /**
@@ -2187,6 +2841,7 @@ declare class GitChangelogFormatter implements ChangelogFormatter {
2187
2841
  protected options: Options & {
2188
2842
  shell: ShellInterface;
2189
2843
  };
2844
+ protected templateEngine: TemplateEngine;
2190
2845
  /**
2191
2846
  * Creates a new GitChangelogFormatter instance
2192
2847
  *
@@ -2352,50 +3007,6 @@ declare class GitChangelogFormatter implements ChangelogFormatter {
2352
3007
  formatScope(scope: string): string;
2353
3008
  }
2354
3009
 
2355
- /**
2356
- * @module GithubChangelog
2357
- * @description GitHub-specific changelog generation
2358
- *
2359
- * This module extends the base changelog functionality with
2360
- * GitHub-specific features like PR linking, commit filtering
2361
- * by directory, and workspace-aware changelog generation.
2362
- *
2363
- * Core Features:
2364
- * - PR-aware commit gathering
2365
- * - Directory-based filtering
2366
- * - GitHub link generation
2367
- * - Workspace changelog transformation
2368
- * - Markdown formatting
2369
- *
2370
- * @example Basic usage
2371
- * ```typescript
2372
- * const changelog = new GithubChangelog({
2373
- * shell,
2374
- * logger,
2375
- * githubRootPath: 'https://github.com/org/repo'
2376
- * }, githubManager);
2377
- *
2378
- * const commits = await changelog.getFullCommit({
2379
- * from: 'v1.0.0',
2380
- * directory: 'packages/pkg-a'
2381
- * });
2382
- * ```
2383
- *
2384
- * @example Workspace transformation
2385
- * ```typescript
2386
- * const workspaces = await changelog.transformWorkspace(
2387
- * [{ name: 'pkg-a', path: 'packages/a' }],
2388
- * context
2389
- * );
2390
- * // Adds formatted changelog to each workspace
2391
- * ```
2392
- */
2393
-
2394
- interface GithubChangelogProps extends GitChangelogProps {
2395
- mergePRcommit?: boolean;
2396
- githubRootPath?: string;
2397
- }
2398
-
2399
3010
  /**
2400
3011
  * @module PluginLoader
2401
3012
  * @description Dynamic plugin loading and instantiation
@@ -2752,4 +3363,4 @@ declare function factory<T, Args extends unknown[]>(Constructor: ConstructorType
2752
3363
  */
2753
3364
  declare function reduceOptions(opts: OptionValues, commonKey?: string): OptionValues;
2754
3365
 
2755
- export { CHANGELOG_ALL_FIELDS, type ConstructorType, type DeepPartial, type ExecutorReleaseContext, GitChangelog, GitChangelogFormatter, type GitChangelogProps, type GithubChangelogProps, type Options, type PackageJson, type PluginClass, type PluginConstructorParams, type PluginTuple, type ReleaseConfig, ReleaseContext, type ReleaseContextOptions$1 as ReleaseContextOptions, ReleaseLabel, type ReleaseLabelCompare, type ReleaseLabelOptions, type ReleaseReturnValue, ReleaseTask, type StepOption, type TemplateContext, factory, load, loaderPluginsFromPluginTuples, reduceOptions, tuple };
3366
+ export { type BaseCommit, CHANGELOG_ALL_FIELDS, type ChangeLogInterface, type ChangelogFormatter, ChangesetVersion, type ChangesetVersionMode, type ChangesetVersionProps, type CommitTuple, type CommitValue, type Commitlint, type ConstructorType, type DeepPartial, type ExecutorReleaseContext, GitChangelog, GitChangelogFormatter, type GitChangelogOptions, type GitChangelogProps, MANIFEST_PATH, type Options, type PackageJson, type PluginClass, type PluginConstructorParams, type PluginTuple, type ReleaseConfig, ReleaseContext, type ReleaseContextOptions$1 as ReleaseContextOptions, type ReleaseGlobalConfig, ReleaseLabel, type ReleaseLabelCompare, type ReleaseLabelOptions, type ReleaseReturnValue, ReleaseTask, type StepOption, TEMPLATE_OPEN, type TemplateContext, type WorkspaceInterface, defaultReleaaseName, factory, load, loaderPluginsFromPluginTuples, reduceOptions, releaseJson, tuple };