Skip to content

Commit 0453041

Browse files
committed
Add JobOptions interface and globalOptions method for flexible job configuration
1 parent 8f94028 commit 0453041

File tree

10 files changed

+488
-66
lines changed

10 files changed

+488
-66
lines changed

.changeset/fix-script-nested-array.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,3 @@
55
Fix regression in YAML import script formatting where multi-line simple script blocks produced a nested array structure (`script: [[...]]`) instead of a flat array (`script: [ ... ]`).
66

77
The importer now flattens multi-line simple commands correctly and preserves template literals only when shell operators (pipes, heredoc, redirects, continuations) are present.
8-

.changeset/job-options-api.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
"@noxify/gitlab-ci-builder": minor
3+
---
4+
5+
Add flexible job configuration options with `JobOptions` interface and `globalOptions()` method.
6+
7+
**New Features:**
8+
9+
- **`JobOptions` interface**: Unified options object for `job()`, `template()`, and `extends()` methods with:
10+
- `resolveExtends?: boolean` - Control whether parent templates are resolved (default: `true`)
11+
- `mergeExisting?: boolean` - Control merge behavior for duplicate job names (default: `true`)
12+
- `hidden?: boolean` - Mark job as template (replaces boolean parameter)
13+
14+
- **`globalOptions(options: GlobalOptions)` method**: Set default options for all jobs:
15+
- `resolveExtends?: boolean` - Disable extends resolution globally
16+
- `mergeExisting?: boolean` - Control default merge behavior
17+
- Job-level options override global settings
18+
19+
**Benefits:**
20+
21+
- Preserve `extends` references in output when `resolveExtends: false`
22+
- Fine-grained control over job merging behavior
23+
- Unified options interface for cleaner API
24+
25+
**Example:**
26+
27+
```ts
28+
const config = new Config()
29+
30+
// Global: disable extends resolution for all jobs
31+
config.globalOptions({ resolveExtends: false })
32+
33+
config.template(".base", { script: ["base command"] })
34+
35+
// This job keeps extends reference (global setting)
36+
config.job("job1", { extends: ".base" })
37+
38+
// This job resolves extends (local override)
39+
config.job("job2", { extends: ".base" }, { resolveExtends: true })
40+
41+
// Replace instead of merge
42+
config.job("test", { stage: "test" })
43+
config.job("test", { script: ["override"] }, { mergeExisting: false })
44+
```

README.md

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@ const cfg = new Config()
3838
.include("./common.yml")
3939

4040
// Template job (hidden)
41-
cfg.template("base", { image: "node:18" })
42-
// same as
43-
// cfg.job("base", { image: "node:18" }, true)
44-
// or
45-
// cfg.job(".base", { image: "node:18" })
41+
config.template("base", { image: "node:18" })
42+
// Or using job() with options:
43+
// config.job("base", { image: "node:18" }, { hidden: true })
44+
// Or with dot prefix:
45+
// config.job(".base", { image: "node:18" })
4646

