release-it-gitea 1.6.0 → 1.8.0

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.
package/README.en.md CHANGED
@@ -57,19 +57,19 @@ npx release-it
57
57
 
58
58
  ### Basic Configuration Options
59
59
 
60
- | Option | Type | Default | Description |
61
- | -------------- | ------- | --------------------------- | ------------------------------ |
62
- | `host` | string | Current repository host | Gitea server URL |
63
- | `owner` | string | Auto-detect from git remote | Repository owner |
64
- | `repository` | string | Auto-detect from git remote | Repository name |
65
- | `release` | boolean | `true` | Whether to create release |
66
- | `releaseTitle` | string | `"v${version}"` | Release title template |
67
- | `releaseNotes` | string | `"${changelog}"` | Release notes template |
68
- | `prerelease` | boolean | `false` | Whether it's a prerelease |
69
- | `draft` | boolean | `false` | Whether it's a draft |
70
- | `tokenRef` | string | `"GITEA_TOKEN"` | API token environment variable |
71
- | `timeout` | number | `30000` | Request timeout (milliseconds) |
72
- | `assets` | array | `[]` | Additional asset files |
60
+ | Option | Type | Default | Description |
61
+ | -------------- | ----------------------------- | --------------------------- | ---------------------------------------------------------------------------------- |
62
+ | `host` | string | Current repository host | Gitea server URL |
63
+ | `owner` | string | Auto-detect from git remote | Repository owner |
64
+ | `repository` | string | Auto-detect from git remote | Repository name |
65
+ | `release` | boolean | `true` | Whether to create release |
66
+ | `releaseTitle` | string \| (context) => string | `"v${version}"` | Release title template, supports variables and function callbacks |
67
+ | `releaseNotes` | string \| (context) => string | `"${changelog}"` | Release notes template, supports variables, Markdown format and function callbacks |
68
+ | `prerelease` | boolean | `false` | Whether it's a prerelease |
69
+ | `draft` | boolean | `false` | Whether it's a draft |
70
+ | `tokenRef` | string | `"GITEA_TOKEN"` | API token environment variable |
71
+ | `timeout` | number | `30000` | Request timeout (milliseconds) |
72
+ | `assets` | array | `[]` | Additional asset files |
73
73
 
74
74
  ### Complete Configuration Example
75
75
 
@@ -261,6 +261,147 @@ The following variables can be used in `releaseTitle` and `releaseNotes`:
261
261
  | `${repo.repository}` | Repository name | `my-repo` |
262
262
  | `${branchName}` | Branch name | `main` |
263
263
 
