release-it-gitea 1.3.2 → 1.4.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.md +196 -65
- package/lib/global.d.d.ts +18 -0
- package/lib/index.d.ts +40 -0
- package/lib/index.js +185 -3
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Release It! Gitea Plugin
|
|
2
2
|
|
|
3
|
-
一个用于 [release-it](https://github.com/release-it/release-it) 的 Gitea
|
|
3
|
+
一个用于 [release-it](https://github.com/release-it/release-it) 的 Gitea 插件,支持自动创建 Gitea 发布并上传附件。
|
|
4
4
|
|
|
5
5
|
## 功能特性
|
|
6
6
|
|
|
7
|
-
- ✅
|
|
8
|
-
- ✅
|
|
9
|
-
- ✅
|
|
10
|
-
- ✅
|
|
11
|
-
- ✅
|
|
12
|
-
- ✅
|
|
13
|
-
- ✅ TypeScript 支持
|
|
7
|
+
- ✅ 自动创建和更新 Gitea 发布
|
|
8
|
+
- ✅ 支持模板变量替换(版本号、变更日志等)
|
|
9
|
+
- ✅ 支持草稿和预发布版本
|
|
10
|
+
- ✅ **新功能:支持文件和文件夹附件上传**
|
|
11
|
+
- ✅ **新功能:支持自动打包文件夹为 ZIP**
|
|
12
|
+
- ✅ **新功能:支持通配符文件匹配**
|
|
14
13
|
|
|
15
14
|
## 安装
|
|
16
15
|
|
|
@@ -47,7 +46,8 @@ export GITEA_TOKEN="your-gitea-api-token"
|
|
|
47
46
|
"releaseTitle": "v${version}",
|
|
48
47
|
"releaseNotes": "${changelog}",
|
|
49
48
|
"prerelease": false,
|
|
50
|
-
"draft": false
|
|
49
|
+
"draft": false,
|
|
50
|
+
"tokenRef": "GITEA_TOKEN"
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
}
|
|
@@ -72,95 +72,226 @@ export GITEA_TOKEN="your-gitea-api-token"
|
|
|
72
72
|
|
|
73
73
|
## 配置选项
|
|
74
74
|
|
|
75
|
-
| 选项 | 类型
|
|
76
|
-
| -------------- |
|
|
77
|
-
| `host` | `string`
|
|
78
|
-
| `owner` | `string`
|
|
79
|
-
| `repository` | `string`
|
|
80
|
-
| `release` | `boolean`
|
|
81
|
-
| `releaseTitle` | `string`
|
|
82
|
-
| `releaseNotes` | `string`
|
|
83
|
-
| `prerelease` | `boolean`
|
|
84
|
-
| `draft` | `boolean`
|
|
85
|
-
| `tokenRef` | `string`
|
|
86
|
-
| `timeout` | `number`
|
|
87
|
-
|
|
88
|
-
## 模板变量
|
|
89
|
-
|
|
90
|
-
在 `releaseTitle` 和 `releaseNotes` 中可以使用以下模板变量:
|
|
91
|
-
|
|
92
|
-
- `${version}` - 当前版本号
|
|
93
|
-
- `${latestVersion}` - 上一个版本号
|
|
94
|
-
- `${changelog}` - 生成的 changelog
|
|
95
|
-
- `${name}` - 项目名称
|
|
96
|
-
- `${repo.owner}` - 仓库所有者
|
|
97
|
-
- `${repo.repository}` - 仓库名称
|
|
98
|
-
- `${branchName}` - 当前分支名
|
|
75
|
+
| 选项 | 类型 | 默认值 | 描述 |
|
|
76
|
+
| -------------- | ---------------------- | ---------------------- | -------------------- |
|
|
77
|
+
| `host` | `string` | 当前仓库的 host | Gitea 服务器 URL |
|
|
78
|
+
| `owner` | `string` | 从 git remote 自动检测 | 仓库所有者 |
|
|
79
|
+
| `repository` | `string` | 从 git remote 自动检测 | 仓库名称 |
|
|
80
|
+
| `release` | `boolean` | `true` | 是否创建发布 |
|
|
81
|
+
| `releaseTitle` | `string` | `"v${version}"` | 发布标题模板 |
|
|
82
|
+
| `releaseNotes` | `string` | `"${changelog}"` | 发布说明模板 |
|
|
83
|
+
| `prerelease` | `boolean` | `false` | 是否为预发布 |
|
|
84
|
+
| `draft` | `boolean` | `false` | 是否为草稿 |
|
|
85
|
+
| `tokenRef` | `string` | `"GITEA_TOKEN"` | API token 环境变量名 |
|
|
86
|
+
| `timeout` | `number` | `30000` | 请求超时时间(毫秒) |
|
|
87
|
+
| `assets` | `(string \| object)[]` | `` | 附加的资源文件 |
|
|
99
88
|
|
|
100
|
-
##
|
|
89
|
+
## 附件上传配置
|
|
101
90
|
|
|
102
|
-
###
|
|
91
|
+
### 基本附件配置
|
|
103
92
|
|
|
104
93
|
```json
|
|
105
94
|
{
|
|
106
95
|
"plugins": {
|
|
107
96
|
"release-it-gitea": {
|
|
108
|
-
"host": "https://gitea.example.com"
|
|
97
|
+
"host": "https://gitea.example.com",
|
|
98
|
+
"owner": "your-username",
|
|
99
|
+
"repository": "your-repo",
|
|
100
|
+
"assets": ["dist/app.js", "README.md", "dist/**/*.min.js"]
|
|
109
101
|
}
|
|
110
102
|
}
|
|
111
103
|
}
|
|
112
104
|
```
|
|
113
105
|
|
|
114
|
-
###
|
|
106
|
+
### 高级附件配置
|
|
115
107
|
|
|
116
108
|
```json
|
|
117
109
|
{
|
|
118
110
|
"plugins": {
|
|
119
111
|
"release-it-gitea": {
|
|
120
112
|
"host": "https://gitea.example.com",
|
|
121
|
-
"owner": "
|
|
122
|
-
"repository": "
|
|
123
|
-
"
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
113
|
+
"owner": "your-username",
|
|
114
|
+
"repository": "your-repo",
|
|
115
|
+
"assets": [
|
|
116
|
+
{
|
|
117
|
+
"path": "dist/**/*",
|
|
118
|
+
"name": "distribution-files.zip",
|
|
119
|
+
"type": "zip",
|
|
120
|
+
"label": "Distribution Files"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"path": "docs/*.md",
|
|
124
|
+
"type": "file",
|
|
125
|
+
"label": "Documentation"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"path": "src",
|
|
129
|
+
"name": "source-code.zip",
|
|
130
|
+
"type": "zip",
|
|
131
|
+
"label": "Source Code"
|
|
132
|
+
}
|
|
133
|
+
]
|
|
130
134
|
}
|
|
131
135
|
}
|
|
132
136
|
}
|
|
133
137
|
```
|
|
134
138
|
|
|
135
|
-
|
|
139
|
+
## 附件配置选项
|
|
140
|
+
|
|
141
|
+
### 字符串格式
|
|
142
|
+
|
|
143
|
+
直接指定文件路径,支持通配符:
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
"assets": [
|
|
147
|
+
"dist/app.js",
|
|
148
|
+
"build/**/*.min.js",
|
|
149
|
+
"*.md"
|
|
150
|
+
]
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 对象格式
|
|
154
|
+
|
|
155
|
+
更详细的配置选项:
|
|
156
|
+
|
|
157
|
+
| 属性 | 类型 | 必需 | 描述 |
|
|
158
|
+
| ------- | --------------- | ---- | ------------------------------------ |
|
|
159
|
+
| `path` | string | ✅ | 文件或文件夹路径,支持通配符 |
|
|
160
|
+
| `name` | string | ❌ | 上传后的文件名,不指定则使用原文件名 |
|
|
161
|
+
| `type` | 'file' \| 'zip' | ❌ | 文件类型,默认为 'file' |
|
|
162
|
+
| `label` | string | ❌ | 文件标签,用于标识文件用途 |
|
|
163
|
+
|
|
164
|
+
### 文件类型说明
|
|
165
|
+
|
|
166
|
+
- **`file`**: 直接上传匹配到的文件
|
|
167
|
+
- **`zip`**: 将匹配到的文件打包成 ZIP 文件后上传
|
|
168
|
+
|
|
169
|
+
## 使用示例
|
|
170
|
+
|
|
171
|
+
### 示例 1:上传构建产物
|
|
136
172
|
|
|
137
173
|
```json
|
|
138
174
|
{
|
|
139
|
-
"
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
175
|
+
"assets": ["dist/bundle.js", "dist/bundle.css", "dist/assets/**/*"]
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### 示例 2:打包源代码
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"assets": [
|
|
184
|
+
{
|
|
185
|
+
"path": "src/**/*",
|
|
186
|
+
"name": "source-v${version}.zip",
|
|
187
|
+
"type": "zip",
|
|
188
|
+
"label": "Source Code"
|
|
144
189
|
}
|
|
145
|
-
|
|
190
|
+
]
|
|
146
191
|
}
|
|
147
192
|
```
|
|
148
193
|
|
|
149
|
-
|
|
194
|
+
### 示例 3:多种文件类型
|
|
150
195
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"assets": [
|
|
199
|
+
"README.md",
|
|
200
|
+
"CHANGELOG.md",
|
|
201
|
+
{
|
|
202
|
+
"path": "dist",
|
|
203
|
+
"name": "build-output.zip",
|
|
204
|
+
"type": "zip"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"path": "docs/**/*.pdf",
|
|
208
|
+
"type": "file",
|
|
209
|
+
"label": "Documentation"
|
|
210
|
+
}
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## 完整配置选项
|
|
216
|
+
|
|
217
|
+
| 选项 | 类型 | 默认值 | 描述 |
|
|
218
|
+
| -------------- | ------- | ---------------- | -------------------- |
|
|
219
|
+
| `host` | string | - | Gitea 服务器地址 |
|
|
220
|
+
| `owner` | string | - | 仓库所有者 |
|
|
221
|
+
| `repository` | string | - | 仓库名称 |
|
|
222
|
+
| `release` | boolean | `true` | 是否启用发布功能 |
|
|
223
|
+
| `releaseTitle` | string | `"v${version}"` | 发布标题模板 |
|
|
224
|
+
| `releaseNotes` | string | `"${changelog}"` | 发布说明模板 |
|
|
225
|
+
| `prerelease` | boolean | `false` | 是否为预发布版本 |
|
|
226
|
+
| `draft` | boolean | `false` | 是否创建为草稿 |
|
|
227
|
+
| `tokenRef` | string | `"GITEA_TOKEN"` | API Token 环境变量名 |
|
|
228
|
+
| `timeout` | number | `30000` | 请求超时时间(毫秒) |
|
|
229
|
+
| `assets` | array | `[]` | 附件配置列表 |
|
|
230
|
+
|
|
231
|
+
## 环境变量
|
|
232
|
+
|
|
233
|
+
设置 Gitea API Token:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
export GITEA_TOKEN=your_gitea_api_token
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
或者使用自定义环境变量名:
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"tokenRef": "MY_GITEA_TOKEN"
|
|
244
|
+
}
|
|
245
|
+
```
|
|
154
246
|
|
|
155
|
-
|
|
247
|
+
```bash
|
|
248
|
+
export MY_GITEA_TOKEN=your_gitea_api_token
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## 模板变量
|
|
252
|
+
|
|
253
|
+
在 `releaseTitle` 和 `releaseNotes` 中可以使用以下变量:
|
|
254
|
+
|
|
255
|
+
- `${version}`: 当前版本号
|
|
256
|
+
- `${latestVersion}`: 上一个版本号
|
|
257
|
+
- `${changelog}`: 变更日志
|
|
258
|
+
- `${name}`: 项目名称
|
|
259
|
+
- `${repo.owner}`: 仓库所有者
|
|
260
|
+
- `${repo.repository}`: 仓库名称
|
|
261
|
+
- `${branchName}`: 分支名称
|
|
262
|
+
|
|
263
|
+
## 故障排除
|
|
264
|
+
|
|
265
|
+
### 常见问题
|
|
266
|
+
|
|
267
|
+
1. **附件上传失败**
|
|
156
268
|
|
|
157
|
-
|
|
269
|
+
- 检查文件路径是否正确
|
|
270
|
+
- 确保文件存在且可读
|
|
271
|
+
- 检查 Gitea API Token 权限
|
|
158
272
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
-
|
|
162
|
-
-
|
|
163
|
-
|
|
273
|
+
2. **ZIP 文件创建失败**
|
|
274
|
+
|
|
275
|
+
- 确保有足够的磁盘空间
|
|
276
|
+
- 检查临时目录权限
|
|
277
|
+
|
|
278
|
+
3. **通配符匹配无文件**
|
|
279
|
+
|
|
280
|
+
- 验证通配符模式是否正确
|
|
281
|
+
- 检查当前工作目录
|
|
282
|
+
|
|
283
|
+
4. **API 请求失败**
|
|
284
|
+
- 检查 Gitea 服务器地址是否正确
|
|
285
|
+
- 验证 API Token 是否有效
|
|
286
|
+
- 确认仓库所有者和名称正确
|
|
287
|
+
|
|
288
|
+
### 调试模式
|
|
289
|
+
|
|
290
|
+
使用 `--verbose` 参数查看详细日志:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
npx release-it --verbose
|
|
294
|
+
```
|
|
164
295
|
|
|
165
296
|
## 开发
|
|
166
297
|
|
package/lib/global.d.d.ts
CHANGED
|
@@ -54,6 +54,21 @@ declare module "release-it" {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/** 附件配置接口 */
|
|
58
|
+
interface GiteaAssetConfig {
|
|
59
|
+
/** 文件或文件夹路径,支持通配符 */
|
|
60
|
+
path: string;
|
|
61
|
+
|
|
62
|
+
/** 上传后的文件名,如果不指定则使用原文件名 */
|
|
63
|
+
name?: string;
|
|
64
|
+
|
|
65
|
+
/** 文件类型:'file' 表示单个文件,'zip' 表示打包成 zip 文件 */
|
|
66
|
+
type?: "file" | "zip";
|
|
67
|
+
|
|
68
|
+
/** 文件标签,用于标识文件用途 */
|
|
69
|
+
label?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
57
72
|
interface GiteaConfig {
|
|
58
73
|
/** Gitea 服务器的完整 URL 地址 */
|
|
59
74
|
host: string;
|
|
@@ -84,4 +99,7 @@ interface GiteaConfig {
|
|
|
84
99
|
|
|
85
100
|
/** API 请求的超时时间,单位为毫秒 */
|
|
86
101
|
timeout?: number;
|
|
102
|
+
|
|
103
|
+
/** 要上传的附件列表 */
|
|
104
|
+
assets?: (GiteaAssetConfig | string)[];
|
|
87
105
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -58,6 +58,46 @@ declare class GiteaPlugin extends Plugin {
|
|
|
58
58
|
* @returns 替换变量后的字符串
|
|
59
59
|
*/
|
|
60
60
|
private interpolate;
|
|
61
|
+
/**
|
|
62
|
+
* 解析附件配置,将字符串格式转换为标准配置对象
|
|
63
|
+
* @param asset 附件配置
|
|
64
|
+
* @returns 标准化的附件配置对象
|
|
65
|
+
*/
|
|
66
|
+
private normalizeAssetConfig;
|
|
67
|
+
/**
|
|
68
|
+
* 根据路径模式匹配文件
|
|
69
|
+
* @param pattern 文件路径模式,支持通配符
|
|
70
|
+
* @returns 匹配到的文件路径数组
|
|
71
|
+
*/
|
|
72
|
+
private resolveFiles;
|
|
73
|
+
/**
|
|
74
|
+
* 创建 ZIP 压缩包
|
|
75
|
+
* @param files 要压缩的文件列表
|
|
76
|
+
* @param outputPath 输出的 ZIP 文件路径
|
|
77
|
+
* @param basePath 基础路径,用于确定文件在压缩包中的相对路径
|
|
78
|
+
* @returns Promise<void>
|
|
79
|
+
*/
|
|
80
|
+
private createZipArchive;
|
|
81
|
+
/**
|
|
82
|
+
* 上传单个附件到 Gitea 发布
|
|
83
|
+
* @param releaseId 发布 ID
|
|
84
|
+
* @param filePath 文件路径
|
|
85
|
+
* @param fileName 上传后的文件名
|
|
86
|
+
* @param label 文件标签
|
|
87
|
+
* @returns 上传结果
|
|
88
|
+
*/
|
|
89
|
+
private uploadAsset;
|
|
90
|
+
/**
|
|
91
|
+
* 处理并上传所有配置的附件
|
|
92
|
+
* @param releaseId 发布 ID
|
|
93
|
+
*/
|
|
94
|
+
private uploadAssets;
|
|
95
|
+
/**
|
|
96
|
+
* 处理单个附件配置
|
|
97
|
+
* @param releaseId 发布 ID
|
|
98
|
+
* @param config 附件配置
|
|
99
|
+
*/
|
|
100
|
+
private processAsset;
|
|
61
101
|
/**
|
|
62
102
|
* 执行发布操作,创建或更新 Gitea 发布.
|
|
63
103
|
* @throws 当发布创建失败时抛出错误
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import archiver from "archiver";
|
|
2
|
+
import FormData from "form-data";
|
|
3
|
+
import { createReadStream, createWriteStream, statSync } from "fs";
|
|
4
|
+
import { glob } from "glob";
|
|
1
5
|
import fetch from "node-fetch";
|
|
6
|
+
import { basename, dirname, join } from "path";
|
|
2
7
|
import { Plugin } from "release-it";
|
|
3
8
|
class GiteaPlugin extends Plugin {
|
|
4
9
|
static isEnabled(config) {
|
|
@@ -17,13 +22,13 @@ class GiteaPlugin extends Plugin {
|
|
|
17
22
|
const repo = this.config.getContext("repo");
|
|
18
23
|
const config = {
|
|
19
24
|
draft: gitea.draft ?? false,
|
|
20
|
-
host: gitea.host,
|
|
25
|
+
host: gitea.host ?? repo.host,
|
|
21
26
|
owner: gitea.owner ?? repo.owner,
|
|
22
27
|
prerelease: gitea.prerelease ?? false,
|
|
23
28
|
release: gitea.release !== false,
|
|
24
29
|
releaseNotes: gitea.releaseNotes ?? "${changelog}",
|
|
25
30
|
releaseTitle: gitea.releaseTitle ?? "v${version}",
|
|
26
|
-
repository: gitea.repository ?? repo.
|
|
31
|
+
repository: gitea.repository ?? repo.project,
|
|
27
32
|
timeout: gitea.timeout ?? 3e4,
|
|
28
33
|
tokenRef: gitea.tokenRef ?? "GITEA_TOKEN"
|
|
29
34
|
};
|
|
@@ -60,7 +65,10 @@ class GiteaPlugin extends Plugin {
|
|
|
60
65
|
* @returns 完整的 API URL
|
|
61
66
|
*/
|
|
62
67
|
buildApiUrl(endpoint) {
|
|
63
|
-
|
|
68
|
+
let host = this.giteaConfig.host.replace(/\/$/, "");
|
|
69
|
+
if (!host.startsWith("http")) {
|
|
70
|
+
host = `https://${host}`;
|
|
71
|
+
}
|
|
64
72
|
return `${host}/api/v1${endpoint}`;
|
|
65
73
|
}
|
|
66
74
|
/**
|
|
@@ -159,6 +167,177 @@ class GiteaPlugin extends Plugin {
|
|
|
159
167
|
const context = this.config.getContext();
|
|
160
168
|
return template.replace(/\$\{version\}/g, context.version).replace(/\$\{latestVersion\}/g, context.latestVersion).replace(/\$\{changelog\}/g, context.changelog).replace(/\$\{name\}/g, context.name).replace(/\$\{repo\.owner\}/g, context.repo.owner).replace(/\$\{repo\.repository\}/g, context.repo.repository).replace(/\$\{branchName\}/g, context.branchName);
|
|
161
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* 解析附件配置,将字符串格式转换为标准配置对象
|
|
172
|
+
* @param asset 附件配置
|
|
173
|
+
* @returns 标准化的附件配置对象
|
|
174
|
+
*/
|
|
175
|
+
normalizeAssetConfig(asset) {
|
|
176
|
+
if (typeof asset === "string") {
|
|
177
|
+
return {
|
|
178
|
+
path: asset,
|
|
179
|
+
type: "file"
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
type: "file",
|
|
184
|
+
...asset
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* 根据路径模式匹配文件
|
|
189
|
+
* @param pattern 文件路径模式,支持通配符
|
|
190
|
+
* @returns 匹配到的文件路径数组
|
|
191
|
+
*/
|
|
192
|
+
async resolveFiles(pattern) {
|
|
193
|
+
try {
|
|
194
|
+
const files = await glob(pattern, {
|
|
195
|
+
absolute: true,
|
|
196
|
+
nodir: true
|
|
197
|
+
});
|
|
198
|
+
return files;
|
|
199
|
+
} catch (error) {
|
|
200
|
+
this.log.warn(`\u6587\u4EF6\u5339\u914D\u5931\u8D25: ${pattern}, \u9519\u8BEF: ${error}`);
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* 创建 ZIP 压缩包
|
|
206
|
+
* @param files 要压缩的文件列表
|
|
207
|
+
* @param outputPath 输出的 ZIP 文件路径
|
|
208
|
+
* @param basePath 基础路径,用于确定文件在压缩包中的相对路径
|
|
209
|
+
* @returns Promise<void>
|
|
210
|
+
*/
|
|
211
|
+
async createZipArchive(files, outputPath, basePath) {
|
|
212
|
+
return new Promise((resolve, reject) => {
|
|
213
|
+
const output = createWriteStream(outputPath);
|
|
214
|
+
const archive = archiver("zip", {
|
|
215
|
+
zlib: { level: 9 }
|
|
216
|
+
});
|
|
217
|
+
output.on("close", () => {
|
|
218
|
+
this.log.verbose(
|
|
219
|
+
`ZIP \u6587\u4EF6\u521B\u5EFA\u5B8C\u6210: ${outputPath} (${archive.pointer()} bytes)`
|
|
220
|
+
);
|
|
221
|
+
resolve();
|
|
222
|
+
});
|
|
223
|
+
archive.on("error", (err) => {
|
|
224
|
+
reject(err);
|
|
225
|
+
});
|
|
226
|
+
archive.pipe(output);
|
|
227
|
+
for (const file of files) {
|
|
228
|
+
const stats = statSync(file);
|
|
229
|
+
if (stats.isFile()) {
|
|
230
|
+
const relativePath = basePath ? file.replace(basePath, "").replace(/^\//, "") : basename(file);
|
|
231
|
+
archive.file(file, { name: relativePath });
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
archive.finalize();
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* 上传单个附件到 Gitea 发布
|
|
239
|
+
* @param releaseId 发布 ID
|
|
240
|
+
* @param filePath 文件路径
|
|
241
|
+
* @param fileName 上传后的文件名
|
|
242
|
+
* @param label 文件标签
|
|
243
|
+
* @returns 上传结果
|
|
244
|
+
*/
|
|
245
|
+
async uploadAsset(releaseId, filePath, fileName, label) {
|
|
246
|
+
const url = this.buildApiUrl(
|
|
247
|
+
`/repos/${this.giteaConfig.owner}/${this.giteaConfig.repository}/releases/${releaseId}/assets`
|
|
248
|
+
);
|
|
249
|
+
const token = this.getToken();
|
|
250
|
+
const form = new FormData();
|
|
251
|
+
form.append("attachment", createReadStream(filePath), {
|
|
252
|
+
contentType: "application/octet-stream",
|
|
253
|
+
filename: fileName
|
|
254
|
+
});
|
|
255
|
+
if (label) {
|
|
256
|
+
form.append("name", label);
|
|
257
|
+
}
|
|
258
|
+
const requestOptions = {
|
|
259
|
+
body: form,
|
|
260
|
+
headers: {
|
|
261
|
+
...form.getHeaders(),
|
|
262
|
+
Authorization: `token ${token}`
|
|
263
|
+
},
|
|
264
|
+
method: "POST",
|
|
265
|
+
timeout: this.giteaConfig.timeout
|
|
266
|
+
};
|
|
267
|
+
this.log.verbose(`\u4E0A\u4F20\u9644\u4EF6: ${fileName} \u5230\u53D1\u5E03 ${releaseId}`);
|
|
268
|
+
try {
|
|
269
|
+
const response = await fetch(url, requestOptions);
|
|
270
|
+
if (!response.ok) {
|
|
271
|
+
const errorText = await response.text();
|
|
272
|
+
throw new Error(`\u9644\u4EF6\u4E0A\u4F20\u5931\u8D25 (${response.status}): ${errorText}`);
|
|
273
|
+
}
|
|
274
|
+
const result = await response.json();
|
|
275
|
+
this.log.info(`\u2705 \u9644\u4EF6\u4E0A\u4F20\u6210\u529F: ${fileName}`);
|
|
276
|
+
return result;
|
|
277
|
+
} catch (error) {
|
|
278
|
+
if (error instanceof Error) {
|
|
279
|
+
throw new Error(`\u9644\u4EF6\u4E0A\u4F20\u5931\u8D25: ${error.message}`);
|
|
280
|
+
}
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* 处理并上传所有配置的附件
|
|
286
|
+
* @param releaseId 发布 ID
|
|
287
|
+
*/
|
|
288
|
+
async uploadAssets(releaseId) {
|
|
289
|
+
const assets = this.giteaConfig.assets;
|
|
290
|
+
if (!assets || assets.length === 0) {
|
|
291
|
+
this.log.verbose("\u6CA1\u6709\u914D\u7F6E\u9644\u4EF6\uFF0C\u8DF3\u8FC7\u4E0A\u4F20");
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
this.log.info(`\u5F00\u59CB\u4E0A\u4F20 ${assets.length} \u4E2A\u9644\u4EF6...`);
|
|
295
|
+
for (const asset of assets) {
|
|
296
|
+
try {
|
|
297
|
+
const config = this.normalizeAssetConfig(asset);
|
|
298
|
+
await this.processAsset(releaseId, config);
|
|
299
|
+
} catch (error) {
|
|
300
|
+
this.log.error(
|
|
301
|
+
`\u9644\u4EF6\u5904\u7406\u5931\u8D25: ${JSON.stringify(asset)}, \u9519\u8BEF: ${error}`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
this.log.info("\u2705 \u6240\u6709\u9644\u4EF6\u5904\u7406\u5B8C\u6210");
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* 处理单个附件配置
|
|
309
|
+
* @param releaseId 发布 ID
|
|
310
|
+
* @param config 附件配置
|
|
311
|
+
*/
|
|
312
|
+
async processAsset(releaseId, config) {
|
|
313
|
+
const files = await this.resolveFiles(config.path);
|
|
314
|
+
if (files.length === 0) {
|
|
315
|
+
this.log.warn(`\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6587\u4EF6: ${config.path}`);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (config.type === "zip") {
|
|
319
|
+
const zipName = config.name || `${basename(config.path)}.zip`;
|
|
320
|
+
const tempZipPath = join(process.cwd(), ".temp", zipName);
|
|
321
|
+
const { mkdirSync } = await import("fs");
|
|
322
|
+
try {
|
|
323
|
+
mkdirSync(dirname(tempZipPath), { recursive: true });
|
|
324
|
+
} catch (error) {
|
|
325
|
+
}
|
|
326
|
+
await this.createZipArchive(files, tempZipPath, dirname(config.path));
|
|
327
|
+
await this.uploadAsset(releaseId, tempZipPath, zipName, config.label);
|
|
328
|
+
const { unlinkSync } = await import("fs");
|
|
329
|
+
try {
|
|
330
|
+
unlinkSync(tempZipPath);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
this.log.warn(`\u6E05\u7406\u4E34\u65F6\u6587\u4EF6\u5931\u8D25: ${tempZipPath}`);
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
for (const file of files) {
|
|
336
|
+
const fileName = config.name || basename(file);
|
|
337
|
+
await this.uploadAsset(releaseId, file, fileName, config.label);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
162
341
|
/**
|
|
163
342
|
* 执行发布操作,创建或更新 Gitea 发布.
|
|
164
343
|
* @throws 当发布创建失败时抛出错误
|
|
@@ -194,6 +373,9 @@ class GiteaPlugin extends Plugin {
|
|
|
194
373
|
release = await this.createRelease(releaseData);
|
|
195
374
|
}
|
|
196
375
|
this.log.info(`\u2705 Gitea \u53D1\u5E03\u521B\u5EFA\u6210\u529F: ${release.html_url}`);
|
|
376
|
+
if (this.giteaConfig.assets && this.giteaConfig.assets.length > 0) {
|
|
377
|
+
await this.uploadAssets(release.id);
|
|
378
|
+
}
|
|
197
379
|
this.config.setContext("releaseUrl", release.html_url);
|
|
198
380
|
} catch (error) {
|
|
199
381
|
if (error instanceof Error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "release-it-gitea",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "release-it gitea plugin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gitea",
|
|
@@ -44,12 +44,16 @@
|
|
|
44
44
|
"*": "prettier --ignore-unknown --write"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
+
"archiver": "^7.0.1",
|
|
48
|
+
"form-data": "^4.0.3",
|
|
49
|
+
"glob": "^11.0.3",
|
|
47
50
|
"node-fetch": "^3.3.2"
|
|
48
51
|
},
|
|
49
52
|
"devDependencies": {
|
|
50
53
|
"@eslint-community/eslint-plugin-eslint-comments": "4.5.0",
|
|
51
54
|
"@eslint/js": "9.22.0",
|
|
52
55
|
"@release-it/conventional-changelog": "10.0.0",
|
|
56
|
+
"@types/archiver": "^6.0.3",
|
|
53
57
|
"@types/eslint-plugin-markdown": "2.0.2",
|
|
54
58
|
"@types/node": "22.13.10",
|
|
55
59
|
"@types/node-fetch": "^2.6.11",
|