@savvy-web/github-action-builder 0.7.4 → 0.7.6
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/bin/github-action-builder.d.ts +1 -0
- package/bin/github-action-builder.js +39 -307
- package/cli/commands/build.js +91 -0
- package/cli/commands/index.js +5 -0
- package/cli/commands/init.js +247 -0
- package/cli/commands/validate.js +42 -0
- package/errors.js +284 -0
- package/github-action.js +302 -0
- package/index.d.ts +1567 -1820
- package/index.js +10 -134
- package/layers/app.js +83 -0
- package/package.json +74 -90
- package/schemas/action-yml.js +110 -0
- package/schemas/config.js +190 -0
- package/schemas/path.js +43 -0
- package/services/build-live.js +223 -0
- package/services/build.js +63 -0
- package/services/config-live.js +111 -0
- package/services/config.js +63 -0
- package/services/persist-local-live.js +210 -0
- package/services/persist-local.js +37 -0
- package/services/validation-live.js +216 -0
- package/services/validation.js +77 -0
- package/tsdoc-metadata.json +11 -11
- package/231.js +0 -5
- package/612.js +0 -5
- package/948.js +0 -931
- /package/{tsconfig → public/tsconfig}/action.json +0 -0
package/948.js
DELETED
|
@@ -1,931 +0,0 @@
|
|
|
1
|
-
import { Context, Data, Effect, Layer, ParseResult, Schema } from "effect";
|
|
2
|
-
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { dirname, join, relative, resolve } from "node:path";
|
|
4
|
-
import { createRsbuild } from "@rsbuild/core";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { createJiti } from "jiti";
|
|
7
|
-
import { createHash } from "node:crypto";
|
|
8
|
-
import { parse } from "yaml-effect";
|
|
9
|
-
import { __webpack_require__ } from "./231.js";
|
|
10
|
-
const ConfigNotFoundBase = Data.TaggedError("ConfigNotFound");
|
|
11
|
-
class ConfigNotFound extends ConfigNotFoundBase {
|
|
12
|
-
}
|
|
13
|
-
const ConfigInvalidBase = Data.TaggedError("ConfigInvalid");
|
|
14
|
-
class ConfigInvalid extends ConfigInvalidBase {
|
|
15
|
-
}
|
|
16
|
-
const ConfigLoadFailedBase = Data.TaggedError("ConfigLoadFailed");
|
|
17
|
-
class ConfigLoadFailed extends ConfigLoadFailedBase {
|
|
18
|
-
}
|
|
19
|
-
const MainEntryMissingBase = Data.TaggedError("MainEntryMissing");
|
|
20
|
-
class MainEntryMissing extends MainEntryMissingBase {
|
|
21
|
-
}
|
|
22
|
-
const EntryFileMissingBase = Data.TaggedError("EntryFileMissing");
|
|
23
|
-
class EntryFileMissing extends (231 == __webpack_require__.j ? EntryFileMissingBase : null) {
|
|
24
|
-
}
|
|
25
|
-
const ActionYmlMissingBase = Data.TaggedError("ActionYmlMissing");
|
|
26
|
-
class ActionYmlMissing extends ActionYmlMissingBase {
|
|
27
|
-
}
|
|
28
|
-
const ActionYmlSyntaxErrorBase = Data.TaggedError("ActionYmlSyntaxError");
|
|
29
|
-
class ActionYmlSyntaxError extends ActionYmlSyntaxErrorBase {
|
|
30
|
-
}
|
|
31
|
-
const ActionYmlSchemaErrorBase = Data.TaggedError("ActionYmlSchemaError");
|
|
32
|
-
class ActionYmlSchemaError extends ActionYmlSchemaErrorBase {
|
|
33
|
-
}
|
|
34
|
-
const ValidationFailedBase = Data.TaggedError("ValidationFailed");
|
|
35
|
-
class ValidationFailed extends ValidationFailedBase {
|
|
36
|
-
}
|
|
37
|
-
const BundleFailedBase = Data.TaggedError("BundleFailed");
|
|
38
|
-
class BundleFailed extends BundleFailedBase {
|
|
39
|
-
}
|
|
40
|
-
const WriteErrorBase = Data.TaggedError("WriteError");
|
|
41
|
-
class WriteError extends WriteErrorBase {
|
|
42
|
-
}
|
|
43
|
-
const CleanErrorBase = Data.TaggedError("CleanError");
|
|
44
|
-
class CleanError extends CleanErrorBase {
|
|
45
|
-
}
|
|
46
|
-
const BuildFailedBase = Data.TaggedError("BuildFailed");
|
|
47
|
-
class BuildFailed extends BuildFailedBase {
|
|
48
|
-
}
|
|
49
|
-
const PersistLocalErrorBase = Data.TaggedError("PersistLocalError");
|
|
50
|
-
class PersistLocalError extends PersistLocalErrorBase {
|
|
51
|
-
}
|
|
52
|
-
const ActionYmlPathErrorBase = Data.TaggedError("ActionYmlPathError");
|
|
53
|
-
class ActionYmlPathError extends ActionYmlPathErrorBase {
|
|
54
|
-
}
|
|
55
|
-
function pathLikeToString(pathLike) {
|
|
56
|
-
if ("string" == typeof pathLike) return pathLike;
|
|
57
|
-
if (Buffer.isBuffer(pathLike)) return pathLike.toString("utf8");
|
|
58
|
-
if (pathLike instanceof URL) return fileURLToPath(pathLike);
|
|
59
|
-
return String(pathLike);
|
|
60
|
-
}
|
|
61
|
-
const PathLikeSchema = Schema.transform(Schema.Union(Schema.String, Schema.instanceOf(Buffer), Schema.instanceOf(URL)), Schema.String, {
|
|
62
|
-
strict: true,
|
|
63
|
-
decode: (pathLike)=>pathLikeToString(pathLike),
|
|
64
|
-
encode: (s)=>s
|
|
65
|
-
});
|
|
66
|
-
const OptionalPathLikeSchema = Schema.optional(PathLikeSchema);
|
|
67
|
-
const BuildRunnerOptionsSchema = Schema.Struct({
|
|
68
|
-
cwd: OptionalPathLikeSchema,
|
|
69
|
-
clean: Schema.optional(Schema.Boolean)
|
|
70
|
-
});
|
|
71
|
-
const BundleStatsSchema = Schema.Struct({
|
|
72
|
-
entry: Schema.String,
|
|
73
|
-
size: Schema.Number,
|
|
74
|
-
duration: Schema.Number,
|
|
75
|
-
outputPath: Schema.String
|
|
76
|
-
});
|
|
77
|
-
const BundleResultSchema = Schema.Struct({
|
|
78
|
-
success: Schema.Boolean,
|
|
79
|
-
stats: Schema.optional(BundleStatsSchema),
|
|
80
|
-
error: Schema.optional(Schema.String)
|
|
81
|
-
});
|
|
82
|
-
const BuildResultSchema = Schema.Struct({
|
|
83
|
-
success: Schema.Boolean,
|
|
84
|
-
entries: Schema.Array(BundleResultSchema),
|
|
85
|
-
duration: Schema.Number,
|
|
86
|
-
error: Schema.optional(Schema.String)
|
|
87
|
-
});
|
|
88
|
-
const BuildService = Context.GenericTag("BuildService");
|
|
89
|
-
const EntriesSchema = Schema.Struct({
|
|
90
|
-
main: Schema.optionalWith(Schema.String, {
|
|
91
|
-
default: ()=>"src/main.ts"
|
|
92
|
-
}),
|
|
93
|
-
pre: Schema.optional(Schema.String),
|
|
94
|
-
post: Schema.optional(Schema.String)
|
|
95
|
-
});
|
|
96
|
-
const BuildOptionsSchema = Schema.Struct({
|
|
97
|
-
minify: Schema.optionalWith(Schema.Boolean, {
|
|
98
|
-
default: ()=>true
|
|
99
|
-
}),
|
|
100
|
-
sourceMap: Schema.optionalWith(Schema.Boolean, {
|
|
101
|
-
default: ()=>false
|
|
102
|
-
}),
|
|
103
|
-
externals: Schema.optionalWith(Schema.Array(Schema.String), {
|
|
104
|
-
default: ()=>[]
|
|
105
|
-
}),
|
|
106
|
-
ignore: Schema.optionalWith(Schema.Array(Schema.String), {
|
|
107
|
-
default: ()=>[]
|
|
108
|
-
})
|
|
109
|
-
});
|
|
110
|
-
const ValidationOptionsSchema = Schema.Struct({
|
|
111
|
-
requireActionYml: Schema.optionalWith(Schema.Boolean, {
|
|
112
|
-
default: ()=>true
|
|
113
|
-
}),
|
|
114
|
-
maxBundleSize: Schema.optional(Schema.String),
|
|
115
|
-
strict: Schema.optional(Schema.Boolean)
|
|
116
|
-
});
|
|
117
|
-
const PersistLocalOptionsSchema = Schema.Struct({
|
|
118
|
-
enabled: Schema.optionalWith(Schema.Boolean, {
|
|
119
|
-
default: ()=>true
|
|
120
|
-
}),
|
|
121
|
-
path: Schema.optionalWith(Schema.String, {
|
|
122
|
-
default: ()=>".github/actions/local"
|
|
123
|
-
}),
|
|
124
|
-
actTemplate: Schema.optionalWith(Schema.Boolean, {
|
|
125
|
-
default: ()=>true
|
|
126
|
-
})
|
|
127
|
-
});
|
|
128
|
-
const ConfigInputSchema = Schema.Struct({
|
|
129
|
-
entries: Schema.optional(Schema.Struct({
|
|
130
|
-
main: Schema.optional(Schema.String),
|
|
131
|
-
pre: Schema.optional(Schema.String),
|
|
132
|
-
post: Schema.optional(Schema.String)
|
|
133
|
-
})),
|
|
134
|
-
build: Schema.optional(Schema.Struct({
|
|
135
|
-
minify: Schema.optional(Schema.Boolean),
|
|
136
|
-
sourceMap: Schema.optional(Schema.Boolean),
|
|
137
|
-
externals: Schema.optional(Schema.Array(Schema.String)),
|
|
138
|
-
ignore: Schema.optional(Schema.Array(Schema.String))
|
|
139
|
-
})),
|
|
140
|
-
validation: Schema.optional(Schema.Struct({
|
|
141
|
-
requireActionYml: Schema.optional(Schema.Boolean),
|
|
142
|
-
maxBundleSize: Schema.optional(Schema.String),
|
|
143
|
-
strict: Schema.optional(Schema.Boolean)
|
|
144
|
-
})),
|
|
145
|
-
persistLocal: Schema.optional(Schema.Struct({
|
|
146
|
-
enabled: Schema.optional(Schema.Boolean),
|
|
147
|
-
path: Schema.optional(Schema.String),
|
|
148
|
-
actTemplate: Schema.optional(Schema.Boolean)
|
|
149
|
-
}))
|
|
150
|
-
});
|
|
151
|
-
const ConfigSchema = Schema.Struct({
|
|
152
|
-
entries: EntriesSchema,
|
|
153
|
-
build: BuildOptionsSchema,
|
|
154
|
-
validation: ValidationOptionsSchema,
|
|
155
|
-
persistLocal: PersistLocalOptionsSchema
|
|
156
|
-
});
|
|
157
|
-
function defineConfig(config = {}) {
|
|
158
|
-
return Schema.decodeUnknownSync(ConfigSchema)({
|
|
159
|
-
entries: config.entries ?? {},
|
|
160
|
-
build: config.build ?? {},
|
|
161
|
-
validation: config.validation ?? {},
|
|
162
|
-
persistLocal: config.persistLocal ?? {}
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
const LoadConfigOptionsSchema = Schema.Struct({
|
|
166
|
-
cwd: OptionalPathLikeSchema,
|
|
167
|
-
configPath: OptionalPathLikeSchema
|
|
168
|
-
});
|
|
169
|
-
const EntryTypeSchema = Schema.Literal("main", "pre", "post");
|
|
170
|
-
const DetectedEntrySchema = Schema.Struct({
|
|
171
|
-
type: EntryTypeSchema,
|
|
172
|
-
path: Schema.String,
|
|
173
|
-
output: Schema.String
|
|
174
|
-
});
|
|
175
|
-
const DetectEntriesResultSchema = Schema.Struct({
|
|
176
|
-
success: Schema.Boolean,
|
|
177
|
-
entries: Schema.Array(DetectedEntrySchema)
|
|
178
|
-
});
|
|
179
|
-
Schema.Struct({
|
|
180
|
-
config: ConfigSchema,
|
|
181
|
-
configPath: Schema.optional(Schema.String),
|
|
182
|
-
usingDefaults: Schema.Boolean
|
|
183
|
-
});
|
|
184
|
-
const ConfigService = Context.GenericTag("ConfigService");
|
|
185
|
-
const IGNORE_STUB_SOURCE = `throw new Error("A module excluded via the build 'ignore' option was loaded at runtime.");\n`;
|
|
186
|
-
function formatBytes(bytes) {
|
|
187
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
188
|
-
if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
189
|
-
return `${(bytes / 1048576).toFixed(1)} MB`;
|
|
190
|
-
}
|
|
191
|
-
function formatBuildResult(result) {
|
|
192
|
-
const lines = [];
|
|
193
|
-
if (result.success) {
|
|
194
|
-
lines.push("Build Summary:");
|
|
195
|
-
for (const entry of result.entries)if (entry.success && entry.stats) {
|
|
196
|
-
const { entry: name, size, duration, outputPath } = entry.stats;
|
|
197
|
-
lines.push(` ✓ ${name}: ${formatBytes(size)} (${duration}ms) → ${outputPath}`);
|
|
198
|
-
}
|
|
199
|
-
lines.push(`\nTotal time: ${result.duration}ms`);
|
|
200
|
-
} else {
|
|
201
|
-
lines.push("Build Failed:");
|
|
202
|
-
for (const entry of result.entries)if (!entry.success) lines.push(` ✗ ${entry.error}`);
|
|
203
|
-
}
|
|
204
|
-
return lines.join("\n");
|
|
205
|
-
}
|
|
206
|
-
function cleanDirectory(dir) {
|
|
207
|
-
return Effect["try"]({
|
|
208
|
-
try: ()=>{
|
|
209
|
-
if (existsSync(dir)) rmSync(dir, {
|
|
210
|
-
recursive: true,
|
|
211
|
-
force: true
|
|
212
|
-
});
|
|
213
|
-
},
|
|
214
|
-
catch: (error)=>new CleanError({
|
|
215
|
-
directory: dir,
|
|
216
|
-
cause: error
|
|
217
|
-
})
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
function writeFile(path, content) {
|
|
221
|
-
return Effect["try"]({
|
|
222
|
-
try: ()=>{
|
|
223
|
-
const dir = resolve(path, "..");
|
|
224
|
-
mkdirSync(dir, {
|
|
225
|
-
recursive: true
|
|
226
|
-
});
|
|
227
|
-
writeFileSync(path, content, "utf8");
|
|
228
|
-
},
|
|
229
|
-
catch: (error)=>new WriteError({
|
|
230
|
-
path,
|
|
231
|
-
cause: error
|
|
232
|
-
})
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
function bundleEntry(entry, config, cwd) {
|
|
236
|
-
return Effect.gen(function*() {
|
|
237
|
-
const startTime = Date.now();
|
|
238
|
-
const outputDir = resolve(cwd, "dist");
|
|
239
|
-
const externalsSet = new Set(config.build.externals);
|
|
240
|
-
const ignoreSet = new Set(config.build.ignore);
|
|
241
|
-
const ignoreAlias = {};
|
|
242
|
-
if (config.build.ignore.length > 0) {
|
|
243
|
-
const stubPath = resolve(cwd, "node_modules", ".cache", "github-action-builder", "ignore-stub.mjs");
|
|
244
|
-
yield* writeFile(stubPath, IGNORE_STUB_SOURCE);
|
|
245
|
-
for (const moduleName of config.build.ignore)ignoreAlias[`${moduleName}$`] = stubPath;
|
|
246
|
-
}
|
|
247
|
-
const rsbuild = yield* Effect.tryPromise({
|
|
248
|
-
try: ()=>createRsbuild({
|
|
249
|
-
rsbuildConfig: {
|
|
250
|
-
source: {
|
|
251
|
-
entry: {
|
|
252
|
-
[entry.type]: entry.path
|
|
253
|
-
}
|
|
254
|
-
},
|
|
255
|
-
resolve: {
|
|
256
|
-
alias: ignoreAlias
|
|
257
|
-
},
|
|
258
|
-
output: {
|
|
259
|
-
target: "node",
|
|
260
|
-
module: true,
|
|
261
|
-
distPath: {
|
|
262
|
-
root: outputDir
|
|
263
|
-
},
|
|
264
|
-
filename: {
|
|
265
|
-
js: "[name].js"
|
|
266
|
-
},
|
|
267
|
-
externals: (data)=>{
|
|
268
|
-
const request = data.request;
|
|
269
|
-
if (!request) return false;
|
|
270
|
-
if (request.startsWith("node:")) return `node-commonjs ${request}`;
|
|
271
|
-
if (externalsSet.has(request) && !ignoreSet.has(request)) return request;
|
|
272
|
-
return false;
|
|
273
|
-
},
|
|
274
|
-
cleanDistPath: false,
|
|
275
|
-
legalComments: "inline",
|
|
276
|
-
minify: config.build.minify,
|
|
277
|
-
sourceMap: config.build.sourceMap ? {
|
|
278
|
-
js: "source-map"
|
|
279
|
-
} : false
|
|
280
|
-
},
|
|
281
|
-
performance: {
|
|
282
|
-
chunkSplit: {
|
|
283
|
-
strategy: "all-in-one"
|
|
284
|
-
}
|
|
285
|
-
},
|
|
286
|
-
tools: {
|
|
287
|
-
rspack: {
|
|
288
|
-
node: {
|
|
289
|
-
__dirname: "node-module",
|
|
290
|
-
__filename: "node-module"
|
|
291
|
-
},
|
|
292
|
-
output: {
|
|
293
|
-
asyncChunks: false
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}),
|
|
299
|
-
catch: (error)=>new BundleFailed({
|
|
300
|
-
entry: entry.path,
|
|
301
|
-
cause: error
|
|
302
|
-
})
|
|
303
|
-
});
|
|
304
|
-
const buildResult = yield* Effect.tryPromise({
|
|
305
|
-
try: ()=>rsbuild.build(),
|
|
306
|
-
catch: (error)=>new BundleFailed({
|
|
307
|
-
entry: entry.path,
|
|
308
|
-
cause: error
|
|
309
|
-
})
|
|
310
|
-
});
|
|
311
|
-
yield* Effect.tryPromise({
|
|
312
|
-
try: ()=>buildResult.close(),
|
|
313
|
-
catch: (error)=>new BundleFailed({
|
|
314
|
-
entry: entry.path,
|
|
315
|
-
cause: new Error(`rsbuild close() failed: ${error}`)
|
|
316
|
-
})
|
|
317
|
-
});
|
|
318
|
-
const outputPath = resolve(outputDir, `${entry.type}.js`);
|
|
319
|
-
const size = yield* Effect["try"]({
|
|
320
|
-
try: ()=>statSync(outputPath).size,
|
|
321
|
-
catch: (error)=>new BundleFailed({
|
|
322
|
-
entry: entry.path,
|
|
323
|
-
cause: error
|
|
324
|
-
})
|
|
325
|
-
});
|
|
326
|
-
const duration = Date.now() - startTime;
|
|
327
|
-
return {
|
|
328
|
-
success: true,
|
|
329
|
-
stats: {
|
|
330
|
-
entry: entry.type,
|
|
331
|
-
size,
|
|
332
|
-
duration,
|
|
333
|
-
outputPath: entry.output
|
|
334
|
-
}
|
|
335
|
-
};
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
const BuildServiceLive = Layer.effect(BuildService, Effect.gen(function*() {
|
|
339
|
-
const configService = yield* ConfigService;
|
|
340
|
-
return {
|
|
341
|
-
build: (config, options = {})=>Effect.gen(function*() {
|
|
342
|
-
const cwd = options.cwd ?? process.cwd();
|
|
343
|
-
const shouldClean = options.clean ?? true;
|
|
344
|
-
const startTime = Date.now();
|
|
345
|
-
const entriesConfig = {
|
|
346
|
-
main: config.entries.main
|
|
347
|
-
};
|
|
348
|
-
if (config.entries.pre) entriesConfig.pre = config.entries.pre;
|
|
349
|
-
if (config.entries.post) entriesConfig.post = config.entries.post;
|
|
350
|
-
const entriesResult = yield* configService.detectEntries(cwd, entriesConfig);
|
|
351
|
-
if (shouldClean) yield* cleanDirectory(resolve(cwd, "dist"));
|
|
352
|
-
const entryResults = [];
|
|
353
|
-
for (const entry of entriesResult.entries){
|
|
354
|
-
const result = yield* Effect.either(bundleEntry(entry, config, cwd));
|
|
355
|
-
if ("Left" === result._tag) {
|
|
356
|
-
const err = result.left;
|
|
357
|
-
entryResults.push({
|
|
358
|
-
success: false,
|
|
359
|
-
error: err.cause instanceof Error ? err.cause.message : String(err.cause)
|
|
360
|
-
});
|
|
361
|
-
} else entryResults.push(result.right);
|
|
362
|
-
}
|
|
363
|
-
yield* writeFile(resolve(cwd, "dist/package.json"), '{ "type": "module" }');
|
|
364
|
-
const duration = Date.now() - startTime;
|
|
365
|
-
const success = entryResults.every((r)=>r.success);
|
|
366
|
-
if (!success) return {
|
|
367
|
-
success,
|
|
368
|
-
entries: entryResults,
|
|
369
|
-
duration,
|
|
370
|
-
error: "One or more entries failed to build"
|
|
371
|
-
};
|
|
372
|
-
return {
|
|
373
|
-
success,
|
|
374
|
-
entries: entryResults,
|
|
375
|
-
duration
|
|
376
|
-
};
|
|
377
|
-
}),
|
|
378
|
-
bundle: (entry, config)=>bundleEntry(entry, config, process.cwd()),
|
|
379
|
-
clean: (outputDir)=>cleanDirectory(outputDir),
|
|
380
|
-
formatResult: formatBuildResult,
|
|
381
|
-
formatBytes: formatBytes
|
|
382
|
-
};
|
|
383
|
-
}));
|
|
384
|
-
const CONFIG_FILENAMES = [
|
|
385
|
-
"action.config.ts",
|
|
386
|
-
"action.config.js",
|
|
387
|
-
"action.config.mjs"
|
|
388
|
-
];
|
|
389
|
-
const DEFAULT_ENTRIES = {
|
|
390
|
-
main: "src/main.ts",
|
|
391
|
-
pre: "src/pre.ts",
|
|
392
|
-
post: "src/post.ts"
|
|
393
|
-
};
|
|
394
|
-
function findConfigFile(cwd) {
|
|
395
|
-
for (const filename of CONFIG_FILENAMES){
|
|
396
|
-
const configPath = resolve(cwd, filename);
|
|
397
|
-
if (existsSync(configPath)) return configPath;
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
function detectOptionalEntry(cwd, type, explicitPath) {
|
|
401
|
-
const defaultPath = DEFAULT_ENTRIES[type];
|
|
402
|
-
const entryPath = explicitPath ?? defaultPath;
|
|
403
|
-
const absolutePath = resolve(cwd, entryPath);
|
|
404
|
-
if (existsSync(absolutePath)) return {
|
|
405
|
-
type,
|
|
406
|
-
path: absolutePath,
|
|
407
|
-
output: `dist/${type}.js`
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
const ConfigServiceLive = Layer.succeed(ConfigService, {
|
|
411
|
-
load: (options = {})=>Effect.gen(function*() {
|
|
412
|
-
const cwd = options.cwd ?? process.cwd();
|
|
413
|
-
const configPath = options.configPath ?? findConfigFile(cwd);
|
|
414
|
-
if (!configPath) return {
|
|
415
|
-
config: defineConfig({}),
|
|
416
|
-
usingDefaults: true
|
|
417
|
-
};
|
|
418
|
-
if (!existsSync(configPath)) return yield* Effect.fail(new ConfigNotFound({
|
|
419
|
-
path: configPath,
|
|
420
|
-
message: "Specified config file does not exist"
|
|
421
|
-
}));
|
|
422
|
-
const absolutePath = resolve(cwd, configPath);
|
|
423
|
-
const configModule = yield* Effect.tryPromise({
|
|
424
|
-
try: async ()=>{
|
|
425
|
-
if (absolutePath.endsWith(".ts")) {
|
|
426
|
-
const jiti = createJiti(absolutePath, {
|
|
427
|
-
interopDefault: true
|
|
428
|
-
});
|
|
429
|
-
return jiti.import(absolutePath);
|
|
430
|
-
}
|
|
431
|
-
return import(absolutePath);
|
|
432
|
-
},
|
|
433
|
-
catch: (error)=>new ConfigLoadFailed({
|
|
434
|
-
path: configPath,
|
|
435
|
-
cause: error
|
|
436
|
-
})
|
|
437
|
-
});
|
|
438
|
-
const configInput = configModule.default;
|
|
439
|
-
if (!configInput || "object" != typeof configInput) return yield* Effect.fail(new ConfigInvalid({
|
|
440
|
-
path: configPath,
|
|
441
|
-
errors: [
|
|
442
|
-
"Config file must export a default configuration object"
|
|
443
|
-
]
|
|
444
|
-
}));
|
|
445
|
-
const config = defineConfig(configInput);
|
|
446
|
-
return {
|
|
447
|
-
config,
|
|
448
|
-
configPath,
|
|
449
|
-
usingDefaults: false
|
|
450
|
-
};
|
|
451
|
-
}),
|
|
452
|
-
resolve: (input = {})=>Effect.succeed(defineConfig(input)),
|
|
453
|
-
detectEntries: (cwd, entries)=>Effect.gen(function*() {
|
|
454
|
-
const detected = [];
|
|
455
|
-
const mainPath = entries?.main ?? DEFAULT_ENTRIES.main;
|
|
456
|
-
const absoluteMainPath = resolve(cwd, mainPath);
|
|
457
|
-
if (!existsSync(absoluteMainPath)) return yield* Effect.fail(new MainEntryMissing({
|
|
458
|
-
expectedPath: mainPath,
|
|
459
|
-
cwd
|
|
460
|
-
}));
|
|
461
|
-
detected.push({
|
|
462
|
-
type: "main",
|
|
463
|
-
path: absoluteMainPath,
|
|
464
|
-
output: "dist/main.js"
|
|
465
|
-
});
|
|
466
|
-
const preEntry = detectOptionalEntry(cwd, "pre", entries?.pre);
|
|
467
|
-
if (preEntry) detected.push(preEntry);
|
|
468
|
-
const postEntry = detectOptionalEntry(cwd, "post", entries?.post);
|
|
469
|
-
if (postEntry) detected.push(postEntry);
|
|
470
|
-
return {
|
|
471
|
-
success: true,
|
|
472
|
-
entries: detected
|
|
473
|
-
};
|
|
474
|
-
})
|
|
475
|
-
});
|
|
476
|
-
const PersistLocalRunnerOptionsSchema = Schema.Struct({
|
|
477
|
-
cwd: Schema.optional(Schema.String)
|
|
478
|
-
});
|
|
479
|
-
const PersistLocalResultSchema = Schema.Struct({
|
|
480
|
-
success: Schema.Boolean,
|
|
481
|
-
filesCopied: Schema.Number,
|
|
482
|
-
filesSkipped: Schema.Number,
|
|
483
|
-
actTemplateGenerated: Schema.Boolean,
|
|
484
|
-
outputPath: Schema.String,
|
|
485
|
-
error: Schema.optional(Schema.String)
|
|
486
|
-
});
|
|
487
|
-
const PersistLocalService = Context.GenericTag("PersistLocalService");
|
|
488
|
-
function fileHash(filePath) {
|
|
489
|
-
const content = readFileSync(filePath);
|
|
490
|
-
return createHash("sha256").update(content).digest("hex");
|
|
491
|
-
}
|
|
492
|
-
function syncFile(src, dest) {
|
|
493
|
-
if (existsSync(dest)) {
|
|
494
|
-
const srcHash = fileHash(src);
|
|
495
|
-
const destHash = fileHash(dest);
|
|
496
|
-
if (srcHash === destHash) return false;
|
|
497
|
-
}
|
|
498
|
-
mkdirSync(dirname(dest), {
|
|
499
|
-
recursive: true
|
|
500
|
-
});
|
|
501
|
-
copyFileSync(src, dest);
|
|
502
|
-
return true;
|
|
503
|
-
}
|
|
504
|
-
function walkDirectory(dir, base = dir) {
|
|
505
|
-
const files = [];
|
|
506
|
-
if (!existsSync(dir)) return files;
|
|
507
|
-
for (const entry of readdirSync(dir, {
|
|
508
|
-
withFileTypes: true
|
|
509
|
-
})){
|
|
510
|
-
const fullPath = join(dir, entry.name);
|
|
511
|
-
if (entry.isDirectory()) files.push(...walkDirectory(fullPath, base));
|
|
512
|
-
else files.push(relative(base, fullPath));
|
|
513
|
-
}
|
|
514
|
-
return files;
|
|
515
|
-
}
|
|
516
|
-
function syncDirectory(srcDir, destDir) {
|
|
517
|
-
const stats = {
|
|
518
|
-
copied: 0,
|
|
519
|
-
skipped: 0
|
|
520
|
-
};
|
|
521
|
-
const srcFiles = walkDirectory(srcDir);
|
|
522
|
-
for (const relPath of srcFiles){
|
|
523
|
-
const copied = syncFile(join(srcDir, relPath), join(destDir, relPath));
|
|
524
|
-
if (copied) stats.copied++;
|
|
525
|
-
else stats.skipped++;
|
|
526
|
-
}
|
|
527
|
-
const srcFileSet = new Set(srcFiles);
|
|
528
|
-
const destFiles = walkDirectory(destDir);
|
|
529
|
-
for (const relPath of destFiles)if (!srcFileSet.has(relPath)) {
|
|
530
|
-
rmSync(join(destDir, relPath), {
|
|
531
|
-
force: true
|
|
532
|
-
});
|
|
533
|
-
let parent = dirname(join(destDir, relPath));
|
|
534
|
-
while(parent !== destDir && existsSync(parent)){
|
|
535
|
-
const entries = readdirSync(parent);
|
|
536
|
-
if (0 === entries.length) {
|
|
537
|
-
rmSync(parent, {
|
|
538
|
-
recursive: true
|
|
539
|
-
});
|
|
540
|
-
parent = dirname(parent);
|
|
541
|
-
} else break;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
return stats;
|
|
545
|
-
}
|
|
546
|
-
function validateActionYmlPaths(actionYmlPath, destDir) {
|
|
547
|
-
return Effect.gen(function*() {
|
|
548
|
-
if (!existsSync(actionYmlPath)) return;
|
|
549
|
-
const content = readFileSync(actionYmlPath, "utf8");
|
|
550
|
-
const parsed = yield* parse(content).pipe(Effect.catchAll(()=>Effect.succeed(null)));
|
|
551
|
-
if (!parsed?.runs) return;
|
|
552
|
-
for (const entryType of [
|
|
553
|
-
"main",
|
|
554
|
-
"pre",
|
|
555
|
-
"post"
|
|
556
|
-
]){
|
|
557
|
-
const specifiedPath = parsed.runs[entryType];
|
|
558
|
-
if (!specifiedPath) continue;
|
|
559
|
-
const expectedPath = resolve(destDir, specifiedPath);
|
|
560
|
-
if (!existsSync(expectedPath)) return yield* Effect.fail(new ActionYmlPathError({
|
|
561
|
-
entryType,
|
|
562
|
-
specifiedPath,
|
|
563
|
-
expectedPath
|
|
564
|
-
}));
|
|
565
|
-
}
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
|
-
const ACTRC_CONTENT = `--container-architecture linux/amd64
|
|
569
|
-
-W .github/workflows/act-test.yml
|
|
570
|
-
`;
|
|
571
|
-
const ACT_WORKFLOW_CONTENT = `name: Local Test
|
|
572
|
-
on:
|
|
573
|
-
workflow_dispatch:
|
|
574
|
-
|
|
575
|
-
jobs:
|
|
576
|
-
test:
|
|
577
|
-
runs-on: ubuntu-latest
|
|
578
|
-
steps:
|
|
579
|
-
- uses: actions/checkout@v6
|
|
580
|
-
- uses: ./.github/actions/local
|
|
581
|
-
`;
|
|
582
|
-
function formatPersistResult(result) {
|
|
583
|
-
const lines = [];
|
|
584
|
-
if (result.success) {
|
|
585
|
-
lines.push("Persist Local Summary:");
|
|
586
|
-
lines.push(` Output: ${result.outputPath}`);
|
|
587
|
-
lines.push(` Files copied: ${result.filesCopied}`);
|
|
588
|
-
lines.push(` Files skipped (unchanged): ${result.filesSkipped}`);
|
|
589
|
-
if (result.actTemplateGenerated) lines.push(" Act template files generated");
|
|
590
|
-
} else lines.push(`Persist Local Failed: ${result.error}`);
|
|
591
|
-
return lines.join("\n");
|
|
592
|
-
}
|
|
593
|
-
const PersistLocalServiceLive = Layer.succeed(PersistLocalService, {
|
|
594
|
-
persist: (config, options = {})=>Effect.gen(function*() {
|
|
595
|
-
const cwd = options.cwd ?? process.cwd();
|
|
596
|
-
const outputPath = resolve(cwd, config.persistLocal.path);
|
|
597
|
-
if (!config.persistLocal.enabled) return {
|
|
598
|
-
success: true,
|
|
599
|
-
filesCopied: 0,
|
|
600
|
-
filesSkipped: 0,
|
|
601
|
-
actTemplateGenerated: false,
|
|
602
|
-
outputPath
|
|
603
|
-
};
|
|
604
|
-
yield* Effect["try"]({
|
|
605
|
-
try: ()=>mkdirSync(outputPath, {
|
|
606
|
-
recursive: true
|
|
607
|
-
}),
|
|
608
|
-
catch: (error)=>new PersistLocalError({
|
|
609
|
-
path: outputPath,
|
|
610
|
-
cause: error
|
|
611
|
-
})
|
|
612
|
-
});
|
|
613
|
-
let totalCopied = 0;
|
|
614
|
-
let totalSkipped = 0;
|
|
615
|
-
const actionYmlSrc = resolve(cwd, "action.yml");
|
|
616
|
-
const actionYmlDest = resolve(outputPath, "action.yml");
|
|
617
|
-
if (existsSync(actionYmlSrc)) {
|
|
618
|
-
const copied = yield* Effect["try"]({
|
|
619
|
-
try: ()=>syncFile(actionYmlSrc, actionYmlDest),
|
|
620
|
-
catch: (error)=>new PersistLocalError({
|
|
621
|
-
path: actionYmlSrc,
|
|
622
|
-
cause: error
|
|
623
|
-
})
|
|
624
|
-
});
|
|
625
|
-
if (copied) totalCopied++;
|
|
626
|
-
else totalSkipped++;
|
|
627
|
-
} else if (existsSync(actionYmlDest)) rmSync(actionYmlDest, {
|
|
628
|
-
force: true
|
|
629
|
-
});
|
|
630
|
-
const distSrc = resolve(cwd, "dist");
|
|
631
|
-
if (existsSync(distSrc) && statSync(distSrc).isDirectory()) {
|
|
632
|
-
const distStats = yield* Effect["try"]({
|
|
633
|
-
try: ()=>syncDirectory(distSrc, resolve(outputPath, "dist")),
|
|
634
|
-
catch: (error)=>new PersistLocalError({
|
|
635
|
-
path: distSrc,
|
|
636
|
-
cause: error
|
|
637
|
-
})
|
|
638
|
-
});
|
|
639
|
-
totalCopied += distStats.copied;
|
|
640
|
-
totalSkipped += distStats.skipped;
|
|
641
|
-
}
|
|
642
|
-
const destActionYml = resolve(outputPath, "action.yml");
|
|
643
|
-
yield* validateActionYmlPaths(destActionYml, outputPath);
|
|
644
|
-
let actTemplateGenerated = false;
|
|
645
|
-
if (config.persistLocal.actTemplate) {
|
|
646
|
-
const actrcPath = resolve(cwd, ".actrc");
|
|
647
|
-
const actWorkflowPath = resolve(cwd, ".github/workflows/act-test.yml");
|
|
648
|
-
if (!existsSync(actrcPath)) {
|
|
649
|
-
yield* Effect["try"]({
|
|
650
|
-
try: ()=>writeFileSync(actrcPath, ACTRC_CONTENT, "utf8"),
|
|
651
|
-
catch: (error)=>new PersistLocalError({
|
|
652
|
-
path: actrcPath,
|
|
653
|
-
cause: error
|
|
654
|
-
})
|
|
655
|
-
});
|
|
656
|
-
actTemplateGenerated = true;
|
|
657
|
-
}
|
|
658
|
-
if (!existsSync(actWorkflowPath)) {
|
|
659
|
-
yield* Effect["try"]({
|
|
660
|
-
try: ()=>{
|
|
661
|
-
mkdirSync(dirname(actWorkflowPath), {
|
|
662
|
-
recursive: true
|
|
663
|
-
});
|
|
664
|
-
writeFileSync(actWorkflowPath, ACT_WORKFLOW_CONTENT, "utf8");
|
|
665
|
-
},
|
|
666
|
-
catch: (error)=>new PersistLocalError({
|
|
667
|
-
path: actWorkflowPath,
|
|
668
|
-
cause: error
|
|
669
|
-
})
|
|
670
|
-
});
|
|
671
|
-
actTemplateGenerated = true;
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
return {
|
|
675
|
-
success: true,
|
|
676
|
-
filesCopied: totalCopied,
|
|
677
|
-
filesSkipped: totalSkipped,
|
|
678
|
-
actTemplateGenerated,
|
|
679
|
-
outputPath
|
|
680
|
-
};
|
|
681
|
-
}),
|
|
682
|
-
formatResult: formatPersistResult
|
|
683
|
-
});
|
|
684
|
-
const BrandingIcon = Schema.Literal("activity", "airplay", "alert-circle", "alert-octagon", "alert-triangle", "align-center", "align-justify", "align-left", "align-right", "anchor", "aperture", "archive", "arrow-down-circle", "arrow-down-left", "arrow-down-right", "arrow-down", "arrow-left-circle", "arrow-left", "arrow-right-circle", "arrow-right", "arrow-up-circle", "arrow-up-left", "arrow-up-right", "arrow-up", "at-sign", "award", "bar-chart-2", "bar-chart", "battery-charging", "battery", "bell-off", "bell", "bluetooth", "bold", "book-open", "book", "bookmark", "box", "briefcase", "calendar", "camera-off", "camera", "cast", "check-circle", "check-square", "check", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "chevrons-down", "chevrons-left", "chevrons-right", "chevrons-up", "circle", "clipboard", "clock", "cloud-drizzle", "cloud-lightning", "cloud-off", "cloud-rain", "cloud-snow", "cloud", "code", "command", "compass", "copy", "corner-down-left", "corner-down-right", "corner-left-down", "corner-left-up", "corner-right-down", "corner-right-up", "corner-up-left", "corner-up-right", "cpu", "credit-card", "crop", "crosshair", "database", "delete", "disc", "dollar-sign", "download-cloud", "download", "droplet", "edit-2", "edit-3", "edit", "external-link", "eye-off", "eye", "fast-forward", "feather", "file-minus", "file-plus", "file-text", "file", "film", "filter", "flag", "folder-minus", "folder-plus", "folder", "gift", "git-branch", "git-commit", "git-merge", "git-pull-request", "globe", "grid", "hard-drive", "hash", "headphones", "heart", "help-circle", "home", "image", "inbox", "info", "italic", "layers", "layout", "life-buoy", "link-2", "link", "list", "loader", "lock", "log-in", "log-out", "mail", "map-pin", "map", "maximize-2", "maximize", "menu", "message-circle", "message-square", "mic-off", "mic", "minimize-2", "minimize", "minus-circle", "minus-square", "minus", "monitor", "moon", "more-horizontal", "more-vertical", "move", "music", "navigation-2", "navigation", "octagon", "package", "paperclip", "pause-circle", "pause", "percent", "phone-call", "phone-forwarded", "phone-incoming", "phone-missed", "phone-off", "phone-outgoing", "phone", "pie-chart", "play-circle", "play", "plus-circle", "plus-square", "plus", "pocket", "power", "printer", "radio", "refresh-ccw", "refresh-cw", "repeat", "rewind", "rotate-ccw", "rotate-cw", "rss", "save", "scissors", "search", "send", "server", "settings", "share-2", "share", "shield-off", "shield", "shopping-bag", "shopping-cart", "shuffle", "sidebar", "skip-back", "skip-forward", "slash", "sliders", "smartphone", "speaker", "square", "star", "stop-circle", "sun", "sunrise", "sunset", "table", "tablet", "tag", "target", "terminal", "thermometer", "thumbs-down", "thumbs-up", "toggle-left", "toggle-right", "trash-2", "trash", "trending-down", "trending-up", "triangle", "truck", "tv", "type", "umbrella", "underline", "unlock", "upload-cloud", "upload", "user-check", "user-minus", "user-plus", "user-x", "user", "users", "video-off", "video", "voicemail", "volume-1", "volume-2", "volume-x", "volume", "watch", "wifi-off", "wifi", "wind", "x-circle", "x-square", "x", "zap-off", "zap", "zoom-in", "zoom-out");
|
|
685
|
-
const ActionInput = Schema.Struct({
|
|
686
|
-
description: Schema.String,
|
|
687
|
-
required: Schema.optional(Schema.Boolean),
|
|
688
|
-
default: Schema.optional(Schema.String),
|
|
689
|
-
deprecationMessage: Schema.optional(Schema.String)
|
|
690
|
-
});
|
|
691
|
-
const ActionOutput = Schema.Struct({
|
|
692
|
-
description: Schema.String
|
|
693
|
-
});
|
|
694
|
-
const Runs = Schema.Struct({
|
|
695
|
-
using: Schema.Literal("node24"),
|
|
696
|
-
main: Schema.String,
|
|
697
|
-
pre: Schema.optional(Schema.String),
|
|
698
|
-
"pre-if": Schema.optional(Schema.String),
|
|
699
|
-
post: Schema.optional(Schema.String),
|
|
700
|
-
"post-if": Schema.optional(Schema.String)
|
|
701
|
-
});
|
|
702
|
-
const BrandingColor = Schema.Literal("white", "black", "yellow", "blue", "green", "orange", "red", "purple", "gray-dark");
|
|
703
|
-
const Branding = Schema.Struct({
|
|
704
|
-
icon: Schema.optional(BrandingIcon),
|
|
705
|
-
color: Schema.optional(BrandingColor)
|
|
706
|
-
});
|
|
707
|
-
const ActionYml = Schema.Struct({
|
|
708
|
-
name: Schema.String,
|
|
709
|
-
description: Schema.String,
|
|
710
|
-
author: Schema.optional(Schema.String),
|
|
711
|
-
inputs: Schema.optional(Schema.Record({
|
|
712
|
-
key: Schema.String,
|
|
713
|
-
value: ActionInput
|
|
714
|
-
})),
|
|
715
|
-
outputs: Schema.optional(Schema.Record({
|
|
716
|
-
key: Schema.String,
|
|
717
|
-
value: ActionOutput
|
|
718
|
-
})),
|
|
719
|
-
runs: Runs,
|
|
720
|
-
branding: Schema.optional(Branding)
|
|
721
|
-
});
|
|
722
|
-
const ValidateOptionsSchema = Schema.Struct({
|
|
723
|
-
cwd: OptionalPathLikeSchema,
|
|
724
|
-
strict: Schema.optional(Schema.Boolean)
|
|
725
|
-
});
|
|
726
|
-
const ValidationErrorSchema = Schema.Struct({
|
|
727
|
-
code: Schema.String,
|
|
728
|
-
message: Schema.String,
|
|
729
|
-
file: Schema.optional(Schema.String),
|
|
730
|
-
suggestion: Schema.optional(Schema.String)
|
|
731
|
-
});
|
|
732
|
-
const ValidationWarningSchema = Schema.Struct({
|
|
733
|
-
code: Schema.String,
|
|
734
|
-
message: Schema.String,
|
|
735
|
-
file: Schema.optional(Schema.String),
|
|
736
|
-
suggestion: Schema.optional(Schema.String)
|
|
737
|
-
});
|
|
738
|
-
const ValidationResultSchema = Schema.Struct({
|
|
739
|
-
valid: Schema.Boolean,
|
|
740
|
-
errors: Schema.Array(ValidationErrorSchema),
|
|
741
|
-
warnings: Schema.Array(ValidationWarningSchema)
|
|
742
|
-
});
|
|
743
|
-
const ActionYmlResultSchema = Schema.Struct({
|
|
744
|
-
valid: Schema.Boolean,
|
|
745
|
-
content: Schema.optional(Schema.Any),
|
|
746
|
-
errors: Schema.Array(ValidationErrorSchema),
|
|
747
|
-
warnings: Schema.Array(ValidationWarningSchema)
|
|
748
|
-
});
|
|
749
|
-
const ValidationService = Context.GenericTag("ValidationService");
|
|
750
|
-
const isCI = ()=>"true" === process.env.CI || "1" === process.env.CI || "true" === process.env.GITHUB_ACTIONS;
|
|
751
|
-
const resolveStrict = (configStrict)=>configStrict ?? isCI();
|
|
752
|
-
const makeWarning = (code, message, suggestion, file)=>void 0 !== file ? {
|
|
753
|
-
code,
|
|
754
|
-
message,
|
|
755
|
-
suggestion,
|
|
756
|
-
file
|
|
757
|
-
} : {
|
|
758
|
-
code,
|
|
759
|
-
message,
|
|
760
|
-
suggestion
|
|
761
|
-
};
|
|
762
|
-
const formatSchemaErrors = (error, filePath)=>[
|
|
763
|
-
{
|
|
764
|
-
path: filePath,
|
|
765
|
-
message: ParseResult.TreeFormatter.formatErrorSync(error)
|
|
766
|
-
}
|
|
767
|
-
];
|
|
768
|
-
const ValidationServiceLive = Layer.effect(ValidationService, Effect.gen(function*() {
|
|
769
|
-
const configService = yield* ConfigService;
|
|
770
|
-
const readActionYml = (path)=>Effect.gen(function*() {
|
|
771
|
-
if (!existsSync(path)) return yield* new ActionYmlMissing({
|
|
772
|
-
cwd: path
|
|
773
|
-
});
|
|
774
|
-
const content = yield* Effect["try"]({
|
|
775
|
-
try: ()=>readFileSync(path, "utf8"),
|
|
776
|
-
catch: ()=>new ActionYmlSyntaxError({
|
|
777
|
-
path,
|
|
778
|
-
message: "Failed to read file"
|
|
779
|
-
})
|
|
780
|
-
});
|
|
781
|
-
const parsed = yield* parse(content).pipe(Effect.mapError((error)=>new ActionYmlSyntaxError({
|
|
782
|
-
path,
|
|
783
|
-
message: error.message
|
|
784
|
-
})));
|
|
785
|
-
if (!parsed || "object" != typeof parsed) return yield* new ActionYmlSyntaxError({
|
|
786
|
-
path,
|
|
787
|
-
message: "action.yml must be an object"
|
|
788
|
-
});
|
|
789
|
-
return parsed;
|
|
790
|
-
});
|
|
791
|
-
const validateSchema = (parsed, path)=>Effect.gen(function*() {
|
|
792
|
-
const result = Schema.decodeUnknownEither(ActionYml)(parsed);
|
|
793
|
-
if ("Left" === result._tag) return yield* new ActionYmlSchemaError({
|
|
794
|
-
path,
|
|
795
|
-
errors: formatSchemaErrors(result.left, path)
|
|
796
|
-
});
|
|
797
|
-
return result.right;
|
|
798
|
-
});
|
|
799
|
-
const checkRecommendations = (content, filePath)=>{
|
|
800
|
-
const warnings = [];
|
|
801
|
-
if (content.branding) {
|
|
802
|
-
const branding = content.branding;
|
|
803
|
-
if (!branding.icon) warnings.push(makeWarning("ACTION_YML_NO_BRANDING_ICON", "Branding icon not specified", "Add branding.icon for better marketplace visibility", filePath));
|
|
804
|
-
if (!branding.color) warnings.push(makeWarning("ACTION_YML_NO_BRANDING_COLOR", "Branding color not specified", "Add branding.color for better marketplace visibility", filePath));
|
|
805
|
-
} else warnings.push(makeWarning("ACTION_YML_NO_BRANDING", "No branding configuration found", "Add branding.icon and branding.color for better marketplace visibility", filePath));
|
|
806
|
-
if (content.inputs) {
|
|
807
|
-
const inputs = content.inputs;
|
|
808
|
-
for (const [name, input] of Object.entries(inputs))if (!input.description) warnings.push(makeWarning("ACTION_YML_INPUT_NO_DESCRIPTION", `Input '${name}' has no description`, `Add a description for the '${name}' input`, filePath));
|
|
809
|
-
}
|
|
810
|
-
if (content.outputs) {
|
|
811
|
-
const outputs = content.outputs;
|
|
812
|
-
for (const [name, output] of Object.entries(outputs))if (!output.description) warnings.push(makeWarning("ACTION_YML_OUTPUT_NO_DESCRIPTION", `Output '${name}' has no description`, `Add a description for the '${name}' output`, filePath));
|
|
813
|
-
}
|
|
814
|
-
return warnings;
|
|
815
|
-
};
|
|
816
|
-
const validateActionYml = (path)=>Effect.gen(function*() {
|
|
817
|
-
const parsed = yield* readActionYml(path);
|
|
818
|
-
const content = yield* validateSchema(parsed, path);
|
|
819
|
-
const warnings = checkRecommendations(parsed, path);
|
|
820
|
-
return {
|
|
821
|
-
valid: true,
|
|
822
|
-
content,
|
|
823
|
-
errors: [],
|
|
824
|
-
warnings
|
|
825
|
-
};
|
|
826
|
-
});
|
|
827
|
-
const checkEntries = (config, cwd)=>Effect.gen(function*() {
|
|
828
|
-
const errors = [];
|
|
829
|
-
const entriesConfig = {
|
|
830
|
-
main: config.entries.main
|
|
831
|
-
};
|
|
832
|
-
if (config.entries.pre) entriesConfig.pre = config.entries.pre;
|
|
833
|
-
if (config.entries.post) entriesConfig.post = config.entries.post;
|
|
834
|
-
const result = yield* Effect.either(configService.detectEntries(cwd, entriesConfig));
|
|
835
|
-
if ("Left" === result._tag && result.left instanceof MainEntryMissing) errors.push({
|
|
836
|
-
code: "MAIN_ENTRY_MISSING",
|
|
837
|
-
message: `Main entry point not found: ${result.left.expectedPath}`,
|
|
838
|
-
file: result.left.expectedPath,
|
|
839
|
-
suggestion: "Create src/main.ts or specify a different path in config"
|
|
840
|
-
});
|
|
841
|
-
return errors;
|
|
842
|
-
});
|
|
843
|
-
const checkActionYml = (config, cwd)=>Effect.gen(function*() {
|
|
844
|
-
const errors = [];
|
|
845
|
-
const warnings = [];
|
|
846
|
-
if (!config.validation.requireActionYml) return {
|
|
847
|
-
errors,
|
|
848
|
-
warnings
|
|
849
|
-
};
|
|
850
|
-
const actionYmlPath = resolve(cwd, "action.yml");
|
|
851
|
-
const result = yield* Effect.either(validateActionYml(actionYmlPath));
|
|
852
|
-
if ("Left" === result._tag) {
|
|
853
|
-
const error = result.left;
|
|
854
|
-
if (error instanceof ActionYmlMissing) warnings.push({
|
|
855
|
-
code: "ACTION_YML_MISSING",
|
|
856
|
-
message: "action.yml not found",
|
|
857
|
-
file: actionYmlPath,
|
|
858
|
-
suggestion: "Create action.yml to define your action metadata"
|
|
859
|
-
});
|
|
860
|
-
else if (error instanceof ActionYmlSyntaxError) errors.push({
|
|
861
|
-
code: "ACTION_YML_SYNTAX_ERROR",
|
|
862
|
-
message: error.message,
|
|
863
|
-
file: error.path
|
|
864
|
-
});
|
|
865
|
-
else if (error instanceof ActionYmlSchemaError) for (const schemaError of error.errors)errors.push({
|
|
866
|
-
code: "ACTION_YML_SCHEMA_ERROR",
|
|
867
|
-
message: schemaError.message,
|
|
868
|
-
file: error.path
|
|
869
|
-
});
|
|
870
|
-
} else warnings.push(...result.right.warnings);
|
|
871
|
-
return {
|
|
872
|
-
errors,
|
|
873
|
-
warnings
|
|
874
|
-
};
|
|
875
|
-
});
|
|
876
|
-
return {
|
|
877
|
-
validate: (config, options = {})=>Effect.gen(function*() {
|
|
878
|
-
const cwd = options.cwd ?? process.cwd();
|
|
879
|
-
const strict = resolveStrict(options.strict ?? config.validation.strict);
|
|
880
|
-
const entryErrors = yield* checkEntries(config, cwd);
|
|
881
|
-
const actionYmlResult = yield* checkActionYml(config, cwd);
|
|
882
|
-
const errors = [
|
|
883
|
-
...entryErrors,
|
|
884
|
-
...actionYmlResult.errors
|
|
885
|
-
];
|
|
886
|
-
const warnings = [
|
|
887
|
-
...actionYmlResult.warnings
|
|
888
|
-
];
|
|
889
|
-
const valid = 0 === errors.length && (!strict || 0 === warnings.length);
|
|
890
|
-
if (strict && warnings.length > 0 && 0 === errors.length) return yield* new ValidationFailed({
|
|
891
|
-
errorCount: 0,
|
|
892
|
-
warningCount: warnings.length,
|
|
893
|
-
message: "Warnings treated as errors in strict mode"
|
|
894
|
-
});
|
|
895
|
-
return {
|
|
896
|
-
valid,
|
|
897
|
-
errors,
|
|
898
|
-
warnings
|
|
899
|
-
};
|
|
900
|
-
}),
|
|
901
|
-
validateActionYml,
|
|
902
|
-
formatResult: (result)=>{
|
|
903
|
-
const lines = [];
|
|
904
|
-
if (result.errors.length > 0) {
|
|
905
|
-
lines.push("Errors:");
|
|
906
|
-
for (const error of result.errors){
|
|
907
|
-
lines.push(` \u2717 ${error.message}`);
|
|
908
|
-
if (error.suggestion) lines.push(` \u2192 ${error.suggestion}`);
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
if (result.warnings.length > 0) {
|
|
912
|
-
if (lines.length > 0) lines.push("");
|
|
913
|
-
lines.push("Warnings:");
|
|
914
|
-
for (const warning of result.warnings){
|
|
915
|
-
lines.push(` \u26A0 ${warning.message}`);
|
|
916
|
-
if (warning.suggestion) lines.push(` \u2192 ${warning.suggestion}`);
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
if (result.valid && 0 === result.errors.length && 0 === result.warnings.length) lines.push("\u2713 All checks passed");
|
|
920
|
-
return lines.join("\n");
|
|
921
|
-
},
|
|
922
|
-
isCI: ()=>Effect.succeed(isCI()),
|
|
923
|
-
isStrict: (configStrict)=>Effect.succeed(resolveStrict(configStrict))
|
|
924
|
-
};
|
|
925
|
-
}));
|
|
926
|
-
const ConfigLayer = 231 == __webpack_require__.j ? ConfigServiceLive : null;
|
|
927
|
-
const ValidationLayer = ValidationServiceLive.pipe(Layer.provide(ConfigServiceLive));
|
|
928
|
-
const BuildLayer = BuildServiceLive.pipe(Layer.provide(ConfigServiceLive));
|
|
929
|
-
const PersistLocalLayer = PersistLocalServiceLive;
|
|
930
|
-
const AppLayer = Layer.mergeAll(ConfigServiceLive, ValidationLayer, BuildLayer, PersistLocalLayer);
|
|
931
|
-
export { ActionYmlMissing, ActionYmlMissingBase, ActionYmlPathError, ActionYmlPathErrorBase, ActionYmlResultSchema, ActionYmlSchemaError, ActionYmlSchemaErrorBase, ActionYmlSyntaxError, ActionYmlSyntaxErrorBase, AppLayer, BuildFailed, BuildFailedBase, BuildLayer, BuildOptionsSchema, BuildResultSchema, BuildRunnerOptionsSchema, BuildService, BundleFailed, BundleFailedBase, BundleResultSchema, BundleStatsSchema, CleanError, CleanErrorBase, ConfigInputSchema, ConfigInvalid, ConfigInvalidBase, ConfigLayer, ConfigLoadFailed, ConfigLoadFailedBase, ConfigNotFound, ConfigNotFoundBase, ConfigSchema, ConfigService, DetectEntriesResultSchema, DetectedEntrySchema, EntriesSchema, EntryFileMissing, EntryFileMissingBase, LoadConfigOptionsSchema, MainEntryMissing, MainEntryMissingBase, PersistLocalError, PersistLocalErrorBase, PersistLocalLayer, PersistLocalOptionsSchema, PersistLocalResultSchema, PersistLocalRunnerOptionsSchema, PersistLocalService, ValidateOptionsSchema, ValidationErrorSchema, ValidationFailed, ValidationFailedBase, ValidationLayer, ValidationOptionsSchema, ValidationResultSchema, ValidationService, ValidationWarningSchema, WriteError, WriteErrorBase, defineConfig };
|