@plainconceptsplatform/workflows 0.20.2 → 0.20.3
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/dist/index.js +0 -0
- package/dist/stack-defaults.js +16 -16
- package/dist/workflow-catalog.d.ts +1 -1
- package/dist/workflow-catalog.js +2 -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/apply-agent-output/action.yml +108 -108
- package/loops/actions/classify-route/action.yml +100 -100
- package/loops/actions/cleanup-artifacts/action.yml +91 -91
- package/loops/actions/close-agent-issues/action.yml +43 -43
- package/loops/actions/collect-app-errors/action.yml +297 -0
- package/loops/actions/collect-app-errors/group-and-redact.mjs +252 -0
- package/loops/actions/collect-app-errors/query-app-errors.sh +104 -0
- package/loops/actions/create-agent-issues/action.yml +52 -52
- package/loops/actions/create-issue-comment/action.yml +29 -29
- package/loops/actions/download-agent-output/action.yml +53 -53
- 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/push-agent-branch/action.yml +45 -45
- package/loops/actions/remove-issue-labels/action.yml +37 -37
- package/loops/actions/update-agent-issues/action.yml +58 -58
- package/loops/actions/validate-review-output/action.yml +35 -35
- package/loops/actions/validate-triage-output/action.yml +36 -36
- package/loops/actions/verify-app-errors/action.yml +11 -0
- package/loops/actions/verify-app-errors/verify-app-errors.mjs +216 -0
- 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 +88 -0
- package/loops/scripts/compile-agent-workflows.mjs +331 -331
- package/loops/templates/agentics/agentics-app-errors.yml +168 -0
- package/loops/templates/agentics/agentics-maintenance.yml +121 -121
- package/loops/templates/ci/app-ci-dotnet-next.yml +330 -330
- package/loops/templates/ci/app-ci-node-monorepo.yml +260 -260
- package/loops/templates/issues/bug_report.yml +109 -109
- package/loops/templates/issues/feature_request.yml +75 -75
- package/loops/templates/release/github-release.yml +30 -30
- package/loops/workflows/authorize-bot-work.yml +105 -105
- package/loops/workflows/shared/opencode-ci.md +206 -206
- package/loops/workflows/shared/platform-defaults.md +19 -19
- package/package.json +9 -8
|
@@ -1,108 +1,108 @@
|
|
|
1
|
-
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/apply-agent-output/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
-
name: Apply agent output
|
|
3
|
-
description: Download the agent artifact and apply its safe outputs in one fixed order, so every worker concludes the same way.
|
|
4
|
-
inputs:
|
|
5
|
-
token:
|
|
6
|
-
description: GitHub token the writes are made with.
|
|
7
|
-
required: true
|
|
8
|
-
artifact-name:
|
|
9
|
-
description: >-
|
|
10
|
-
Name of the agent artifact. Callers pass the activation job's artifact_prefix output
|
|
11
|
-
followed by "agent"; gh-aw derives that prefix from the workflow's inputs, so it is
|
|
12
|
-
non-empty for every workflow_call worker and the bare name never resolves.
|
|
13
|
-
required: true
|
|
14
|
-
merge-pull-request:
|
|
15
|
-
description: Apply merge_pull_request items.
|
|
16
|
-
required: false
|
|
17
|
-
default: 'false'
|
|
18
|
-
push-to-branch:
|
|
19
|
-
description: Apply push_to_pull_request_branch items.
|
|
20
|
-
required: false
|
|
21
|
-
default: 'false'
|
|
22
|
-
close-issues:
|
|
23
|
-
description: Apply close_issue items.
|
|
24
|
-
required: false
|
|
25
|
-
default: 'false'
|
|
26
|
-
create-issues:
|
|
27
|
-
description: Apply create_issue items.
|
|
28
|
-
required: false
|
|
29
|
-
default: 'false'
|
|
30
|
-
update-issues:
|
|
31
|
-
description: Apply update_issue items.
|
|
32
|
-
required: false
|
|
33
|
-
default: 'false'
|
|
34
|
-
apply-labels:
|
|
35
|
-
description: Apply add_labels and remove_labels items.
|
|
36
|
-
required: false
|
|
37
|
-
default: 'true'
|
|
38
|
-
fallback-issue-number:
|
|
39
|
-
description: Issue an update_issue item targets when it names none.
|
|
40
|
-
required: false
|
|
41
|
-
default: ''
|
|
42
|
-
outputs:
|
|
43
|
-
item-count:
|
|
44
|
-
description: Number of items the agent produced.
|
|
45
|
-
value: ${{ steps.output.outputs.item-count }}
|
|
46
|
-
first-issue-number:
|
|
47
|
-
description: Issue number of the first issue created, or empty.
|
|
48
|
-
value: ${{ steps.create-issues.outputs.first-issue-number }}
|
|
49
|
-
runs:
|
|
50
|
-
using: composite
|
|
51
|
-
steps:
|
|
52
|
-
- name: Download agent output
|
|
53
|
-
id: output
|
|
54
|
-
uses: ./.github/actions/download-agent-output
|
|
55
|
-
with:
|
|
56
|
-
artifact-name: ${{ inputs.artifact-name }}
|
|
57
|
-
|
|
58
|
-
- name: Merge pull request
|
|
59
|
-
if: inputs.merge-pull-request == 'true' && steps.output.outputs.item-count != '0'
|
|
60
|
-
uses: ./.github/actions/merge-agent-pr
|
|
61
|
-
with:
|
|
62
|
-
output-file: ${{ steps.output.outputs.output-file }}
|
|
63
|
-
token: ${{ inputs.token }}
|
|
64
|
-
|
|
65
|
-
- name: Push to pull request branch
|
|
66
|
-
if: inputs.push-to-branch == 'true' && steps.output.outputs.item-count != '0'
|
|
67
|
-
uses: ./.github/actions/push-agent-branch
|
|
68
|
-
with:
|
|
69
|
-
output-file: ${{ steps.output.outputs.output-file }}
|
|
70
|
-
bundle-file: ${{ steps.output.outputs.bundle-file }}
|
|
71
|
-
token: ${{ inputs.token }}
|
|
72
|
-
|
|
73
|
-
- name: Close issues
|
|
74
|
-
if: inputs.close-issues == 'true' && steps.output.outputs.item-count != '0'
|
|
75
|
-
uses: ./.github/actions/close-agent-issues
|
|
76
|
-
with:
|
|
77
|
-
output-file: ${{ steps.output.outputs.output-file }}
|
|
78
|
-
token: ${{ inputs.token }}
|
|
79
|
-
|
|
80
|
-
- name: Update issues
|
|
81
|
-
if: inputs.update-issues == 'true' && steps.output.outputs.item-count != '0'
|
|
82
|
-
uses: ./.github/actions/update-agent-issues
|
|
83
|
-
with:
|
|
84
|
-
output-file: ${{ steps.output.outputs.output-file }}
|
|
85
|
-
token: ${{ inputs.token }}
|
|
86
|
-
fallback-issue-number: ${{ inputs.fallback-issue-number }}
|
|
87
|
-
|
|
88
|
-
- name: Create issues
|
|
89
|
-
id: create-issues
|
|
90
|
-
if: inputs.create-issues == 'true' && steps.output.outputs.item-count != '0'
|
|
91
|
-
uses: ./.github/actions/create-agent-issues
|
|
92
|
-
with:
|
|
93
|
-
output-file: ${{ steps.output.outputs.output-file }}
|
|
94
|
-
token: ${{ inputs.token }}
|
|
95
|
-
|
|
96
|
-
- name: Post agent comments
|
|
97
|
-
if: steps.output.outputs.item-count != '0'
|
|
98
|
-
uses: ./.github/actions/apply-agent-comments
|
|
99
|
-
with:
|
|
100
|
-
output-file: ${{ steps.output.outputs.output-file }}
|
|
101
|
-
token: ${{ inputs.token }}
|
|
102
|
-
|
|
103
|
-
- name: Apply agent label changes
|
|
104
|
-
if: inputs.apply-labels == 'true' && steps.output.outputs.item-count != '0'
|
|
105
|
-
uses: ./.github/actions/apply-agent-labels
|
|
106
|
-
with:
|
|
107
|
-
output-file: ${{ steps.output.outputs.output-file }}
|
|
108
|
-
token: ${{ inputs.token }}
|
|
1
|
+
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/apply-agent-output/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
+
name: Apply agent output
|
|
3
|
+
description: Download the agent artifact and apply its safe outputs in one fixed order, so every worker concludes the same way.
|
|
4
|
+
inputs:
|
|
5
|
+
token:
|
|
6
|
+
description: GitHub token the writes are made with.
|
|
7
|
+
required: true
|
|
8
|
+
artifact-name:
|
|
9
|
+
description: >-
|
|
10
|
+
Name of the agent artifact. Callers pass the activation job's artifact_prefix output
|
|
11
|
+
followed by "agent"; gh-aw derives that prefix from the workflow's inputs, so it is
|
|
12
|
+
non-empty for every workflow_call worker and the bare name never resolves.
|
|
13
|
+
required: true
|
|
14
|
+
merge-pull-request:
|
|
15
|
+
description: Apply merge_pull_request items.
|
|
16
|
+
required: false
|
|
17
|
+
default: 'false'
|
|
18
|
+
push-to-branch:
|
|
19
|
+
description: Apply push_to_pull_request_branch items.
|
|
20
|
+
required: false
|
|
21
|
+
default: 'false'
|
|
22
|
+
close-issues:
|
|
23
|
+
description: Apply close_issue items.
|
|
24
|
+
required: false
|
|
25
|
+
default: 'false'
|
|
26
|
+
create-issues:
|
|
27
|
+
description: Apply create_issue items.
|
|
28
|
+
required: false
|
|
29
|
+
default: 'false'
|
|
30
|
+
update-issues:
|
|
31
|
+
description: Apply update_issue items.
|
|
32
|
+
required: false
|
|
33
|
+
default: 'false'
|
|
34
|
+
apply-labels:
|
|
35
|
+
description: Apply add_labels and remove_labels items.
|
|
36
|
+
required: false
|
|
37
|
+
default: 'true'
|
|
38
|
+
fallback-issue-number:
|
|
39
|
+
description: Issue an update_issue item targets when it names none.
|
|
40
|
+
required: false
|
|
41
|
+
default: ''
|
|
42
|
+
outputs:
|
|
43
|
+
item-count:
|
|
44
|
+
description: Number of items the agent produced.
|
|
45
|
+
value: ${{ steps.output.outputs.item-count }}
|
|
46
|
+
first-issue-number:
|
|
47
|
+
description: Issue number of the first issue created, or empty.
|
|
48
|
+
value: ${{ steps.create-issues.outputs.first-issue-number }}
|
|
49
|
+
runs:
|
|
50
|
+
using: composite
|
|
51
|
+
steps:
|
|
52
|
+
- name: Download agent output
|
|
53
|
+
id: output
|
|
54
|
+
uses: ./.github/actions/download-agent-output
|
|
55
|
+
with:
|
|
56
|
+
artifact-name: ${{ inputs.artifact-name }}
|
|
57
|
+
|
|
58
|
+
- name: Merge pull request
|
|
59
|
+
if: inputs.merge-pull-request == 'true' && steps.output.outputs.item-count != '0'
|
|
60
|
+
uses: ./.github/actions/merge-agent-pr
|
|
61
|
+
with:
|
|
62
|
+
output-file: ${{ steps.output.outputs.output-file }}
|
|
63
|
+
token: ${{ inputs.token }}
|
|
64
|
+
|
|
65
|
+
- name: Push to pull request branch
|
|
66
|
+
if: inputs.push-to-branch == 'true' && steps.output.outputs.item-count != '0'
|
|
67
|
+
uses: ./.github/actions/push-agent-branch
|
|
68
|
+
with:
|
|
69
|
+
output-file: ${{ steps.output.outputs.output-file }}
|
|
70
|
+
bundle-file: ${{ steps.output.outputs.bundle-file }}
|
|
71
|
+
token: ${{ inputs.token }}
|
|
72
|
+
|
|
73
|
+
- name: Close issues
|
|
74
|
+
if: inputs.close-issues == 'true' && steps.output.outputs.item-count != '0'
|
|
75
|
+
uses: ./.github/actions/close-agent-issues
|
|
76
|
+
with:
|
|
77
|
+
output-file: ${{ steps.output.outputs.output-file }}
|
|
78
|
+
token: ${{ inputs.token }}
|
|
79
|
+
|
|
80
|
+
- name: Update issues
|
|
81
|
+
if: inputs.update-issues == 'true' && steps.output.outputs.item-count != '0'
|
|
82
|
+
uses: ./.github/actions/update-agent-issues
|
|
83
|
+
with:
|
|
84
|
+
output-file: ${{ steps.output.outputs.output-file }}
|
|
85
|
+
token: ${{ inputs.token }}
|
|
86
|
+
fallback-issue-number: ${{ inputs.fallback-issue-number }}
|
|
87
|
+
|
|
88
|
+
- name: Create issues
|
|
89
|
+
id: create-issues
|
|
90
|
+
if: inputs.create-issues == 'true' && steps.output.outputs.item-count != '0'
|
|
91
|
+
uses: ./.github/actions/create-agent-issues
|
|
92
|
+
with:
|
|
93
|
+
output-file: ${{ steps.output.outputs.output-file }}
|
|
94
|
+
token: ${{ inputs.token }}
|
|
95
|
+
|
|
96
|
+
- name: Post agent comments
|
|
97
|
+
if: steps.output.outputs.item-count != '0'
|
|
98
|
+
uses: ./.github/actions/apply-agent-comments
|
|
99
|
+
with:
|
|
100
|
+
output-file: ${{ steps.output.outputs.output-file }}
|
|
101
|
+
token: ${{ inputs.token }}
|
|
102
|
+
|
|
103
|
+
- name: Apply agent label changes
|
|
104
|
+
if: inputs.apply-labels == 'true' && steps.output.outputs.item-count != '0'
|
|
105
|
+
uses: ./.github/actions/apply-agent-labels
|
|
106
|
+
with:
|
|
107
|
+
output-file: ${{ steps.output.outputs.output-file }}
|
|
108
|
+
token: ${{ inputs.token }}
|
|
@@ -1,100 +1,100 @@
|
|
|
1
|
-
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/classify-route/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
-
name: Classify route
|
|
3
|
-
description: Classify the triggering event into exactly one route, and resolve the issue a gated pull request closes.
|
|
4
|
-
inputs:
|
|
5
|
-
token:
|
|
6
|
-
description: GitHub token with pull-requests read access.
|
|
7
|
-
required: true
|
|
8
|
-
outputs:
|
|
9
|
-
route:
|
|
10
|
-
description: The single route this event selects, or `none`.
|
|
11
|
-
value: ${{ steps.classify.outputs.route }}
|
|
12
|
-
issue-number:
|
|
13
|
-
description: Issue number the route operates on.
|
|
14
|
-
value: ${{ steps.classify.outputs.issue-number }}
|
|
15
|
-
pr-number:
|
|
16
|
-
description: Pull request number the route operates on.
|
|
17
|
-
value: ${{ steps.classify.outputs.pr-number }}
|
|
18
|
-
linked-issue:
|
|
19
|
-
description: Issue the pull request closes. Empty for routes that do not target one.
|
|
20
|
-
value: ${{ steps.linked-issue.outputs.issue }}
|
|
21
|
-
ci-conclusion:
|
|
22
|
-
description: Conclusion reported by the CI run that triggered a merge-gate route.
|
|
23
|
-
value: ${{ steps.classify.outputs.ci-conclusion }}
|
|
24
|
-
ci-run-id:
|
|
25
|
-
description: Workflow run ID of that CI run.
|
|
26
|
-
value: ${{ steps.classify.outputs.ci-run-id }}
|
|
27
|
-
merge-gate-attempts:
|
|
28
|
-
description: Failed merge-gate attempts already made against this CI verdict.
|
|
29
|
-
value: ${{ steps.classify.outputs.merge-gate-attempts }}
|
|
30
|
-
implement-attempts:
|
|
31
|
-
description: Implement runs already made for this issue that died before producing an answer.
|
|
32
|
-
value: ${{ steps.classify.outputs.implement-attempts }}
|
|
33
|
-
refine-mode:
|
|
34
|
-
description: first or rerefine.
|
|
35
|
-
value: ${{ steps.classify.outputs.refine-mode }}
|
|
36
|
-
triage-mode:
|
|
37
|
-
description: first or retriage.
|
|
38
|
-
value: ${{ steps.classify.outputs.triage-mode }}
|
|
39
|
-
trigger-kind:
|
|
40
|
-
description: scheduled or manual.
|
|
41
|
-
value: ${{ steps.classify.outputs.trigger-kind }}
|
|
42
|
-
runs:
|
|
43
|
-
using: composite
|
|
44
|
-
steps:
|
|
45
|
-
- name: Classify the event
|
|
46
|
-
id: classify
|
|
47
|
-
shell: bash
|
|
48
|
-
env:
|
|
49
|
-
EVENT: ${{ github.event_name }}
|
|
50
|
-
ACTION: ${{ github.event.action }}
|
|
51
|
-
LABEL: ${{ github.event.label.name }}
|
|
52
|
-
ISSUE_LABELS: ${{ toJSON(github.event.issue.labels.*.name) }}
|
|
53
|
-
ISSUE_STATE: ${{ github.event.issue.state }}
|
|
54
|
-
EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
55
|
-
EVENT_PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
56
|
-
COMMENT_ON_PR: ${{ github.event.issue.pull_request != '' }}
|
|
57
|
-
COMMENT_SENDER_TYPE: ${{ github.event.comment.user.type }}
|
|
58
|
-
ACTOR: ${{ github.actor }}
|
|
59
|
-
RUN_PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }}
|
|
60
|
-
RUN_CONCLUSION: ${{ github.event.workflow_run.conclusion }}
|
|
61
|
-
RUN_ID: ${{ github.event.workflow_run.id }}
|
|
62
|
-
SCHEDULE: ${{ github.event.schedule }}
|
|
63
|
-
OPERATION: ${{ github.event.inputs.operation }}
|
|
64
|
-
INPUT_ISSUE_NUMBER: ${{ github.event.inputs.issue-number }}
|
|
65
|
-
INPUT_PR_NUMBER: ${{ github.event.inputs.pr-number }}
|
|
66
|
-
INPUT_MODE: ${{ github.event.inputs.mode }}
|
|
67
|
-
INPUT_CI_CONCLUSION: ${{ github.event.inputs.ci-conclusion }}
|
|
68
|
-
INPUT_CI_RUN_ID: ${{ github.event.inputs.ci-run-id }}
|
|
69
|
-
INPUT_ATTEMPTS_SO_FAR: ${{ github.event.inputs.attempts_so_far }}
|
|
70
|
-
INPUT_TRIGGER_KIND: ${{ github.event.inputs.trigger-kind }}
|
|
71
|
-
run: |
|
|
72
|
-
set -euo pipefail
|
|
73
|
-
|
|
74
|
-
bash "${GITHUB_ACTION_PATH}/classify-route.sh" | tee "$RUNNER_TEMP/route.env"
|
|
75
|
-
cat "$RUNNER_TEMP/route.env" >> "$GITHUB_OUTPUT"
|
|
76
|
-
|
|
77
|
-
route="$(sed -n 's/^route=//p' "$RUNNER_TEMP/route.env")"
|
|
78
|
-
reason="$(sed -n 's/^error=//p' "$RUNNER_TEMP/route.env")"
|
|
79
|
-
|
|
80
|
-
if [ "$route" = "none" ] && [ -n "$reason" ]; then
|
|
81
|
-
echo "::notice::No route for this event: $reason"
|
|
82
|
-
fi
|
|
83
|
-
|
|
84
|
-
- name: Resolve the issue the pull request closes
|
|
85
|
-
id: linked-issue
|
|
86
|
-
if: steps.classify.outputs.route == 'merge-gate' || steps.classify.outputs.route == 'apply-review'
|
|
87
|
-
shell: bash
|
|
88
|
-
env:
|
|
89
|
-
GH_TOKEN: ${{ inputs.token }}
|
|
90
|
-
REPO: ${{ github.repository }}
|
|
91
|
-
PR_NUMBER: ${{ steps.classify.outputs.pr-number }}
|
|
92
|
-
run: |
|
|
93
|
-
set -euo pipefail
|
|
94
|
-
|
|
95
|
-
issue="$(gh pr view "$PR_NUMBER" --repo "$REPO" \
|
|
96
|
-
--json closingIssuesReferences \
|
|
97
|
-
--jq '.closingIssuesReferences[0].number // empty')"
|
|
98
|
-
|
|
99
|
-
echo "issue=${issue}" >> "$GITHUB_OUTPUT"
|
|
100
|
-
echo "Pull request #${PR_NUMBER} closes issue #${issue:-<none>}"
|
|
1
|
+
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/classify-route/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
+
name: Classify route
|
|
3
|
+
description: Classify the triggering event into exactly one route, and resolve the issue a gated pull request closes.
|
|
4
|
+
inputs:
|
|
5
|
+
token:
|
|
6
|
+
description: GitHub token with pull-requests read access.
|
|
7
|
+
required: true
|
|
8
|
+
outputs:
|
|
9
|
+
route:
|
|
10
|
+
description: The single route this event selects, or `none`.
|
|
11
|
+
value: ${{ steps.classify.outputs.route }}
|
|
12
|
+
issue-number:
|
|
13
|
+
description: Issue number the route operates on.
|
|
14
|
+
value: ${{ steps.classify.outputs.issue-number }}
|
|
15
|
+
pr-number:
|
|
16
|
+
description: Pull request number the route operates on.
|
|
17
|
+
value: ${{ steps.classify.outputs.pr-number }}
|
|
18
|
+
linked-issue:
|
|
19
|
+
description: Issue the pull request closes. Empty for routes that do not target one.
|
|
20
|
+
value: ${{ steps.linked-issue.outputs.issue }}
|
|
21
|
+
ci-conclusion:
|
|
22
|
+
description: Conclusion reported by the CI run that triggered a merge-gate route.
|
|
23
|
+
value: ${{ steps.classify.outputs.ci-conclusion }}
|
|
24
|
+
ci-run-id:
|
|
25
|
+
description: Workflow run ID of that CI run.
|
|
26
|
+
value: ${{ steps.classify.outputs.ci-run-id }}
|
|
27
|
+
merge-gate-attempts:
|
|
28
|
+
description: Failed merge-gate attempts already made against this CI verdict.
|
|
29
|
+
value: ${{ steps.classify.outputs.merge-gate-attempts }}
|
|
30
|
+
implement-attempts:
|
|
31
|
+
description: Implement runs already made for this issue that died before producing an answer.
|
|
32
|
+
value: ${{ steps.classify.outputs.implement-attempts }}
|
|
33
|
+
refine-mode:
|
|
34
|
+
description: first or rerefine.
|
|
35
|
+
value: ${{ steps.classify.outputs.refine-mode }}
|
|
36
|
+
triage-mode:
|
|
37
|
+
description: first or retriage.
|
|
38
|
+
value: ${{ steps.classify.outputs.triage-mode }}
|
|
39
|
+
trigger-kind:
|
|
40
|
+
description: scheduled or manual.
|
|
41
|
+
value: ${{ steps.classify.outputs.trigger-kind }}
|
|
42
|
+
runs:
|
|
43
|
+
using: composite
|
|
44
|
+
steps:
|
|
45
|
+
- name: Classify the event
|
|
46
|
+
id: classify
|
|
47
|
+
shell: bash
|
|
48
|
+
env:
|
|
49
|
+
EVENT: ${{ github.event_name }}
|
|
50
|
+
ACTION: ${{ github.event.action }}
|
|
51
|
+
LABEL: ${{ github.event.label.name }}
|
|
52
|
+
ISSUE_LABELS: ${{ toJSON(github.event.issue.labels.*.name) }}
|
|
53
|
+
ISSUE_STATE: ${{ github.event.issue.state }}
|
|
54
|
+
EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
55
|
+
EVENT_PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
56
|
+
COMMENT_ON_PR: ${{ github.event.issue.pull_request != '' }}
|
|
57
|
+
COMMENT_SENDER_TYPE: ${{ github.event.comment.user.type }}
|
|
58
|
+
ACTOR: ${{ github.actor }}
|
|
59
|
+
RUN_PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }}
|
|
60
|
+
RUN_CONCLUSION: ${{ github.event.workflow_run.conclusion }}
|
|
61
|
+
RUN_ID: ${{ github.event.workflow_run.id }}
|
|
62
|
+
SCHEDULE: ${{ github.event.schedule }}
|
|
63
|
+
OPERATION: ${{ github.event.inputs.operation }}
|
|
64
|
+
INPUT_ISSUE_NUMBER: ${{ github.event.inputs.issue-number }}
|
|
65
|
+
INPUT_PR_NUMBER: ${{ github.event.inputs.pr-number }}
|
|
66
|
+
INPUT_MODE: ${{ github.event.inputs.mode }}
|
|
67
|
+
INPUT_CI_CONCLUSION: ${{ github.event.inputs.ci-conclusion }}
|
|
68
|
+
INPUT_CI_RUN_ID: ${{ github.event.inputs.ci-run-id }}
|
|
69
|
+
INPUT_ATTEMPTS_SO_FAR: ${{ github.event.inputs.attempts_so_far }}
|
|
70
|
+
INPUT_TRIGGER_KIND: ${{ github.event.inputs.trigger-kind }}
|
|
71
|
+
run: |
|
|
72
|
+
set -euo pipefail
|
|
73
|
+
|
|
74
|
+
bash "${GITHUB_ACTION_PATH}/classify-route.sh" | tee "$RUNNER_TEMP/route.env"
|
|
75
|
+
cat "$RUNNER_TEMP/route.env" >> "$GITHUB_OUTPUT"
|
|
76
|
+
|
|
77
|
+
route="$(sed -n 's/^route=//p' "$RUNNER_TEMP/route.env")"
|
|
78
|
+
reason="$(sed -n 's/^error=//p' "$RUNNER_TEMP/route.env")"
|
|
79
|
+
|
|
80
|
+
if [ "$route" = "none" ] && [ -n "$reason" ]; then
|
|
81
|
+
echo "::notice::No route for this event: $reason"
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
- name: Resolve the issue the pull request closes
|
|
85
|
+
id: linked-issue
|
|
86
|
+
if: steps.classify.outputs.route == 'merge-gate' || steps.classify.outputs.route == 'apply-review'
|
|
87
|
+
shell: bash
|
|
88
|
+
env:
|
|
89
|
+
GH_TOKEN: ${{ inputs.token }}
|
|
90
|
+
REPO: ${{ github.repository }}
|
|
91
|
+
PR_NUMBER: ${{ steps.classify.outputs.pr-number }}
|
|
92
|
+
run: |
|
|
93
|
+
set -euo pipefail
|
|
94
|
+
|
|
95
|
+
issue="$(gh pr view "$PR_NUMBER" --repo "$REPO" \
|
|
96
|
+
--json closingIssuesReferences \
|
|
97
|
+
--jq '.closingIssuesReferences[0].number // empty')"
|
|
98
|
+
|
|
99
|
+
echo "issue=${issue}" >> "$GITHUB_OUTPUT"
|
|
100
|
+
echo "Pull request #${PR_NUMBER} closes issue #${issue:-<none>}"
|
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/cleanup-artifacts/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
-
name: Clean up artifacts
|
|
3
|
-
description: Delete workflow artifacts older than the configured retention period.
|
|
4
|
-
inputs:
|
|
5
|
-
token:
|
|
6
|
-
description: GitHub token with actions write access.
|
|
7
|
-
required: true
|
|
8
|
-
artifact-retention-days:
|
|
9
|
-
description: Retention period in days. Invalid or empty values use 3.
|
|
10
|
-
required: false
|
|
11
|
-
default: ''
|
|
12
|
-
runs:
|
|
13
|
-
using: composite
|
|
14
|
-
steps:
|
|
15
|
-
- name: Delete expired artifacts
|
|
16
|
-
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
17
|
-
env:
|
|
18
|
-
ARTIFACT_RETENTION_DAYS: ${{ inputs.artifact-retention-days }}
|
|
19
|
-
with:
|
|
20
|
-
github-token: ${{ inputs.token }}
|
|
21
|
-
script: |
|
|
22
|
-
const parsedDays = Number.parseInt(process.env.ARTIFACT_RETENTION_DAYS, 10);
|
|
23
|
-
const days = Number.isInteger(parsedDays) && parsedDays > 0 ? parsedDays : 3;
|
|
24
|
-
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
|
|
25
|
-
|
|
26
|
-
core.info(`Retention: ${days} days (cutoff ${cutoff.toISOString()})`);
|
|
27
|
-
|
|
28
|
-
const artifacts = await github.paginate(github.rest.actions.listArtifactsForRepo, {
|
|
29
|
-
...context.repo,
|
|
30
|
-
per_page: 100,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const expired = artifacts.filter((artifact) => new Date(artifact.created_at) < cutoff);
|
|
34
|
-
|
|
35
|
-
let deleted = 0;
|
|
36
|
-
const failed = [];
|
|
37
|
-
|
|
38
|
-
for (const artifact of expired) {
|
|
39
|
-
// GitHub answers a delete with a 5xx now and then. Three tries, because the
|
|
40
|
-
// alternative is a red daily run over one artifact that tomorrow's run would
|
|
41
|
-
// have removed anyway.
|
|
42
|
-
let lastError;
|
|
43
|
-
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
44
|
-
try {
|
|
45
|
-
await github.rest.actions.deleteArtifact({
|
|
46
|
-
...context.repo,
|
|
47
|
-
artifact_id: artifact.id,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
core.info(`Deleted ${artifact.name} (created ${artifact.created_at})`);
|
|
51
|
-
deleted += 1;
|
|
52
|
-
lastError = undefined;
|
|
53
|
-
break;
|
|
54
|
-
} catch (error) {
|
|
55
|
-
lastError = error;
|
|
56
|
-
if (attempt < 3) await new Promise((wait) => setTimeout(wait, attempt * 2000));
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (lastError) {
|
|
61
|
-
core.warning(
|
|
62
|
-
`Could not delete ${artifact.name} (${artifact.id}) after 3 tries: ${lastError.message}`,
|
|
63
|
-
);
|
|
64
|
-
failed.push({ name: artifact.name, status: lastError.status });
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
await core.summary
|
|
69
|
-
.addHeading('Artifact cleanup', 2)
|
|
70
|
-
.addList([
|
|
71
|
-
`Retention: ${days} days (cutoff ${cutoff.toISOString()})`,
|
|
72
|
-
`Deleted: ${deleted}`,
|
|
73
|
-
`Failed to delete: ${failed.length}`,
|
|
74
|
-
`Within retention: ${artifacts.length - expired.length}`,
|
|
75
|
-
])
|
|
76
|
-
.write();
|
|
77
|
-
|
|
78
|
-
// Deleting nothing at all while expired artifacts exist is a permission or
|
|
79
|
-
// configuration problem, and has to be loud. One stubborn artifact among many is
|
|
80
|
-
// the API having a bad minute: tomorrow's run picks it up, and failing here only
|
|
81
|
-
// teaches people to ignore a red cleanup. A 4xx is never transient, so it fails
|
|
82
|
-
// whatever else succeeded.
|
|
83
|
-
const names = failed.map((entry) => entry.name).join(', ');
|
|
84
|
-
const structural = failed.some((entry) => entry.status && entry.status < 500);
|
|
85
|
-
if (failed.length > 0 && (deleted === 0 || structural)) {
|
|
86
|
-
core.setFailed(`${failed.length} artifact(s) could not be deleted: ${names}`);
|
|
87
|
-
} else if (failed.length > 0) {
|
|
88
|
-
core.warning(
|
|
89
|
-
`${failed.length} artifact(s) survived this run and will be retried tomorrow: ${names}`,
|
|
90
|
-
);
|
|
91
|
-
}
|
|
1
|
+
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/cleanup-artifacts/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
+
name: Clean up artifacts
|
|
3
|
+
description: Delete workflow artifacts older than the configured retention period.
|
|
4
|
+
inputs:
|
|
5
|
+
token:
|
|
6
|
+
description: GitHub token with actions write access.
|
|
7
|
+
required: true
|
|
8
|
+
artifact-retention-days:
|
|
9
|
+
description: Retention period in days. Invalid or empty values use 3.
|
|
10
|
+
required: false
|
|
11
|
+
default: ''
|
|
12
|
+
runs:
|
|
13
|
+
using: composite
|
|
14
|
+
steps:
|
|
15
|
+
- name: Delete expired artifacts
|
|
16
|
+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
17
|
+
env:
|
|
18
|
+
ARTIFACT_RETENTION_DAYS: ${{ inputs.artifact-retention-days }}
|
|
19
|
+
with:
|
|
20
|
+
github-token: ${{ inputs.token }}
|
|
21
|
+
script: |
|
|
22
|
+
const parsedDays = Number.parseInt(process.env.ARTIFACT_RETENTION_DAYS, 10);
|
|
23
|
+
const days = Number.isInteger(parsedDays) && parsedDays > 0 ? parsedDays : 3;
|
|
24
|
+
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
|
|
25
|
+
|
|
26
|
+
core.info(`Retention: ${days} days (cutoff ${cutoff.toISOString()})`);
|
|
27
|
+
|
|
28
|
+
const artifacts = await github.paginate(github.rest.actions.listArtifactsForRepo, {
|
|
29
|
+
...context.repo,
|
|
30
|
+
per_page: 100,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const expired = artifacts.filter((artifact) => new Date(artifact.created_at) < cutoff);
|
|
34
|
+
|
|
35
|
+
let deleted = 0;
|
|
36
|
+
const failed = [];
|
|
37
|
+
|
|
38
|
+
for (const artifact of expired) {
|
|
39
|
+
// GitHub answers a delete with a 5xx now and then. Three tries, because the
|
|
40
|
+
// alternative is a red daily run over one artifact that tomorrow's run would
|
|
41
|
+
// have removed anyway.
|
|
42
|
+
let lastError;
|
|
43
|
+
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
44
|
+
try {
|
|
45
|
+
await github.rest.actions.deleteArtifact({
|
|
46
|
+
...context.repo,
|
|
47
|
+
artifact_id: artifact.id,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
core.info(`Deleted ${artifact.name} (created ${artifact.created_at})`);
|
|
51
|
+
deleted += 1;
|
|
52
|
+
lastError = undefined;
|
|
53
|
+
break;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
lastError = error;
|
|
56
|
+
if (attempt < 3) await new Promise((wait) => setTimeout(wait, attempt * 2000));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (lastError) {
|
|
61
|
+
core.warning(
|
|
62
|
+
`Could not delete ${artifact.name} (${artifact.id}) after 3 tries: ${lastError.message}`,
|
|
63
|
+
);
|
|
64
|
+
failed.push({ name: artifact.name, status: lastError.status });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
await core.summary
|
|
69
|
+
.addHeading('Artifact cleanup', 2)
|
|
70
|
+
.addList([
|
|
71
|
+
`Retention: ${days} days (cutoff ${cutoff.toISOString()})`,
|
|
72
|
+
`Deleted: ${deleted}`,
|
|
73
|
+
`Failed to delete: ${failed.length}`,
|
|
74
|
+
`Within retention: ${artifacts.length - expired.length}`,
|
|
75
|
+
])
|
|
76
|
+
.write();
|
|
77
|
+
|
|
78
|
+
// Deleting nothing at all while expired artifacts exist is a permission or
|
|
79
|
+
// configuration problem, and has to be loud. One stubborn artifact among many is
|
|
80
|
+
// the API having a bad minute: tomorrow's run picks it up, and failing here only
|
|
81
|
+
// teaches people to ignore a red cleanup. A 4xx is never transient, so it fails
|
|
82
|
+
// whatever else succeeded.
|
|
83
|
+
const names = failed.map((entry) => entry.name).join(', ');
|
|
84
|
+
const structural = failed.some((entry) => entry.status && entry.status < 500);
|
|
85
|
+
if (failed.length > 0 && (deleted === 0 || structural)) {
|
|
86
|
+
core.setFailed(`${failed.length} artifact(s) could not be deleted: ${names}`);
|
|
87
|
+
} else if (failed.length > 0) {
|
|
88
|
+
core.warning(
|
|
89
|
+
`${failed.length} artifact(s) survived this run and will be retried tomorrow: ${names}`,
|
|
90
|
+
);
|
|
91
|
+
}
|