Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
e752467
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 15, 2026
3959fd1
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 15, 2026
b910620
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 15, 2026
b199bde
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 15, 2026
d64a874
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 15, 2026
b637168
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 15, 2026
f4da070
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 17, 2026
8c840d8
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 17, 2026
4dd336a
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 17, 2026
53f8788
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 17, 2026
1ee529d
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 17, 2026
35d048a
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 17, 2026
08014fd
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 17, 2026
2a02b9a
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 17, 2026
4ce66f2
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 17, 2026
06fb607
bugfix-314-error-merging-release-branch-after-successful-deployment: …
efraespada Feb 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Full documentation: **[docs.page/vypdev/copilot](https://docs.page/vypdev/copilo
| [OpenCode (AI)](https://docs.page/vypdev/copilot/opencode-integration) | Progress, Bugbot, think, AI PR description |
| [Testing OpenCode locally](https://docs.page/vypdev/copilot/testing-opencode-plan-locally) | Run check-progress, detect-potential-problems, recommend-steps via CLI |
| [Single actions](https://docs.page/vypdev/copilot/single-actions) | On-demand: check progress, think, create release/tag, deployed |
| [Deploy label and merge flow](docs/single-actions/deploy-label-and-merge.mdx) | Deploy/deployed labels, post-deploy merges, waiting for checks per PR |
| [Issues](https://docs.page/vypdev/copilot/issues) | Issue configuration and types (feature, bugfix, hotfix, release, docs, chore) |
| [Pull requests](https://docs.page/vypdev/copilot/pull-requests) | PR configuration and AI description |
| [Troubleshooting](https://docs.page/vypdev/copilot/troubleshooting) | Common issues and solutions |
Expand Down
53 changes: 46 additions & 7 deletions build/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51275,8 +51275,14 @@ const github = __importStar(__nccwpck_require__(5438));
const logger_1 = __nccwpck_require__(8836);
const result_1 = __nccwpck_require__(7305);
/**
* Repository for merging branches (via PR or direct merge).
* Isolated to allow unit tests with mocked Octokit.
* Repository for merging branches: creates a PR, waits for that PR's check runs (or status checks),
* then merges the PR; on failure, falls back to a direct Git merge.
*
* Check runs are filtered by PR (pull_requests) so we only wait for the current PR's checks,
* not those of another PR sharing the same head (e.g. release→main vs release→develop).
* If the PR has no check runs after a short wait, we proceed to merge (branch may have no required checks).
*
* @see docs/single-actions/deploy-label-and-merge.mdx for the deploy flow and check-wait behaviour.
*/
class MergeRepository {
constructor() {
Expand Down Expand Up @@ -51318,33 +51324,41 @@ This PR merges **${head}** into **${base}**.
'\n\nThis PR was automatically created by [`copilot`](https://github.com/vypdev/copilot).',
});
const iteration = 10;
/** Give workflows a short window to register check runs for this PR; after this, we allow merge with no check runs (e.g. branch has no required checks). */
const maxWaitForPrChecksAttempts = 3;
if (timeout > iteration) {
// Wait for checks to complete - can use regular token for reading checks
let checksCompleted = false;
let attempts = 0;
let waitForPrChecksAttempts = 0;
const maxAttempts = timeout > iteration ? Math.floor(timeout / iteration) : iteration;
while (!checksCompleted && attempts < maxAttempts) {
const { data: checkRuns } = await octokit.rest.checks.listForRef({
owner: owner,
repo: repository,
ref: head,
});
// Only consider check runs that are for this PR. When the same branch is used in
// multiple PRs (e.g. release→master and release→develop), listForRef returns runs
// for all PRs; we must wait for runs tied to the current PR or we may see completed
// runs from the other PR and merge before this PR's checks have run.
const runsForThisPr = checkRuns.check_runs.filter(run => run.pull_requests?.some(pr => pr.number === pullRequest.number));
// Get commit status checks for the PR head commit
const { data: commitStatus } = await octokit.rest.repos.getCombinedStatusForRef({
owner: owner,
repo: repository,
ref: head,
});
(0, logger_1.logDebugInfo)(`Combined status state: ${commitStatus.state}`);
(0, logger_1.logDebugInfo)(`Number of check runs: ${checkRuns.check_runs.length}`);
// If there are check runs, prioritize those over status checks
if (checkRuns.check_runs.length > 0) {
const pendingCheckRuns = checkRuns.check_runs.filter(check => check.status !== 'completed');
(0, logger_1.logDebugInfo)(`Number of check runs for this PR: ${runsForThisPr.length} (total on ref: ${checkRuns.check_runs.length})`);
// If there are check runs for this PR, wait for them to complete
if (runsForThisPr.length > 0) {
const pendingCheckRuns = runsForThisPr.filter(check => check.status !== 'completed');
if (pendingCheckRuns.length === 0) {
checksCompleted = true;
(0, logger_1.logDebugInfo)('All check runs have completed.');
// Verify if all checks passed
const failedChecks = checkRuns.check_runs.filter(check => check.conclusion === 'failure');
const failedChecks = runsForThisPr.filter(check => check.conclusion === 'failure');
if (failedChecks.length > 0) {
throw new Error(`Checks failed: ${failedChecks.map(check => check.name).join(', ')}`);
}
Expand All @@ -51359,6 +51373,21 @@ This PR merges **${head}** into **${base}**.
continue;
}
}
else if (checkRuns.check_runs.length > 0 && runsForThisPr.length === 0) {
// There are runs on the ref but none for this PR. Either workflows for this PR
// haven't registered yet, or this PR/base has no required checks.
waitForPrChecksAttempts++;
if (waitForPrChecksAttempts >= maxWaitForPrChecksAttempts) {
checksCompleted = true;
(0, logger_1.logDebugInfo)(`No check runs for this PR after ${maxWaitForPrChecksAttempts} polls; proceeding to merge (branch may have no required checks).`);
}
else {
(0, logger_1.logDebugInfo)('Check runs exist on ref but none for this PR yet; waiting for workflows to register.');
await new Promise(resolve => setTimeout(resolve, iteration * 1000));
attempts++;
}
continue;
}
else {
// Fall back to status checks if no check runs exist
const pendingChecks = commitStatus.statuses.filter(status => {
Expand Down Expand Up @@ -53911,6 +53940,16 @@ const branch_repository_1 = __nccwpck_require__(7701);
const issue_repository_1 = __nccwpck_require__(57);
const logger_1 = __nccwpck_require__(8836);
const task_emoji_1 = __nccwpck_require__(9785);
/**
* Single action run after a successful deployment (triggered with the "deployed" action and an issue number).
*
* Requires the issue to have the "deploy" label and not already have the "deployed" label. Then:
* 1. Replaces the "deploy" label with "deployed".
* 2. If a release or hotfix branch is configured: merges it into default and develop (each via PR, waiting for that PR's checks).
* 3. Closes the issue only when all merges succeed.
*
* @see docs/single-actions/deploy-label-and-merge.mdx for the full flow and how merge/check waiting works.
*/
class DeployedActionUseCase {
constructor() {
this.taskId = 'DeployedActionUseCase';
Expand Down
10 changes: 8 additions & 2 deletions build/cli/src/data/repository/merge_repository.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Result } from '../model/result';
/**
* Repository for merging branches (via PR or direct merge).
* Isolated to allow unit tests with mocked Octokit.
* Repository for merging branches: creates a PR, waits for that PR's check runs (or status checks),
* then merges the PR; on failure, falls back to a direct Git merge.
*
* Check runs are filtered by PR (pull_requests) so we only wait for the current PR's checks,
* not those of another PR sharing the same head (e.g. release→main vs release→develop).
* If the PR has no check runs after a short wait, we proceed to merge (branch may have no required checks).
*
* @see docs/single-actions/deploy-label-and-merge.mdx for the deploy flow and check-wait behaviour.
*/
export declare class MergeRepository {
mergeBranch: (owner: string, repository: string, head: string, base: string, timeout: number, token: string) => Promise<Result[]>;
Expand Down
10 changes: 10 additions & 0 deletions build/cli/src/usecase/actions/deployed_action_use_case.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import { Execution } from "../../data/model/execution";
import { Result } from "../../data/model/result";
import { ParamUseCase } from "../base/param_usecase";
/**
* Single action run after a successful deployment (triggered with the "deployed" action and an issue number).
*
* Requires the issue to have the "deploy" label and not already have the "deployed" label. Then:
* 1. Replaces the "deploy" label with "deployed".
* 2. If a release or hotfix branch is configured: merges it into default and develop (each via PR, waiting for that PR's checks).
* 3. Closes the issue only when all merges succeed.
*
* @see docs/single-actions/deploy-label-and-merge.mdx for the full flow and how merge/check waiting works.
*/
export declare class DeployedActionUseCase implements ParamUseCase<Execution, Result[]> {
taskId: string;
private issueRepository;
Expand Down
53 changes: 46 additions & 7 deletions build/github_action/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46343,8 +46343,14 @@ const github = __importStar(__nccwpck_require__(5438));
const logger_1 = __nccwpck_require__(8836);
const result_1 = __nccwpck_require__(7305);
/**
* Repository for merging branches (via PR or direct merge).
* Isolated to allow unit tests with mocked Octokit.
* Repository for merging branches: creates a PR, waits for that PR's check runs (or status checks),
* then merges the PR; on failure, falls back to a direct Git merge.
*
* Check runs are filtered by PR (pull_requests) so we only wait for the current PR's checks,
* not those of another PR sharing the same head (e.g. release→main vs release→develop).
* If the PR has no check runs after a short wait, we proceed to merge (branch may have no required checks).
*
* @see docs/single-actions/deploy-label-and-merge.mdx for the deploy flow and check-wait behaviour.
*/
class MergeRepository {
constructor() {
Expand Down Expand Up @@ -46386,33 +46392,41 @@ This PR merges **${head}** into **${base}**.
'\n\nThis PR was automatically created by [`copilot`](https://github.com/vypdev/copilot).',
});
const iteration = 10;
/** Give workflows a short window to register check runs for this PR; after this, we allow merge with no check runs (e.g. branch has no required checks). */
const maxWaitForPrChecksAttempts = 3;
if (timeout > iteration) {
// Wait for checks to complete - can use regular token for reading checks
let checksCompleted = false;
let attempts = 0;
let waitForPrChecksAttempts = 0;
const maxAttempts = timeout > iteration ? Math.floor(timeout / iteration) : iteration;
while (!checksCompleted && attempts < maxAttempts) {
const { data: checkRuns } = await octokit.rest.checks.listForRef({
owner: owner,
repo: repository,
ref: head,
});
// Only consider check runs that are for this PR. When the same branch is used in
// multiple PRs (e.g. release→master and release→develop), listForRef returns runs
// for all PRs; we must wait for runs tied to the current PR or we may see completed
// runs from the other PR and merge before this PR's checks have run.
const runsForThisPr = checkRuns.check_runs.filter(run => run.pull_requests?.some(pr => pr.number === pullRequest.number));
// Get commit status checks for the PR head commit
const { data: commitStatus } = await octokit.rest.repos.getCombinedStatusForRef({
owner: owner,
repo: repository,
ref: head,
});
(0, logger_1.logDebugInfo)(`Combined status state: ${commitStatus.state}`);
(0, logger_1.logDebugInfo)(`Number of check runs: ${checkRuns.check_runs.length}`);
// If there are check runs, prioritize those over status checks
if (checkRuns.check_runs.length > 0) {
const pendingCheckRuns = checkRuns.check_runs.filter(check => check.status !== 'completed');
(0, logger_1.logDebugInfo)(`Number of check runs for this PR: ${runsForThisPr.length} (total on ref: ${checkRuns.check_runs.length})`);
// If there are check runs for this PR, wait for them to complete
if (runsForThisPr.length > 0) {
const pendingCheckRuns = runsForThisPr.filter(check => check.status !== 'completed');
if (pendingCheckRuns.length === 0) {
checksCompleted = true;
(0, logger_1.logDebugInfo)('All check runs have completed.');
// Verify if all checks passed
const failedChecks = checkRuns.check_runs.filter(check => check.conclusion === 'failure');
const failedChecks = runsForThisPr.filter(check => check.conclusion === 'failure');
if (failedChecks.length > 0) {
throw new Error(`Checks failed: ${failedChecks.map(check => check.name).join(', ')}`);
}
Expand All @@ -46427,6 +46441,21 @@ This PR merges **${head}** into **${base}**.
continue;
}
}
else if (checkRuns.check_runs.length > 0 && runsForThisPr.length === 0) {
// There are runs on the ref but none for this PR. Either workflows for this PR
// haven't registered yet, or this PR/base has no required checks.
waitForPrChecksAttempts++;
if (waitForPrChecksAttempts >= maxWaitForPrChecksAttempts) {
checksCompleted = true;
(0, logger_1.logDebugInfo)(`No check runs for this PR after ${maxWaitForPrChecksAttempts} polls; proceeding to merge (branch may have no required checks).`);
}
else {
(0, logger_1.logDebugInfo)('Check runs exist on ref but none for this PR yet; waiting for workflows to register.');
await new Promise(resolve => setTimeout(resolve, iteration * 1000));
attempts++;
}
continue;
}
else {
// Fall back to status checks if no check runs exist
const pendingChecks = commitStatus.statuses.filter(status => {
Expand Down Expand Up @@ -48979,6 +49008,16 @@ const branch_repository_1 = __nccwpck_require__(7701);
const issue_repository_1 = __nccwpck_require__(57);
const logger_1 = __nccwpck_require__(8836);
const task_emoji_1 = __nccwpck_require__(9785);
/**
* Single action run after a successful deployment (triggered with the "deployed" action and an issue number).
*
* Requires the issue to have the "deploy" label and not already have the "deployed" label. Then:
* 1. Replaces the "deploy" label with "deployed".
* 2. If a release or hotfix branch is configured: merges it into default and develop (each via PR, waiting for that PR's checks).
* 3. Closes the issue only when all merges succeed.
*
* @see docs/single-actions/deploy-label-and-merge.mdx for the full flow and how merge/check waiting works.
*/
class DeployedActionUseCase {
constructor() {
this.taskId = 'DeployedActionUseCase';
Expand Down
10 changes: 8 additions & 2 deletions build/github_action/src/data/repository/merge_repository.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Result } from '../model/result';
/**
* Repository for merging branches (via PR or direct merge).
* Isolated to allow unit tests with mocked Octokit.
* Repository for merging branches: creates a PR, waits for that PR's check runs (or status checks),
* then merges the PR; on failure, falls back to a direct Git merge.
*
* Check runs are filtered by PR (pull_requests) so we only wait for the current PR's checks,
* not those of another PR sharing the same head (e.g. release→main vs release→develop).
* If the PR has no check runs after a short wait, we proceed to merge (branch may have no required checks).
*
* @see docs/single-actions/deploy-label-and-merge.mdx for the deploy flow and check-wait behaviour.
*/
export declare class MergeRepository {
mergeBranch: (owner: string, repository: string, head: string, base: string, timeout: number, token: string) => Promise<Result[]>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import { Execution } from "../../data/model/execution";
import { Result } from "../../data/model/result";
import { ParamUseCase } from "../base/param_usecase";
/**
* Single action run after a successful deployment (triggered with the "deployed" action and an issue number).
*
* Requires the issue to have the "deploy" label and not already have the "deployed" label. Then:
* 1. Replaces the "deploy" label with "deployed".
* 2. If a release or hotfix branch is configured: merges it into default and develop (each via PR, waiting for that PR's checks).
* 3. Closes the issue only when all merges succeed.
*
* @see docs/single-actions/deploy-label-and-merge.mdx for the full flow and how merge/check waiting works.
*/
export declare class DeployedActionUseCase implements ParamUseCase<Execution, Result[]> {
taskId: string;
private issueRepository;
Expand Down
Loading