Skip to content
Open
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
39 changes: 26 additions & 13 deletions packages/cli/src/deploy-rollout-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,31 @@ export const DeployRolloutMetadataSchema = z
source: z.enum(['managed']).optional(),
channel: z.enum(['edge', 'stable', 'commit']).optional(),
rollout_org_ids: z.array(z.string().min(1)).optional(),
rollout_id: z
.string()
.min(1)
.regex(/^[A-Za-z0-9._:-]+$/)
.optional(),
})
.strict();
.passthrough();

export type DeployRolloutMetadata = z.infer<typeof DeployRolloutMetadataSchema>;

function hasDeployRolloutMetadata(value: DeployRolloutMetadata): boolean {
return Object.entries(value).some(([, fieldValue]) => {
if (fieldValue === undefined) {
return false;
}
if (Array.isArray(fieldValue)) {
return fieldValue.length > 0;
}
if (typeof fieldValue === 'string') {
return fieldValue.length > 0;
}
return true;
});
}

export function parseDeployRolloutMetadata(
raw: string | undefined
): DeployRolloutMetadata | undefined {
Expand All @@ -30,7 +50,7 @@ export function parseDeployRolloutMetadata(
const details = result.error.issues.map((issue) => issue.message).join(', ');
throw new Error(`Invalid deploy metadata: ${details}`);
}
if (!result.data.source && !result.data.channel && !result.data.rollout_org_ids?.length) {
if (!hasDeployRolloutMetadata(result.data)) {
return undefined;
}
return result.data;
Expand All @@ -49,18 +69,11 @@ export function mergeDeployRolloutMetadata<T extends { deployment?: Record<strin
if (!rolloutMetadata || !build.deployment) {
return build;
}
const deployment = { ...build.deployment };
if (rolloutMetadata.source) {
deployment.source = rolloutMetadata.source;
}
if (rolloutMetadata.channel) {
deployment.channel = rolloutMetadata.channel;
}
if (rolloutMetadata.rollout_org_ids?.length) {
deployment.rollout_org_ids = rolloutMetadata.rollout_org_ids;
}
return {
...build,
deployment,
deployment: {
...build.deployment,
...rolloutMetadata,
},
};
}
67 changes: 67 additions & 0 deletions packages/cli/test/deploy-rollout-metadata.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { describe, expect, test } from 'bun:test';

import {
mergeDeployRolloutMetadata,
parseDeployRolloutMetadata,
} from '../src/deploy-rollout-metadata';

describe('parseDeployRolloutMetadata', () => {
test('accepts rollout_id for Obs-triggered managed deploys', () => {
expect(
parseDeployRolloutMetadata(
'{"source":"managed","channel":"edge","rollout_id":"rollout_3FdsBqGeMpUSDtpBt1uz2n6Drps"}'
)
).toEqual({
source: 'managed',
channel: 'edge',
rollout_id: 'rollout_3FdsBqGeMpUSDtpBt1uz2n6Drps',
});
});

test('passes through unknown keys for forward-compatible managed metadata', () => {
expect(
parseDeployRolloutMetadata(
'{"source":"managed","channel":"edge","rollout_id":"rollout_test","initiated_by":"user_abc"}'
)
).toEqual({
source: 'managed',
channel: 'edge',
rollout_id: 'rollout_test',
initiated_by: 'user_abc',
});
});

test('rejects invalid known fields', () => {
expect(() => parseDeployRolloutMetadata('{"source":"github","channel":"edge"}')).toThrow(
'Invalid deploy metadata'
);
});
});

describe('mergeDeployRolloutMetadata', () => {
test('merges rollout metadata onto deployment', () => {
expect(
mergeDeployRolloutMetadata(
{
deployment: {
source: 'managed',
channel: 'edge',
},
},
{
source: 'managed',
channel: 'edge',
rollout_id: 'rollout_test',
initiated_by: 'user_abc',
}
)
).toEqual({
deployment: {
source: 'managed',
channel: 'edge',
rollout_id: 'rollout_test',
initiated_by: 'user_abc',
},
});
});
});
5 changes: 5 additions & 0 deletions packages/server/src/api/project/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ export const BuildMetadataSchema = z.object({
source: z.enum(['github', 'cli', 'managed']).optional(),
channel: z.enum(['edge', 'stable', 'commit']).optional(),
rollout_org_ids: z.array(z.string()).optional(),
rollout_id: z
.string()
.min(1)
.optional()
.describe('correlation id for a managed shared-source rollout wave'),
})
),
launch: LaunchMetadataSchema.optional().describe(
Expand Down
Loading