264
+ ### Function Callback Configuration
265
+
266
+ In JavaScript configuration files, you can pass function callbacks for `releaseTitle` and `releaseNotes` for more flexible configuration:
267
+
268
+ ```js
269
+ // .release-it.js
270
+ module.exports = {
271
+ plugins: {
272
+ "release-it-gitea": {
273
+ host: "https://gitea.example.com",
274
+ owner: "your-username",
275
+ repository: "your-repo",
276
+ // Using a function to generate dynamic release titles
277
+ releaseTitle: (context) => {
278
+ const date = new Date().toISOString().split("T")[0];
279
+ return `🚀 ${context.name} v${context.version} (${date})`;
280
+ },
281
+ // Using a function to generate dynamic release notes
282
+ releaseNotes: (context) => {
283
+ const sections = context.changelog.split("\n## ");
284
+ const features = sections.find(
285
+ (s) => s.startsWith("Features") || s.startsWith("New Features"),
286
+ );
287
+ const fixes = sections.find(
288
+ (s) => s.startsWith("Bug Fixes") || s.startsWith("Fixes"),
289
+ );
290
+
291
+ return `## ${context.name} v${context.version} Release Notes
292
+
293
+ ## ✨ New Features
294
+ ${features ? "## " + features : "None"}
295
+
296
+ ## 🐛 Bug Fixes
297
+ ${fixes ? "## " + fixes : "None"}
298
+
299
+ ## 📦 Installation
300
+ \`\`\`
301
+ npm install ${context.name}@${context.version}
302
+ \`\`\``;
303
+ },
304
+ // Other configurations...
305
+ },
306
+ },
307
+ };
308
+ ```
309
+
310
+ Advantages of function callbacks:
311
+
312
+ - Can perform complex logical processing
313
+ - Can access the complete context object
314
+ - Can dynamically generate content based on conditions
315
+ - Can integrate external data or API results
316
+
317
+ > **Note**: Function callbacks are only available when using JavaScript configuration files (such as `.release-it.js` or `.release-it.cjs`); string templates must be used in JSON configuration files.
318
+
319
+ ### Using NPM Packages
320
+
321
+ `releaseTitle` and `releaseNotes` also support referencing external NPM packages using the `npm:` prefix to generate content:
322
+
323
+ ```json
324
+ {
325
+ "plugins": {
326
+ "release-it-gitea": {
327
+ "releaseTitle": "npm:my-release-notes-generator",
328
+ "releaseNotes": "npm:my-changelog-formatter"
329
+ }
330
+ }
331
+ }
332
+ ```
333
+
334
+ How to use NPM packages:
335
+
336
+ 1. Create and publish an NPM package that exports the following methods:
337
+
338
+ ```js
339
+ // my-release-notes-generator package example
340
+ module.exports = {
341
+ releaseTitle: function (context) {
342
+ return `Release v${context.version} - ${new Date().toLocaleDateString()}`;
343
+ },
344
+ releaseNotes: function (context) {
345
+ // Custom formatting logic
346
+ return `# ${context.name} v${context.version}\n\n${context.changelog}`;
347
+ },
348
+ };
349
+ ```
350
+
351
+ 2. Install the package:
352
+
353
+ ```bash
354
+ npm install --save-dev my-release-notes-generator
355
+ ```
356
+
357
+ 3. Reference in configuration:
358
+ ```json
359
+ {
360
+ "releaseTitle": "npm:my-release-notes-generator",
361
+ "releaseNotes": "npm:my-release-notes-generator"
362
+ }
363
+ ```
364
+
365
+ Advantages of using NPM packages:
366
+
367
+ - Can share the same release title and description format across multiple projects
368
+ - Can maintain and update release format independently of the project
369
+ - Supports more complex logic and dependencies
370
+ - Can be used in JSON configurations, not limited to JavaScript configurations
371
+
372
+ ### Context Object Properties
373
+
374
+ In function callbacks, you can access the following context object properties:
375
+
376
+ | Property | Type | Description | Example Value |
377
+ | ----------------- | ------ | ------------------------------- | -------------------------------- |
378
+ | `version` | string | Current version | `"1.2.3"` |
379
+ | `latestVersion` | string | Previous version | `"1.2.2"` |
380
+ | `changelog` | string | Generated changelog content | `"## Bug Fixes\n\n* Fixed..."` |
381
+ | `name` | string | Project name | `"my-project"` |
382
+ | `branchName` | string | Current branch name | `"main"` |
383
+ | `releaseUrl` | string | Release URL (only after update) | `"https://gitea.com/.../v1.2.3"` |
384
+ | `repo` | object | Repository related information | |
385
+ | `repo.host` | string | Repository host address | `"gitea.example.com"` |
386
+ | `repo.owner` | string | Repository owner | `"username"` |
387
+ | `repo.project` | string | Project name | `"my-repo"` |
388
+ | `repo.protocol` | string | Repository protocol | `"https"` |
389
+ | `repo.remote` | string | Remote repository name | `"origin"` |
390
+ | `repo.repository` | string | Repository name | `"my-repo"` |
391
+
392
+ ### Example: Using context properties to generate custom release titles
393
+
394
+ ```js
395
+ releaseTitle: (context) => {
396
+ const emoji = context.version.includes("beta")
397
+ ? "🧪"
398
+ : context.version.includes("alpha")
399
+ ? "🚧"
400
+ : "🚀";
401
+ return `${emoji} ${context.name} v${context.version} [${context.branchName}]`;
402
+ };
403
+ ```
404
+
264
405
  ### Template Usage Example
265
406
 
266
407
  ```json
package/README.md CHANGED
@@ -57,19 +57,19 @@ npx release-it
57
57
 
58
58
  ### 基本配置选项
59
59
 
