fix(fleet): align migration preview and clean CI
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1136,6 +1136,70 @@ describe('v1-to-v2 migration preview', (): void => {
|
|||||||
expect(preview.environment.map(({ agent }) => agent)).toEqual(['coder0']);
|
expect(preview.environment.map(({ agent }) => agent)).toEqual(['coder0']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[
|
||||||
|
'tmux connector with matrix settings',
|
||||||
|
'connector:\n kind: tmux\n matrix:\n homeserver_url: https://rejected-matrix.example.test\n user_id: "@bot:example.test"\n room_id: "!room:example.test"\n',
|
||||||
|
'connector.matrix',
|
||||||
|
'https://rejected-matrix.example.test',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'tmux connector with discord settings',
|
||||||
|
'connector:\n kind: tmux\n discord:\n channel_id: rejected-discord-channel\n',
|
||||||
|
'connector.discord',
|
||||||
|
'rejected-discord-channel',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'discord connector with matrix settings',
|
||||||
|
'connector:\n kind: discord\n discord:\n channel_id: channel\n matrix:\n homeserver_url: https://rejected-matrix.example.test\n user_id: "@bot:example.test"\n room_id: "!room:example.test"\n',
|
||||||
|
'connector.matrix',
|
||||||
|
'https://rejected-matrix.example.test',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'matrix connector with discord settings',
|
||||||
|
'connector:\n kind: matrix\n matrix:\n homeserver_url: https://matrix.example.test\n user_id: "@bot:example.test"\n room_id: "!room:example.test"\n discord:\n channel_id: rejected-discord-channel\n',
|
||||||
|
'connector.discord',
|
||||||
|
'rejected-discord-channel',
|
||||||
|
],
|
||||||
|
] as const)(
|
||||||
|
'blocks %s to match production v1 connector exclusivity',
|
||||||
|
async (_label, connectorSource, expectedPath, rejectedValue) => {
|
||||||
|
const preview = await previewV1ToV2Migration({
|
||||||
|
source: v1Source.replace(/connector:\n[\s\S]*$/, connectorSource),
|
||||||
|
decisions: decisions(),
|
||||||
|
observations: observations(),
|
||||||
|
personaDirs: await semanticDirs(),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(preview.status).toBe('blocked');
|
||||||
|
expect(preview.blockers).toContainEqual(
|
||||||
|
expect.objectContaining({ code: 'invalid-connector', path: expectedPath }),
|
||||||
|
);
|
||||||
|
expect(preview).not.toHaveProperty('canonicalRoster');
|
||||||
|
expect(preview).not.toHaveProperty('canonicalYaml');
|
||||||
|
expect(JSON.stringify(preview.blockers)).not.toContain(rejectedValue);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it('blocks whitespace-padded connector kinds to match exact production v1 parsing', async () => {
|
||||||
|
const rejectedKind = ' discord ';
|
||||||
|
const preview = await previewV1ToV2Migration({
|
||||||
|
source: v1Source.replace('kind: discord', `kind: "${rejectedKind}"`),
|
||||||
|
decisions: decisions(),
|
||||||
|
observations: observations(),
|
||||||
|
personaDirs: await semanticDirs(),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(preview.status).toBe('blocked');
|
||||||
|
expect(preview.blockers).toContainEqual(
|
||||||
|
expect.objectContaining({ code: 'invalid-connector', path: 'connector.kind' }),
|
||||||
|
);
|
||||||
|
expect(preview).not.toHaveProperty('canonicalRoster');
|
||||||
|
expect(preview).not.toHaveProperty('canonicalYaml');
|
||||||
|
expect(preview.evidence).not.toHaveProperty('candidate');
|
||||||
|
expect(JSON.stringify(preview.blockers)).not.toContain(rejectedKind);
|
||||||
|
});
|
||||||
|
|
||||||
it('blocks malformed schema-only remote and connector fields', async () => {
|
it('blocks malformed schema-only remote and connector fields', async () => {
|
||||||
const source = v1Source
|
const source = v1Source
|
||||||
.replace('host: build.example.test', 'host: 42')
|
.replace('host: build.example.test', 'host: 42')
|
||||||
|
|||||||
@@ -1005,10 +1005,48 @@ function validateConnector(
|
|||||||
): void {
|
): void {
|
||||||
if (!present) return;
|
if (!present) return;
|
||||||
rejectUnknownKeys(connector, new Set(['kind', 'matrix', 'discord']), 'connector', blockers);
|
rejectUnknownKeys(connector, new Set(['kind', 'matrix', 'discord']), 'connector', blockers);
|
||||||
const kind = stringValue(connector.kind);
|
const kind = typeof connector.kind === 'string' ? connector.kind : undefined;
|
||||||
if (!kind || !['tmux', 'discord', 'matrix'].includes(kind)) {
|
if (!kind || !['tmux', 'discord', 'matrix'].includes(kind)) {
|
||||||
blockers.push(blocker('invalid-connector', 'connector.kind', 'Unsupported connector kind.'));
|
blockers.push(blocker('invalid-connector', 'connector.kind', 'Unsupported connector kind.'));
|
||||||
}
|
}
|
||||||
|
if (kind === 'tmux') {
|
||||||
|
if (connector.matrix !== undefined) {
|
||||||
|
blockers.push(
|
||||||
|
blocker(
|
||||||
|
'invalid-connector',
|
||||||
|
'connector.matrix',
|
||||||
|
'Tmux connector must not define Matrix connector data.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (connector.discord !== undefined) {
|
||||||
|
blockers.push(
|
||||||
|
blocker(
|
||||||
|
'invalid-connector',
|
||||||
|
'connector.discord',
|
||||||
|
'Tmux connector must not define Discord connector data.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (kind === 'discord' && connector.matrix !== undefined) {
|
||||||
|
blockers.push(
|
||||||
|
blocker(
|
||||||
|
'invalid-connector',
|
||||||
|
'connector.matrix',
|
||||||
|
'Discord connector must not define Matrix connector data.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (kind === 'matrix' && connector.discord !== undefined) {
|
||||||
|
blockers.push(
|
||||||
|
blocker(
|
||||||
|
'invalid-connector',
|
||||||
|
'connector.discord',
|
||||||
|
'Matrix connector must not define Discord connector data.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (kind === 'matrix' && connector.matrix === undefined) {
|
if (kind === 'matrix' && connector.matrix === undefined) {
|
||||||
blockers.push(
|
blockers.push(
|
||||||
blocker('invalid-connector', 'connector.matrix', 'Matrix connector data is required.'),
|
blocker('invalid-connector', 'connector.matrix', 'Matrix connector data is required.'),
|
||||||
|
|||||||
8
packages/mosaic/turbo.json
Normal file
8
packages/mosaic/turbo.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": ["//"],
|
||||||
|
"tasks": {
|
||||||
|
"test": {
|
||||||
|
"dependsOn": ["^build", "build"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user