@savvy-web/github-action-builder 0.7.3 → 0.7.5

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/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 };