@vistta/cli 1.0.1 → 1.1.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 +3 -1
- package/bin.js +10 -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 +1 -5
- package/commands/test.js +38 -170
- package/global.d.ts +68 -0
- package/index.js +1 -0
- package/loaders/bundler.js +3 -1
- package/loaders/index.js +2 -2
- package/main.js +1 -1
- package/package.json +10 -11
- package/register.js +8 -7
- package/tests/bundler.test.js +118 -0
- package/tests/loader.test.js +23 -0
- package/tests/runtime.test.js +76 -0
- package/tests/test-runner.test.js +83 -0
- package/tsconfig.json +4 -5
- package/utils.js +85 -20
- package/.gitea/workflows/prerelease.yml +0 -62
- package/.gitea/workflows/release.yml +0 -124
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { mkdtempSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { Runtime } from "../classes/runtime.js";
|
|
5
|
+
|
|
6
|
+
suite("Runtime", () => {
|
|
7
|
+
test("resolve yields distinct URLs for plain vs bundler imports of the same file", async () => {
|
|
8
|
+
const runtime = new Runtime({ loaders: [], resolvers: [], options: {} });
|
|
9
|
+
const file = import.meta.filename || new URL(import.meta.url).pathname.replace(/^\//, "");
|
|
10
|
+
const captured = [];
|
|
11
|
+
const next = (url) => {
|
|
12
|
+
captured.push(url);
|
|
13
|
+
return url;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
await runtime.resolve(file, {}, next);
|
|
17
|
+
await runtime.resolve(file, { importAttributes: { type: "bundler" } }, next);
|
|
18
|
+
await runtime.resolve(file, { conditions: ["bundler"] }, next);
|
|
19
|
+
|
|
20
|
+
expect(captured[0]).not.toEqual(captured[1]);
|
|
21
|
+
expect(captured[0]).not.toEqual(captured[2]);
|
|
22
|
+
expect(captured[1]).toEqual(captured[2]);
|
|
23
|
+
expect(/\?__bundler__$/.test(captured[1])).toEqual(true);
|
|
24
|
+
expect(/\?__bundler__$/.test(captured[0])).toEqual(false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("resolve strips bundler suffix from parentURL when resolving relatives", async () => {
|
|
28
|
+
const runtime = new Runtime({ loaders: [], resolvers: [], options: {} });
|
|
29
|
+
const here = new URL(".", import.meta.url).href;
|
|
30
|
+
const parentURL = here + "runtime.test.js?__bundler__";
|
|
31
|
+
let captured;
|
|
32
|
+
await runtime.resolve("./runtime.test.js", { parentURL, importAttributes: { type: "bundler" } }, (url) => {
|
|
33
|
+
captured = url;
|
|
34
|
+
return url;
|
|
35
|
+
});
|
|
36
|
+
expect(captured.includes("?__bundler__?")).toEqual(false);
|
|
37
|
+
expect(/\?__bundler__$/.test(captured)).toEqual(true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("resolve detects bundler mode from importAttributes.type even without conditions", async () => {
|
|
41
|
+
const runtime = new Runtime({ loaders: [], resolvers: [], options: {} });
|
|
42
|
+
const file = import.meta.filename || new URL(import.meta.url).pathname.replace(/^\//, "");
|
|
43
|
+
const captured = [];
|
|
44
|
+
await runtime.resolve(file, { importAttributes: { type: "bundler" } }, (url) => {
|
|
45
|
+
captured.push(url);
|
|
46
|
+
return url;
|
|
47
|
+
});
|
|
48
|
+
expect(/\?__bundler__$/.test(captured[0])).toEqual(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("resolve marks context.file for absolute bundler imports", async () => {
|
|
52
|
+
const runtime = new Runtime({ loaders: [], resolvers: [], options: {} });
|
|
53
|
+
const dir = mkdtempSync(join(tmpdir(), "vistta-runtime-"));
|
|
54
|
+
const file = join(dir, "page.jsx");
|
|
55
|
+
writeFileSync(file, "export default 1;");
|
|
56
|
+
const ctx = { importAttributes: { type: "bundler" } };
|
|
57
|
+
await runtime.resolve(file, ctx, (url) => url);
|
|
58
|
+
expect(ctx.file).toEqual(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("resolve preserves non-bundler query string params", async () => {
|
|
62
|
+
const runtime = new Runtime({ loaders: [], resolvers: ["js"], options: {} });
|
|
63
|
+
const dir = mkdtempSync(join(tmpdir(), "vistta-runtime-"));
|
|
64
|
+
const file = join(dir, "data.js");
|
|
65
|
+
writeFileSync(file, "export default 1;");
|
|
66
|
+
const here = new URL(import.meta.url);
|
|
67
|
+
const hereURL = new URL(".", here).href;
|
|
68
|
+
const captured = [];
|
|
69
|
+
await runtime.resolve("./data.js?type=json", { parentURL: hereURL }, (url) => {
|
|
70
|
+
captured.push(url);
|
|
71
|
+
return url;
|
|
72
|
+
});
|
|
73
|
+
expect(captured[0].includes("type=json")).toEqual(true);
|
|
74
|
+
expect(/\?__bundler__$/.test(captured[0])).toEqual(false);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { TestRunner } from "../classes/test-runner.js";
|
|
2
|
+
|
|
3
|
+
suite("TestRunner", () => {
|
|
4
|
+
test("suite().test() registers a passing test", async () => {
|
|
5
|
+
const runner = new TestRunner();
|
|
6
|
+
runner.suite("s", () => {
|
|
7
|
+
runner.test("t", () => 1);
|
|
8
|
+
});
|
|
9
|
+
const results = await runner.run();
|
|
10
|
+
expect(results[0].name).toEqual("s");
|
|
11
|
+
expect(results[0].tests[0].name).toEqual("t");
|
|
12
|
+
expect(results[0].tests[0].status).toEqual("pass");
|
|
13
|
+
expect(results[0].tests[0].result).toEqual(1);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("failing test reports status=fail with the error", async () => {
|
|
17
|
+
const runner = new TestRunner();
|
|
18
|
+
runner.suite("s", () => {
|
|
19
|
+
runner.test("boom", () => {
|
|
20
|
+
throw new Error("kaboom");
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
const results = await runner.run();
|
|
24
|
+
expect(results[0].tests[0].status).toEqual("fail");
|
|
25
|
+
expect(results[0].tests[0].error.message).toEqual("kaboom");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("async tests are awaited before status is set", async () => {
|
|
29
|
+
const runner = new TestRunner();
|
|
30
|
+
runner.suite("s", () => {
|
|
31
|
+
runner.test("async", async () => {
|
|
32
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
33
|
+
return "done";
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
const results = await runner.run();
|
|
37
|
+
expect(results[0].tests[0].status).toEqual("pass");
|
|
38
|
+
expect(results[0].tests[0].result).toEqual("done");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("test.skip is recorded as 'skip'", async () => {
|
|
42
|
+
const runner = new TestRunner();
|
|
43
|
+
runner.suite("s", () => {
|
|
44
|
+
runner.test.skip("skipped", () => {});
|
|
45
|
+
});
|
|
46
|
+
const results = await runner.run();
|
|
47
|
+
expect(results[0].tests[0].status).toEqual("skip");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("only: option restricts execution to marked tests", async () => {
|
|
51
|
+
const runner = new TestRunner({ only: true });
|
|
52
|
+
runner.suite("s", () => {
|
|
53
|
+
runner.test("not run", () => "x");
|
|
54
|
+
runner.test.only("run", () => "y");
|
|
55
|
+
});
|
|
56
|
+
const results = await runner.run();
|
|
57
|
+
const names = results[0].tests.map((t) => t.name);
|
|
58
|
+
expect(names.length).toEqual(1);
|
|
59
|
+
expect(names[0]).toEqual("run");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("filter option restricts by test name (case-insensitive)", async () => {
|
|
63
|
+
const runner = new TestRunner({ filter: "MATCH" });
|
|
64
|
+
runner.suite("s", () => {
|
|
65
|
+
runner.test("will match", () => {});
|
|
66
|
+
runner.test("will skip", () => {});
|
|
67
|
+
});
|
|
68
|
+
const results = await runner.run();
|
|
69
|
+
const names = results[0].tests.map((t) => t.name);
|
|
70
|
+
expect(names.length).toEqual(1);
|
|
71
|
+
expect(names[0]).toEqual("will match");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("suite errors are swallowed and surfaced via empty tests", async () => {
|
|
75
|
+
const runner = new TestRunner();
|
|
76
|
+
runner.suite("s", () => {
|
|
77
|
+
throw new Error("setup failed");
|
|
78
|
+
});
|
|
79
|
+
const results = await runner.run();
|
|
80
|
+
expect(results.length).toEqual(1);
|
|
81
|
+
expect(results[0].tests.length).toEqual(0);
|
|
82
|
+
});
|
|
83
|
+
});
|
package/tsconfig.json
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"strict": false,
|
|
4
4
|
"noEmit": true,
|
|
5
5
|
"allowJs": true,
|
|
6
|
+
"checkJs": true,
|
|
6
7
|
"resolveJsonModule": true,
|
|
7
8
|
"module": "esnext",
|
|
8
9
|
"target": "ESNext",
|
|
@@ -10,9 +11,7 @@
|
|
|
10
11
|
"moduleResolution": "bundler",
|
|
11
12
|
"skipLibCheck": true,
|
|
12
13
|
"jsx": "react",
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
}
|
|
14
|
+
"paths": {}
|
|
15
|
+
},
|
|
16
|
+
"include": ["./**/*.js", "./global.d.ts"]
|
|
18
17
|
}
|
package/utils.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
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 {
|
|
4
|
+
import { platform } from "node:os";
|
|
5
|
+
import { isAbsolute } from "node:path";
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
7
|
import { inc, satisfies, valid } from "semver";
|
|
6
8
|
|
|
9
|
+
export const CWD = process.cwd();
|
|
10
|
+
export const FALLBACK = "*";
|
|
11
|
+
export const BUNDLER_SUFFIX = "?__bundler__";
|
|
12
|
+
export const BUNDLER_SUFFIX_RE = /\?__bundler__$/;
|
|
13
|
+
export const WITH_TYPE_RE = /[?&]__with_type=([^&#]*)/;
|
|
7
14
|
export const ENABLED_NODE_OPTIONS = {
|
|
8
15
|
"-w": "NODE_WATCH",
|
|
9
16
|
"--watch": "NODE_WATCH",
|
|
@@ -20,7 +27,7 @@ export const ENABLED_NODE_OPTIONS = {
|
|
|
20
27
|
|
|
21
28
|
export async function importJSON(filepath) {
|
|
22
29
|
try {
|
|
23
|
-
return JSON.parse(await fs.readFile(filepath));
|
|
30
|
+
return JSON.parse(String(await fs.readFile(filepath)));
|
|
24
31
|
} catch {
|
|
25
32
|
return {};
|
|
26
33
|
}
|
|
@@ -29,7 +36,7 @@ export async function importJSON(filepath) {
|
|
|
29
36
|
export async function importEnv(filepath) {
|
|
30
37
|
try {
|
|
31
38
|
const env = {};
|
|
32
|
-
const entries = (await fs.readFile(filepath, "utf8")).split("\n");
|
|
39
|
+
const entries = String(await fs.readFile(filepath, "utf8")).split("\n");
|
|
33
40
|
for (let i = 0, len = entries.length; i < len; i++) {
|
|
34
41
|
const [key, value] = entries[i].split("=");
|
|
35
42
|
env[key] = value;
|
|
@@ -43,7 +50,6 @@ export async function importEnv(filepath) {
|
|
|
43
50
|
export async function importConfig(options = {}) {
|
|
44
51
|
try {
|
|
45
52
|
const helper = async (file) => {
|
|
46
|
-
file = fileURLToPath(import.meta.resolve(file));
|
|
47
53
|
const {
|
|
48
54
|
extends: parent,
|
|
49
55
|
cliOptions: cli = {},
|
|
@@ -54,15 +60,15 @@ export async function importConfig(options = {}) {
|
|
|
54
60
|
const commands = Object.keys(cli.commands);
|
|
55
61
|
const dirname = fs.dirname(file);
|
|
56
62
|
for (let i = 0, len = commands.length; i < len; i++)
|
|
57
|
-
cli.commands[commands[i]] =
|
|
63
|
+
cli.commands[commands[i]] = resolve(dirname, cli.commands[commands[i]]);
|
|
58
64
|
}
|
|
59
|
-
if (parent) return assign(await helper(parent), { cli, compiler, bundler });
|
|
65
|
+
if (parent) return assign(await helper(ensurePath(parent)), { cli, compiler, bundler });
|
|
60
66
|
return { cli, compiler, bundler };
|
|
61
67
|
};
|
|
62
68
|
let file = fs.resolve(process.cwd(), "tsconfig.json");
|
|
63
69
|
if (!fs.existsSync(file)) file = fs.resolve(process.cwd(), "jsconfig.json");
|
|
64
70
|
if (!fs.existsSync(file)) return options;
|
|
65
|
-
return assign(options, await helper(file));
|
|
71
|
+
return assign(options, await helper(ensurePath(file)));
|
|
66
72
|
} catch (e) {
|
|
67
73
|
console.error(e.trace || e.message);
|
|
68
74
|
return options;
|
|
@@ -92,7 +98,14 @@ export async function getOutdatedPackages(dirname) {
|
|
|
92
98
|
const packageName = keys[i];
|
|
93
99
|
const { version, resolved, link } = packages["node_modules/" + packageName] || {};
|
|
94
100
|
if (link) continue;
|
|
95
|
-
if (!version)
|
|
101
|
+
if (!version) {
|
|
102
|
+
data[packageName] = {
|
|
103
|
+
package: path,
|
|
104
|
+
dev,
|
|
105
|
+
};
|
|
106
|
+
promises.push({ name: packageName });
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
96
109
|
promises.push(
|
|
97
110
|
new Promise((resolve, reject) =>
|
|
98
111
|
fetch(resolved ? resolved.split("/-/")[0] + "/latest" : `https://registry.npmjs.org/${packageName}/latest`)
|
|
@@ -116,7 +129,11 @@ export async function getOutdatedPackages(dirname) {
|
|
|
116
129
|
const module = data[values[i]?.name];
|
|
117
130
|
module.name = values[i]?.name;
|
|
118
131
|
module.latest = values[i]?.version;
|
|
119
|
-
if (!module
|
|
132
|
+
if (!module.current) {
|
|
133
|
+
result.push(module);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (!module || !module?.latest || !module?.wanted) throw new Error(`Internal Error`);
|
|
120
137
|
if (module.current === module.latest) continue;
|
|
121
138
|
module.wanted = satisfies(module.latest, module.wanted) ? module.latest : module.current;
|
|
122
139
|
result.push(module);
|
|
@@ -140,9 +157,8 @@ export function saveCrashReport() {
|
|
|
140
157
|
if (!fs.existsSync(crashFolder)) fs.mkdir(crashFolder);
|
|
141
158
|
const logs = console.logs;
|
|
142
159
|
let output = "";
|
|
143
|
-
for (let i = 0, len = logs.length; i < len; i++)
|
|
144
|
-
output += logs[i].time.
|
|
145
|
-
}
|
|
160
|
+
for (let i = 0, len = logs.length; i < len; i++)
|
|
161
|
+
output += logs[i].time.toLocalISOString() + ": " + logs[i].toString({ time: false });
|
|
146
162
|
fs.writeFileSync(fs.resolve(crashFolder, Date.now() + ".log"), output);
|
|
147
163
|
}
|
|
148
164
|
|
|
@@ -160,22 +176,60 @@ export function run(script, ...args) {
|
|
|
160
176
|
});
|
|
161
177
|
}
|
|
162
178
|
|
|
163
|
-
/**
|
|
164
|
-
* Function to parse command line arguments.
|
|
165
|
-
* @param {string[]} args
|
|
166
|
-
* @returns
|
|
167
|
-
*/
|
|
168
179
|
export function parseArgs(args) {
|
|
169
|
-
const result = [[], {}];
|
|
180
|
+
const result = /** @type {[string[], Record<string, any>]} */ ([[], {}]);
|
|
170
181
|
for (let i = 0, len = args.length; i < len; i++) {
|
|
171
|
-
const
|
|
182
|
+
const lower = args[i].toLowerCase();
|
|
183
|
+
const eq = lower.indexOf("=");
|
|
184
|
+
const option = eq === -1 ? lower : lower.slice(0, eq);
|
|
185
|
+
const value = eq === -1 ? undefined : args[i].slice(eq + 1);
|
|
172
186
|
if (option.startsWith("--")) result[1][option.slice(2)] = evaluate(value);
|
|
173
187
|
else result[0].push(args[i]);
|
|
174
188
|
}
|
|
175
189
|
return result;
|
|
176
190
|
}
|
|
177
191
|
|
|
178
|
-
export
|
|
192
|
+
export function resolve(...values) {
|
|
193
|
+
if (platform() === "win32") return pathToFileURL(fs.resolve(...values)).toString();
|
|
194
|
+
return fs.resolve(...values);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Append the bundler marker to a URL/path so it occupies a distinct slot in
|
|
199
|
+
* Node's ESM module cache from the same file imported without the marker.
|
|
200
|
+
* @param {string} url
|
|
201
|
+
* @returns {string}
|
|
202
|
+
*/
|
|
203
|
+
export function markBundler(url) {
|
|
204
|
+
return BUNDLER_SUFFIX_RE.test(url) ? url : url + BUNDLER_SUFFIX;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Remove the bundler marker from a URL/path if present.
|
|
209
|
+
* @param {string | undefined | null} url
|
|
210
|
+
* @returns {string}
|
|
211
|
+
*/
|
|
212
|
+
export function stripBundler(url) {
|
|
213
|
+
return url ? url.replace(BUNDLER_SUFFIX_RE, "") : "";
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Build a stable, sorted query string encoding the import attributes so that
|
|
218
|
+
* two imports of the same file with different `with: { ... }` attributes land
|
|
219
|
+
* in distinct ESM module cache slots. Mirrors the bundler marker pattern.
|
|
220
|
+
* @param {Record<string, any> | undefined | null} attrs
|
|
221
|
+
* @returns {string}
|
|
222
|
+
*/
|
|
223
|
+
export function markAttributes(attrs) {
|
|
224
|
+
if (!attrs) return "";
|
|
225
|
+
const keys = Object.keys(attrs)
|
|
226
|
+
.filter((k) => attrs[k] != null)
|
|
227
|
+
.sort();
|
|
228
|
+
if (keys.length === 0) return "";
|
|
229
|
+
return keys.map((k) => `${encodeURIComponent(k)}=${encodeURIComponent(String(attrs[k]))}`).join("&");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export { assign, async, extract, remove };
|
|
179
233
|
|
|
180
234
|
async function getProjectLock(path) {
|
|
181
235
|
if (fs.existsSync(path + "/package-lock.json")) return await importJSON(fs.resolve(path, "package-lock.json"));
|
|
@@ -206,3 +260,14 @@ function evaluate(value) {
|
|
|
206
260
|
if (value === "false") return false;
|
|
207
261
|
return value;
|
|
208
262
|
}
|
|
263
|
+
|
|
264
|
+
function ensurePath(value) {
|
|
265
|
+
try {
|
|
266
|
+
if (isAbsolute(value)) return value;
|
|
267
|
+
value = import.meta.resolve(value);
|
|
268
|
+
if (isAbsolute(value)) return value;
|
|
269
|
+
return fileURLToPath(value);
|
|
270
|
+
} catch {
|
|
271
|
+
return value;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
@@ -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);
|