From db01d4ece1c404215122f446c2f929fda482242f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 3 Apr 2026 16:53:44 +0000
Subject: [PATCH 1/3] Initial plan
From eb6b439bcb7fbccc59fcd976529ee7e4fadd16c1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 3 Apr 2026 17:08:43 +0000
Subject: [PATCH 2/3] Add workflow to create missing repository labels:
constraint-solving, problem-of-the-day
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/d6e9d52e-eb6a-46af-a3fd-d3250276d028
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.github/workflows/create-labels.yml | 54 +++++++++++++++++++++++++++++
scratchpad/labels.md | 5 +++
2 files changed, 59 insertions(+)
create mode 100644 .github/workflows/create-labels.yml
diff --git a/.github/workflows/create-labels.yml b/.github/workflows/create-labels.yml
new file mode 100644
index 00000000000..bf89a14201a
--- /dev/null
+++ b/.github/workflows/create-labels.yml
@@ -0,0 +1,54 @@
+name: Create Repository Labels
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - main
+ paths:
+ - '.github/workflows/create-labels.yml'
+
+permissions:
+ issues: write
+
+jobs:
+ create-labels:
+ name: Create Missing Labels
+ runs-on: ubuntu-latest
+ if: github.repository == 'github/gh-aw'
+ steps:
+ - name: Create repository labels
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ with:
+ script: |
+ const labels = [
+ {
+ name: 'constraint-solving',
+ color: '0075ca',
+ description: 'Constraint solving problems and algorithms',
+ },
+ {
+ name: 'problem-of-the-day',
+ color: 'e4e669',
+ description: 'Problem of the day',
+ },
+ ];
+
+ for (const label of labels) {
+ try {
+ await github.rest.issues.createLabel({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ name: label.name,
+ color: label.color,
+ description: label.description,
+ });
+ core.info(`✅ Created label: ${label.name}`);
+ } catch (error) {
+ if (error.status === 422) {
+ core.info(`ℹ️ Label already exists: ${label.name}`);
+ } else {
+ core.setFailed(`Failed to create label '${label.name}': ${error.message}`);
+ }
+ }
+ }
diff --git a/scratchpad/labels.md b/scratchpad/labels.md
index 0f9ea020ff4..6e07c1ab711 100644
--- a/scratchpad/labels.md
+++ b/scratchpad/labels.md
@@ -39,6 +39,10 @@ Labels help organize and triage issues for better project management. Use labels
- **ai-inspected** - Issue reviewed by AI workflow
- **smoke-copilot** - Smoke test results
+### Domain Labels (used by scheduled workflows)
+- **constraint-solving** - Constraint solving problems and algorithms (used by Constraint Solving POTD workflow)
+- **problem-of-the-day** - Problem of the day (used by Constraint Solving POTD workflow)
+
### Status Labels
- **good first issue** - Suitable for new contributors
- **dependencies** - Dependency updates
@@ -108,6 +112,7 @@ Type: bug, enhancement, documentation, question, testing
Priority: priority-high, priority-medium, priority-low
Component: cli, workflow, mcp, actions, engine, automation
Workflow: ai-generated, plan, ai-inspected, smoke-copilot
+Domain: constraint-solving, problem-of-the-day
Status: good first issue, dependencies
```
From b347244ada7a6501b5f35472745ecacbf522d718 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 3 Apr 2026 17:47:29 +0000
Subject: [PATCH 3/3] Replace standalone create-labels workflow with compile
--json label reporting and agentics-maintenance create_labels operation
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/55ecc334-0656-4098-9246-3d7b1c15f4c0
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.github/aw/github-agentic-workflows.md | 13 ++
.github/workflows/agentics-maintenance.yml | 51 +++++++-
.github/workflows/create-labels.yml | 54 --------
actions/setup/js/create_labels.cjs | 139 +++++++++++++++++++++
pkg/cli/compile_config.go | 1 +
pkg/cli/compile_validation.go | 1 +
pkg/cli/compile_workflow_processor.go | 61 +++++++++
pkg/workflow/maintenance_workflow.go | 50 +++++++-
pkg/workflow/maintenance_workflow_test.go | 29 ++++-
9 files changed, 336 insertions(+), 63 deletions(-)
delete mode 100644 .github/workflows/create-labels.yml
create mode 100644 actions/setup/js/create_labels.cjs
diff --git a/.github/aw/github-agentic-workflows.md b/.github/aw/github-agentic-workflows.md
index 4ca304d69e4..9759db520c5 100644
--- a/.github/aw/github-agentic-workflows.md
+++ b/.github/aw/github-agentic-workflows.md
@@ -66,10 +66,23 @@ gh aw compile --poutine # Supply chain security analyzer
# Strict mode with all scanners
gh aw compile --actionlint --zizmor --poutine
+
+# Output validation results as JSON (includes labels referenced in safe-outputs)
+gh aw compile --json --no-emit
```
**Best Practice**: Always run `gh aw compile` after every workflow change to ensure the GitHub Actions YAML is up to date.
+**Agentic Maintenance Workflow Operations:**
+
+The generated `agentics-maintenance.yml` workflow supports these `workflow_dispatch` operations:
+
+- `disable` / `enable` — Disable or re-enable all agentic workflows
+- `update` — Update workflow metadata (opens a PR for changed files)
+- `upgrade` — Upgrade gh-aw version and dependencies (opens a PR)
+- `safe_outputs` — Replay safe outputs from a previous run (provide `run_url`)
+- `create_labels` — Compile all workflows and create any labels referenced in `safe-outputs` that are missing from the repository
+
## Complete Frontmatter Schema
The YAML frontmatter supports these fields:
diff --git a/.github/workflows/agentics-maintenance.yml b/.github/workflows/agentics-maintenance.yml
index de967ee6732..68c22092c3b 100644
--- a/.github/workflows/agentics-maintenance.yml
+++ b/.github/workflows/agentics-maintenance.yml
@@ -49,6 +49,7 @@ on:
- 'update'
- 'upgrade'
- 'safe_outputs'
+ - 'create_labels'
run_url:
description: 'Run URL or run ID to replay safe outputs from (e.g. https://github.com/owner/repo/actions/runs/12345 or 12345). Required when operation is safe_outputs.'
required: false
@@ -106,7 +107,7 @@ jobs:
await main();
run_operation:
- if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.operation != '' && github.event.inputs.operation != 'safe_outputs' && !github.event.repository.fork }}
+ if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.operation != '' && github.event.inputs.operation != 'safe_outputs' && github.event.inputs.operation != 'create_labels' && !github.event.repository.fork }}
runs-on: ubuntu-slim
permissions:
actions: write
@@ -201,6 +202,54 @@ jobs:
const { main } = require('${{ runner.temp }}/gh-aw/actions/apply_safe_outputs_replay.cjs');
await main();
+ create_labels:
+ if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.operation == 'create_labels' && !github.event.repository.fork }}
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ issues: write
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+
+ - name: Setup Scripts
+ uses: ./actions/setup
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+
+ - name: Check admin/maintainer permissions
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_team_member.cjs');
+ await main();
+
+ - name: Setup Go
+ uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
+ with:
+ go-version-file: go.mod
+ cache: true
+
+ - name: Build gh-aw
+ run: make build
+
+ - name: Create missing labels
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_CMD_PREFIX: ./gh-aw
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/create_labels.cjs');
+ await main();
+
compile-workflows:
if: ${{ !github.event.repository.fork && (github.event_name != 'workflow_dispatch' || github.event.inputs.operation == '') }}
runs-on: ubuntu-slim
diff --git a/.github/workflows/create-labels.yml b/.github/workflows/create-labels.yml
deleted file mode 100644
index bf89a14201a..00000000000
--- a/.github/workflows/create-labels.yml
+++ /dev/null
@@ -1,54 +0,0 @@
-name: Create Repository Labels
-
-on:
- workflow_dispatch:
- push:
- branches:
- - main
- paths:
- - '.github/workflows/create-labels.yml'
-
-permissions:
- issues: write
-
-jobs:
- create-labels:
- name: Create Missing Labels
- runs-on: ubuntu-latest
- if: github.repository == 'github/gh-aw'
- steps:
- - name: Create repository labels
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- with:
- script: |
- const labels = [
- {
- name: 'constraint-solving',
- color: '0075ca',
- description: 'Constraint solving problems and algorithms',
- },
- {
- name: 'problem-of-the-day',
- color: 'e4e669',
- description: 'Problem of the day',
- },
- ];
-
- for (const label of labels) {
- try {
- await github.rest.issues.createLabel({
- owner: context.repo.owner,
- repo: context.repo.repo,
- name: label.name,
- color: label.color,
- description: label.description,
- });
- core.info(`✅ Created label: ${label.name}`);
- } catch (error) {
- if (error.status === 422) {
- core.info(`ℹ️ Label already exists: ${label.name}`);
- } else {
- core.setFailed(`Failed to create label '${label.name}': ${error.message}`);
- }
- }
- }
diff --git a/actions/setup/js/create_labels.cjs b/actions/setup/js/create_labels.cjs
new file mode 100644
index 00000000000..1e659bf527f
--- /dev/null
+++ b/actions/setup/js/create_labels.cjs
@@ -0,0 +1,139 @@
+// @ts-check
+///
+
+const { getErrorMessage } = require("./error_helpers.cjs");
+const { ERR_SYSTEM } = require("./error_codes.cjs");
+
+/**
+ * Generate a deterministic pastel hex color string from a label name.
+ * Produces colors in the pastel range (128–191 per channel) for readability.
+ *
+ * @param {string} name
+ * @returns {string} Six-character hex color (no leading #)
+ */
+function deterministicLabelColor(name) {
+ let hash = 0;
+ for (let i = 0; i < name.length; i++) {
+ hash = (hash * 31 + name.charCodeAt(i)) >>> 0;
+ }
+ // Map to pastel range: 128–223 per channel
+ const r = 128 + (hash & 0x3f);
+ const g = 128 + ((hash >> 6) & 0x3f);
+ const b = 128 + ((hash >> 12) & 0x3f);
+ return ((r << 16) | (g << 8) | b).toString(16).padStart(6, "0");
+}
+
+/**
+ * Compile all agentic workflows, collect the labels referenced in safe-outputs
+ * configurations, and create any labels that are missing from the repository.
+ *
+ * Required environment variables:
+ * GH_AW_CMD_PREFIX - Command prefix: './gh-aw' (dev) or 'gh aw' (release)
+ *
+ * @returns {Promise}
+ */
+async function main() {
+ const cmdPrefixStr = process.env.GH_AW_CMD_PREFIX || "gh aw";
+ const [bin, ...prefixArgs] = cmdPrefixStr.split(" ").filter(Boolean);
+
+ // Run compile --json --no-emit to collect labels without writing lock files.
+ // Use ignoreReturnCode because compile exits non-zero when some workflows have errors,
+ // but still produces valid JSON output for all (valid and invalid) workflows.
+ const compileArgs = [...prefixArgs, "compile", "--json", "--no-emit"];
+ core.info(`Running: ${bin} ${compileArgs.join(" ")}`);
+
+ let compileOutput;
+ try {
+ const result = await exec.getExecOutput(bin, compileArgs, { ignoreReturnCode: true });
+ compileOutput = result.stdout;
+ // Only treat as a fatal error when the exit is non-zero AND there is no stdout at all.
+ // A non-zero exit with JSON on stdout means some workflows failed validation but we
+ // still have label data from the successfully-parsed ones — continue processing.
+ if (result.exitCode !== 0 && !compileOutput.trim()) {
+ throw new Error(`${ERR_SYSTEM}: compile exited with code ${result.exitCode}: ${result.stderr}`);
+ }
+ } catch (err) {
+ core.setFailed(`Failed to run compile: ${getErrorMessage(err)}`);
+ return;
+ }
+
+ // Parse JSON output
+ let validationResults;
+ try {
+ validationResults = JSON.parse(compileOutput);
+ } catch (err) {
+ core.setFailed(`Failed to parse compile JSON output: ${getErrorMessage(err)}`);
+ return;
+ }
+
+ // Collect all unique labels across all workflows
+ /** @type {Set} */
+ const allLabels = new Set();
+ for (const result of validationResults) {
+ if (Array.isArray(result.labels)) {
+ for (const label of result.labels) {
+ if (typeof label === "string" && label.trim()) {
+ allLabels.add(label.trim());
+ }
+ }
+ }
+ }
+
+ if (allLabels.size === 0) {
+ core.info("No labels found in safe-outputs configurations — nothing to create");
+ return;
+ }
+
+ core.info(`Found ${allLabels.size} unique label(s) in safe-outputs: ${[...allLabels].join(", ")}`);
+
+ // Fetch all existing labels from the repository
+ const { owner, repo } = context.repo;
+ let existingLabels;
+ try {
+ existingLabels = await github.paginate(github.rest.issues.listLabelsForRepo, {
+ owner,
+ repo,
+ per_page: 100,
+ });
+ } catch (err) {
+ core.setFailed(`Failed to list repository labels: ${getErrorMessage(err)}`);
+ return;
+ }
+
+ const existingLabelNames = new Set(existingLabels.map(l => l.name.toLowerCase()));
+
+ // Create missing labels
+ let created = 0;
+ let skipped = 0;
+
+ for (const labelName of allLabels) {
+ if (existingLabelNames.has(labelName.toLowerCase())) {
+ core.info(`ℹ️ Label already exists: ${labelName}`);
+ skipped++;
+ } else {
+ try {
+ await github.rest.issues.createLabel({
+ owner,
+ repo,
+ name: labelName,
+ color: deterministicLabelColor(labelName),
+ description: "",
+ });
+ core.info(`✅ Created label: ${labelName}`);
+ created++;
+ } catch (err) {
+ // 422 means label already exists (race condition) — treat as success
+ if (err && typeof err === "object" && /** @type {any} */ err.status === 422) {
+ core.info(`ℹ️ Label already exists (concurrent): ${labelName}`);
+ skipped++;
+ } else {
+ core.warning(`Failed to create label '${labelName}': ${getErrorMessage(err)}`);
+ }
+ }
+ }
+ }
+
+ core.info(`Done: ${created} label(s) created, ${skipped} already existed`);
+}
+
+module.exports = { main };
diff --git a/pkg/cli/compile_config.go b/pkg/cli/compile_config.go
index ea711b5d9a2..c58c942b825 100644
--- a/pkg/cli/compile_config.go
+++ b/pkg/cli/compile_config.go
@@ -59,4 +59,5 @@ type ValidationResult struct {
Errors []CompileValidationError `json:"errors"`
Warnings []CompileValidationError `json:"warnings"`
CompiledFile string `json:"compiled_file,omitempty"`
+ Labels []string `json:"labels,omitempty"` // Labels referenced in safe-outputs configurations
}
diff --git a/pkg/cli/compile_validation.go b/pkg/cli/compile_validation.go
index 915d93ee69b..4148001188c 100644
--- a/pkg/cli/compile_validation.go
+++ b/pkg/cli/compile_validation.go
@@ -239,6 +239,7 @@ func sanitizeValidationResults(results []ValidationResult) []ValidationResult {
CompiledFile: result.CompiledFile,
Errors: sliceutil.Map(result.Errors, sanitizeError),
Warnings: sliceutil.Map(result.Warnings, sanitizeError),
+ Labels: result.Labels,
}
})
}
diff --git a/pkg/cli/compile_workflow_processor.go b/pkg/cli/compile_workflow_processor.go
index 7844ebd8506..a425d9329ec 100644
--- a/pkg/cli/compile_workflow_processor.go
+++ b/pkg/cli/compile_workflow_processor.go
@@ -142,6 +142,67 @@ func compileWorkflowFile(
if !noEmit {
result.validationResult.CompiledFile = lockFile
}
+
+ // Collect labels from safe-outputs for JSON output (used by create-labels maintenance operation)
+ result.validationResult.Labels = extractSafeOutputLabels(workflowData)
+
compileWorkflowProcessorLog.Printf("Successfully processed workflow file: %s", resolvedFile)
return result
}
+
+// extractSafeOutputLabels collects all unique labels referenced in safe-outputs configurations.
+// These are labels that should exist in the repository for the workflow to function correctly.
+// Scans: create-issue.labels/allowed-labels, create-discussion.labels/allowed-labels,
+// create-pull-request.labels/allowed-labels, and add-labels.allowed.
+func extractSafeOutputLabels(data *workflow.WorkflowData) []string {
+ if data == nil || data.SafeOutputs == nil {
+ return nil
+ }
+
+ seen := make(map[string]bool)
+ var labels []string
+
+ addLabel := func(label string) {
+ if label != "" && !seen[label] {
+ seen[label] = true
+ labels = append(labels, label)
+ }
+ }
+
+ so := data.SafeOutputs
+
+ if so.CreateIssues != nil {
+ for _, l := range so.CreateIssues.Labels {
+ addLabel(l)
+ }
+ for _, l := range so.CreateIssues.AllowedLabels {
+ addLabel(l)
+ }
+ }
+
+ if so.CreateDiscussions != nil {
+ for _, l := range so.CreateDiscussions.Labels {
+ addLabel(l)
+ }
+ for _, l := range so.CreateDiscussions.AllowedLabels {
+ addLabel(l)
+ }
+ }
+
+ if so.CreatePullRequests != nil {
+ for _, l := range so.CreatePullRequests.Labels {
+ addLabel(l)
+ }
+ for _, l := range so.CreatePullRequests.AllowedLabels {
+ addLabel(l)
+ }
+ }
+
+ if so.AddLabels != nil {
+ for _, l := range so.AddLabels.Allowed {
+ addLabel(l)
+ }
+ }
+
+ return labels
+}
diff --git a/pkg/workflow/maintenance_workflow.go b/pkg/workflow/maintenance_workflow.go
index bceef12a29c..c4a4ec588d7 100644
--- a/pkg/workflow/maintenance_workflow.go
+++ b/pkg/workflow/maintenance_workflow.go
@@ -220,6 +220,7 @@ on:
- 'update'
- 'upgrade'
- 'safe_outputs'
+ - 'create_labels'
run_url:
description: 'Run URL or run ID to replay safe outputs from (e.g. https://github.com/owner/repo/actions/runs/12345 or 12345). Required when operation is safe_outputs.'
required: false
@@ -300,10 +301,10 @@ jobs:
await main();
`)
- // Add unified run_operation job for all dispatch operations except safe_outputs
+ // Add unified run_operation job for all dispatch operations except safe_outputs and create_labels
yaml.WriteString(`
run_operation:
- if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.operation != '' && github.event.inputs.operation != 'safe_outputs' && !github.event.repository.fork }}
+ if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.operation != '' && github.event.inputs.operation != 'safe_outputs' && github.event.inputs.operation != 'create_labels' && !github.event.repository.fork }}
runs-on: ubuntu-slim
permissions:
actions: write
@@ -396,6 +397,51 @@ jobs:
await main();
`)
+ // Add create_labels job for workflow_dispatch with operation == 'create_labels'
+ yaml.WriteString(`
+ create_labels:
+ if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.operation == 'create_labels' && !github.event.repository.fork }}
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ issues: write
+ steps:
+ - name: Checkout repository
+ uses: ` + GetActionPin("actions/checkout") + `
+ with:
+ persist-credentials: false
+
+ - name: Setup Scripts
+ uses: ` + setupActionRef + `
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+
+ - name: Check admin/maintainer permissions
+ uses: ` + GetActionPin("actions/github-script") + `
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_team_member.cjs');
+ await main();
+
+`)
+
+ yaml.WriteString(generateInstallCLISteps(actionMode, version, actionTag, resolver))
+ yaml.WriteString(` - name: Create missing labels
+ uses: ` + GetActionPin("actions/github-script") + `
+ env:
+ GH_AW_CMD_PREFIX: ` + getCLICmdPrefix(actionMode) + `
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/create_labels.cjs');
+ await main();
+`)
+
// Add compile-workflows and zizmor-scan jobs only in dev mode
// These jobs are specific to the gh-aw repository and require go.mod, make build, etc.
// User repositories won't have these dependencies, so we skip them in release mode
diff --git a/pkg/workflow/maintenance_workflow_test.go b/pkg/workflow/maintenance_workflow_test.go
index 3947d0b928c..8c1677d8659 100644
--- a/pkg/workflow/maintenance_workflow_test.go
+++ b/pkg/workflow/maintenance_workflow_test.go
@@ -281,11 +281,12 @@ func TestGenerateMaintenanceWorkflow_OperationJobConditions(t *testing.T) {
yaml := string(content)
operationSkipCondition := `github.event_name != 'workflow_dispatch' || github.event.inputs.operation == ''`
- operationRunCondition := `github.event_name == 'workflow_dispatch' && github.event.inputs.operation != '' && github.event.inputs.operation != 'safe_outputs'`
+ operationRunCondition := `github.event_name == 'workflow_dispatch' && github.event.inputs.operation != '' && github.event.inputs.operation != 'safe_outputs' && github.event.inputs.operation != 'create_labels'`
applySafeOutputsCondition := `github.event_name == 'workflow_dispatch' && github.event.inputs.operation == 'safe_outputs'`
+ createLabelsCondition := `github.event_name == 'workflow_dispatch' && github.event.inputs.operation == 'create_labels'`
const jobSectionSearchRange = 300
- const runOpSectionSearchRange = 200
+ const runOpSectionSearchRange = 400
// Jobs that should be disabled when operation is set
disabledJobs := []string{"close-expired-entities:", "compile-workflows:", "zizmor-scan:", "secret-validation:"}
@@ -329,6 +330,22 @@ func TestGenerateMaintenanceWorkflow_OperationJobConditions(t *testing.T) {
}
}
+ // create_labels job should be triggered when operation == 'create_labels'
+ createLabelsIdx := strings.Index(yaml, "\n create_labels:")
+ if createLabelsIdx == -1 {
+ t.Errorf("Job create_labels not found in generated workflow")
+ } else {
+ createLabelsSection := yaml[createLabelsIdx : createLabelsIdx+runOpSectionSearchRange]
+ if !strings.Contains(createLabelsSection, createLabelsCondition) {
+ t.Errorf("Job create_labels should have the activation condition %q in:\n%s", createLabelsCondition, createLabelsSection)
+ }
+ }
+
+ // Verify create_labels is an option in the operation choices
+ if !strings.Contains(yaml, "- 'create_labels'") {
+ t.Error("workflow_dispatch operation choices should include 'create_labels'")
+ }
+
// Verify safe_outputs is an option in the operation choices
if !strings.Contains(yaml, "- 'safe_outputs'") {
t.Error("workflow_dispatch operation choices should include 'safe_outputs'")
@@ -572,12 +589,12 @@ func TestGenerateMaintenanceWorkflow_RunOperationCLICodegen(t *testing.T) {
t.Fatalf("Expected maintenance workflow to be generated: %v", err)
}
yaml := string(content)
- // Both run_operation and compile_workflows should use the same setup-go version
- // (both use GetActionPin, not hardcoded pins). Exactly 2 occurrences expected.
+ // run_operation, create_labels, and compile_workflows should use the same setup-go version
+ // (all use GetActionPin, not hardcoded pins). Exactly 3 occurrences expected.
setupGoPin := GetActionPin("actions/setup-go")
occurrences := strings.Count(yaml, setupGoPin)
- if occurrences != 2 {
- t.Errorf("Expected exactly 2 occurrences of pinned setup-go ref %q (run_operation + compile_workflows), got %d in:\n%s",
+ if occurrences != 3 {
+ t.Errorf("Expected exactly 3 occurrences of pinned setup-go ref %q (run_operation + create_labels + compile_workflows), got %d in:\n%s",
setupGoPin, occurrences, yaml)
}
})