@vistta/cli 1.0.1 → 1.2.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/LICENSE +674 -188
- package/README.md +150 -11
- package/bin.js +14 -4
- package/classes/bundler.js +172 -36
- package/classes/loader.js +1 -1
- package/classes/runtime.js +25 -15
- package/classes/test-runner.js +210 -0
- package/commands/bundle.js +4 -3
- package/commands/default.js +3 -1
- package/commands/project.js +110 -74
- package/commands/quality.js +52 -0
- package/commands/task.js +23 -0
- package/commands/test.js +39 -171
- package/config.js +128 -0
- package/config.schema.json +150 -0
- package/global.d.ts +68 -0
- package/index.js +4 -0
- package/loaders/bundler.js +3 -1
- package/loaders/index.js +2 -2
- package/main.js +1 -1
- package/package.json +17 -14
- package/register.js +15 -7
- package/tests/bundler.test.js +118 -0
- package/tests/config.test.js +217 -0
- package/tests/index.test.js +140 -0
- package/tests/loader.test.js +23 -0
- package/tests/project.test.js +116 -0
- package/tests/runtime.test.js +76 -0
- package/tests/test-runner.test.js +83 -0
- package/tools.js +210 -0
- package/tsconfig.json +4 -5
- package/utils.js +122 -108
- package/.gitea/workflows/prerelease.yml +0 -62
- package/.gitea/workflows/release.yml +0 -124
package/utils.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import fs from "@vistta/fs";
|
|
2
2
|
import { assign, async, extract, remove } from "@vistta/utils";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { platform } from "node:os";
|
|
5
|
+
import { relative } from "node:path";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
import { inc, valid } from "semver";
|
|
8
|
+
import { loadProjectConfig, mergeConfig } from "./config.js";
|
|
6
9
|
|
|
10
|
+
export const CWD = process.cwd();
|
|
11
|
+
export const FALLBACK = "*";
|
|
12
|
+
export const BUNDLER_SUFFIX = "?__bundler__";
|
|
13
|
+
export const BUNDLER_SUFFIX_RE = /\?__bundler__$/;
|
|
14
|
+
export const WITH_TYPE_RE = /[?&]__with_type=([^&#]*)/;
|
|
7
15
|
export const ENABLED_NODE_OPTIONS = {
|
|
8
16
|
"-w": "NODE_WATCH",
|
|
9
17
|
"--watch": "NODE_WATCH",
|
|
@@ -20,7 +28,7 @@ export const ENABLED_NODE_OPTIONS = {
|
|
|
20
28
|
|
|
21
29
|
export async function importJSON(filepath) {
|
|
22
30
|
try {
|
|
23
|
-
return JSON.parse(await fs.readFile(filepath));
|
|
31
|
+
return JSON.parse(String(await fs.readFile(filepath)));
|
|
24
32
|
} catch {
|
|
25
33
|
return {};
|
|
26
34
|
}
|
|
@@ -29,7 +37,7 @@ export async function importJSON(filepath) {
|
|
|
29
37
|
export async function importEnv(filepath) {
|
|
30
38
|
try {
|
|
31
39
|
const env = {};
|
|
32
|
-
const entries = (await fs.readFile(filepath, "utf8")).split("\n");
|
|
40
|
+
const entries = String(await fs.readFile(filepath, "utf8")).split("\n");
|
|
33
41
|
for (let i = 0, len = entries.length; i < len; i++) {
|
|
34
42
|
const [key, value] = entries[i].split("=");
|
|
35
43
|
env[key] = value;
|
|
@@ -41,85 +49,70 @@ export async function importEnv(filepath) {
|
|
|
41
49
|
}
|
|
42
50
|
|
|
43
51
|
export async function importConfig(options = {}) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
compilerOptions: compiler = {},
|
|
51
|
-
bundlerOptions: bundler = {},
|
|
52
|
-
} = await importJSON(file);
|
|
53
|
-
if (cli.commands) {
|
|
54
|
-
const commands = Object.keys(cli.commands);
|
|
55
|
-
const dirname = fs.dirname(file);
|
|
56
|
-
for (let i = 0, len = commands.length; i < len; i++)
|
|
57
|
-
cli.commands[commands[i]] = fs.resolve(dirname, cli.commands[commands[i]]);
|
|
58
|
-
}
|
|
59
|
-
if (parent) return assign(await helper(parent), { cli, compiler, bundler });
|
|
60
|
-
return { cli, compiler, bundler };
|
|
61
|
-
};
|
|
62
|
-
let file = fs.resolve(process.cwd(), "tsconfig.json");
|
|
63
|
-
if (!fs.existsSync(file)) file = fs.resolve(process.cwd(), "jsconfig.json");
|
|
64
|
-
if (!fs.existsSync(file)) return options;
|
|
65
|
-
return assign(options, await helper(file));
|
|
66
|
-
} catch (e) {
|
|
67
|
-
console.error(e.trace || e.message);
|
|
68
|
-
return options;
|
|
69
|
-
}
|
|
52
|
+
const { config } = loadProjectConfig();
|
|
53
|
+
return mergeConfig(options, {
|
|
54
|
+
cli: config.cli || {},
|
|
55
|
+
compiler: config.compilerOptions || {},
|
|
56
|
+
bundler: config.bundlerOptions || {},
|
|
57
|
+
});
|
|
70
58
|
}
|
|
71
59
|
|
|
72
60
|
export async function getOutdatedPackages(dirname) {
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
}
|
|
61
|
+
const lock = await getProjectLock(dirname);
|
|
62
|
+
const packages = lock.packages || {};
|
|
63
|
+
if (!fs.existsSync(fs.resolve(dirname, "package.json"))) throw new Error("No Package found");
|
|
64
|
+
const output = await new Promise((resolve, reject) => {
|
|
65
|
+
const npmPath = process.env.npm_execpath;
|
|
66
|
+
const args = ["outdated", "--json", "--long", "--include=dev"];
|
|
67
|
+
const windowsShell = !npmPath && platform() === "win32";
|
|
68
|
+
const command = npmPath ? process.execPath : windowsShell ? process.env.ComSpec || "cmd.exe" : "npm";
|
|
69
|
+
const child = spawn(
|
|
70
|
+
command,
|
|
71
|
+
npmPath ? [npmPath, ...args] : windowsShell ? ["/d", "/s", "/c", "npm " + args.join(" ")] : args,
|
|
72
|
+
{
|
|
73
|
+
cwd: dirname,
|
|
74
|
+
windowsHide: true,
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
let stdout = "";
|
|
78
|
+
let stderr = "";
|
|
79
|
+
child.stdout.on("data", (value) => {
|
|
80
|
+
stdout += value;
|
|
81
|
+
});
|
|
82
|
+
child.stderr.on("data", (value) => {
|
|
83
|
+
stderr += value;
|
|
84
|
+
});
|
|
85
|
+
child.on("error", reject);
|
|
86
|
+
child.on("close", (code) => {
|
|
87
|
+
try {
|
|
88
|
+
const data = JSON.parse(stdout || "{}");
|
|
89
|
+
if ((code !== 0 && code !== 1) || data.error)
|
|
90
|
+
reject(new Error(data.error?.summary || stderr || `npm outdated exited with code ${code}`));
|
|
91
|
+
else resolve(data);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
reject(new Error(stderr || error.message));
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
112
97
|
const result = [];
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
98
|
+
for (const [name, entries] of Object.entries(output)) {
|
|
99
|
+
for (const entry of Array.isArray(entries) ? entries : [entries]) {
|
|
100
|
+
const location = entry.location
|
|
101
|
+
? relative(lock.dirname, entry.location).replaceAll("\\", "/")
|
|
102
|
+
: "node_modules/" + name;
|
|
103
|
+
if (packages[location]?.link || packages["node_modules/" + name]?.link) continue;
|
|
104
|
+
if (entry.type !== "dependencies" && entry.type !== "devDependencies") continue;
|
|
105
|
+
const packagePath = fs.resolve(lock.dirname, entry.dependedByLocation || "", "package.json");
|
|
106
|
+
if (relative(lock.dirname, dirname) && relative(dirname, packagePath) !== "package.json") continue;
|
|
107
|
+
result.push({
|
|
108
|
+
name,
|
|
109
|
+
package: packagePath,
|
|
110
|
+
current: entry.current,
|
|
111
|
+
wanted: entry.wanted,
|
|
112
|
+
latest: entry.latest,
|
|
113
|
+
dev: entry.type === "devDependencies",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
123
116
|
}
|
|
124
117
|
return result;
|
|
125
118
|
}
|
|
@@ -140,9 +133,8 @@ export function saveCrashReport() {
|
|
|
140
133
|
if (!fs.existsSync(crashFolder)) fs.mkdir(crashFolder);
|
|
141
134
|
const logs = console.logs;
|
|
142
135
|
let output = "";
|
|
143
|
-
for (let i = 0, len = logs.length; i < len; i++)
|
|
144
|
-
output += logs[i].time.
|
|
145
|
-
}
|
|
136
|
+
for (let i = 0, len = logs.length; i < len; i++)
|
|
137
|
+
output += logs[i].time.toLocalISOString() + ": " + logs[i].toString({ time: false });
|
|
146
138
|
fs.writeFileSync(fs.resolve(crashFolder, Date.now() + ".log"), output);
|
|
147
139
|
}
|
|
148
140
|
|
|
@@ -160,47 +152,69 @@ export function run(script, ...args) {
|
|
|
160
152
|
});
|
|
161
153
|
}
|
|
162
154
|
|
|
163
|
-
/**
|
|
164
|
-
* Function to parse command line arguments.
|
|
165
|
-
* @param {string[]} args
|
|
166
|
-
* @returns
|
|
167
|
-
*/
|
|
168
155
|
export function parseArgs(args) {
|
|
169
|
-
const result = [[], {}];
|
|
156
|
+
const result = /** @type {[string[], Record<string, any>]} */ ([[], {}]);
|
|
170
157
|
for (let i = 0, len = args.length; i < len; i++) {
|
|
171
|
-
const
|
|
158
|
+
const lower = args[i].toLowerCase();
|
|
159
|
+
const eq = lower.indexOf("=");
|
|
160
|
+
const option = eq === -1 ? lower : lower.slice(0, eq);
|
|
161
|
+
const value = eq === -1 ? undefined : args[i].slice(eq + 1);
|
|
172
162
|
if (option.startsWith("--")) result[1][option.slice(2)] = evaluate(value);
|
|
173
163
|
else result[0].push(args[i]);
|
|
174
164
|
}
|
|
175
165
|
return result;
|
|
176
166
|
}
|
|
177
167
|
|
|
178
|
-
export
|
|
168
|
+
export function resolve(...values) {
|
|
169
|
+
if (platform() === "win32") return pathToFileURL(fs.resolve(...values)).toString();
|
|
170
|
+
return fs.resolve(...values);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Append the bundler marker to a URL/path so it occupies a distinct slot in
|
|
175
|
+
* Node's ESM module cache from the same file imported without the marker.
|
|
176
|
+
* @param {string} url
|
|
177
|
+
* @returns {string}
|
|
178
|
+
*/
|
|
179
|
+
export function markBundler(url) {
|
|
180
|
+
return BUNDLER_SUFFIX_RE.test(url) ? url : url + BUNDLER_SUFFIX;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Remove the bundler marker from a URL/path if present.
|
|
185
|
+
* @param {string | undefined | null} url
|
|
186
|
+
* @returns {string}
|
|
187
|
+
*/
|
|
188
|
+
export function stripBundler(url) {
|
|
189
|
+
return url ? url.replace(BUNDLER_SUFFIX_RE, "") : "";
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Build a stable, sorted query string encoding the import attributes so that
|
|
194
|
+
* two imports of the same file with different `with: { ... }` attributes land
|
|
195
|
+
* in distinct ESM module cache slots. Mirrors the bundler marker pattern.
|
|
196
|
+
* @param {Record<string, any> | undefined | null} attrs
|
|
197
|
+
* @returns {string}
|
|
198
|
+
*/
|
|
199
|
+
export function markAttributes(attrs) {
|
|
200
|
+
if (!attrs) return "";
|
|
201
|
+
const keys = Object.keys(attrs)
|
|
202
|
+
.filter((k) => attrs[k] != null)
|
|
203
|
+
.sort();
|
|
204
|
+
if (keys.length === 0) return "";
|
|
205
|
+
return keys.map((k) => `${encodeURIComponent(k)}=${encodeURIComponent(String(attrs[k]))}`).join("&");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export { assign, async, extract, remove };
|
|
179
209
|
|
|
180
210
|
async function getProjectLock(path) {
|
|
181
|
-
if (fs.existsSync(path + "/package-lock.json"))
|
|
211
|
+
if (fs.existsSync(path + "/package-lock.json"))
|
|
212
|
+
return { ...(await importJSON(fs.resolve(path, "package-lock.json"))), dirname: path };
|
|
182
213
|
const newPath = fs.resolve(path, "..");
|
|
183
214
|
if (newPath === path) throw new Error("No Package Lock found.");
|
|
184
215
|
return await getProjectLock(newPath);
|
|
185
216
|
}
|
|
186
217
|
|
|
187
|
-
async function resolveWorkspacesDirnames(workspaces) {
|
|
188
|
-
const paths = [];
|
|
189
|
-
for (let i = 0, len = workspaces.length; i < len; i++) {
|
|
190
|
-
if (!workspaces[i].includes("*")) {
|
|
191
|
-
paths.push(workspaces[i]);
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
const entries = fs.glob(workspaces[i]);
|
|
195
|
-
let entry = (await entries.next())?.value;
|
|
196
|
-
while (entry) {
|
|
197
|
-
paths.push(entry);
|
|
198
|
-
entry = (await entries.next())?.value;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return paths;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
218
|
function evaluate(value) {
|
|
205
219
|
if (value == null || value === "true") return true;
|
|
206
220
|
if (value === "false") return false;
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
name: Development Workflow
|
|
2
|
-
on:
|
|
3
|
-
push:
|
|
4
|
-
branches:
|
|
5
|
-
- development
|
|
6
|
-
env:
|
|
7
|
-
module: cli
|
|
8
|
-
jobs:
|
|
9
|
-
Validation:
|
|
10
|
-
runs-on: ubuntu-latest
|
|
11
|
-
container:
|
|
12
|
-
image: node:latest
|
|
13
|
-
steps:
|
|
14
|
-
- name: Checkout
|
|
15
|
-
uses: tiagomta/checkout@v1
|
|
16
|
-
with:
|
|
17
|
-
repository: vistta/workspace
|
|
18
|
-
ref: main
|
|
19
|
-
token: ${{ secrets.TOKEN }}
|
|
20
|
-
fetch-depth: 0
|
|
21
|
-
submodules: true
|
|
22
|
-
- name: Close Issues
|
|
23
|
-
uses: tiagomta/gitea@v1
|
|
24
|
-
with:
|
|
25
|
-
token: ${{ secrets.TOKEN }}
|
|
26
|
-
working-directory: ./packages/${{ env.module }}
|
|
27
|
-
command: issues close --fromCommit
|
|
28
|
-
- name: Setup Node
|
|
29
|
-
run: npm ci
|
|
30
|
-
- name: Module Validation
|
|
31
|
-
working-directory: ./packages/${{ env.module }}
|
|
32
|
-
run: npx vistta project outdated
|
|
33
|
-
continue-on-error: true
|
|
34
|
-
- name: Eslint Validation
|
|
35
|
-
working-directory: ./packages/${{ env.module }}
|
|
36
|
-
run: npx eslint .
|
|
37
|
-
continue-on-error: true
|
|
38
|
-
- name: Typescript Validation
|
|
39
|
-
working-directory: ./packages/${{ env.module }}
|
|
40
|
-
run: npx tsc
|
|
41
|
-
continue-on-error: true
|
|
42
|
-
- name: Unit Tests
|
|
43
|
-
working-directory: ./packages/${{ env.module }}
|
|
44
|
-
run: npx vistta test
|
|
45
|
-
|
|
46
|
-
PreRelease:
|
|
47
|
-
needs: Validation
|
|
48
|
-
runs-on: ubuntu-latest
|
|
49
|
-
container:
|
|
50
|
-
image: node:latest
|
|
51
|
-
steps:
|
|
52
|
-
- name: Checkout
|
|
53
|
-
uses: tiagomta/checkout@v1
|
|
54
|
-
- name: PreRelease
|
|
55
|
-
uses: tiagomta/npm@v1
|
|
56
|
-
with:
|
|
57
|
-
command: version prerelease SNAPSHOT-${{ gitea.run_id }} --email devops@vistta.org
|
|
58
|
-
- name: Publish PreRelease (Gitea)
|
|
59
|
-
uses: tiagomta/npm@v1
|
|
60
|
-
with:
|
|
61
|
-
command: publish --scope vistta --registry https://git.vistta.org/api/packages/vistta/npm/ --tag snapshot
|
|
62
|
-
token: ${{ secrets.TOKEN }}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
name: Release Workflow
|
|
2
|
-
on:
|
|
3
|
-
workflow_dispatch:
|
|
4
|
-
inputs:
|
|
5
|
-
type:
|
|
6
|
-
description: "Release Type"
|
|
7
|
-
required: true
|
|
8
|
-
type: choice
|
|
9
|
-
options:
|
|
10
|
-
- Patch
|
|
11
|
-
- Minor
|
|
12
|
-
- Major
|
|
13
|
-
npm_publish:
|
|
14
|
-
description: "Publish to NPM Registry"
|
|
15
|
-
required: false
|
|
16
|
-
default: true
|
|
17
|
-
type: boolean
|
|
18
|
-
gitea_publish:
|
|
19
|
-
description: "Publish to Gitea Registry"
|
|
20
|
-
required: false
|
|
21
|
-
default: true
|
|
22
|
-
type: boolean
|
|
23
|
-
|
|
24
|
-
env:
|
|
25
|
-
module: cli
|
|
26
|
-
ref: refs/heads/development
|
|
27
|
-
jobs:
|
|
28
|
-
Validation:
|
|
29
|
-
runs-on: ubuntu-latest
|
|
30
|
-
container:
|
|
31
|
-
image: node:latest
|
|
32
|
-
steps:
|
|
33
|
-
- name: Checkout
|
|
34
|
-
uses: tiagomta/checkout@v1
|
|
35
|
-
with:
|
|
36
|
-
repository: vistta/workspace
|
|
37
|
-
ref: main
|
|
38
|
-
token: ${{ secrets.TOKEN }}
|
|
39
|
-
fetch-depth: 0
|
|
40
|
-
submodules: true
|
|
41
|
-
- name: Setup Node
|
|
42
|
-
run: npm ci
|
|
43
|
-
- name: Module Validation
|
|
44
|
-
working-directory: ./packages/${{ env.module }}
|
|
45
|
-
run: npx vistta project outdated
|
|
46
|
-
- name: Eslint Validation
|
|
47
|
-
working-directory: ./packages/${{ env.module }}
|
|
48
|
-
run: npx eslint .
|
|
49
|
-
- name: Typescript Validation
|
|
50
|
-
working-directory: ./packages/${{ env.module }}
|
|
51
|
-
run: npx tsc
|
|
52
|
-
- name: Unit Tests
|
|
53
|
-
working-directory: ./packages/${{ env.module }}
|
|
54
|
-
run: npx vistta test
|
|
55
|
-
|
|
56
|
-
Release:
|
|
57
|
-
needs: Validation
|
|
58
|
-
runs-on: ubuntu-latest
|
|
59
|
-
container:
|
|
60
|
-
image: node:latest
|
|
61
|
-
if: github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/development'
|
|
62
|
-
steps:
|
|
63
|
-
- name: Checkout
|
|
64
|
-
uses: tiagomta/checkout@v1
|
|
65
|
-
with:
|
|
66
|
-
ref: ${{ env.ref }}
|
|
67
|
-
token: ${{ secrets.TOKEN }}
|
|
68
|
-
- name: NPM Release
|
|
69
|
-
uses: tiagomta/nodejs-run@v1
|
|
70
|
-
id: npm
|
|
71
|
-
with:
|
|
72
|
-
target: inline
|
|
73
|
-
run: |
|
|
74
|
-
await git.config("user.email", "devops@vistta.org");
|
|
75
|
-
const version = await npm.version("${{ inputs.type }}".toLowerCase());
|
|
76
|
-
// Release Gitea
|
|
77
|
-
if (`${{ inputs.gitea_publish }}`.toLowerCase() === "true") {
|
|
78
|
-
await npm.config("set", "@vistta:registry", "https://git.vistta.org/api/packages/vistta/npm/");
|
|
79
|
-
await npm.config("set", "--", "//git.vistta.org/api/packages/vistta/npm/:_authToken", "${{ secrets.TOKEN }}");
|
|
80
|
-
await npm.publish();
|
|
81
|
-
}
|
|
82
|
-
// Release NPM
|
|
83
|
-
if (`${{ inputs.npm_publish }}`.toLowerCase() === "true") {
|
|
84
|
-
await npm.config("set", "@vistta:registry", "https://registry.npmjs.org/");
|
|
85
|
-
await npm.config("set", "--", "//registry.npmjs.org/:_authToken", "${{ secrets.NPM_TOKEN }}");
|
|
86
|
-
await npm.publish("--access", "public");
|
|
87
|
-
}
|
|
88
|
-
// Update Branches
|
|
89
|
-
await git.push("origin", "${{ env.ref }}", "--tags");
|
|
90
|
-
await git.fetch("origin", "master");
|
|
91
|
-
await git.checkout("master");
|
|
92
|
-
await git.reset("--hard", "${{ env.ref }}");
|
|
93
|
-
await git.push("origin", "master", "--force");
|
|
94
|
-
result = version;
|
|
95
|
-
- name: Gitea Release
|
|
96
|
-
uses: tiagomta/nodejs-run@v1
|
|
97
|
-
with:
|
|
98
|
-
target: inline
|
|
99
|
-
run: |
|
|
100
|
-
const version = (`${{ steps.npm.outputs.result }}`).trim();
|
|
101
|
-
const milestone = await gitea.milestones.create("${{ inputs.type }} (" + version + ")");
|
|
102
|
-
const issues = await gitea.issues.list({ state: "closed" });
|
|
103
|
-
const types = {};
|
|
104
|
-
for (let issue of issues) {
|
|
105
|
-
const type =
|
|
106
|
-
issue.labels.find((l) => l.name.startsWith("Project/"))?.name.slice(8) +
|
|
107
|
-
"s";
|
|
108
|
-
if (type && issue.milestone == milestone.id) {
|
|
109
|
-
if (!types[type]) types[type] = `## ${type}\n`;
|
|
110
|
-
types[type] += `- ${issue.title} (#${issue.number})\n`;
|
|
111
|
-
} else if (issue.milestone == null) {
|
|
112
|
-
await gitea.issues.update(issue.number, { milestone: milestone.id });
|
|
113
|
-
if (type) {
|
|
114
|
-
if (!types[type]) types[type] = `## ${type}\n`;
|
|
115
|
-
types[type] += `- ${issue.title} (#${issue.number})\n`;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
await gitea.releases.create({
|
|
120
|
-
tag_name: version,
|
|
121
|
-
name: "${{ inputs.type }} (" + version + ")",
|
|
122
|
-
body: Object.values(types).join("\n")
|
|
123
|
-
});
|
|
124
|
-
await gitea.milestones.close(milestone.id);
|