60
- | 选项 | 类型 | 默认值 | 描述 |
61
- | -------------- | ------- | ---------------------- | -------------------- |
62
- | `host` | string | 当前仓库的 host | Gitea 服务器 URL |
63
- | `owner` | string | 从 git remote 自动检测 | 仓库所有者 |
64
- | `repository` | string | 从 git remote 自动检测 | 仓库名称 |
65
- | `release` | boolean | `true` | 是否创建发布 |
66
- | `releaseTitle` | string | `"v${version}"` | 发布标题模板 |
67
- | `releaseNotes` | string | `"${changelog}"` | 发布说明模板 |
68
- | `prerelease` | boolean | `false` | 是否为预发布 |
69
- | `draft` | boolean | `false` | 是否为草稿 |
70
- | `tokenRef` | string | `"GITEA_TOKEN"` | API token 环境变量名 |
71
- | `timeout` | number | `30000` | 请求超时时间(毫秒) |
72
- | `assets` | array | `[]` | 附加的资源文件 |
60
+ | 选项 | 类型 | 默认值 | 描述 |
61
+ | -------------- | ----------------------------- | ---------------------- | -------------------------------------------------- |
62
+ | `host` | string | 当前仓库的 host | Gitea 服务器 URL |
63
+ | `owner` | string | 从 git remote 自动检测 | 仓库所有者 |
64
+ | `repository` | string | 从 git remote 自动检测 | 仓库名称 |
65
+ | `release` | boolean | `true` | 是否创建发布 |
66
+ | `releaseTitle` | string \| (context) => string | `"v${version}"` | 发布标题模板,支持变量替换和函数回调 |
67
+ | `releaseNotes` | string \| (context) => string | `"${changelog}"` | 发布说明模板,支持变量替换、Markdown格式和函数回调 |
68
+ | `prerelease` | boolean | `false` | 是否为预发布 |
69
+ | `draft` | boolean | `false` | 是否为草稿 |
70
+ | `tokenRef` | string | `"GITEA_TOKEN"` | API token 环境变量名 |
71
+ | `timeout` | number | `30000` | 请求超时时间(毫秒) |
72
+ | `assets` | array | `[]` | 附加的资源文件 |
73
73
 
74
74
  ### 完整配置示例
75
75
 
@@ -261,6 +261,147 @@ export MY_GITEA_TOKEN=your_gitea_api_token
261
261
  | `${repo.repository}` | 仓库名称 | `my-repo` |
262
262
  | `${branchName}` | 分支名称 | `main` |
263
263
 
