Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 17 additions & 3 deletions build/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53465,7 +53465,7 @@ class DeploymentOrchestrationUseCase {
return await this.confirmPublication.invoke(context);
if (context.singleAction.isFailedDeploymentAction)
return await this.recordFailure.invoke(context);
return undefined;
throw new application_error_1.ApplicationError("workflow.invalid-event", "Deployment command does not select a supported phase.");
}
async handleFailure(context, error) {
if (error instanceof deployment_state_boundary_1.SupersededDeploymentInvocationError) {
Expand Down Expand Up @@ -57993,11 +57993,25 @@ function projectDeploymentOrchestrationContext(source) {
? { agentConfiguration: Object.freeze({ ...source.ai.getAgentConfiguration('planner') }) }
: {}),
labels: Object.freeze({
...source.labels,
isRelease: source.labels.isRelease,
isHotfix: source.labels.isHotfix,
deploy: source.labels.deploy,
deployed: source.labels.deployed,
lifecycle: Object.freeze({ ...source.labels.lifecycle }),
}),
deployment: deepFreezeCopy(source.deployment),
singleAction: Object.freeze({ ...source.singleAction }),
singleAction: Object.freeze({
issue: source.singleAction.issue,
version: source.singleAction.version,
title: source.singleAction.title,
changelog: source.singleAction.changelog,
operationId: source.singleAction.operationId,
message: source.singleAction.message,
isPrepareDeploymentAction: source.singleAction.isPrepareDeploymentAction,
isContinueDeploymentAction: source.singleAction.isContinueDeploymentAction,
isPublishedDeploymentAction: source.singleAction.isPublishedDeploymentAction,
isFailedDeploymentAction: source.singleAction.isFailedDeploymentAction,
}),
pullRequest: Object.freeze({ ...source.pullRequest }),
currentConfiguration: {
...source.currentConfiguration,
Expand Down
20 changes: 17 additions & 3 deletions build/github_action/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51779,7 +51779,7 @@ class DeploymentOrchestrationUseCase {
return await this.confirmPublication.invoke(context);
if (context.singleAction.isFailedDeploymentAction)
return await this.recordFailure.invoke(context);
return undefined;
throw new application_error_1.ApplicationError("workflow.invalid-event", "Deployment command does not select a supported phase.");
}
async handleFailure(context, error) {
if (error instanceof deployment_state_boundary_1.SupersededDeploymentInvocationError) {
Expand Down Expand Up @@ -56577,11 +56577,25 @@ function projectDeploymentOrchestrationContext(source) {
? { agentConfiguration: Object.freeze({ ...source.ai.getAgentConfiguration('planner') }) }
: {}),
labels: Object.freeze({
...source.labels,
isRelease: source.labels.isRelease,
isHotfix: source.labels.isHotfix,
deploy: source.labels.deploy,
deployed: source.labels.deployed,
lifecycle: Object.freeze({ ...source.labels.lifecycle }),
}),
deployment: deepFreezeCopy(source.deployment),
singleAction: Object.freeze({ ...source.singleAction }),
singleAction: Object.freeze({
issue: source.singleAction.issue,
version: source.singleAction.version,
title: source.singleAction.title,
changelog: source.singleAction.changelog,
operationId: source.singleAction.operationId,
message: source.singleAction.message,
isPrepareDeploymentAction: source.singleAction.isPrepareDeploymentAction,
isContinueDeploymentAction: source.singleAction.isContinueDeploymentAction,
isPublishedDeploymentAction: source.singleAction.isPublishedDeploymentAction,
isFailedDeploymentAction: source.singleAction.isFailedDeploymentAction,
}),
pullRequest: Object.freeze({ ...source.pullRequest }),
currentConfiguration: {
...source.currentConfiguration,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vypdev/copilot",
"version": "3.3.1",
"version": "3.4.0",
"packageManager": "pnpm@10.12.4",
"pnpm": {
"overrides": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { DEFAULT_COPILOT_LIFECYCLE_LABELS } from '../../../domain/copilot_lifecycle';
import { DEFAULT_DEPLOYMENT_CONFIGURATION } from '../../../domain/deployment_configuration';
import type { DeploymentOrchestrationContext } from '../../ports/deployment_orchestration_ports';
import { Labels } from '../../../data/model/labels';
import { SingleAction } from '../../../data/model/single_action';
import { ACTIONS } from '../../../data/model/action_types';
import {
projectAgentActivityContext,
projectBranchObservationContext,
Expand Down Expand Up @@ -117,6 +120,44 @@ function source(): DeepMutable<PushSingleActionContextSource> {
}

describe('push and single-action context projection', () => {
it.each(['release', 'hotfix'].flatMap(kind => [
[kind, ACTIONS.PREPARE_DEPLOYMENT, 'isPrepareDeploymentAction'] as const,
[kind, ACTIONS.CONTINUE_DEPLOYMENT, 'isContinueDeploymentAction'] as const,
[kind, ACTIONS.PUBLISHED_DEPLOYMENT, 'isPublishedDeploymentAction'] as const,
[kind, ACTIONS.FAILED_DEPLOYMENT, 'isFailedDeploymentAction'] as const,
]))('preserves %s model getters for %s', (kind, action, selectedFlag) => {
const labels = new Labels(
'bug', 'bugfix', 'hotfix', 'enhancement', 'feature', 'release', 'question', 'help',
'deploy', 'deployed', 'docs', 'documentation', 'chore', 'maintenance',
'priority: high', 'priority: medium', 'priority: low', 'priority: none',
'size: XXL', 'size: XL', 'size: L', 'size: M', 'size: S', 'size: XS',
);
labels.currentIssueLabels = [kind, 'deploy'];
const singleAction = new SingleAction(action, '42', '3.4.0', 'Release', 'Changes', '', '', '', 'operation-12345678');
const input = {
...source(), labels, singleAction,
deployment: { ...DEFAULT_DEPLOYMENT_CONFIGURATION },
workflows: { release: 'release.yml', hotfix: 'hotfix.yml' },
} as unknown as DeploymentOrchestrationContext;

const projected = projectDeploymentOrchestrationContext(input);
expect(projected.singleAction).toMatchObject({
issue: 42, version: '3.4.0', title: 'Release', changelog: 'Changes', operationId: 'operation-12345678',
});
for (const flag of ['isPrepareDeploymentAction', 'isContinueDeploymentAction', 'isPublishedDeploymentAction', 'isFailedDeploymentAction'] as const) {
expect(projected.singleAction[flag]).toBe(flag === selectedFlag);
}
expect(projected.labels).toEqual({
isRelease: kind === 'release', isHotfix: kind === 'hotfix',
deploy: 'deploy', deployed: 'deployed', lifecycle: DEFAULT_COPILOT_LIFECYCLE_LABELS,
});
singleAction.currentSingleAction = '';
labels.currentIssueLabels = [];
expect(projected.singleAction[selectedFlag]).toBe(true);
expect(projected.labels.isRelease).toBe(kind === 'release');
expect(Object.isFrozen(projected.singleAction)).toBe(true);
});

it('copies and freezes deployment publication state', () => {
const input = source();
const projected = projectDeploymentPublicationContext(input);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import {
readDeploymentOperationState,
} from "../../../../domain/deployment_state_fence";
import { ResolveMessageCatalogUseCase } from "../../../usecases/localization/resolve_message_catalog_use_case";
import { SingleAction } from "../../../../data/model/single_action";
import { ACTIONS } from "../../../../data/model/action_types";
import { projectDeploymentOrchestrationContext } from "../../push_single_action_contexts";

const sourceSha = "a".repeat(40);
const originSha = "b".repeat(40);
Expand Down Expand Up @@ -221,13 +224,28 @@ function harness() {
describe("DeploymentOrchestrationUseCase", () => {
it("prepares a durable operation and opens exactly one promotion PR", async () => {
const value = harness();
const input = execution("prepare");
const source = execution("prepare");
source.singleAction = new SingleAction(ACTIONS.PREPARE_DEPLOYMENT, '355', '3.4.0', 'Release', 'Changes');
const input = projectDeploymentOrchestrationContext(source);
const result = await value.useCase.invoke(input);
expect(result[0].success).toBe(true);
expect(value.pullRequests.createManagedPullRequest).toHaveBeenCalledTimes(1);
expect(input.currentConfiguration.deploymentOrchestration).toEqual(expect.objectContaining({ phase: "promotion_pr_pending", promotionPullRequest: 40 }));
});

it("rejects a deployment command with no selected phase instead of reporting an empty success", async () => {
const value = harness();
const input = execution("prepare");
Object.assign(input.singleAction, { isPrepareDeploymentAction: false });
const results = await value.useCase.invoke(input);

expect(results).toHaveLength(1);
expect(results[0].success).toBe(false);
expect(value.state.save).not.toHaveBeenCalled();
expect(value.pullRequests.createManagedPullRequest).not.toHaveBeenCalled();
expect(value.continuation.dispatch).not.toHaveBeenCalled();
});

it("resolves one catalog per destination and snapshots the effective locale profile", async () => {
const value = harness();
const input = execution("prepare");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { toApplicationError } from "../../errors/application_error";
import { ApplicationError, toApplicationError } from "../../errors/application_error";
import type { DeploymentOrchestrationContext } from "../../ports/deployment_orchestration_ports";
import type { DeploymentOrchestrationDependencies } from "../../services/deployment_orchestration_dependencies";
import {
Expand Down Expand Up @@ -57,7 +57,7 @@ export class DeploymentOrchestrationUseCase implements ParamUseCase<DeploymentOr
if (context.singleAction.isContinueDeploymentAction) return await this.continueDeployment.invoke(context);
if (context.singleAction.isPublishedDeploymentAction) return await this.confirmPublication.invoke(context);
if (context.singleAction.isFailedDeploymentAction) return await this.recordFailure.invoke(context);
return undefined;
throw new ApplicationError("workflow.invalid-event", "Deployment command does not select a supported phase.");
}

private async handleFailure(
Expand Down
18 changes: 16 additions & 2 deletions src/application/usecases/push_single_action_contexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,25 @@ export function projectDeploymentOrchestrationContext(
? { agentConfiguration: Object.freeze({ ...source.ai.getAgentConfiguration('planner') }) }
: {}),
labels: Object.freeze({
...source.labels,
isRelease: source.labels.isRelease,
isHotfix: source.labels.isHotfix,
deploy: source.labels.deploy,
deployed: source.labels.deployed,
lifecycle: Object.freeze({ ...source.labels.lifecycle }),
}),
deployment: deepFreezeCopy(source.deployment) as DeploymentOrchestrationContext['deployment'],
singleAction: Object.freeze({ ...source.singleAction }),
singleAction: Object.freeze({
issue: source.singleAction.issue,
version: source.singleAction.version,
title: source.singleAction.title,
changelog: source.singleAction.changelog,
operationId: source.singleAction.operationId,
message: source.singleAction.message,
isPrepareDeploymentAction: source.singleAction.isPrepareDeploymentAction,
isContinueDeploymentAction: source.singleAction.isContinueDeploymentAction,
isPublishedDeploymentAction: source.singleAction.isPublishedDeploymentAction,
isFailedDeploymentAction: source.singleAction.isFailedDeploymentAction,
}),
pullRequest: Object.freeze({ ...source.pullRequest }),
currentConfiguration: {
...source.currentConfiguration,
Expand Down
Loading