@plainconceptsplatform/workflows 1.9.0 → 1.10.0
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/README.md +1 -1
- package/dist/catalog-installation.js +2 -2
- package/dist/package-baseline.js +4 -1
- package/dist/route-processing.js +4 -1
- package/dist/worker-env.d.ts +1 -1
- package/dist/worker-env.js +6 -2
- package/dist/workflow-catalog.d.ts +3 -1
- package/dist/workflow-catalog.js +6 -0
- package/loops/actions/add-issue-labels/action.yml +50 -50
- package/loops/actions/agent-output.cjs +17 -17
- package/loops/actions/apply-agent-bundle/action.yml +24 -24
- package/loops/actions/apply-agent-comments/action.yml +42 -42
- package/loops/actions/apply-agent-labels/action.yml +55 -55
- package/loops/actions/audit-close/action.yml +13 -6
- package/loops/actions/classify-route/classify-route.sh +3 -1
- package/loops/actions/cleanup-artifacts/action.yml +91 -91
- package/loops/actions/close-agent-issues/action.yml +43 -43
- package/loops/actions/collect-doc-drift/action.yml +29 -0
- package/loops/actions/collect-doc-drift/collect-doc-drift.mjs +133 -0
- package/loops/actions/create-issue-comment/action.yml +29 -29
- package/loops/actions/download-agent-output/action.yml +53 -53
- package/loops/actions/identify-gate-subject/action.yml +4 -0
- package/loops/actions/land-pull-request/action.yml +10 -0
- package/loops/actions/land-pull-request/land-pull-request.sh +14 -0
- package/loops/actions/link-pr-to-issue/action.yml +40 -40
- package/loops/actions/list-open-issues/action.yml +33 -33
- package/loops/actions/load-issue-context/action.yml +45 -45
- package/loops/actions/merge-agent-pr/action.yml +49 -49
- package/loops/actions/remove-issue-labels/action.yml +37 -37
- package/loops/actions/report-workflow-errors/action.yml +1 -1
- package/loops/actions/update-agent-issues/action.yml +58 -58
- package/loops/actions/validate-refine-output/action.yml +6 -1
- package/loops/actions/validate-refine-output/validate-refine-output.sh +20 -2
- package/loops/actions/validate-review-output/action.yml +35 -35
- package/loops/actions/validate-triage-output/action.yml +36 -36
- package/loops/actions/verify-composite-actions/action.yml +9 -9
- package/loops/actions/verify-refine-output/action.yml +9 -9
- package/loops/actions/verify-route-matrix/action.yml +9 -9
- package/loops/actions/verify-route-matrix/verify-route-matrix.sh +38 -5
- package/loops/docs/diagrams.md +45 -2
- package/loops/templates/agentics/agentics-maintenance.yml +121 -121
- package/loops/templates/issues/bug_report.yml +109 -109
- package/loops/templates/issues/feature_request.yml +75 -75
- package/loops/workflows/agent-docs-audit.md +266 -0
- package/loops/workflows/agent-merge-gate.md +11 -7
- package/loops/workflows/agent-refine.md +61 -11
- package/loops/workflows/shared/platform-defaults.md +53 -53
- package/loops/workflows/work-router.yml +120 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ workflows add refine --template agentics-checks --force # routes + template + m
|
|
|
46
46
|
workflows remove audit # uninstall the audit worker and drop it from the router
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
Route names: refine, implement, triage, apply-review, merge-gate, audit, release. Unknown arguments produce an error.
|
|
49
|
+
Route names: refine, implement, triage, apply-review, merge-gate, audit, docs-audit, release. `docs-audit` is optional: it installs only when named. Unknown arguments produce an error.
|
|
50
50
|
|
|
51
51
|
## Update
|
|
52
52
|
|
|
@@ -5,7 +5,7 @@ import { dirname, join, relative, resolve } from "node:path";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import { parse as parseYaml } from "yaml";
|
|
8
|
-
import { catalogTemplates, mandatoryFiles, retiredWorkflowFiles,
|
|
8
|
+
import { catalogTemplates, defaultRouteNames, mandatoryFiles, retiredWorkflowFiles, templateNames, workflowRoutes } from "./workflow-catalog.js";
|
|
9
9
|
import { processRoutes, excludedWorkerFiles } from "./route-processing.js";
|
|
10
10
|
import { generateOpencodeCi, generateOpencodeConfig, generateStackDefaults, injectStackEnv, shippedVerifyDefaults } from "./stack-defaults.js";
|
|
11
11
|
import { mergeRouter, mergeWorker } from "./worker-env.js";
|
|
@@ -40,7 +40,7 @@ const normalizeEol = (text) => text.replaceAll("\r\n", "\n");
|
|
|
40
40
|
export async function installCatalog(repositoryPath, options = {}) {
|
|
41
41
|
const version = options.packageVersion ?? await packageVersion();
|
|
42
42
|
const sourcePath = options.sourcePath ?? catalogSourcePath();
|
|
43
|
-
const selectedRoutes = options.selectedRoutes ??
|
|
43
|
+
const selectedRoutes = options.selectedRoutes ?? defaultRouteNames;
|
|
44
44
|
const allFiles = [...await catalogFiles(sourcePath), ...mandatoryFileSpecs(sourcePath)];
|
|
45
45
|
const deduplicated = allFiles.filter((file, index) => allFiles.findIndex((f) => f.target === file.target) === index).sort((left, right) => left.target.localeCompare(right.target));
|
|
46
46
|
const excluded = excludedWorkerFiles(selectedRoutes);
|
package/dist/package-baseline.js
CHANGED
|
@@ -24,8 +24,11 @@ export async function packageVersion() {
|
|
|
24
24
|
export function isValidVersion(version) {
|
|
25
25
|
return new RegExp(`^${VERSION}$`).test(version);
|
|
26
26
|
}
|
|
27
|
+
// The header opens a line near the top of the file. A document that only quotes it is not ours:
|
|
28
|
+
// matching it anywhere is how `update` deleted Guard's ADR 0010 and a Compass ADR from `docs/`.
|
|
29
|
+
const HEADER_LINE = new RegExp(`^\\s*(?:#|//|<!--)?\\s*${escapeRegex(HEADER)}[@.]`);
|
|
27
30
|
export function hasOwnershipHeader(content) {
|
|
28
|
-
return content.
|
|
31
|
+
return content.split("\n", 5).some((line) => HEADER_LINE.test(line));
|
|
29
32
|
}
|
|
30
33
|
/** The version recorded in a file's ownership header, or undefined for an unstamped file. */
|
|
31
34
|
export function installedVersion(content) {
|
package/dist/route-processing.js
CHANGED
|
@@ -4,6 +4,7 @@ import { routeNames, workflowRoutes } from "./workflow-catalog.js";
|
|
|
4
4
|
// route matrix reads the installed worker files, so neither is rewritten here.
|
|
5
5
|
const routeCrons = {
|
|
6
6
|
audit: "17 1 * * 1",
|
|
7
|
+
"docs-audit": "47 2 1,15 * *",
|
|
7
8
|
};
|
|
8
9
|
// Deterministic router jobs that exist only to serve one worker route. They leave with it, or
|
|
9
10
|
// the route matrix finds a `route == 'implement'` guard in a router with no implement worker.
|
|
@@ -19,9 +20,11 @@ const routeHelperOperations = {
|
|
|
19
20
|
};
|
|
20
21
|
export function stripRouteFromRouter(yaml, route) {
|
|
21
22
|
let result = yaml;
|
|
23
|
+
// By the slot comment as well as the value: a repository picks its own slot, and the shipped
|
|
24
|
+
// line carries that comment, so matching the bare default alone left the cron behind.
|
|
22
25
|
const cron = routeCrons[route];
|
|
23
26
|
if (cron !== undefined) {
|
|
24
|
-
result = result.replace(new RegExp(`^ - cron: "
|
|
27
|
+
result = result.replace(new RegExp(`^ - cron: "(?:${escapeRegex(cron)}"|[^"]*" # ${escapeRegex(route)} slot.*)\\n`, "gm"), "");
|
|
25
28
|
}
|
|
26
29
|
for (const job of [`call-${route}`, ...(routeHelperJobs[route] ?? [])]) {
|
|
27
30
|
result = removeJobBlock(result, job);
|
package/dist/worker-env.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ export declare function mergeWorkerEnv(packageContent: string, consumerContent:
|
|
|
32
32
|
content: string;
|
|
33
33
|
report: WorkerMergeReport;
|
|
34
34
|
};
|
|
35
|
-
/** Copy the router's own `env:` values into the
|
|
35
|
+
/** Copy the router's own `env:` values into the literal lines that cannot read them. */
|
|
36
36
|
export declare function mirrorRouterLiterals(router: string): string;
|
|
37
37
|
/** The package's router with the consumer's `env:` values put back and mirrored into place. */
|
|
38
38
|
export declare function mergeRouter(packageContent: string, consumerContent: string, baselineContent?: string): {
|
package/dist/worker-env.js
CHANGED
|
@@ -142,13 +142,14 @@ function engineEndpoint(content) {
|
|
|
142
142
|
// now: every job, router and release included, belongs on the Azure fleet (agents-arc, runner
|
|
143
143
|
// group `agentic`). The route matrix asserts it, so a repo cannot drift back without a red run. Per the standing rule for this package, anything a
|
|
144
144
|
// consumer really must vary belongs in `env:` at the top of the file, not in `runs-on`.
|
|
145
|
-
// GitHub evaluates no expression in a `workflow_run.workflows:` list or in a `cron:`, so the
|
|
145
|
+
// GitHub evaluates no expression in a `workflow_run.workflows:` list or in a `cron:`, so the
|
|
146
146
|
// router values that are also needed there cannot be read from `env:` at those two lines. The
|
|
147
147
|
// installer copies them in instead, which is what keeps the pair in step across an update; the
|
|
148
148
|
// route matrix asserts the copies still agree.
|
|
149
149
|
const CI_NAME_TRIGGER = /^(\s*workflows: \[")[^"]*("\]\s*)$/m;
|
|
150
150
|
const AUDIT_CRON_LINE = /^(\s*- cron: ")[^"]*(" # audit slot.*)$/m;
|
|
151
|
-
|
|
151
|
+
const DOCS_AUDIT_CRON_LINE = /^(\s*- cron: ")[^"]*(" # docs-audit slot.*)$/m;
|
|
152
|
+
/** Copy the router's own `env:` values into the literal lines that cannot read them. */
|
|
152
153
|
export function mirrorRouterLiterals(router) {
|
|
153
154
|
const values = new Map(parseYamlEnv(router).entries.map((entry) => [entry.key, unquote(entryValue(entry))]));
|
|
154
155
|
const ciName = values.get("CI_WORKFLOW_NAME");
|
|
@@ -158,6 +159,9 @@ export function mirrorRouterLiterals(router) {
|
|
|
158
159
|
result = result.replace(CI_NAME_TRIGGER, `$1${ciName}$2`);
|
|
159
160
|
if (auditCron !== undefined)
|
|
160
161
|
result = result.replace(AUDIT_CRON_LINE, `$1${auditCron}$2`);
|
|
162
|
+
const docsAuditCron = values.get("DOCS_AUDIT_CRON");
|
|
163
|
+
if (docsAuditCron !== undefined)
|
|
164
|
+
result = result.replace(DOCS_AUDIT_CRON_LINE, `$1${docsAuditCron}$2`);
|
|
161
165
|
return result;
|
|
162
166
|
}
|
|
163
167
|
function unquote(value) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const routeNames: readonly ["refine", "implement", "triage", "apply-review", "merge-gate", "audit", "release"];
|
|
1
|
+
export declare const routeNames: readonly ["refine", "implement", "triage", "apply-review", "merge-gate", "audit", "docs-audit", "release"];
|
|
2
2
|
export type RouteName = (typeof routeNames)[number];
|
|
3
3
|
export interface WorkflowRoute {
|
|
4
4
|
readonly name: RouteName;
|
|
@@ -9,6 +9,8 @@ export interface WorkflowRoute {
|
|
|
9
9
|
readonly companions?: readonly string[];
|
|
10
10
|
}
|
|
11
11
|
export declare const workflowRoutes: readonly WorkflowRoute[];
|
|
12
|
+
/** What an install with no explicit selection gets. An optional worker is added by name. */
|
|
13
|
+
export declare const defaultRouteNames: readonly RouteName[];
|
|
12
14
|
export declare const retiredWorkflowFiles: readonly ["agent-visual-verify.md", "agent-visual-verify.lock.yml"];
|
|
13
15
|
export declare const packageOwnedTargets: readonly [".github/actions", ".github/workflows/agent-*.md", ".github/workflows/shared/platform-defaults.md", ".github/workflows/shared/opencode-ci.md", ".github/workflows/work-router.yml", ".github/workflows/evidence.yml", "scripts/compile-agent-workflows.mjs", "opencode.ci.json", "docs/diagrams.md"];
|
|
14
16
|
export interface MandatoryFile {
|
package/dist/workflow-catalog.js
CHANGED
|
@@ -5,6 +5,7 @@ export const routeNames = [
|
|
|
5
5
|
"apply-review",
|
|
6
6
|
"merge-gate",
|
|
7
7
|
"audit",
|
|
8
|
+
"docs-audit",
|
|
8
9
|
"release",
|
|
9
10
|
];
|
|
10
11
|
export const workflowRoutes = [
|
|
@@ -14,8 +15,13 @@ export const workflowRoutes = [
|
|
|
14
15
|
{ name: "apply-review", worker: "agent-apply-review.md", description: "Applies reviewer feedback to an open pull request the bot authored, then pushes the fixes to the same branch.", defaultEnabled: true },
|
|
15
16
|
{ name: "merge-gate", worker: "agent-merge-gate.md", description: "Decides what happens to the pull request the merge train hands it, the bot's or a person's: repair it, merge it, or hand it to a human.", defaultEnabled: true },
|
|
16
17
|
{ name: "audit", worker: "agent-audit.md", description: "Read-only repository audit. Finds 5-7 problems, scores each 1-10, and files a single issue that Refine then sizes and splits.", defaultEnabled: true },
|
|
18
|
+
{ name: "docs-audit", worker: "agent-docs-audit.md", description: "Read-only documentation audit, every fortnight. Checks the documentation against the code and the Platform standard, and files one issue that Refine then sizes and splits, only when something is stale.", defaultEnabled: false },
|
|
17
19
|
{ name: "release", worker: "agent-release.md", description: "Writes release notes from the commit log, then a deterministic job opens a version-bump pull request with auto-merge; once it merges, release-tag.yml tags the commit and publishes the GitHub Release. Manual dispatch only (operation=release).", defaultEnabled: true, companions: ["release-tag.yml"] },
|
|
18
20
|
];
|
|
21
|
+
/** What an install with no explicit selection gets. An optional worker is added by name. */
|
|
22
|
+
export const defaultRouteNames = workflowRoutes
|
|
23
|
+
.filter((route) => route.defaultEnabled)
|
|
24
|
+
.map((route) => route.name);
|
|
19
25
|
// Workflow files the package used to ship and no longer does. `.github/workflows/` is not pruned
|
|
20
26
|
// wholesale, because a missing worker there means a route the consumer left out, so a retired one
|
|
21
27
|
// is named here or it sits in every consumer forever with a lock that still compiles.
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/add-issue-labels/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
-
name: Add issue labels
|
|
3
|
-
description: Add one or more labels to an issue or pull request.
|
|
4
|
-
inputs:
|
|
5
|
-
token:
|
|
6
|
-
description: GitHub token used by github-script.
|
|
7
|
-
required: true
|
|
8
|
-
issue-number:
|
|
9
|
-
description: Issue or pull request number.
|
|
10
|
-
required: true
|
|
11
|
-
labels:
|
|
12
|
-
description: Labels to add, one per line. A comma-separated list is accepted as well.
|
|
13
|
-
required: true
|
|
14
|
-
runs:
|
|
15
|
-
using: composite
|
|
16
|
-
steps:
|
|
17
|
-
- name: Add labels
|
|
18
|
-
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
19
|
-
env:
|
|
20
|
-
ISSUE_NUMBER: ${{ inputs.issue-number }}
|
|
21
|
-
LABELS: ${{ inputs.labels }}
|
|
22
|
-
with:
|
|
23
|
-
github-token: ${{ inputs.token }}
|
|
24
|
-
script: |
|
|
25
|
-
const labels = process.env.LABELS.split(/\r?\n|,/).map((label) => label.trim()).filter(Boolean);
|
|
26
|
-
if (labels.length === 0) {
|
|
27
|
-
core.notice('No labels to add.');
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Create what does not exist yet, so shipping a new label in the package does not
|
|
32
|
-
// require a person to go and make it in every repository first. Its sibling
|
|
33
|
-
// remove-issue-labels has swallowed 404 for a long time; this one had no guard at all,
|
|
34
|
-
// so a label the package introduced would fail the step in every consumer until
|
|
35
|
-
// somebody created it by hand. 422 is "already exists", which is the happy path on
|
|
36
|
-
// every run after the first.
|
|
37
|
-
for (const name of labels) {
|
|
38
|
-
try {
|
|
39
|
-
await github.rest.issues.createLabel({ ...context.repo, name, color: 'ededed' });
|
|
40
|
-
core.notice(`Created the ${name} label.`);
|
|
41
|
-
} catch (error) {
|
|
42
|
-
if (error.status !== 422) throw error;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
await github.rest.issues.addLabels({
|
|
47
|
-
...context.repo,
|
|
48
|
-
issue_number: Number(process.env.ISSUE_NUMBER),
|
|
49
|
-
labels,
|
|
50
|
-
});
|
|
1
|
+
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/add-issue-labels/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
+
name: Add issue labels
|
|
3
|
+
description: Add one or more labels to an issue or pull request.
|
|
4
|
+
inputs:
|
|
5
|
+
token:
|
|
6
|
+
description: GitHub token used by github-script.
|
|
7
|
+
required: true
|
|
8
|
+
issue-number:
|
|
9
|
+
description: Issue or pull request number.
|
|
10
|
+
required: true
|
|
11
|
+
labels:
|
|
12
|
+
description: Labels to add, one per line. A comma-separated list is accepted as well.
|
|
13
|
+
required: true
|
|
14
|
+
runs:
|
|
15
|
+
using: composite
|
|
16
|
+
steps:
|
|
17
|
+
- name: Add labels
|
|
18
|
+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
19
|
+
env:
|
|
20
|
+
ISSUE_NUMBER: ${{ inputs.issue-number }}
|
|
21
|
+
LABELS: ${{ inputs.labels }}
|
|
22
|
+
with:
|
|
23
|
+
github-token: ${{ inputs.token }}
|
|
24
|
+
script: |
|
|
25
|
+
const labels = process.env.LABELS.split(/\r?\n|,/).map((label) => label.trim()).filter(Boolean);
|
|
26
|
+
if (labels.length === 0) {
|
|
27
|
+
core.notice('No labels to add.');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Create what does not exist yet, so shipping a new label in the package does not
|
|
32
|
+
// require a person to go and make it in every repository first. Its sibling
|
|
33
|
+
// remove-issue-labels has swallowed 404 for a long time; this one had no guard at all,
|
|
34
|
+
// so a label the package introduced would fail the step in every consumer until
|
|
35
|
+
// somebody created it by hand. 422 is "already exists", which is the happy path on
|
|
36
|
+
// every run after the first.
|
|
37
|
+
for (const name of labels) {
|
|
38
|
+
try {
|
|
39
|
+
await github.rest.issues.createLabel({ ...context.repo, name, color: 'ededed' });
|
|
40
|
+
core.notice(`Created the ${name} label.`);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error.status !== 422) throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await github.rest.issues.addLabels({
|
|
47
|
+
...context.repo,
|
|
48
|
+
issue_number: Number(process.env.ISSUE_NUMBER),
|
|
49
|
+
labels,
|
|
50
|
+
});
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
// Managed by @plainconceptsplatform/workflows. Source: loops/actions/agent-output.cjs. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
2
|
-
const fs = require('node:fs');
|
|
3
|
-
|
|
4
|
-
// Every apply-agent-* action reads the same artifact the same way: a missing file means the
|
|
5
|
-
// agent produced nothing, which is a normal outcome rather than a failure.
|
|
6
|
-
function readAgentItems(outputFile, type) {
|
|
7
|
-
if (!outputFile || !fs.existsSync(outputFile)) {
|
|
8
|
-
return [];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const parsed = JSON.parse(fs.readFileSync(outputFile, 'utf8'));
|
|
12
|
-
const items = Array.isArray(parsed.items) ? parsed.items : [];
|
|
13
|
-
|
|
14
|
-
return type ? items.filter((item) => item.type === type) : items;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
module.exports = { readAgentItems };
|
|
1
|
+
// Managed by @plainconceptsplatform/workflows. Source: loops/actions/agent-output.cjs. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
2
|
+
const fs = require('node:fs');
|
|
3
|
+
|
|
4
|
+
// Every apply-agent-* action reads the same artifact the same way: a missing file means the
|
|
5
|
+
// agent produced nothing, which is a normal outcome rather than a failure.
|
|
6
|
+
function readAgentItems(outputFile, type) {
|
|
7
|
+
if (!outputFile || !fs.existsSync(outputFile)) {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const parsed = JSON.parse(fs.readFileSync(outputFile, 'utf8'));
|
|
12
|
+
const items = Array.isArray(parsed.items) ? parsed.items : [];
|
|
13
|
+
|
|
14
|
+
return type ? items.filter((item) => item.type === type) : items;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = { readAgentItems };
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/apply-agent-bundle/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
-
name: Apply agent bundle
|
|
3
|
-
description: Fast-forward an intended branch to the single commit exported by a verified git bundle.
|
|
4
|
-
inputs:
|
|
5
|
-
bundle-file:
|
|
6
|
-
description: Path to the git bundle from the agent artifact.
|
|
7
|
-
required: true
|
|
8
|
-
target-branch:
|
|
9
|
-
description: Branch that must fast-forward to the bundle commit.
|
|
10
|
-
required: true
|
|
11
|
-
base-branch:
|
|
12
|
-
description: Existing branch used only when target-branch does not exist.
|
|
13
|
-
required: false
|
|
14
|
-
default: ''
|
|
15
|
-
runs:
|
|
16
|
-
using: composite
|
|
17
|
-
steps:
|
|
18
|
-
- name: Fast-forward branch from bundle
|
|
19
|
-
shell: bash
|
|
20
|
-
env:
|
|
21
|
-
BUNDLE_FILE: ${{ inputs.bundle-file }}
|
|
22
|
-
TARGET_BRANCH: ${{ inputs.target-branch }}
|
|
23
|
-
BASE_BRANCH: ${{ inputs.base-branch }}
|
|
24
|
-
run: bash "$GITHUB_ACTION_PATH/apply-bundle.sh" "$BUNDLE_FILE" "$TARGET_BRANCH" "$BASE_BRANCH"
|
|
1
|
+
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/apply-agent-bundle/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
+
name: Apply agent bundle
|
|
3
|
+
description: Fast-forward an intended branch to the single commit exported by a verified git bundle.
|
|
4
|
+
inputs:
|
|
5
|
+
bundle-file:
|
|
6
|
+
description: Path to the git bundle from the agent artifact.
|
|
7
|
+
required: true
|
|
8
|
+
target-branch:
|
|
9
|
+
description: Branch that must fast-forward to the bundle commit.
|
|
10
|
+
required: true
|
|
11
|
+
base-branch:
|
|
12
|
+
description: Existing branch used only when target-branch does not exist.
|
|
13
|
+
required: false
|
|
14
|
+
default: ''
|
|
15
|
+
runs:
|
|
16
|
+
using: composite
|
|
17
|
+
steps:
|
|
18
|
+
- name: Fast-forward branch from bundle
|
|
19
|
+
shell: bash
|
|
20
|
+
env:
|
|
21
|
+
BUNDLE_FILE: ${{ inputs.bundle-file }}
|
|
22
|
+
TARGET_BRANCH: ${{ inputs.target-branch }}
|
|
23
|
+
BASE_BRANCH: ${{ inputs.base-branch }}
|
|
24
|
+
run: bash "$GITHUB_ACTION_PATH/apply-bundle.sh" "$BUNDLE_FILE" "$TARGET_BRANCH" "$BASE_BRANCH"
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/apply-agent-comments/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
-
name: Apply agent comments
|
|
3
|
-
description: Post every add_comment item from agent_output.json.
|
|
4
|
-
inputs:
|
|
5
|
-
output-file:
|
|
6
|
-
description: Path to agent_output.json.
|
|
7
|
-
required: true
|
|
8
|
-
token:
|
|
9
|
-
description: GitHub token with issues:write and pull-requests:write.
|
|
10
|
-
required: true
|
|
11
|
-
runs:
|
|
12
|
-
using: composite
|
|
13
|
-
steps:
|
|
14
|
-
- name: Post comments
|
|
15
|
-
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
16
|
-
env:
|
|
17
|
-
AGENT_OUTPUT_LIB: ${{ github.action_path }}/../agent-output.cjs
|
|
18
|
-
OUTPUT_FILE: ${{ inputs.output-file }}
|
|
19
|
-
with:
|
|
20
|
-
github-token: ${{ inputs.token }}
|
|
21
|
-
script: |
|
|
22
|
-
const { readAgentItems } = require(process.env.AGENT_OUTPUT_LIB);
|
|
23
|
-
const items = readAgentItems(process.env.OUTPUT_FILE, 'add_comment');
|
|
24
|
-
|
|
25
|
-
let posted = 0;
|
|
26
|
-
|
|
27
|
-
for (const item of items) {
|
|
28
|
-
if (!item.item_number) {
|
|
29
|
-
core.warning(`add_comment item has no item_number, skipping: ${JSON.stringify(item)}`);
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
await github.rest.issues.createComment({
|
|
34
|
-
...context.repo,
|
|
35
|
-
issue_number: Number(item.item_number),
|
|
36
|
-
body: item.body,
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
posted += 1;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
core.info(`Posted ${posted} comment(s).`);
|
|
1
|
+
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/apply-agent-comments/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
+
name: Apply agent comments
|
|
3
|
+
description: Post every add_comment item from agent_output.json.
|
|
4
|
+
inputs:
|
|
5
|
+
output-file:
|
|
6
|
+
description: Path to agent_output.json.
|
|
7
|
+
required: true
|
|
8
|
+
token:
|
|
9
|
+
description: GitHub token with issues:write and pull-requests:write.
|
|
10
|
+
required: true
|
|
11
|
+
runs:
|
|
12
|
+
using: composite
|
|
13
|
+
steps:
|
|
14
|
+
- name: Post comments
|
|
15
|
+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
16
|
+
env:
|
|
17
|
+
AGENT_OUTPUT_LIB: ${{ github.action_path }}/../agent-output.cjs
|
|
18
|
+
OUTPUT_FILE: ${{ inputs.output-file }}
|
|
19
|
+
with:
|
|
20
|
+
github-token: ${{ inputs.token }}
|
|
21
|
+
script: |
|
|
22
|
+
const { readAgentItems } = require(process.env.AGENT_OUTPUT_LIB);
|
|
23
|
+
const items = readAgentItems(process.env.OUTPUT_FILE, 'add_comment');
|
|
24
|
+
|
|
25
|
+
let posted = 0;
|
|
26
|
+
|
|
27
|
+
for (const item of items) {
|
|
28
|
+
if (!item.item_number) {
|
|
29
|
+
core.warning(`add_comment item has no item_number, skipping: ${JSON.stringify(item)}`);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
await github.rest.issues.createComment({
|
|
34
|
+
...context.repo,
|
|
35
|
+
issue_number: Number(item.item_number),
|
|
36
|
+
body: item.body,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
posted += 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
core.info(`Posted ${posted} comment(s).`);
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/apply-agent-labels/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
-
name: Apply agent labels
|
|
3
|
-
description: Apply every add_labels and remove_labels item from agent_output.json.
|
|
4
|
-
inputs:
|
|
5
|
-
output-file:
|
|
6
|
-
description: Path to agent_output.json.
|
|
7
|
-
required: true
|
|
8
|
-
token:
|
|
9
|
-
description: GitHub token with issues:write.
|
|
10
|
-
required: true
|
|
11
|
-
runs:
|
|
12
|
-
using: composite
|
|
13
|
-
steps:
|
|
14
|
-
- name: Apply label changes
|
|
15
|
-
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
16
|
-
env:
|
|
17
|
-
AGENT_OUTPUT_LIB: ${{ github.action_path }}/../agent-output.cjs
|
|
18
|
-
OUTPUT_FILE: ${{ inputs.output-file }}
|
|
19
|
-
with:
|
|
20
|
-
github-token: ${{ inputs.token }}
|
|
21
|
-
script: |
|
|
22
|
-
const { readAgentItems } = require(process.env.AGENT_OUTPUT_LIB);
|
|
23
|
-
const items = readAgentItems(process.env.OUTPUT_FILE)
|
|
24
|
-
.filter((item) => item.type === 'add_labels' || item.type === 'remove_labels');
|
|
25
|
-
|
|
26
|
-
let added = 0;
|
|
27
|
-
let removed = 0;
|
|
28
|
-
|
|
29
|
-
for (const item of items) {
|
|
30
|
-
const labels = (item.labels ?? []).map((label) => label.name).filter(Boolean);
|
|
31
|
-
|
|
32
|
-
if (!item.item_number || labels.length === 0) {
|
|
33
|
-
core.warning(`${item.type} item is not actionable, skipping: ${JSON.stringify(item)}`);
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const issue_number = Number(item.item_number);
|
|
38
|
-
|
|
39
|
-
if (item.type === 'add_labels') {
|
|
40
|
-
await github.rest.issues.addLabels({ ...context.repo, issue_number, labels });
|
|
41
|
-
added += labels.length;
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
for (const name of labels) {
|
|
46
|
-
try {
|
|
47
|
-
await github.rest.issues.removeLabel({ ...context.repo, issue_number, name });
|
|
48
|
-
removed += 1;
|
|
49
|
-
} catch (error) {
|
|
50
|
-
if (error.status !== 404) throw error;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
core.info(`Added ${added} label(s), removed ${removed}.`);
|
|
1
|
+
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/apply-agent-labels/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
+
name: Apply agent labels
|
|
3
|
+
description: Apply every add_labels and remove_labels item from agent_output.json.
|
|
4
|
+
inputs:
|
|
5
|
+
output-file:
|
|
6
|
+
description: Path to agent_output.json.
|
|
7
|
+
required: true
|
|
8
|
+
token:
|
|
9
|
+
description: GitHub token with issues:write.
|
|
10
|
+
required: true
|
|
11
|
+
runs:
|
|
12
|
+
using: composite
|
|
13
|
+
steps:
|
|
14
|
+
- name: Apply label changes
|
|
15
|
+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
16
|
+
env:
|
|
17
|
+
AGENT_OUTPUT_LIB: ${{ github.action_path }}/../agent-output.cjs
|
|
18
|
+
OUTPUT_FILE: ${{ inputs.output-file }}
|
|
19
|
+
with:
|
|
20
|
+
github-token: ${{ inputs.token }}
|
|
21
|
+
script: |
|
|
22
|
+
const { readAgentItems } = require(process.env.AGENT_OUTPUT_LIB);
|
|
23
|
+
const items = readAgentItems(process.env.OUTPUT_FILE)
|
|
24
|
+
.filter((item) => item.type === 'add_labels' || item.type === 'remove_labels');
|
|
25
|
+
|
|
26
|
+
let added = 0;
|
|
27
|
+
let removed = 0;
|
|
28
|
+
|
|
29
|
+
for (const item of items) {
|
|
30
|
+
const labels = (item.labels ?? []).map((label) => label.name).filter(Boolean);
|
|
31
|
+
|
|
32
|
+
if (!item.item_number || labels.length === 0) {
|
|
33
|
+
core.warning(`${item.type} item is not actionable, skipping: ${JSON.stringify(item)}`);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const issue_number = Number(item.item_number);
|
|
38
|
+
|
|
39
|
+
if (item.type === 'add_labels') {
|
|
40
|
+
await github.rest.issues.addLabels({ ...context.repo, issue_number, labels });
|
|
41
|
+
added += labels.length;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const name of labels) {
|
|
46
|
+
try {
|
|
47
|
+
await github.rest.issues.removeLabel({ ...context.repo, issue_number, name });
|
|
48
|
+
removed += 1;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (error.status !== 404) throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
core.info(`Added ${added} label(s), removed ${removed}.`);
|
|
@@ -21,12 +21,19 @@ runs:
|
|
|
21
21
|
with:
|
|
22
22
|
github-token: ${{ inputs.token }}
|
|
23
23
|
script: |
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
// A documentation audit is the same chain: a report Refine splits into children. A
|
|
25
|
+
// report carrying both labels must not be processed twice.
|
|
26
|
+
const reportsById = new Map();
|
|
27
|
+
for (const labels of ['audit', 'docs-audit']) {
|
|
28
|
+
const found = await github.paginate(github.rest.issues.listForRepo, {
|
|
29
|
+
...context.repo,
|
|
30
|
+
state: 'open',
|
|
31
|
+
labels,
|
|
32
|
+
per_page: 100,
|
|
33
|
+
});
|
|
34
|
+
for (const report of found) reportsById.set(report.number, report);
|
|
35
|
+
}
|
|
36
|
+
const reports = [...reportsById.values()];
|
|
30
37
|
|
|
31
38
|
if (reports.length === 0) {
|
|
32
39
|
core.info('No open audit reports.');
|
|
@@ -13,6 +13,7 @@ set -euo pipefail
|
|
|
13
13
|
# fired on a cron this file does not know classifies to no route and dies silently, so the
|
|
14
14
|
# route matrix asserts the two still agree.
|
|
15
15
|
readonly AUDIT_CRON="${AUDIT_CRON:-17 1 * * 1}"
|
|
16
|
+
readonly DOCS_AUDIT_CRON="${DOCS_AUDIT_CRON:-47 2 1,15 * *}"
|
|
16
17
|
readonly AUDIT_CLOSE_CRON="43 3 * * *"
|
|
17
18
|
readonly CLEANUP_ARTIFACTS_CRON="0 6 * * *"
|
|
18
19
|
readonly RECONCILE_BOT_PR_RUNS_CRON="17 * * * *"
|
|
@@ -192,6 +193,7 @@ classify_route() {
|
|
|
192
193
|
trigger_kind="scheduled"
|
|
193
194
|
case "${SCHEDULE:-}" in
|
|
194
195
|
"$AUDIT_CRON") route="audit" ;;
|
|
196
|
+
"$DOCS_AUDIT_CRON") route="docs-audit" ;;
|
|
195
197
|
"$AUDIT_CLOSE_CRON") route="audit-close" ;;
|
|
196
198
|
"$CLEANUP_ARTIFACTS_CRON") route="cleanup-artifacts" ;;
|
|
197
199
|
"$RECONCILE_BOT_PR_RUNS_CRON") route="reconcile-bot-pr-runs" ;;
|
|
@@ -266,7 +268,7 @@ classify_route() {
|
|
|
266
268
|
error="operation 'evidence' needs a positive pr-number, got '${INPUT_PR_NUMBER:-}'"
|
|
267
269
|
fi
|
|
268
270
|
;;
|
|
269
|
-
audit)
|
|
271
|
+
audit | docs-audit)
|
|
270
272
|
route="${OPERATION}"
|
|
271
273
|
trigger_kind="${INPUT_TRIGGER_KIND:-manual}"
|
|
272
274
|
;;
|