264
+ ### 函数回调配置
265
+
266
+ 在 JavaScript 配置文件中,可以为 `releaseTitle` 和 `releaseNotes` 传入函数回调,实现更灵活的配置:
267
+
268
+ ```js
269
+ // .release-it.js
270
+ module.exports = {
271
+ plugins: {
272
+ "release-it-gitea": {
273
+ host: "https://gitea.example.com",
274
+ owner: "your-username",
275
+ repository: "your-repo",
276
+ // 使用函数生成动态的发布标题
277
+ releaseTitle: (context) => {
278
+ const date = new Date().toISOString().split("T")[0];
279
+ return `🚀 ${context.name} v${context.version} (${date})`;
280
+ },
281
+ // 使用函数生成动态的发布说明
282
+ releaseNotes: (context) => {
283
+ const sections = context.changelog.split("\n## ");
284
+ const features = sections.find(
285
+ (s) => s.startsWith("Features") || s.startsWith("新特性"),
286
+ );
287
+ const fixes = sections.find(
288
+ (s) => s.startsWith("Bug Fixes") || s.startsWith("修复"),
289
+ );
290
+
291
+ return `## ${context.name} v${context.version} 发布说明
292
+
293
+ ## ✨ 新特性
294
+ ${features ? "## " + features : "无"}
295
+
296
+ ## 🐛 问题修复
297
+ ${fixes ? "## " + fixes : "无"}
298
+
299
+ ## 📦 安装
300
+ \`\`\`
301
+ npm install ${context.name}@${context.version}
302
+ \`\`\``;
303
+ },
304
+ // 其他配置...
305
+ },
306
+ },
307
+ };
308
+ ```
309
+
310
+ 使用函数回调的优势:
311
+
312
+ - 可以进行复杂的逻辑处理
313
+ - 可以访问完整的 context 对象
314
+ - 可以根据条件动态生成内容
315
+ - 可以整合外部数据或API结果
316
+
317
+ > **注意**: 函数回调仅在使用 JavaScript 配置文件 (如 `.release-it.js` 或 `.release-it.cjs`) 时可用,在 JSON 配置文件中只能使用字符串模板。
318
+
319
+ ### 使用 NPM 包
320
+
321
+ `releaseTitle` 和 `releaseNotes` 还支持通过 `npm:` 前缀引用外部 NPM 包来生成内容:
322
+
323
+ ```json
324
+ {
325
+ "plugins": {
326
+ "release-it-gitea": {
327
+ "releaseTitle": "npm:my-release-notes-generator",
328
+ "releaseNotes": "npm:my-changelog-formatter"
329
+ }
330
+ }
331
+ }
332
+ ```
333
+
334
+ 使用 NPM 包的方式:
335
+
336
+ 1. 创建并发布一个 NPM 包,该包导出以下方法:
337
+
338
+ ```js
339
+ // my-release-notes-generator 包示例
340
+ module.exports = {
341
+ releaseTitle: function (context) {
342
+ return `Release v${context.version} - ${new Date().toLocaleDateString()}`;
343
+ },
344
+ releaseNotes: function (context) {
345
+ // 自定义格式化逻辑
346
+ return `# ${context.name} v${context.version}\n\n${context.changelog}`;
347
+ },
348
+ };
349
+ ```
350
+
351
+ 2. 安装该包:
352
+
353
+ ```bash
354
+ npm install --save-dev my-release-notes-generator
355
+ ```
356
+
357
+ 3. 在配置中引用:
358
+ ```json
359
+ {
360
+ "releaseTitle": "npm:my-release-notes-generator",
361
+ "releaseNotes": "npm:my-release-notes-generator"
362
+ }
363
+ ```
364
+
365
+ 使用 NPM 包的优势:
366
+
367
+ - 可以在多个项目间共享相同的发布标题和说明格式
368
+ - 可以独立于项目维护和更新发布格式
369
+ - 支持更复杂的逻辑和依赖
370
+ - 可以在 JSON 配置中使用,不仅限于 JavaScript 配置
371
+
372
+ ### Context 对象属性
373
+
374
+ 在函数回调中,您可以访问以下 context 对象属性:
375
+
376
+ | 属性 | 类型 | 描述 | 示例值 |
377
+ | ----------------- | ------ | ----------------------------- | -------------------------------- |
378
+ | `version` | string | 当前版本号 | `"1.2.3"` |
379
+ | `latestVersion` | string | 上一个版本号 | `"1.2.2"` |
380
+ | `changelog` | string | 生成的变更日志内容 | `"## Bug Fixes\n\n* 修复..."` |
381
+ | `name` | string | 项目名称 | `"my-project"` |
382
+ | `branchName` | string | 当前分支名 | `"main"` |
383
+ | `releaseUrl` | string | 发布 URL (仅在更新发布后可用) | `"https://gitea.com/.../v1.2.3"` |
384
+ | `repo` | object | 仓库相关信息 | |
385
+ | `repo.host` | string | 仓库主机地址 | `"gitea.example.com"` |
386
+ | `repo.owner` | string | 仓库所有者 | `"username"` |
387
+ | `repo.project` | string | 项目名称 | `"my-repo"` |
388
+ | `repo.protocol` | string | 仓库协议 | `"https"` |
389
+ | `repo.remote` | string | 远程仓库名称 | `"origin"` |
390
+ | `repo.repository` | string | 仓库名称 | `"my-repo"` |
391
+
392
+ ### 示例:使用 context 属性生成自定义发布标题
393
+
394
+ ```js
395
+ releaseTitle: (context) => {
396
+ const emoji = context.version.includes("beta")
397
+ ? "🧪"
398
+ : context.version.includes("alpha")
399
+ ? "🚧"
400
+ : "🚀";
401
+ return `${emoji} ${context.name} v${context.version} [${context.branchName}]`;
402
+ };
403
+ ```
404
+
264
405
  ### 模板使用示例
265
406
 
266
407
  ```json
