@octalmesh/seagull-core 0.0.2
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.md +21 -0
- package/README.md +52 -0
- package/dist/index.mjs +1364 -0
- package/package.json +45 -0
- package/src/config/loader.ts +353 -0
- package/src/config/publishing.ts +37 -0
- package/src/config/resolve-config-file.ts +38 -0
- package/src/config/schema.ts +270 -0
- package/src/config/template.ts +113 -0
- package/src/config/types.ts +139 -0
- package/src/generator/generator.ts +33 -0
- package/src/generator/registry.ts +51 -0
- package/src/generator/types.ts +38 -0
- package/src/generators/openapi-generator-cli/index.ts +1 -0
- package/src/generators/openapi-generator-cli/openapi-generator-cli.generator.ts +122 -0
- package/src/generators/openapi-generator-cli/patchers/go-module.patcher.ts +31 -0
- package/src/generators/openapi-generator-cli/patchers/maven.patcher.ts +44 -0
- package/src/generators/openapi-generator-cli/patchers/npm.patcher.ts +32 -0
- package/src/generators/openapi-generator-cli/patchers/patcher.ts +17 -0
- package/src/generators/openapi-typescript/index.ts +1 -0
- package/src/generators/openapi-typescript/openapi-typescript.generator.ts +62 -0
- package/src/git/git.ts +118 -0
- package/src/index.ts +45 -0
- package/src/process/exec.ts +53 -0
- package/src/process/resolve-bin.ts +41 -0
- package/src/readme/default-templates.ts +300 -0
- package/src/readme/readme-renderer.ts +69 -0
- package/src/redocly/redocly-sync.ts +67 -0
- package/src/version/version.ts +67 -0
- package/tsconfig.json +34 -0
- package/tsdown.config.ts +21 -0
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@octalmesh/seagull-core",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "The Seagull engine - config loading and SDK generators - as an internal library.",
|
|
5
|
+
"author": "OctalMesh <contact@octalmesh.com> (https://octalmesh.com)",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/OctalMesh/Seagull/tree/main/packages/core",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/OctalMesh/Seagull.git",
|
|
12
|
+
"directory": "packages/core"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/OctalMesh/Seagull/issues",
|
|
16
|
+
"email": "security@octalmesh.com"
|
|
17
|
+
},
|
|
18
|
+
"main": "./dist/index.mjs",
|
|
19
|
+
"types": "./dist/index.d.mts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.mts",
|
|
23
|
+
"default": "./dist/index.mjs"
|
|
24
|
+
},
|
|
25
|
+
"./package.json": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=22.22.0"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@openapitools/openapi-generator-cli": "^2.41.0",
|
|
35
|
+
"@redocly/cli": "^2.51.1",
|
|
36
|
+
"openapi-typescript": "^7.13.0",
|
|
37
|
+
"yaml": "^2.9.0",
|
|
38
|
+
"zod": "^4.5.4"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsdown",
|
|
42
|
+
"dev": "tsdown --watch",
|
|
43
|
+
"typecheck": "tsc --noEmit"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { parse as parseYaml } from "yaml";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
type ArtifactRefInput,
|
|
9
|
+
type ContractInput,
|
|
10
|
+
type GeneratorDefInput,
|
|
11
|
+
type PublishingInput,
|
|
12
|
+
type PublishingOverrideInput,
|
|
13
|
+
type RootConfigInput,
|
|
14
|
+
rootConfigSchema,
|
|
15
|
+
} from "./schema";
|
|
16
|
+
import { buildTemplateContext, interpolate, interpolateDeep } from "./template";
|
|
17
|
+
import type {
|
|
18
|
+
ResolvedArtifact,
|
|
19
|
+
ResolvedConfig,
|
|
20
|
+
ResolvedContract,
|
|
21
|
+
ResolvedPublishing,
|
|
22
|
+
} from "./types";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Reads and validates a CLI config file, throwing a readable, multi-issue error
|
|
26
|
+
* message if it doesn't match the schema.
|
|
27
|
+
*
|
|
28
|
+
* @param configPath - Absolute path to the YAML config file.
|
|
29
|
+
* @returns The validated (but not yet resolved) raw config.
|
|
30
|
+
*/
|
|
31
|
+
function readRawConfig(configPath: string): RootConfigInput {
|
|
32
|
+
const raw: unknown = parseYaml(readFileSync(configPath, "utf8"));
|
|
33
|
+
const result = rootConfigSchema.safeParse(raw);
|
|
34
|
+
|
|
35
|
+
if (!result.success) {
|
|
36
|
+
const issues = z.prettifyError(result.error);
|
|
37
|
+
|
|
38
|
+
throw new Error(`Invalid ${path.basename(configPath)}:\n${issues}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return result.data;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Deep-merges a generator override (from an artifact's `overrides:` block)
|
|
46
|
+
* onto its base generator def. `additionalProperties`, `maven`, and
|
|
47
|
+
* `publishing` are merged key-by-key; every other field is a plain override.
|
|
48
|
+
*
|
|
49
|
+
* @param base - The base generator def, looked up by id from `generators:`.
|
|
50
|
+
* @param overrides - The partial override from the artifact reference, if any.
|
|
51
|
+
* @returns The merged generator def.
|
|
52
|
+
*/
|
|
53
|
+
function mergeGeneratorOverride(
|
|
54
|
+
base: GeneratorDefInput,
|
|
55
|
+
overrides: Partial<GeneratorDefInput> | undefined,
|
|
56
|
+
): GeneratorDefInput {
|
|
57
|
+
if (!overrides) {
|
|
58
|
+
return base;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
...base,
|
|
63
|
+
...overrides,
|
|
64
|
+
maven: overrides.maven ? { ...base.maven, ...overrides.maven } : base.maven,
|
|
65
|
+
additionalProperties: {
|
|
66
|
+
...base.additionalProperties,
|
|
67
|
+
...overrides.additionalProperties,
|
|
68
|
+
},
|
|
69
|
+
publishing: mergePublishingOverride(base.publishing, overrides.publishing),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Deep-merges two partial `publishing:` overrides (from a generator def and
|
|
75
|
+
* an artifact-ref's `overrides:` block) into one.
|
|
76
|
+
*
|
|
77
|
+
* @param base - The base override (maybe undefined).
|
|
78
|
+
* @param overrides - The overriding override (maybe undefined).
|
|
79
|
+
* @returns The merged override, or undefined if both inputs were.
|
|
80
|
+
*/
|
|
81
|
+
function mergePublishingOverride(
|
|
82
|
+
base: PublishingOverrideInput | undefined,
|
|
83
|
+
overrides: PublishingOverrideInput | undefined,
|
|
84
|
+
): PublishingOverrideInput | undefined {
|
|
85
|
+
if (!overrides) {
|
|
86
|
+
return base;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
...base,
|
|
91
|
+
...overrides,
|
|
92
|
+
npm: overrides.npm ? { ...base?.npm, ...overrides.npm } : base?.npm,
|
|
93
|
+
maven: overrides.maven
|
|
94
|
+
? { ...base?.maven, ...overrides.maven }
|
|
95
|
+
: base?.maven,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Applies a (possibly partial) `publishing:` override onto the required
|
|
101
|
+
* root-level `publishing:` block, producing a fully complete result - the
|
|
102
|
+
* root block is guaranteed complete by the schema, so there's no fallback
|
|
103
|
+
* case to handle here (unlike {@link mergePublishingOverride}).
|
|
104
|
+
*
|
|
105
|
+
* @param root - The root-level `publishing:` config (required, always
|
|
106
|
+
* complete).
|
|
107
|
+
* @param override - The generator/artifact-level override, if any.
|
|
108
|
+
* @returns The fully complete, merged publishing config.
|
|
109
|
+
*/
|
|
110
|
+
function applyPublishingOverride(
|
|
111
|
+
root: PublishingInput,
|
|
112
|
+
override: PublishingOverrideInput | undefined,
|
|
113
|
+
): PublishingInput {
|
|
114
|
+
if (!override) {
|
|
115
|
+
return root;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
...root,
|
|
120
|
+
...override,
|
|
121
|
+
npm: override.npm ? { ...root.npm, ...override.npm } : root.npm,
|
|
122
|
+
maven: override.maven ? { ...root.maven, ...override.maven } : root.maven,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Resolves a single artifact reference (string id, or `{ generator, overrides,
|
|
128
|
+
* as }`) into its `{ id, def }` pair, looking up the base generator by id and
|
|
129
|
+
* applying any overrides.
|
|
130
|
+
*
|
|
131
|
+
* @param ref - The artifact reference from a contract's `artifacts:`
|
|
132
|
+
* list.
|
|
133
|
+
* @param generators - The full `generators:` map from the raw config.
|
|
134
|
+
* @param contractName - The owning contract's name.
|
|
135
|
+
* @returns The artifact's resolved id and generator def (templates not yet
|
|
136
|
+
* interpolated).
|
|
137
|
+
*/
|
|
138
|
+
function resolveArtifactRef(
|
|
139
|
+
ref: ArtifactRefInput,
|
|
140
|
+
generators: RootConfigInput["generators"],
|
|
141
|
+
contractName: string,
|
|
142
|
+
): { id: string; def: GeneratorDefInput } {
|
|
143
|
+
const generatorId = typeof ref === "string" ? ref : ref.generator;
|
|
144
|
+
const base = generators[generatorId];
|
|
145
|
+
|
|
146
|
+
if (!base) {
|
|
147
|
+
const available = Object.keys(generators).sort().join(", ");
|
|
148
|
+
|
|
149
|
+
throw new Error(
|
|
150
|
+
`Contract "${contractName}" references unknown generator "${generatorId}" ` +
|
|
151
|
+
`(available: ${available})`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const id = typeof ref === "string" ? ref : (ref.as ?? ref.generator);
|
|
156
|
+
const overrides = typeof ref === "string" ? undefined : ref.overrides;
|
|
157
|
+
|
|
158
|
+
return { id, def: mergeGeneratorOverride(base, overrides) };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Resolves an artifact's publishing conventions: applies the (already
|
|
163
|
+
* override-merged) generator-level `publishing:` override onto the required
|
|
164
|
+
* root-level `publishing:` block, then interpolates every field except `tag`
|
|
165
|
+
* (which keeps `{version}` unresolved, since it isn't known until publish time;
|
|
166
|
+
* see `config/publishing.ts`).
|
|
167
|
+
*
|
|
168
|
+
* @param generatorPublishing - The (override-merged) generator's own
|
|
169
|
+
* `publishing:` override, if any.
|
|
170
|
+
* @param rootPublishing - The required root-level `publishing:` config
|
|
171
|
+
* from the config file.
|
|
172
|
+
* @param context - The flattened template context for this artifact
|
|
173
|
+
* (`service`, `id`, `github.*`, `vars.*`).
|
|
174
|
+
* @returns The fully resolved publishing conventions for this artifact.
|
|
175
|
+
*/
|
|
176
|
+
function resolvePublishing(
|
|
177
|
+
generatorPublishing: PublishingOverrideInput | undefined,
|
|
178
|
+
rootPublishing: PublishingInput,
|
|
179
|
+
context: Record<string, string>,
|
|
180
|
+
): ResolvedPublishing {
|
|
181
|
+
const merged = applyPublishingOverride(rootPublishing, generatorPublishing);
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
branch: interpolate(merged.branch, context),
|
|
185
|
+
tagTemplate: merged.tag,
|
|
186
|
+
repositoryUrl: interpolate(merged.repositoryUrl, context),
|
|
187
|
+
npmRegistry: interpolate(merged.npm.registry, context),
|
|
188
|
+
npmAccess: merged.npm.access,
|
|
189
|
+
mavenRepositoryId: interpolate(merged.maven.repositoryId, context),
|
|
190
|
+
mavenRepositoryUrl: interpolate(merged.maven.repositoryUrl, context),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Interpolates templates and resolves absolute paths for a single artifact.
|
|
196
|
+
*
|
|
197
|
+
* @param id - The artifact's resolved id (output folder / branch /
|
|
198
|
+
* tag segment).
|
|
199
|
+
* @param def - The (override-merged, not-yet-interpolated)
|
|
200
|
+
* generator def.
|
|
201
|
+
* @param rootDir - Absolute repo root, `readme` template paths are
|
|
202
|
+
* resolved relative to this.
|
|
203
|
+
* @param sdkDir - Absolute path to the SDK output root (`<dist>/sdk`).
|
|
204
|
+
* @param contractName - The owning contract's name.
|
|
205
|
+
* @param contractContext - The flattened template context for this contract
|
|
206
|
+
* (`service`, `github.*`, `vars.*` - not yet `id`).
|
|
207
|
+
* @param rootPublishing - The required root-level `publishing:` config from
|
|
208
|
+
* the config file.
|
|
209
|
+
* @returns The fully resolved artifact.
|
|
210
|
+
*/
|
|
211
|
+
function resolveArtifact(
|
|
212
|
+
id: string,
|
|
213
|
+
def: GeneratorDefInput,
|
|
214
|
+
rootDir: string,
|
|
215
|
+
sdkDir: string,
|
|
216
|
+
contractName: string,
|
|
217
|
+
contractContext: Record<string, string>,
|
|
218
|
+
rootPublishing: PublishingInput,
|
|
219
|
+
): ResolvedArtifact {
|
|
220
|
+
const context = { ...contractContext, id };
|
|
221
|
+
const resolved = interpolateDeep(def, context);
|
|
222
|
+
const publishing = resolvePublishing(def.publishing, rootPublishing, context);
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
id,
|
|
226
|
+
tool: resolved.tool,
|
|
227
|
+
lang: resolved.lang,
|
|
228
|
+
kind: resolved.kind,
|
|
229
|
+
generator: resolved.generator,
|
|
230
|
+
outputDir: path.join(sdkDir, contractName, id),
|
|
231
|
+
branch: publishing.branch,
|
|
232
|
+
publishing,
|
|
233
|
+
additionalProperties: resolved.additionalProperties,
|
|
234
|
+
package: resolved.package,
|
|
235
|
+
goModule: resolved.goModule,
|
|
236
|
+
goPackageName: resolved.goPackageName,
|
|
237
|
+
maven: resolved.maven,
|
|
238
|
+
readmeTemplate: resolved.readme
|
|
239
|
+
? path.resolve(rootDir, resolved.readme)
|
|
240
|
+
: undefined,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Resolves a single contract: its entrypoint path and every artifact in its
|
|
246
|
+
* `artifacts:` list.
|
|
247
|
+
*
|
|
248
|
+
* @param input - The raw contract config.
|
|
249
|
+
* @param rootDir - Absolute repo root, entrypoints/`readme` paths are
|
|
250
|
+
* resolved relative to this.
|
|
251
|
+
* @param sdkDir - Absolute path to the SDK output root (`<dist>/sdk`).
|
|
252
|
+
* @param generators - The full `generators:` map from the raw config.
|
|
253
|
+
* @param githubCtx - `{ owner, repo }`, exposed to templates as
|
|
254
|
+
* `{github.owner}`/`{github.repo}`.
|
|
255
|
+
* @param vars - The `vars:` tree from the raw config, exposed as
|
|
256
|
+
* `{vars.*}`.
|
|
257
|
+
* @param rootPublishing - The required root-level `publishing:` config from the
|
|
258
|
+
* config file.
|
|
259
|
+
* @returns The fully resolved contract.
|
|
260
|
+
*/
|
|
261
|
+
function resolveContract(
|
|
262
|
+
input: ContractInput,
|
|
263
|
+
rootDir: string,
|
|
264
|
+
sdkDir: string,
|
|
265
|
+
generators: RootConfigInput["generators"],
|
|
266
|
+
githubCtx: { owner: string; repo: string },
|
|
267
|
+
vars: RootConfigInput["vars"],
|
|
268
|
+
rootPublishing: PublishingInput,
|
|
269
|
+
): ResolvedContract {
|
|
270
|
+
const context = buildTemplateContext({
|
|
271
|
+
service: input.name,
|
|
272
|
+
github: githubCtx,
|
|
273
|
+
vars,
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
const artifacts = input.artifacts.map((ref): ResolvedArtifact => {
|
|
277
|
+
const { id, def } = resolveArtifactRef(ref, generators, input.name);
|
|
278
|
+
|
|
279
|
+
return resolveArtifact(
|
|
280
|
+
id,
|
|
281
|
+
def,
|
|
282
|
+
rootDir,
|
|
283
|
+
sdkDir,
|
|
284
|
+
input.name,
|
|
285
|
+
context,
|
|
286
|
+
rootPublishing,
|
|
287
|
+
);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
const entrypoint = path.join(rootDir, input.entrypoint);
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
name: input.name,
|
|
294
|
+
title: input.title,
|
|
295
|
+
entrypoint,
|
|
296
|
+
entrypointRelative: path.relative(rootDir, entrypoint),
|
|
297
|
+
artifacts,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Loads, validates, and fully resolves a CLI config file - the single entry
|
|
303
|
+
* point every command uses to get its configuration.
|
|
304
|
+
*
|
|
305
|
+
* Unlike a build tool bundled into the consumer's own repo, seagull is
|
|
306
|
+
* installed as a dependency, so it has no way to guess where the consumer's
|
|
307
|
+
* config lives on its own - `configPath` must be supplied by the caller (the
|
|
308
|
+
* CLI resolves it via `resolveConfigPath()` in `config/resolve-config-file.ts`,
|
|
309
|
+
* or `--config`).
|
|
310
|
+
*
|
|
311
|
+
* @param configPath - Absolute path to the CLI config file.
|
|
312
|
+
* @returns The fully resolved config.
|
|
313
|
+
*/
|
|
314
|
+
export function loadConfig(configPath: string): ResolvedConfig {
|
|
315
|
+
const rootDir = path.dirname(configPath);
|
|
316
|
+
const raw = readRawConfig(configPath);
|
|
317
|
+
|
|
318
|
+
const distDir = path.resolve(rootDir, raw.paths.dist);
|
|
319
|
+
const specsDir = raw.paths.specs
|
|
320
|
+
? path.resolve(rootDir, raw.paths.specs)
|
|
321
|
+
: path.join(distDir, "specs");
|
|
322
|
+
const docsDir = raw.paths.docs
|
|
323
|
+
? path.resolve(rootDir, raw.paths.docs)
|
|
324
|
+
: path.join(distDir, "docs");
|
|
325
|
+
const sdkDir = raw.paths.sdk
|
|
326
|
+
? path.resolve(rootDir, raw.paths.sdk)
|
|
327
|
+
: path.join(distDir, "sdk");
|
|
328
|
+
|
|
329
|
+
const contracts = raw.contracts.map((contract) =>
|
|
330
|
+
resolveContract(
|
|
331
|
+
contract,
|
|
332
|
+
rootDir,
|
|
333
|
+
sdkDir,
|
|
334
|
+
raw.generators,
|
|
335
|
+
raw.github,
|
|
336
|
+
raw.vars,
|
|
337
|
+
raw.publishing,
|
|
338
|
+
),
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
configVersion: raw.configVersion,
|
|
343
|
+
rootDir,
|
|
344
|
+
paths: { dist: distDir, specs: specsDir, docs: docsDir, sdk: sdkDir },
|
|
345
|
+
github: raw.github,
|
|
346
|
+
vars: raw.vars,
|
|
347
|
+
docs: raw.docs,
|
|
348
|
+
contracts,
|
|
349
|
+
allArtifacts: contracts.flatMap((contract) =>
|
|
350
|
+
contract.artifacts.map((artifact) => ({ contract, artifact })),
|
|
351
|
+
),
|
|
352
|
+
};
|
|
353
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { buildTemplateContext, interpolate } from "./template";
|
|
2
|
+
import type { ResolvedArtifact, VarsTree } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Renders an artifact's final git tag from its `publishing.tagTemplate` -
|
|
6
|
+
* the one piece of `publishing:` config that can't be resolved at config-load
|
|
7
|
+
* time, since it needs the artifact's version, which is only known once the
|
|
8
|
+
* contract's spec has been bundled.
|
|
9
|
+
*
|
|
10
|
+
* @param artifact - The resolved artifact (for `id` and
|
|
11
|
+
* `publishing.tagTemplate`).
|
|
12
|
+
* @param contractName - The owning contract's name, exposed to the template as
|
|
13
|
+
* `{service}`.
|
|
14
|
+
* @param version - The resolved SDK version, exposed to the template as
|
|
15
|
+
* `{version}`.
|
|
16
|
+
* @param github - `{ owner, repo }`, exposed as `{github.owner}`/
|
|
17
|
+
* `{github.repo}`.
|
|
18
|
+
* @param vars - The config's `vars:` tree, exposed as `{vars.*}`.
|
|
19
|
+
* @returns The rendered tag name.
|
|
20
|
+
*/
|
|
21
|
+
export function renderArtifactTag(
|
|
22
|
+
artifact: ResolvedArtifact,
|
|
23
|
+
contractName: string,
|
|
24
|
+
version: string,
|
|
25
|
+
github: { owner: string; repo: string },
|
|
26
|
+
vars: VarsTree,
|
|
27
|
+
): string {
|
|
28
|
+
const context = buildTemplateContext({
|
|
29
|
+
service: contractName,
|
|
30
|
+
id: artifact.id,
|
|
31
|
+
version,
|
|
32
|
+
github,
|
|
33
|
+
vars,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return interpolate(artifact.publishing.tagTemplate, context);
|
|
37
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Config filenames CLI recognizes, checked in this order.
|
|
6
|
+
*/
|
|
7
|
+
export const CONFIG_FILENAMES = [
|
|
8
|
+
".seagull",
|
|
9
|
+
".seagull.yaml",
|
|
10
|
+
".seagull.yml",
|
|
11
|
+
"seagull.yaml",
|
|
12
|
+
"seagull.yml",
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Finds the CLI config file in a directory, trying each of
|
|
17
|
+
* {@link CONFIG_FILENAMES} in order.
|
|
18
|
+
*
|
|
19
|
+
* @param cwd - The directory to look in (typically `process.cwd()`).
|
|
20
|
+
* @returns The absolute path to the first matching config file.
|
|
21
|
+
* @throws Error if none of the candidate filenames exist in `cwd`.
|
|
22
|
+
*
|
|
23
|
+
* @see {@link CONFIG_FILENAMES} - the list of filenames checked, in order.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveConfigPath(cwd: string): string {
|
|
26
|
+
for (const filename of CONFIG_FILENAMES) {
|
|
27
|
+
const candidate = path.join(cwd, filename);
|
|
28
|
+
|
|
29
|
+
if (existsSync(candidate)) {
|
|
30
|
+
return candidate;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
throw new Error(
|
|
35
|
+
`No CLI config found in ${cwd} - looked for: ${CONFIG_FILENAMES.join(", ")}. ` +
|
|
36
|
+
`Create one of these, or pass --config <path>.`,
|
|
37
|
+
);
|
|
38
|
+
}
|