47-
cfg.extends(".base", "unittest", {
47+
config.extends(".base", "unittest", {
4848
stage: "test",
4949
script: ["npm run test"],
5050
})
@@ -355,6 +355,67 @@ export default function (config: Config) {
355355

356356
**Note:** If both default and named exports are present, the default export takes precedence.
357357

358+
## Job Options & Global Settings
359+
360+
### Job Options
361+
362+
The `job()`, `template()`, and `extends()` methods accept an optional `JobOptions` parameter for fine-grained control:
363+
364+
```ts
365+
interface JobOptions {
366+
hidden?: boolean // Mark as template (prefix with dot)
367+
mergeExisting?: boolean // Merge with existing job/template (default: true)
368+
resolveExtends?: boolean // Resolve parent extends (default: true)
369+
}
370+
```
371+
372+
**Example:**
373+
374+
```ts
375+
const config = new Config()
376+
377+
// Create a hidden template
378+
config.job("base", { image: "node:22" }, { hidden: true })
379+
// Same as: config.template(".base", { image: "node:22" })
380+
381+
// Replace instead of merge
382+
config.job("build", { stage: "build", script: ["npm run build"] })
383+
config.job("build", { script: ["npm run build:prod"] }, { mergeExisting: false })
384+
// Result: { script: ["npm run build:prod"] } (stage removed)
385+
386+
// Keep extends reference (don't resolve parent)
387+
config.template(".base", { script: ["base command"] })
388+
config.job("child", { extends: ".base" }, { resolveExtends: false })
389+
// Output keeps: extends: ".base"
390+
```
391+
392+
### Global Options
393+
394+
Set default behavior for all jobs using `globalOptions()`. Job-level options override global settings:
395+
396+
```ts
397+
const config = new Config()
398+
399+
// Disable extends resolution globally
400+
config.globalOptions({ resolveExtends: false })
401+
402+
config.template(".base", { script: ["base"] })
403+
404+
// This job keeps extends (global setting)
405+
config.job("job1", { extends: ".base" })
406+
// Output: { extends: ".base" }
407+
408+
// This job resolves extends (local override)
409+
config.job("job2", { extends: ".base" }, { resolveExtends: true })
410+
// Output: { script: ["base"] }
411+
```
412+
413+
**Use Cases:**
414+
415+
- **Preserve extends in output**: Set `resolveExtends: false` to keep GitLab CI's native extends instead of inlining parent properties
416+
- **Strict replacement**: Set `mergeExisting: false` globally to prevent accidental job merging
417+
- **Conditional resolution**: Disable extends resolution globally, then selectively enable it for specific jobs
418+
358419
## API Reference
359420

360421
This reference summarizes the primary `Config` API surface. Method signatures reflect
@@ -369,6 +430,11 @@ the runtime builder and are derived from the JSDoc on the source `Config` class.
369430
- `addStage(stage: string): Config`
370431
- Convenience wrapper for adding a single stage.
371432

433+
- `globalOptions(options: GlobalOptions): Config`
434+
- Set global options that apply to all jobs and templates.
435+
- Options: `{ resolveExtends?: boolean, mergeExisting?: boolean }`
436+
- Job-level options override global settings.
437+
372438
- `workflow(workflow: GitLabCi["workflow"]): Config`
373439
- Set or deep-merge the top-level `workflow` configuration (typically `rules`).
374440

@@ -387,25 +453,28 @@ the runtime builder and are derived from the JSDoc on the source `Config` class.
387453
- `getJob(name: string): JobDefinition | undefined`
388454
- Look up a job or template definition by name (templates start with `.`).
389455

390-
- `template(name: string, definition: JobDefinition, options?: { mergeExisting?: boolean }): Config`
456+
- `template(name: string, definition: JobDefinition, options?: JobOptions): Config`
391457
- Define or deep-merge a hidden template job. The stored template name will have a leading `.`.
458+
- Options: `{ mergeExisting?: boolean, resolveExtends?: boolean, hidden?: boolean }`
392459

393460
- `include(item: IncludeDefinition | IncludeDefinition[]): Config`
394461
- Add include entries. Accepts strings, objects or arrays and normalizes strings to
395462
`{ local: "..." }` or `{ remote: "https://..." }` depending on the value.
396463

397-
- `job(name: string, definition: JobDefinition, hidden?: boolean, options?: { mergeExisting?: boolean }): Config`
398-
- Create or merge a job. If `name` starts with `.` or `hidden` is true, the call delegates
464+
- `job(name: string, definition: JobDefinition, options?: JobOptions): Config`
465+
- Create or merge a job. If `name` starts with `.` or `options.hidden` is true, the call delegates
399466
to `template()` and ensures a single leading `.` on the stored template name.
467+
- Options: `{ hidden?: boolean, mergeExisting?: boolean, resolveExtends?: boolean }`
400468

401469
- `macro<T extends MacroArgs>(key: string, callback: (config: Config, args: T) => void): void`
402470
- Register a macro function for programmatic job generation.
403471

404472
- `from<T extends MacroArgs>(key: string, args: T): void`
405473
- Invoke a previously registered macro.
406474

407-
- `extends(fromName: string | string[], name: string, job?: JobDefinition, hidden?: boolean): void`
475+
- `extends(fromName: string | string[], name: string, job?: JobDefinition, options?: JobOptions): void`
408476
- Create a job that will extend one or more templates/jobs (injects an `extends` property).
477+
- Options: `{ hidden?: boolean, mergeExisting?: boolean, resolveExtends?: boolean }`
409478

410479
- `dynamicInclude(cwd: string, globs: string[]): Promise<void>`
411480
- Import TypeScript modules matched by the provided globs and call their exported `extendConfig`.

0 commit comments

Comments
 (0)