package/lib/global.d.d.ts CHANGED
@@ -2,6 +2,7 @@ declare module "release-it" {
2
2
  interface Config {
3
3
  getContext(): Context;
4
4
  getContext(path: string): unknown;
5
+ isDryRun: boolean;
5
6
  setContext(path: string, value: unknown): void;
6
7
  }
7
8
 
package/lib/index.d.ts CHANGED
@@ -5,7 +5,6 @@ declare class GiteaPlugin extends Plugin {
5
5
  /**
6
6
  * 获取并验证 Gitea 配置信息
7
7
  * @returns 验证后的 Gitea 配置对象
8
- * @throws 当配置缺失或无效时抛出错误
9
8
  */
10
9
  private get giteaConfig();
11
10
  /**
@@ -99,9 +98,8 @@ declare class GiteaPlugin extends Plugin {
99
98
  */
100
99
  private processAsset;
101
100
  /**
102
- * 处理单个附件配置
103
- * @param releaseId 发布 ID
104
- * @param config 附件配置
101
+ * 获取发布说明内容
102
+ * @returns 发布说明字符串
105
103
  */
106
104
  private getReleaseNotes;
107
105
  private getReleaseTitle;
package/lib/index.js CHANGED
@@ -7,12 +7,11 @@ import { basename, dirname, join } from "path";
7
7
  import { Plugin } from "release-it";
8
8
  class GiteaPlugin extends Plugin {
9
9
  static isEnabled(config) {
10
- return Boolean(config?.release !== false);
10
+ return config?.release !== false;
11
11
  }
12
12
  /**
13
13
  * 获取并验证 Gitea 配置信息
14
14
  * @returns 验证后的 Gitea 配置对象
15
- * @throws 当配置缺失或无效时抛出错误
16
15
  */
17
16
  get giteaConfig() {
18
17
  const gitea = this.getContext();
@@ -102,7 +101,7 @@ class GiteaPlugin extends Plugin {
102
101
  if (!response.ok) {
103
102
  const errorText = await response.text();
104
103
  throw new Error(
105
- `Gitea API \u8BF7\u6C42\u5931\u8D25 (${response.status.toString()}): ${errorText}`
104
+ `Gitea API \u8BF7\u6C42\u5931\u8D25 (${String(response.status)}): ${errorText}`
106
105
  );
107
106
  }
108
107
  const data = await response.json();
@@ -198,7 +197,7 @@ class GiteaPlugin extends Plugin {
198
197
  });
199
198
  return files;
200
199
  } catch (error) {
201
- this.log.warn(`\u6587\u4EF6\u5339\u914D\u5931\u8D25: ${pattern}, \u9519\u8BEF: ${error}`);
200
+ this.log.warn(`\u6587\u4EF6\u5339\u914D\u5931\u8D25: ${pattern}, \u9519\u8BEF: ${String(error)}`);
202
201
  return [];
203
202
  }
204
203
  }
@@ -217,7 +216,7 @@ class GiteaPlugin extends Plugin {
217
216
  });
218
217
  output.on("close", () => {
219
218
  this.log.verbose(
220
- `ZIP \u6587\u4EF6\u521B\u5EFA\u5B8C\u6210: ${outputPath} (${archive.pointer()} bytes)`
219
+ `ZIP \u6587\u4EF6\u521B\u5EFA\u5B8C\u6210: ${outputPath} (${String(archive.pointer())} bytes)`
221
220
  );
222
221
  resolve();
223
222
  });
@@ -232,7 +231,7 @@ class GiteaPlugin extends Plugin {
232
231
  archive.file(file, { name: relativePath });
233
232
  }
234
233
  }
235
- archive.finalize();
234
+ void archive.finalize();
236
235
  });
237
236
  }
238
237
  /**
@@ -244,8 +243,12 @@ class GiteaPlugin extends Plugin {
244
243
  * @returns 上传结果
245
244
  */
