@vypdev/copilot 3.10.4 → 3.10.6
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 +14 -0
- package/action.yml +16 -1
- package/build/api/index.js +1162 -76
- package/build/api/src/application/ports/bugbot_pull_request_resolution_ports.d.ts +4 -3
- package/build/api/src/application/ports/bugbot_pull_request_write_ports.d.ts +4 -3
- package/build/api/src/application/ports/pull_request_review_comment_ports.d.ts +4 -0
- package/build/api/src/data/model/application_error.d.ts +17 -2
- package/build/api/src/domain/change_size_reconciliation.d.ts +15 -0
- package/build/api/src/domain/copilot_command.d.ts +25 -0
- package/build/api/src/domain/copilot_command_validation.d.ts +10 -0
- package/build/api/src/domain/copilot_command_vocabulary.d.ts +3 -0
- package/build/api/src/domain/issue_workflow_diagnostic.d.ts +10 -0
- package/build/api/src/domain/issue_workflow_field_mapping.d.ts +10 -0
- package/build/api/src/domain/issue_workflow_profile.d.ts +67 -0
- package/build/api/src/domain/progress_binary_evidence_diagnostic.d.ts +9 -0
- package/build/api/src/domain/verification_failure.d.ts +15 -0
- package/build/cli/index.js +4352 -530
- package/build/deployment_recovery/index.js +41863 -0
- package/build/github_action/index.js +4930 -550
- package/deployment-recovery/action.yml +52 -0
- package/deployment-recovery/index.cjs +3 -0
- package/issue-admission/action.yml +41 -0
- package/issue-admission/index.cjs +13 -0
- package/package.json +19 -8
- package/setup/workflows/copilot_commit.yml +2 -0
- package/setup/workflows/copilot_issue.yml +57 -1
- package/setup/workflows/hotfix_workflow.yml +151 -7
- package/setup/workflows/release_workflow.yml +151 -7
|
@@ -8,11 +8,25 @@ on:
|
|
|
8
8
|
required: true
|
|
9
9
|
default: prepare
|
|
10
10
|
type: choice
|
|
11
|
+
# Add replace only after configuring the complete project verifier and enabling it below.
|
|
11
12
|
options: [prepare, publish, resume]
|
|
12
13
|
operation-id:
|
|
13
14
|
description: 'Internal durable operation identifier'
|
|
14
15
|
required: false
|
|
15
16
|
default: ''
|
|
17
|
+
expected-revision:
|
|
18
|
+
description: 'Current durable revision; required only for replace'
|
|
19
|
+
required: false
|
|
20
|
+
default: ''
|
|
21
|
+
source-sha:
|
|
22
|
+
description: 'Reviewed corrected source before complete replacement preparation'
|
|
23
|
+
required: false
|
|
24
|
+
default: ''
|
|
25
|
+
confirm-unpublished:
|
|
26
|
+
description: 'Confirm the exact version is unpublished in every project registry'
|
|
27
|
+
type: boolean
|
|
28
|
+
required: false
|
|
29
|
+
default: false
|
|
16
30
|
version:
|
|
17
31
|
description: 'Release version (MAJOR.MINOR.PATCH)'
|
|
18
32
|
required: false
|
|
@@ -50,6 +64,11 @@ jobs:
|
|
|
50
64
|
ISSUE: ${{ inputs.issue }}
|
|
51
65
|
TITLE: ${{ inputs.title }}
|
|
52
66
|
CHANGELOG: ${{ inputs.changelog }}
|
|
67
|
+
EXPECTED_REVISION: ${{ inputs.expected-revision }}
|
|
68
|
+
SOURCE_SHA: ${{ inputs.source-sha }}
|
|
69
|
+
CONFIRM_UNPUBLISHED: ${{ inputs.confirm-unpublished }}
|
|
70
|
+
# Workflow-owned opt-in; never supplied by an issue or dispatch input.
|
|
71
|
+
REPLACEMENT_ENABLED: 'false'
|
|
53
72
|
with:
|
|
54
73
|
script: |
|
|
55
74
|
const mode = process.env.MODE;
|
|
@@ -59,6 +78,10 @@ jobs:
|
|
|
59
78
|
const issue = Number(issueText);
|
|
60
79
|
const title = process.env.TITLE || '';
|
|
61
80
|
const changelog = process.env.CHANGELOG || '';
|
|
81
|
+
const revisionText = process.env.EXPECTED_REVISION || '';
|
|
82
|
+
const revision = Number(revisionText);
|
|
83
|
+
const sourceSha = process.env.SOURCE_SHA || '';
|
|
84
|
+
const confirmed = process.env.CONFIRM_UNPUBLISHED || 'false';
|
|
62
85
|
const errors = [];
|
|
63
86
|
if (!/^[1-9][0-9]*$/.test(issueText) || !Number.isSafeInteger(issue) || issue < 1) errors.push('Issue must be a positive safe integer.');
|
|
64
87
|
if (!/^[0-9]+\.[0-9]+\.[0-9]+$/.test(version)) errors.push('Version must use MAJOR.MINOR.PATCH.');
|
|
@@ -66,25 +89,89 @@ jobs:
|
|
|
66
89
|
if (operationId) errors.push('Operation ID must be empty in prepare mode.');
|
|
67
90
|
if (!title || title.length > 1000) errors.push('Title must contain 1-1000 characters.');
|
|
68
91
|
if (!changelog || changelog.length > 50000) errors.push('Changelog must contain 1-50000 characters.');
|
|
92
|
+
} else if (mode === 'replace') {
|
|
93
|
+
if (process.env.REPLACEMENT_ENABLED !== 'true') errors.push('Candidate replacement is disabled. Configure the complete project verifier, enable it in this workflow and then add replace to the choices.');
|
|
94
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{7,127}$/.test(operationId)) errors.push('Operation ID is invalid.');
|
|
95
|
+
if (!/^[1-9][0-9]*$/.test(revisionText) || !Number.isSafeInteger(revision) || revision < 1 || revision >= Number.MAX_SAFE_INTEGER) errors.push('Replacement revision is invalid.');
|
|
96
|
+
if (!/^[a-f0-9]{40}$/.test(sourceSha)) errors.push('Replacement source must be a full reviewed SHA.');
|
|
97
|
+
if (version.length > 64 || confirmed !== 'true') errors.push('Confirm the exact unpublished replacement version.');
|
|
98
|
+
if (title || changelog) errors.push('Replacement preserves the stored title and changelog.');
|
|
69
99
|
} else if (mode === 'publish' || mode === 'resume') {
|
|
70
100
|
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{7,127}$/.test(operationId)) errors.push('Operation ID is invalid.');
|
|
71
101
|
if (title || changelog) errors.push('Title and changelog must be empty in publish or resume mode.');
|
|
72
|
-
} else errors.push('Mode must be prepare, publish, or resume.');
|
|
102
|
+
} else errors.push('Mode must be prepare, replace, publish, or resume.');
|
|
103
|
+
if (mode !== 'replace' && (revisionText || sourceSha || confirmed !== 'false')) errors.push('Replacement proof inputs are allowed only in replace mode.');
|
|
73
104
|
if (errors.length) core.setFailed(errors.join(' '));
|
|
74
105
|
|
|
106
|
+
candidate-preflight:
|
|
107
|
+
name: Inspect unpublished candidate replacement
|
|
108
|
+
if: ${{ inputs.mode == 'replace' }}
|
|
109
|
+
runs-on: ubuntu-latest
|
|
110
|
+
needs: validate
|
|
111
|
+
timeout-minutes: 5
|
|
112
|
+
permissions:
|
|
113
|
+
contents: read
|
|
114
|
+
issues: read
|
|
115
|
+
pull-requests: read
|
|
116
|
+
actions: read
|
|
117
|
+
outputs:
|
|
118
|
+
mode: ${{ steps.inspect.outputs.mode }}
|
|
119
|
+
source-branch: ${{ steps.inspect.outputs.source-branch }}
|
|
120
|
+
candidate-sha: ${{ steps.inspect.outputs.candidate-sha }}
|
|
121
|
+
production-base-sha: ${{ steps.inspect.outputs.production-base-sha }}
|
|
122
|
+
operation-id: ${{ steps.inspect.outputs.operation-id }}
|
|
123
|
+
expected-revision: ${{ steps.inspect.outputs.expected-revision }}
|
|
124
|
+
steps:
|
|
125
|
+
- name: Read native candidate authority
|
|
126
|
+
id: inspect
|
|
127
|
+
uses: vypdev/copilot/deployment-recovery@v3
|
|
128
|
+
with:
|
|
129
|
+
issue: ${{ inputs.issue }}
|
|
130
|
+
operation-id: ${{ inputs.operation-id }}
|
|
131
|
+
expected-revision: ${{ inputs.expected-revision }}
|
|
132
|
+
version: ${{ inputs.version }}
|
|
133
|
+
source-sha: ${{ inputs.source-sha }}
|
|
134
|
+
confirm-unpublished: ${{ inputs.confirm-unpublished }}
|
|
135
|
+
issue-workflow-profile: ${{ vars.COPILOT_ISSUE_WORKFLOW_PROFILE || '' }}
|
|
136
|
+
release-label: release
|
|
137
|
+
hotfix-label: hotfix
|
|
138
|
+
repository-locale: ${{ vars.REPOSITORY_LOCALE || 'en-US' }}
|
|
139
|
+
token: ${{ secrets.PAT }}
|
|
140
|
+
|
|
75
141
|
prepare-version-files:
|
|
76
142
|
name: Prepare and validate release
|
|
77
|
-
if: ${{ inputs.mode == 'prepare' }}
|
|
143
|
+
if: ${{ !cancelled() && needs.validate.result == 'success' && (inputs.mode == 'prepare' || (inputs.mode == 'replace' && needs.candidate-preflight.result == 'success' && needs.candidate-preflight.outputs.mode == 'prepare')) }}
|
|
78
144
|
runs-on: ubuntu-latest
|
|
79
|
-
needs: validate
|
|
145
|
+
needs: [validate, candidate-preflight]
|
|
80
146
|
timeout-minutes: 30
|
|
81
147
|
permissions:
|
|
82
148
|
contents: write
|
|
149
|
+
outputs:
|
|
150
|
+
candidate-sha: ${{ steps.candidate-identity.outputs.candidate-sha }}
|
|
83
151
|
steps:
|
|
84
152
|
- uses: actions/checkout@v5
|
|
85
153
|
with:
|
|
86
154
|
persist-credentials: true
|
|
155
|
+
ref: ${{ needs.candidate-preflight.outputs.source-branch || github.ref }}
|
|
156
|
+
- name: Verify reviewed replacement source
|
|
157
|
+
if: ${{ inputs.mode == 'replace' }}
|
|
158
|
+
uses: actions/github-script@v9
|
|
159
|
+
env:
|
|
160
|
+
EXPECTED_SHA: ${{ inputs.source-sha }}
|
|
161
|
+
with:
|
|
162
|
+
script: |
|
|
163
|
+
const { execFileSync } = require('node:child_process');
|
|
164
|
+
const current = execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).trim();
|
|
165
|
+
if (!/^[a-f0-9]{40}$/.test(process.env.EXPECTED_SHA || '') || current !== process.env.EXPECTED_SHA) {
|
|
166
|
+
core.setFailed('The reviewed replacement source moved before preparation. No new candidate proof is available.');
|
|
167
|
+
}
|
|
87
168
|
# Add project-specific version, build, validation, smoke-test and generated-file steps here.
|
|
169
|
+
- name: Complete fresh replacement verification
|
|
170
|
+
id: replacement-verification
|
|
171
|
+
if: ${{ inputs.mode == 'replace' }}
|
|
172
|
+
run: |
|
|
173
|
+
echo "Configure this step with every required project validator, including registry vacancy, before enabling candidate replacement."
|
|
174
|
+
exit 1
|
|
88
175
|
- name: Commit prepared release files
|
|
89
176
|
uses: EndBug/add-and-commit@v9
|
|
90
177
|
with:
|
|
@@ -93,12 +180,29 @@ jobs:
|
|
|
93
180
|
committer_email: actions@github.com
|
|
94
181
|
default_author: user_info
|
|
95
182
|
message: 'gh-action: prepare release ${{ inputs.version }}'
|
|
183
|
+
- name: Confirm prepared replacement identity
|
|
184
|
+
id: candidate-identity
|
|
185
|
+
if: ${{ inputs.mode == 'replace' }}
|
|
186
|
+
uses: actions/github-script@v9
|
|
187
|
+
env:
|
|
188
|
+
SOURCE_BRANCH: ${{ needs.candidate-preflight.outputs.source-branch }}
|
|
189
|
+
VERIFICATION_RESULT: ${{ steps.replacement-verification.outcome }}
|
|
190
|
+
with:
|
|
191
|
+
script: |
|
|
192
|
+
if (process.env.VERIFICATION_RESULT !== 'success') throw new Error('Complete replacement verification was not confirmed.');
|
|
193
|
+
const { execFileSync } = require('node:child_process');
|
|
194
|
+
const candidate = execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).trim();
|
|
195
|
+
const dirty = execFileSync('git', ['status', '--porcelain'], { encoding: 'utf8' }).trim();
|
|
196
|
+
if (!/^[a-f0-9]{40}$/.test(candidate) || !process.env.SOURCE_BRANCH || dirty) throw new Error('The prepared candidate is not an exact clean commit.');
|
|
197
|
+
const response = await github.rest.git.getRef({ ...context.repo, ref: `heads/${process.env.SOURCE_BRANCH}` });
|
|
198
|
+
if (response.data.object?.type !== 'commit' || response.data.object.sha !== candidate) throw new Error('The prepared source moved before candidate proof was recorded.');
|
|
199
|
+
core.setOutput('candidate-sha', candidate);
|
|
96
200
|
|
|
97
201
|
promote:
|
|
98
202
|
name: Open production promotion
|
|
99
|
-
if: ${{ inputs.mode == 'prepare' }}
|
|
203
|
+
if: ${{ !cancelled() && needs.validate.result == 'success' && ((inputs.mode == 'prepare' && needs.prepare-version-files.result == 'success') || (inputs.mode == 'replace' && needs.candidate-preflight.result == 'success' && ((needs.candidate-preflight.outputs.mode == 'prepare' && needs.prepare-version-files.result == 'success' && needs.prepare-version-files.outputs.candidate-sha != '') || needs.candidate-preflight.outputs.mode == 'recover'))) }}
|
|
100
204
|
runs-on: ubuntu-latest
|
|
101
|
-
needs: prepare-version-files
|
|
205
|
+
needs: [validate, candidate-preflight, prepare-version-files]
|
|
102
206
|
timeout-minutes: 10
|
|
103
207
|
permissions:
|
|
104
208
|
contents: read
|
|
@@ -109,7 +213,9 @@ jobs:
|
|
|
109
213
|
- uses: actions/checkout@v5
|
|
110
214
|
with:
|
|
111
215
|
persist-credentials: false
|
|
216
|
+
ref: ${{ inputs.mode == 'replace' && (needs.candidate-preflight.outputs.mode == 'recover' && needs.candidate-preflight.outputs.candidate-sha || needs.prepare-version-files.outputs.candidate-sha) || github.ref }}
|
|
112
217
|
- name: Create or resume promotion PR
|
|
218
|
+
if: ${{ inputs.mode == 'prepare' }}
|
|
113
219
|
uses: vypdev/copilot@v3
|
|
114
220
|
with:
|
|
115
221
|
single-action: prepare_deployment_action
|
|
@@ -140,6 +246,44 @@ jobs:
|
|
|
140
246
|
issues-locale: ${{ vars.ISSUES_LOCALE || '' }}
|
|
141
247
|
pull-requests-locale: ${{ vars.PULL_REQUESTS_LOCALE || '' }}
|
|
142
248
|
token: ${{ secrets.PAT }}
|
|
249
|
+
- name: Replace or recover unpublished candidate
|
|
250
|
+
if: ${{ inputs.mode == 'replace' }}
|
|
251
|
+
uses: vypdev/copilot@v3
|
|
252
|
+
with:
|
|
253
|
+
single-action: replace_deployment_action
|
|
254
|
+
single-action-operation-id: ${{ needs.candidate-preflight.outputs.operation-id }}
|
|
255
|
+
single-action-expected-revision: ${{ needs.candidate-preflight.outputs.expected-revision }}
|
|
256
|
+
single-action-reviewed-source-sha: ${{ inputs.source-sha }}
|
|
257
|
+
single-action-candidate-sha: ${{ needs.candidate-preflight.outputs.mode == 'recover' && needs.candidate-preflight.outputs.candidate-sha || needs.prepare-version-files.outputs.candidate-sha }}
|
|
258
|
+
single-action-production-base-sha: ${{ needs.candidate-preflight.outputs.production-base-sha }}
|
|
259
|
+
single-action-confirm-unpublished: ${{ inputs.confirm-unpublished }}
|
|
260
|
+
single-action-issue: ${{ inputs.issue }}
|
|
261
|
+
issue-workflow-profile: ${{ vars.COPILOT_ISSUE_WORKFLOW_PROFILE || '' }}
|
|
262
|
+
single-action-version: ${{ inputs.version }}
|
|
263
|
+
single-action-title: ${{ inputs.title }}
|
|
264
|
+
single-action-changelog: ${{ inputs.changelog }}
|
|
265
|
+
main-branch: ${{ vars.MAIN_BRANCH || 'master' }}
|
|
266
|
+
development-branch: ${{ vars.DEVELOPMENT_BRANCH || 'develop' }}
|
|
267
|
+
release-tree: ${{ vars.RELEASE_TREE || 'release' }}
|
|
268
|
+
hotfix-tree: ${{ vars.HOTFIX_TREE || 'hotfix' }}
|
|
269
|
+
release-workflow: ${{ vars.RELEASE_WORKFLOW || 'release_workflow.yml' }}
|
|
270
|
+
hotfix-workflow: ${{ vars.HOTFIX_WORKFLOW || 'hotfix_workflow.yml' }}
|
|
271
|
+
release-reconciliation-strategy: ${{ vars.RELEASE_RECONCILIATION_STRATEGY || 'production-lineage' }}
|
|
272
|
+
hotfix-reconciliation-strategy: ${{ vars.HOTFIX_RECONCILIATION_STRATEGY || 'production-lineage' }}
|
|
273
|
+
reconciliation-pr-mode: ${{ vars.RECONCILIATION_PR_MODE || 'auto' }}
|
|
274
|
+
merge-queue-check-attestations: ${{ vars.MERGE_QUEUE_CHECK_ATTESTATIONS || '[]' }}
|
|
275
|
+
reconciliation-backmerge-mode: ${{ vars.RECONCILIATION_BACKMERGE_MODE || 'auto' }}
|
|
276
|
+
hotfix-active-release-policy: ${{ vars.HOTFIX_ACTIVE_RELEASE_POLICY || 'prefer-release' }}
|
|
277
|
+
reconciliation-tree: ${{ vars.RECONCILIATION_TREE || 'sync' }}
|
|
278
|
+
reconciliation-cleanup: ${{ vars.RECONCILIATION_CLEANUP || 'all' }}
|
|
279
|
+
reconciliation-issue-completion: ${{ vars.RECONCILIATION_ISSUE_COMPLETION || 'close' }}
|
|
280
|
+
orchestration-presentation-mode: ${{ vars.ORCHESTRATION_PRESENTATION_MODE || 'guided' }}
|
|
281
|
+
orchestration-diagrams: ${{ vars.ORCHESTRATION_DIAGRAMS || 'true' }}
|
|
282
|
+
orchestration-comment-mode: ${{ vars.ORCHESTRATION_COMMENT_MODE || 'update' }}
|
|
283
|
+
repository-locale: ${{ vars.REPOSITORY_LOCALE || 'en-US' }}
|
|
284
|
+
issues-locale: ${{ vars.ISSUES_LOCALE || '' }}
|
|
285
|
+
pull-requests-locale: ${{ vars.PULL_REQUESTS_LOCALE || '' }}
|
|
286
|
+
token: ${{ secrets.PAT }}
|
|
143
287
|
|
|
144
288
|
publish:
|
|
145
289
|
name: Publish accepted production commit
|
|
@@ -239,8 +383,8 @@ jobs:
|
|
|
239
383
|
name: Report orchestration failure
|
|
240
384
|
runs-on: ubuntu-latest
|
|
241
385
|
timeout-minutes: 5
|
|
242
|
-
needs: [validate, prepare-version-files, promote, publish, resume-reconciliation]
|
|
243
|
-
if: ${{ failure() && needs.validate.result == 'success' }}
|
|
386
|
+
needs: [validate, candidate-preflight, prepare-version-files, promote, publish, resume-reconciliation]
|
|
387
|
+
if: ${{ failure() && needs.validate.result == 'success' && inputs.mode != 'replace' }}
|
|
244
388
|
permissions:
|
|
245
389
|
issues: write
|
|
246
390
|
steps:
|