@vypdev/copilot 3.10.5 → 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 +4299 -531
- package/build/deployment_recovery/index.js +41863 -0
- package/build/github_action/index.js +4877 -551
- 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
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: Inspect unpublished candidate replacement
|
|
2
|
+
description: Read-only native prerequisite and replay inspection before project preparation.
|
|
3
|
+
inputs:
|
|
4
|
+
token:
|
|
5
|
+
description: Existing product token; this action performs only native reads.
|
|
6
|
+
required: true
|
|
7
|
+
issue:
|
|
8
|
+
description: Positive launcher issue.
|
|
9
|
+
required: true
|
|
10
|
+
operation-id:
|
|
11
|
+
description: Exact previous deployment operation.
|
|
12
|
+
required: true
|
|
13
|
+
expected-revision:
|
|
14
|
+
description: Exact previous durable revision.
|
|
15
|
+
required: true
|
|
16
|
+
version:
|
|
17
|
+
description: Exact unpublished version.
|
|
18
|
+
required: true
|
|
19
|
+
source-sha:
|
|
20
|
+
description: Reviewed corrected source before complete preparation.
|
|
21
|
+
required: true
|
|
22
|
+
confirm-unpublished:
|
|
23
|
+
description: Explicit maintainer confirmation after inspecting every project registry.
|
|
24
|
+
default: 'false'
|
|
25
|
+
issue-workflow-profile:
|
|
26
|
+
description: Same enabled workflows and field mappings as the ordinary preparation action.
|
|
27
|
+
default: ''
|
|
28
|
+
release-label:
|
|
29
|
+
description: Configured release-kind label.
|
|
30
|
+
default: release
|
|
31
|
+
hotfix-label:
|
|
32
|
+
description: Configured hotfix-kind label.
|
|
33
|
+
default: hotfix
|
|
34
|
+
repository-locale:
|
|
35
|
+
description: Repository Summary language; English and Spanish have static catalogs.
|
|
36
|
+
default: en-US
|
|
37
|
+
outputs:
|
|
38
|
+
mode:
|
|
39
|
+
description: prepare requires full validation; recover reuses the recorded candidate; recorded does not mutate.
|
|
40
|
+
source-branch:
|
|
41
|
+
description: Native operation-owned source branch.
|
|
42
|
+
candidate-sha:
|
|
43
|
+
description: Reviewed source for new preparation, or existing validated successor for recovery.
|
|
44
|
+
production-base-sha:
|
|
45
|
+
description: Native base for fresh replacement or the recorded replacement base for recovery.
|
|
46
|
+
operation-id:
|
|
47
|
+
description: Exact previous operation.
|
|
48
|
+
expected-revision:
|
|
49
|
+
description: Exact previous revision.
|
|
50
|
+
runs:
|
|
51
|
+
using: node24
|
|
52
|
+
main: index.cjs
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Copilot issue event admission
|
|
2
|
+
description: Read-only issue admission and successful-reconciliation receipts, without agent setup.
|
|
3
|
+
inputs:
|
|
4
|
+
issue-event-admission-mode:
|
|
5
|
+
description: Isolated mode, inspect before work or record after successful work.
|
|
6
|
+
required: true
|
|
7
|
+
token:
|
|
8
|
+
description: Existing product credential for native identity and issue reads.
|
|
9
|
+
required: true
|
|
10
|
+
configuration:
|
|
11
|
+
description: Repository Variables snapshot used only in a credential-free state digest.
|
|
12
|
+
default: ''
|
|
13
|
+
repository-locale:
|
|
14
|
+
description: Job Summary locale, English or Spanish with regional and English fallback.
|
|
15
|
+
default: en-US
|
|
16
|
+
deploy-label:
|
|
17
|
+
description: Deployment intent label; it must never be coalesced.
|
|
18
|
+
default: deploy
|
|
19
|
+
release-label:
|
|
20
|
+
description: Release intent label; must match the normal Action.
|
|
21
|
+
default: release
|
|
22
|
+
hotfix-label:
|
|
23
|
+
description: Hotfix intent label; must match the normal Action.
|
|
24
|
+
default: hotfix
|
|
25
|
+
receipt-key:
|
|
26
|
+
description: Exact digest returned by this run's successful inspect step.
|
|
27
|
+
default: ''
|
|
28
|
+
outputs:
|
|
29
|
+
execute:
|
|
30
|
+
description: True only when the normal guarded issue pipeline should run.
|
|
31
|
+
reason:
|
|
32
|
+
description: Closed admission reason; no issue text or credentials.
|
|
33
|
+
receipt-key:
|
|
34
|
+
description: Opaque native state, workflow and runtime digest.
|
|
35
|
+
receipt-name:
|
|
36
|
+
description: Artifact name after an unchanged successful reconciliation.
|
|
37
|
+
receipt-path:
|
|
38
|
+
description: Private temporary receipt file for native artifact upload.
|
|
39
|
+
runs:
|
|
40
|
+
using: node24
|
|
41
|
+
main: index.cjs
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Metadata's required:true is advisory; never fall through to the mutation entry.
|
|
4
|
+
const mode = (process.env['INPUT_ISSUE-EVENT-ADMISSION-MODE'] ?? '').trim();
|
|
5
|
+
if (mode !== 'inspect' && mode !== 'record') {
|
|
6
|
+
process.stderr.write('Issue admission requires inspect or record mode. No pipeline was started.\n');
|
|
7
|
+
process.exitCode = 1;
|
|
8
|
+
} else {
|
|
9
|
+
// This isolated action has no JSON input-override channel or native worker mode.
|
|
10
|
+
delete process.env.INPUT_VARS_JSON;
|
|
11
|
+
process.env.COPILOT_ISSUE_ADMISSION_BOOTSTRAP = '1';
|
|
12
|
+
void require('../build/github_action/index.js').runGitHubActionEntry();
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vypdev/copilot",
|
|
3
|
-
"version": "3.10.
|
|
3
|
+
"version": "3.10.6",
|
|
4
4
|
"packageManager": "pnpm@10.12.4",
|
|
5
5
|
"pnpm": {
|
|
6
6
|
"overrides": {
|
|
@@ -34,9 +34,12 @@
|
|
|
34
34
|
},
|
|
35
35
|
"files": [
|
|
36
36
|
"action.yml",
|
|
37
|
+
"issue-admission/",
|
|
38
|
+
"deployment-recovery/",
|
|
37
39
|
"build/cli/index.js",
|
|
38
40
|
"build/web/",
|
|
39
41
|
"build/github_action/index.js",
|
|
42
|
+
"build/deployment_recovery/index.js",
|
|
40
43
|
"build/api/index.js",
|
|
41
44
|
"build/api/src/",
|
|
42
45
|
"setup/ISSUE_TEMPLATE/",
|
|
@@ -55,7 +58,7 @@
|
|
|
55
58
|
"node": ">=24"
|
|
56
59
|
},
|
|
57
60
|
"scripts": {
|
|
58
|
-
"build": "node scripts/clean-generated-bundles.cjs && pnpm run build:web && ncc build src/actions/github_action.ts -o build/github_action && ncc build src/cli.ts -o build/cli && ncc build src/api.ts -o build/api && node scripts/prepare-generated-bundles.cjs && node scripts/set-cli-executable.cjs",
|
|
61
|
+
"build": "node scripts/clean-generated-bundles.cjs && pnpm run build:web && ncc build src/actions/github_action.ts -o build/github_action && ncc build src/actions/deployment_candidate_preflight_action.ts -o build/deployment_recovery && ncc build src/cli.ts -o build/cli && ncc build src/api.ts -o build/api && node scripts/prepare-generated-bundles.cjs && node scripts/set-cli-executable.cjs && pnpm run build:extension",
|
|
59
62
|
"build:web": "vite build --config web/vite.config.mts",
|
|
60
63
|
"check:web": "svelte-check --tsconfig web/tsconfig.json && vite build --config web/vite.config.mts",
|
|
61
64
|
"validate:build": "node scripts/validate-build.cjs",
|
|
@@ -63,20 +66,27 @@
|
|
|
63
66
|
"smoke:npm-package": "node scripts/smoke-test-npm-package.cjs",
|
|
64
67
|
"test": "jest",
|
|
65
68
|
"test:watch": "jest --watch",
|
|
66
|
-
"test:coverage": "jest --coverage --json --outputFile=coverage/setup-acceptance-jest.json && node scripts/validate-coverage-budgets.cjs && node scripts/validate-setup-assistant-acceptance.cjs coverage/setup-acceptance-jest.json",
|
|
69
|
+
"test:coverage": "jest --coverage --json --outputFile=coverage/setup-acceptance-jest.json && node scripts/validate-coverage-budgets.cjs && node scripts/validate-setup-assistant-acceptance.cjs coverage/setup-acceptance-jest.json && pnpm run test:extension",
|
|
67
70
|
"eval:bugbot": "jest src/tooling/__tests__/bugbot_quality_eval.test.ts src/tooling/__tests__/bugbot_benchmark.test.ts --runInBand",
|
|
68
|
-
"typecheck": "tsc --noEmit",
|
|
71
|
+
"typecheck": "tsc --noEmit && pnpm run typecheck:extension",
|
|
69
72
|
"metrics:architecture": "node scripts/collect-architecture-metrics.cjs",
|
|
70
|
-
"lint": "eslint src",
|
|
73
|
+
"lint": "eslint src extension",
|
|
71
74
|
"validate:agent-docs": "node scripts/validate-agent-documentation.cjs",
|
|
72
75
|
"validate:docs-page": "node scripts/validate-docs-page-assets.cjs",
|
|
73
76
|
"validate:documentation": "node scripts/validate-documentation-contract.cjs",
|
|
74
77
|
"validate:workflows": "node scripts/validate-workflow-contract.cjs",
|
|
75
|
-
"generate:specifications": "node scripts/validate-specification-catalog.cjs --write && node scripts/validate-github-communication-test-budget.cjs",
|
|
76
|
-
"validate:specifications": "node scripts/validate-specification-catalog.cjs && node scripts/validate-github-communication-test-budget.cjs",
|
|
78
|
+
"generate:specifications": "node scripts/validate-specification-catalog.cjs --write && node scripts/validate-github-communication-test-budget.cjs && pnpm run validate:browser-extension-specifications",
|
|
79
|
+
"validate:specifications": "node scripts/validate-specification-catalog.cjs && node scripts/validate-github-communication-test-budget.cjs && pnpm run validate:browser-extension-specifications",
|
|
77
80
|
"validate:setup-acceptance": "node scripts/validate-setup-assistant-acceptance.cjs",
|
|
78
81
|
"lint:fix": "eslint src --fix",
|
|
79
|
-
"postinstall": "node scripts/install-git-hooks.cjs"
|
|
82
|
+
"postinstall": "node scripts/install-git-hooks.cjs",
|
|
83
|
+
"validate:browser-extension-specifications": "node --test test-support/browser-extension-specifications.test.cjs && node scripts/validate-browser-extension-specifications.cjs",
|
|
84
|
+
"build:extension": "pnpm --dir extension run build",
|
|
85
|
+
"typecheck:extension": "pnpm --dir extension run typecheck",
|
|
86
|
+
"test:extension": "pnpm --dir extension run test",
|
|
87
|
+
"validate:extension": "pnpm --dir extension run validate",
|
|
88
|
+
"test:browsers:extension": "pnpm --dir extension run test:browsers",
|
|
89
|
+
"build:safari:extension": "pnpm --dir extension run build:safari"
|
|
80
90
|
},
|
|
81
91
|
"keywords": [],
|
|
82
92
|
"author": "Efra Espada",
|
|
@@ -98,6 +108,7 @@
|
|
|
98
108
|
"@types/jest": "^30.0.0",
|
|
99
109
|
"@types/node": "^22.9.1",
|
|
100
110
|
"@vercel/ncc": "^0.36.1",
|
|
111
|
+
"ajv": "6.15.0",
|
|
101
112
|
"eslint": "^10.10.0",
|
|
102
113
|
"jest": "^30.5.1",
|
|
103
114
|
"svelte": "^5.57.1",
|
|
@@ -19,6 +19,7 @@ jobs:
|
|
|
19
19
|
group: copilot-push-${{ github.repository }}-${{ github.ref_name }}
|
|
20
20
|
cancel-in-progress: true
|
|
21
21
|
permissions:
|
|
22
|
+
checks: write
|
|
22
23
|
contents: read
|
|
23
24
|
steps:
|
|
24
25
|
- name: Checkout Repository
|
|
@@ -104,6 +105,7 @@ jobs:
|
|
|
104
105
|
fixer-executable: ${{ vars.FIXER_EXECUTABLE }}
|
|
105
106
|
token: ${{ secrets.PAT }}
|
|
106
107
|
env:
|
|
108
|
+
COPILOT_EVIDENCE_TOKEN: ${{ github.token }}
|
|
107
109
|
AGENT_PROVIDER: ${{ vars.AGENT_PROVIDER || 'codex' }}
|
|
108
110
|
AGENT_MODEL_PROVIDER: ${{ vars.AGENT_MODEL_PROVIDER || 'openai' }}
|
|
109
111
|
AGENT_MODEL: ${{ vars.AGENT_MODEL }}
|
|
@@ -4,9 +4,40 @@ on:
|
|
|
4
4
|
issues:
|
|
5
5
|
types: [opened, reopened, closed, edited, labeled, unlabeled, assigned, unassigned]
|
|
6
6
|
|
|
7
|
+
# Keep every pending lifecycle event; never replace a start/reopen with a label event.
|
|
8
|
+
# GitHub bounds this queue at 100 pending runs. Independent issues use different groups.
|
|
9
|
+
concurrency:
|
|
10
|
+
group: copilot-issue-${{ github.repository }}-${{ github.event.issue.number }}
|
|
11
|
+
cancel-in-progress: false
|
|
12
|
+
queue: max
|
|
13
|
+
|
|
7
14
|
jobs:
|
|
15
|
+
issue-admission:
|
|
16
|
+
name: Admit issue event
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
timeout-minutes: 5
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
actions: read
|
|
22
|
+
outputs:
|
|
23
|
+
execute: ${{ steps.inspect.outputs.execute }}
|
|
24
|
+
receipt-key: ${{ steps.inspect.outputs.receipt-key }}
|
|
25
|
+
steps:
|
|
26
|
+
- name: Inspect native issue event
|
|
27
|
+
id: inspect
|
|
28
|
+
uses: vypdev/copilot/issue-admission@v3
|
|
29
|
+
with:
|
|
30
|
+
repository-locale: ${{ vars.REPOSITORY_LOCALE || 'en-US' }}
|
|
31
|
+
issue-event-admission-mode: inspect
|
|
32
|
+
token: ${{ secrets.PAT }}
|
|
33
|
+
configuration: ${{ format('[{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21},{22},{23},{24},{25},{26},{27},{28},{29},{30},{31},{32},{33},{34},{35},{36},{37},{38},{39},{40},{41},{42},{43},{44},{45},{46},{47},{48},{49},{50},{51},{52},{53},{54},{55},{56},{57},{58},{59},{60},{61},{62},{63},{64}]', toJSON(vars.AGENT_ALLOWED_MODELS), toJSON(vars.AGENT_ALLOWED_MODEL_PROVIDERS), toJSON(vars.AGENT_EFFORT), toJSON(vars.AGENT_EXECUTABLE), toJSON(vars.AGENT_MODEL), toJSON(vars.AGENT_MODEL_PROVIDER), toJSON(vars.AGENT_PROVIDER), toJSON(vars.AI_IGNORE_FILES), toJSON(vars.AI_INCLUDE_REASONING), toJSON(vars.AI_MEMBERS_ONLY), toJSON(vars.AI_PULL_REQUEST_DESCRIPTION_MODE), toJSON(vars.BUGBOT_AUTOFIX_VERIFY_COMMANDS), toJSON(vars.BUGBOT_COMMENT_LIMIT), toJSON(vars.BUGBOT_DRY_RUN), toJSON(vars.BUGBOT_EFFORT), toJSON(vars.BUGBOT_FAIL_ON_UNRESOLVED), toJSON(vars.BUGBOT_ORGANIZATION_RULES), toJSON(vars.BUGBOT_REVIEW_DRAFTS), toJSON(vars.BUGBOT_SEVERITY), toJSON(vars.BUGBOT_SUGGESTED_CHANGES), toJSON(vars.BUGBOT_TELEMETRY), toJSON(vars.BUGBOT_TRACE_RULES), toJSON(vars.BUGFIX_TREE), toJSON(vars.CHORE_TREE), toJSON(vars.COMMIT_PREFIX_TRANSFORMS), toJSON(vars.COPILOT_ISSUE_WORKFLOW_PROFILE), toJSON(vars.DEBUG), toJSON(vars.DEPLOY_LABEL), toJSON(vars.DESIRED_ASSIGNEES_COUNT), toJSON(vars.DESIRED_REVIEWERS_COUNT), toJSON(vars.DEVELOPMENT_BRANCH), toJSON(vars.DOCS_TREE), toJSON(vars.FEATURE_TREE), toJSON(vars.FINDINGS_EFFORT), toJSON(vars.FINDINGS_EXECUTABLE), toJSON(vars.FINDINGS_MODEL), toJSON(vars.FINDINGS_MODEL_PROVIDER), toJSON(vars.FINDINGS_PROVIDER), toJSON(vars.FIXER_EFFORT), toJSON(vars.FIXER_EXECUTABLE), toJSON(vars.FIXER_MODEL), toJSON(vars.FIXER_MODEL_PROVIDER), toJSON(vars.FIXER_PROVIDER), toJSON(vars.HOTFIX_TREE), toJSON(vars.ISSUES_LOCALE), toJSON(vars.ISSUE_MANAGED_BRANCHES), toJSON(vars.MAIN_BRANCH), toJSON(vars.PLANNER_EFFORT), toJSON(vars.PLANNER_EXECUTABLE), toJSON(vars.PLANNER_MODEL), toJSON(vars.PLANNER_MODEL_PROVIDER), toJSON(vars.PLANNER_PROVIDER), toJSON(vars.PRE_BRANCH_SDD), toJSON(vars.PROJECT_COLUMN_ISSUE_CREATED), toJSON(vars.PROJECT_COLUMN_ISSUE_IN_PROGRESS), toJSON(vars.PROJECT_COLUMN_PULL_REQUEST_CREATED), toJSON(vars.PROJECT_COLUMN_PULL_REQUEST_IN_PROGRESS), toJSON(vars.PROJECT_IDS), toJSON(vars.PULL_REQUESTS_LOCALE), toJSON(vars.RELEASE_TREE), toJSON(vars.REOPEN_ISSUE_ON_PUSH), toJSON(vars.REPOSITORY_LOCALE), toJSON(vars.REVIEWER_PROVIDER), toJSON(vars.TESTER_PROVIDER), toJSON(vars.VERIFICATION_SETUP)) }}
|
|
34
|
+
deploy-label: ${{ vars.DEPLOY_LABEL || 'deploy' }}
|
|
35
|
+
env:
|
|
36
|
+
COPILOT_ISSUE_ADMISSION_TOKEN: ${{ github.token }}
|
|
37
|
+
|
|
8
38
|
copilot-issues:
|
|
9
|
-
|
|
39
|
+
needs: issue-admission
|
|
40
|
+
if: ${{ needs.issue-admission.outputs.execute == 'true' }}
|
|
10
41
|
name: Copilot - Issue
|
|
11
42
|
runs-on: ubuntu-latest
|
|
12
43
|
timeout-minutes: 120
|
|
@@ -24,7 +55,9 @@ jobs:
|
|
|
24
55
|
# Optional trusted-main setup: VERIFICATION_SETUP=true and .github/actions/copilot-verification-setup/action.yml.
|
|
25
56
|
# https://docs.page/vypdev/copilot/security-operations/operations/verification#repository-verification-prerequisites
|
|
26
57
|
- uses: vypdev/copilot@v3
|
|
58
|
+
id: copilot
|
|
27
59
|
with:
|
|
60
|
+
deploy-label: ${{ vars.DEPLOY_LABEL || 'deploy' }}
|
|
28
61
|
repository-locale: ${{ vars.REPOSITORY_LOCALE || 'en-US' }}
|
|
29
62
|
issues-locale: ${{ vars.ISSUES_LOCALE || '' }}
|
|
30
63
|
pull-requests-locale: ${{ vars.PULL_REQUESTS_LOCALE || '' }}
|
|
@@ -101,3 +134,26 @@ jobs:
|
|
|
101
134
|
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
|
|
102
135
|
CURSOR_API_KEY: ${{ (vars.AGENT_PROVIDER == 'cursor' || vars.FINDINGS_PROVIDER == 'cursor' || vars.FIXER_PROVIDER == 'cursor' || vars.PLANNER_PROVIDER == 'cursor' || vars.REVIEWER_PROVIDER == 'cursor' || vars.TESTER_PROVIDER == 'cursor') && secrets.CURSOR_API_KEY || '' }}
|
|
103
136
|
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
|
|
137
|
+
|
|
138
|
+
- name: Record unchanged successful reconciliation
|
|
139
|
+
id: receipt
|
|
140
|
+
if: ${{ success() && steps.copilot.outcome == 'success' && needs.issue-admission.outputs.receipt-key != '' }}
|
|
141
|
+
continue-on-error: true
|
|
142
|
+
uses: vypdev/copilot/issue-admission@v3
|
|
143
|
+
with:
|
|
144
|
+
repository-locale: ${{ vars.REPOSITORY_LOCALE || 'en-US' }}
|
|
145
|
+
issue-event-admission-mode: record
|
|
146
|
+
token: ${{ secrets.PAT }}
|
|
147
|
+
configuration: ${{ format('[{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21},{22},{23},{24},{25},{26},{27},{28},{29},{30},{31},{32},{33},{34},{35},{36},{37},{38},{39},{40},{41},{42},{43},{44},{45},{46},{47},{48},{49},{50},{51},{52},{53},{54},{55},{56},{57},{58},{59},{60},{61},{62},{63},{64}]', toJSON(vars.AGENT_ALLOWED_MODELS), toJSON(vars.AGENT_ALLOWED_MODEL_PROVIDERS), toJSON(vars.AGENT_EFFORT), toJSON(vars.AGENT_EXECUTABLE), toJSON(vars.AGENT_MODEL), toJSON(vars.AGENT_MODEL_PROVIDER), toJSON(vars.AGENT_PROVIDER), toJSON(vars.AI_IGNORE_FILES), toJSON(vars.AI_INCLUDE_REASONING), toJSON(vars.AI_MEMBERS_ONLY), toJSON(vars.AI_PULL_REQUEST_DESCRIPTION_MODE), toJSON(vars.BUGBOT_AUTOFIX_VERIFY_COMMANDS), toJSON(vars.BUGBOT_COMMENT_LIMIT), toJSON(vars.BUGBOT_DRY_RUN), toJSON(vars.BUGBOT_EFFORT), toJSON(vars.BUGBOT_FAIL_ON_UNRESOLVED), toJSON(vars.BUGBOT_ORGANIZATION_RULES), toJSON(vars.BUGBOT_REVIEW_DRAFTS), toJSON(vars.BUGBOT_SEVERITY), toJSON(vars.BUGBOT_SUGGESTED_CHANGES), toJSON(vars.BUGBOT_TELEMETRY), toJSON(vars.BUGBOT_TRACE_RULES), toJSON(vars.BUGFIX_TREE), toJSON(vars.CHORE_TREE), toJSON(vars.COMMIT_PREFIX_TRANSFORMS), toJSON(vars.COPILOT_ISSUE_WORKFLOW_PROFILE), toJSON(vars.DEBUG), toJSON(vars.DEPLOY_LABEL), toJSON(vars.DESIRED_ASSIGNEES_COUNT), toJSON(vars.DESIRED_REVIEWERS_COUNT), toJSON(vars.DEVELOPMENT_BRANCH), toJSON(vars.DOCS_TREE), toJSON(vars.FEATURE_TREE), toJSON(vars.FINDINGS_EFFORT), toJSON(vars.FINDINGS_EXECUTABLE), toJSON(vars.FINDINGS_MODEL), toJSON(vars.FINDINGS_MODEL_PROVIDER), toJSON(vars.FINDINGS_PROVIDER), toJSON(vars.FIXER_EFFORT), toJSON(vars.FIXER_EXECUTABLE), toJSON(vars.FIXER_MODEL), toJSON(vars.FIXER_MODEL_PROVIDER), toJSON(vars.FIXER_PROVIDER), toJSON(vars.HOTFIX_TREE), toJSON(vars.ISSUES_LOCALE), toJSON(vars.ISSUE_MANAGED_BRANCHES), toJSON(vars.MAIN_BRANCH), toJSON(vars.PLANNER_EFFORT), toJSON(vars.PLANNER_EXECUTABLE), toJSON(vars.PLANNER_MODEL), toJSON(vars.PLANNER_MODEL_PROVIDER), toJSON(vars.PLANNER_PROVIDER), toJSON(vars.PRE_BRANCH_SDD), toJSON(vars.PROJECT_COLUMN_ISSUE_CREATED), toJSON(vars.PROJECT_COLUMN_ISSUE_IN_PROGRESS), toJSON(vars.PROJECT_COLUMN_PULL_REQUEST_CREATED), toJSON(vars.PROJECT_COLUMN_PULL_REQUEST_IN_PROGRESS), toJSON(vars.PROJECT_IDS), toJSON(vars.PULL_REQUESTS_LOCALE), toJSON(vars.RELEASE_TREE), toJSON(vars.REOPEN_ISSUE_ON_PUSH), toJSON(vars.REPOSITORY_LOCALE), toJSON(vars.REVIEWER_PROVIDER), toJSON(vars.TESTER_PROVIDER), toJSON(vars.VERIFICATION_SETUP)) }}
|
|
148
|
+
deploy-label: ${{ vars.DEPLOY_LABEL || 'deploy' }}
|
|
149
|
+
receipt-key: ${{ needs.issue-admission.outputs.receipt-key }}
|
|
150
|
+
|
|
151
|
+
- name: Retain successful reconciliation receipt
|
|
152
|
+
if: ${{ steps.receipt.outputs.receipt-path != '' }}
|
|
153
|
+
continue-on-error: true
|
|
154
|
+
uses: actions/upload-artifact@v5
|
|
155
|
+
with:
|
|
156
|
+
name: ${{ steps.receipt.outputs.receipt-name }}
|
|
157
|
+
path: ${{ steps.receipt.outputs.receipt-path }}
|
|
158
|
+
retention-days: 1
|
|
159
|
+
if-no-files-found: error
|
|
@@ -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: 'Hotfix 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 hotfix
|
|
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 hotfix 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 hotfix ${{ 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:
|