246
245
  async uploadAsset(releaseId, filePath, fileName, label) {
246
+ if (this.config.isDryRun) {
247
+ this.log.info(`[\u6A21\u62DF\u6267\u884C] \u4E0A\u4F20\u9644\u4EF6: ${fileName}`);
248
+ return { id: 0 };
249
+ }
247
250
  const url = this.buildApiUrl(
248
- `/repos/${this.giteaConfig.owner}/${this.giteaConfig.repository}/releases/${releaseId}/assets`
251
+ `/repos/${this.giteaConfig.owner}/${this.giteaConfig.repository}/releases/${String(releaseId)}/assets`
249
252
  );
250
253
  const token = this.getToken();
251
254
  const form = new FormData();
@@ -265,12 +268,14 @@ class GiteaPlugin extends Plugin {
265
268
  method: "POST",
266
269
  timeout: this.giteaConfig.timeout
267
270
  };
268
- this.log.verbose(`\u4E0A\u4F20\u9644\u4EF6: ${fileName} \u5230\u53D1\u5E03 ${releaseId}`);
271
+ this.log.info(`\u4E0A\u4F20\u9644\u4EF6: ${fileName} \u5230\u53D1\u5E03 ${String(releaseId)}`);
269
272
  try {
270
273
  const response = await fetch(url, requestOptions);
271
274
  if (!response.ok) {
272
275
  const errorText = await response.text();
273
- throw new Error(`\u9644\u4EF6\u4E0A\u4F20\u5931\u8D25 (${response.status}): ${errorText}`);
276
+ throw new Error(
277
+ `\u9644\u4EF6\u4E0A\u4F20\u5931\u8D25 (${String(response.status)}): ${errorText}`
278
+ );
274
279
  }
275
280
  const result = await response.json();
276
281
  this.log.info(`\u2705 \u9644\u4EF6\u4E0A\u4F20\u6210\u529F: ${fileName}`);
@@ -292,14 +297,14 @@ class GiteaPlugin extends Plugin {
292
297
  this.log.verbose("\u6CA1\u6709\u914D\u7F6E\u9644\u4EF6\uFF0C\u8DF3\u8FC7\u4E0A\u4F20");
293
298
  return;
294
299
  }
295
- this.log.info(`\u5F00\u59CB\u4E0A\u4F20 ${assets.length} \u4E2A\u9644\u4EF6...`);
300
+ this.log.info(`\u5F00\u59CB\u4E0A\u4F20 ${String(assets.length)} \u4E2A\u9644\u4EF6...`);
296
301
  for (const asset of assets) {
297
302
  try {
298
303
  const config = this.normalizeAssetConfig(asset);
299
304
  await this.processAsset(releaseId, config);
300
305
  } catch (error) {
301
306
  this.log.error(
302
- `\u9644\u4EF6\u5904\u7406\u5931\u8D25: ${JSON.stringify(asset)}, \u9519\u8BEF: ${error}`
307
+ `\u9644\u4EF6\u5904\u7406\u5931\u8D25: ${JSON.stringify(asset)}, \u9519\u8BEF: ${String(error)}`
303
308
  );
304
309
  }
305
310
  }
@@ -317,12 +322,12 @@ class GiteaPlugin extends Plugin {
317
322
  return;
318
323
  }
319
324
  if (config.type === "zip") {
320
- const zipName = config.name || `${basename(config.path)}.zip`;
325
+ const zipName = config.name ?? `${basename(config.path)}.zip`;
321
326
  const tempZipPath = join(process.cwd(), ".temp", zipName);
322
327
  const { mkdirSync } = await import("fs");
323
328
  try {
324
329
  mkdirSync(dirname(tempZipPath), { recursive: true });
325
- } catch (error) {
330
+ } catch {
326
331
  }
327
332
  await this.createZipArchive(
328
333
  files,
@@ -333,34 +338,77 @@ class GiteaPlugin extends Plugin {
333
338
  const { unlinkSync } = await import("fs");
334
339
  try {
335
340
  unlinkSync(tempZipPath);
336
- } catch (error) {
341
+ } catch {
337
342
  this.log.warn(`\u6E05\u7406\u4E34\u65F6\u6587\u4EF6\u5931\u8D25: ${tempZipPath}`);
338
343
  }
339
344
  } else {
340
345
  for (const file of files) {
341
- const fileName = config.name || basename(file);
346
+ const fileName = config.name ?? basename(file);
342
347
  await this.uploadAsset(releaseId, file, fileName, config.label);
343
348
  }
344
349
  }
345
350
  }
346
351
  /**
347
- * 处理单个附件配置
348
- * @param releaseId 发布 ID
349
- * @param config 附件配置
352
+ * 获取发布说明内容
353
+ * @returns 发布说明字符串
350
354
  */
351
- getReleaseNotes() {
355
+ async getReleaseNotes() {
352
356
  const releaseNotes = this.giteaConfig.releaseNotes;
353
357
  if (typeof releaseNotes === "string") {
354
- return this.interpolate(releaseNotes);
358
+ if (releaseNotes.startsWith("npm:")) {
359
+ const npmPackage = releaseNotes.slice(4);
360
+ try {
361
+ const npmHandler = await import(npmPackage);
362
+ if (typeof npmHandler !== "function" && // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
363
+ typeof npmHandler.default !== "function") {
364
+ throw new Error(`${npmPackage} not found npm`);
365
+ }
366
+ const handler = (
367
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
368
+ typeof npmHandler.default === "function" ? (
369
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
370
+ npmHandler.default
371
+ ) : npmHandler
372
+ );
373
+ return handler.releaseNotes(this.config.getContext());
374
+ } catch (error) {
375
+ console.error(error);
376
+ throw new Error(`${npmPackage} not found npm`);
377
+ }
378
+ } else {
379
+ return this.interpolate(releaseNotes);
380
+ }
355
381
  } else if (typeof releaseNotes === "function") {
356
382
  return releaseNotes(this.config.getContext());
357
383
  }
358
384
  return this.config.getContext("changelog");
359
385
  }
360
- getReleaseTitle() {
386
+ async getReleaseTitle() {
361
387
  const releaseTitle = this.giteaConfig.releaseTitle;
362
388
  if (typeof releaseTitle === "string") {
363
- return this.interpolate(releaseTitle);
389
+ if (releaseTitle.startsWith("npm:")) {
390
+ const npmPackage = releaseTitle.slice(4);
391
+ try {
392
+ const npmHandler = await import(npmPackage);
393
+ if (typeof npmHandler !== "function" && // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
394
+ typeof npmHandler.default !== "function") {
395
+ throw new Error(`${npmPackage} not found npm`);
396
+ }
397
+ const handler = (
398
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
399
+ typeof npmHandler.default === "function" ? (
400
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
401
+ npmHandler.default
402
+ ) : npmHandler
403
+ );
404
+ return handler.releaseTitle(this.config.getContext());
405
+ } catch (error) {
406
+ console.error(error);
407
+ throw new Error(`${npmPackage} not found npm`);
408
+ }
409
+ } else {
410
+ return this.interpolate(releaseTitle);
411
+ }
364
412
  } else if (typeof releaseTitle === "function") {
365
413
  return releaseTitle(this.config.getContext());
366
414
  }
@@ -375,9 +423,10 @@ class GiteaPlugin extends Plugin {
375
423
  this.log.info("Gitea \u53D1\u5E03\u529F\u80FD\u5DF2\u7981\u7528");
376
424
  return;
377
425
  }
426
+ const isDryRun = this.config.isDryRun;
378
427
  const tagName = this.config.getContext("tagName");
379
- const releaseTitle = this.getReleaseTitle();
380
- const releaseNotes = this.getReleaseNotes();
428
+ const releaseTitle = await this.getReleaseTitle();
429
+ const releaseNotes = await this.getReleaseNotes();
381
430
  this.log.info(`\u51C6\u5907\u521B\u5EFA Gitea \u53D1\u5E03: ${releaseTitle}`);
382
431
  const releaseData = {
383
432
  body: releaseNotes,
@@ -386,6 +435,14 @@ class GiteaPlugin extends Plugin {
386
435
  prerelease: this.giteaConfig.prerelease ?? false,
387
436
  tag_name: tagName
388
437
  };
438
+ if (isDryRun) {
439
+ this.log.info(`[\u6A21\u62DF\u6267\u884C] \u6B63\u5728\u521B\u5EFA/\u66F4\u65B0\u53D1\u5E03 ${tagName}`);
440
+ this.log.verbose(`\u53D1\u5E03\u6570\u636E: ${JSON.stringify(releaseData)}`);
441
+ if (this.giteaConfig.assets && this.giteaConfig.assets.length > 0) {
442
+ await this.uploadAssets(0);
443
+ }
444
+ return;
445
+ }
389
446
  try {
390
447
  const exists = await this.releaseExists(tagName);
391
448
  let release;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "release-it-gitea",
3
- "version": "1.6.0",
4
- "description": "release-it gitea plugin",
3
+ "version": "1.8.0",
4
+ "description": "release-it gitea plugin.",
5
5
  "keywords": [
6
6
  "gitea",
7
7
  "release-it",
@@ -19,22 +19,20 @@
19
19
  "type": "module",
20
20
  "main": "lib/index.js",
21
21
  "files": [
22
- "LICENSE.md",
23
- "README.md",
24
- "lib/",
25
- "package.json"
22
+ "lib/"
26
23
  ],
27
24
  "scripts": {
28
25
  "build": "tsup",
29
26
  "demo": "node scripts/demo.js",
30
27
  "format": "prettier .",
31
28
  "lint": "eslint . --max-warnings 0",
29
+ "lint:all": "pnpm run lint && pnpm run lint:knip && pnpm run lint:md && pnpm run lint:packages && pnpm run lint:spelling",
32
30
  "lint:knip": "knip",
33
- "lint:md": "markdownlint \"**/*.md\" \".github/**/*.md\" --rules sentences-per-line",
31
+ "lint:md": "markdownlint \"**/*.md\" \".github/**/*.md\"",
34
32
  "lint:packages": "pnpm dedupe --check",
35
33
  "lint:spelling": "cspell \"**\" \".github/**/*\"",
36
34
  "prepare": "husky",
37
- "test": "vitest",
35
+ "test": "vitest run",
38
36
  "test:e2e": "vitest run test/",
39
37
  "test:manual": "node scripts/test-manual.js",
40
38
  "test:unit": "vitest run src/",
@@ -46,45 +44,41 @@
46
44
  "dependencies": {
47
45
  "archiver": "^7.0.1",
48
46
  "form-data": "^4.0.3",
49
- "glob": "^11.0.3",
47
+ "glob": "^13.0.0",
50
48
  "node-fetch": "^3.3.2"
51
49
  },
52
50
  "devDependencies": {
53
51
  "@eslint-community/eslint-plugin-eslint-comments": "4.5.0",
54
52
  "@eslint/js": "9.22.0",
55
53
  "@release-it/conventional-changelog": "10.0.0",
56
- "@types/archiver": "^6.0.3",
57
- "@types/eslint-plugin-markdown": "2.0.2",
54
+ "@types/archiver": "^7.0.0",
58
55
  "@types/node": "22.13.10",
59
- "@types/node-fetch": "^2.6.11",
60
- "@vitest/coverage-v8": "3.0.9",
56
+ "@vitest/coverage-v8": "4.0.16",
61
57
  "@vitest/eslint-plugin": "1.1.38",
62
- "console-fail-test": "0.5.0",
63
- "cspell": "8.17.5",
58
+ "console-fail-test": "0.6.1",
59
+ "cspell": "9.4.0",
64
60
  "eslint": "9.22.0",
65
- "eslint-plugin-jsdoc": "50.6.8",
61
+ "eslint-plugin-jsdoc": "61.5.0",
66
62
  "eslint-plugin-jsonc": "2.20.0",
67
- "eslint-plugin-markdown": "5.1.0",
68
63
  "eslint-plugin-n": "17.16.2",
69
- "eslint-plugin-package-json": "0.29.0",
70
- "eslint-plugin-perfectionist": "4.11.0",
71
- "eslint-plugin-regexp": "2.7.0",
72
- "eslint-plugin-yml": "1.17.0",
64
+ "eslint-plugin-package-json": "0.87.1",
65
+ "eslint-plugin-perfectionist": "5.2.0",
66
+ "eslint-plugin-regexp": "2.10.0",
67
+ "eslint-plugin-yml": "1.19.1",
73
68
  "husky": "9.1.7",
74
- "knip": "5.46.0",
75
- "lint-staged": "15.5.0",
76
- "markdownlint": "0.37.4",
77
- "markdownlint-cli": "0.44.0",
78
- "prettier": "3.5.3",
79
- "prettier-plugin-curly": "0.3.1",
69
+ "knip": "5.79.0",
70
+ "lint-staged": "16.2.7",
71
+ "markdownlint": "0.40.0",
72
+ "markdownlint-cli": "0.47.0",
73
+ "prettier": "3.7.4",
74
+ "prettier-plugin-curly": "0.4.1",
80
75
  "prettier-plugin-packagejson": "2.5.10",
81
- "prettier-plugin-sh": "0.15.0",
82
- "release-it": "19.0.3",
83
- "sentences-per-line": "0.3.0",
84
- "tsup": "8.4.0",
85
- "typescript": "5.8.2",
86
- "typescript-eslint": "8.26.1",
87
- "vitest": "3.0.9"
76
+ "prettier-plugin-sh": "0.18.0",
77
+ "release-it": "19.2.2",
78
+ "tsup": "8.5.1",
79
+ "typescript": "5.9.3",
80
+ "typescript-eslint": "8.51.0",
81
+ "vitest": "4.0.16"
88
82
  },
89
83
  "peerDependencies": {
90
84
  "release-it": "^19.0.3"
@@ -95,5 +89,12 @@
95
89
  },
96
90
  "publishConfig": {
97
91
  "provenance": true
92
+ },
93
+ "pnpm": {
94
+ "overrides": {
95
+ "form-data": "^4.0.5",
96
+ "glob": "^11.1.0",
97
+ "vite": "^6.4.0"
98
+ }
98
99
  }
99
100
  }