Skip to content

Commit 5a21526

Browse files
committed
Merge branch 'main' into add-plugin-targets
# Conflicts: # nx.json
2 parents ff016c4 + b08a9a7 commit 5a21526

File tree

11 files changed

+361
-42
lines changed

11 files changed

+361
-42
lines changed

nx.json

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,9 @@
4949
}
5050
],
5151
"sharedGlobals": [
52-
{
53-
"runtime": "npm --version"
54-
},
55-
{
56-
"runtime": "node --version"
57-
}
52+
{ "runtime": "node -e \"console.log(require('os').platform())\"" },
53+
{ "runtime": "node -v" },
54+
{ "runtime": "npm -v" }
5855
]
5956
},
6057
"targetDefaults": {
@@ -289,6 +286,33 @@
289286
}
290287
}
291288
},
289+
"namedInputs": {
290+
"default": [
291+
"{projectRoot}/**/*",
292+
"sharedGlobals",
293+
"!{projectRoot}/dist/**/*"
294+
],
295+
"production": [
296+
"default",
297+
"!{projectRoot}/eslint.config.?(c)js",
298+
"!{projectRoot}/**/?(*.)test.[jt]s?(x)?(.snap)",
299+
"!{projectRoot}/tsconfig.test.json",
300+
"!{projectRoot}/src/test-setup.[jt]s",
301+
"!{projectRoot}/test-setup.[jt]s",
302+
"!{projectRoot}/**/?(*.)mock.[jt]s?(x)",
303+
"!{projectRoot}/vitest.@(unit|int|e2e).config.[jt]s",
304+
"!{projectRoot}/@(test|mocks)/**/*",
305+
"!{projectRoot}/perf/**/*",
306+
"!{projectRoot}/tools/**/*",
307+
"!{projectRoot}/code-pushup.config.?(m)[jt]s",
308+
"!{projectRoot}/zod2md.config.ts"
309+
],
310+
"sharedGlobals": [
311+
{ "runtime": "node -e \"console.log(require('os').platform())\"" },
312+
{ "runtime": "node -v" },
313+
{ "runtime": "npm -v" }
314+
]
315+
},
292316
"workspaceLayout": {
293317
"appsDir": "examples",
294318
"libsDir": "packages"

packages/ci/src/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
export type { SourceFileIssue } from './lib/issues.js';
22
export type * from './lib/models.js';
33
export {
4-
MONOREPO_TOOLS,
54
isMonorepoTool,
5+
MONOREPO_TOOLS,
66
type MonorepoTool,
77
} from './lib/monorepo/index.js';
88
export { runInCI } from './lib/run.js';
9+
export { configPatternsSchema } from './lib/schemas.js';
10+
export {
11+
DEFAULT_SETTINGS,
12+
MAX_SEARCH_COMMITS,
13+
MIN_SEARCH_COMMITS,
14+
parseConfigPatternsFromString,
15+
} from './lib/settings.js';

packages/ci/src/lib/constants.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

packages/ci/src/lib/monorepo/list-projects.unit.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import path from 'node:path';
33
import type { PackageJson } from 'type-fest';
44
import { MEMFS_VOLUME } from '@code-pushup/test-utils';
55
import * as utils from '@code-pushup/utils';
6-
import { DEFAULT_SETTINGS } from '../constants.js';
76
import type { Settings } from '../models.js';
7+
import { DEFAULT_SETTINGS } from '../settings.js';
88
import {
99
type MonorepoProjects,
1010
listMonorepoProjects,

packages/ci/src/lib/run-utils.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,6 @@ import {
2828
runCompare,
2929
runPrintConfig,
3030
} from './cli/index.js';
31-
import {
32-
DEFAULT_SETTINGS,
33-
MAX_SEARCH_COMMITS,
34-
MIN_SEARCH_COMMITS,
35-
} from './constants.js';
3631
import { listChangedFiles, normalizeGitRef } from './git.js';
3732
import { type SourceFileIssue, filterRelevantIssues } from './issues.js';
3833
import type {
@@ -49,6 +44,11 @@ import type {
4944
import type { ProjectConfig } from './monorepo/index.js';
5045
import { saveOutputFiles } from './output-files.js';
5146
import { downloadFromPortal } from './portal/download.js';
47+
import {
48+
DEFAULT_SETTINGS,
49+
MAX_SEARCH_COMMITS,
50+
MIN_SEARCH_COMMITS,
51+
} from './settings.js';
5252

5353
export type RunEnv = {
5454
refs: NormalizedGitRefs;

packages/ci/src/lib/schemas.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { ZodError, z } from 'zod';
2+
import {
3+
DEFAULT_PERSIST_CONFIG,
4+
persistConfigSchema,
5+
slugSchema,
6+
uploadConfigSchema,
7+
} from '@code-pushup/models';
8+
import { interpolate } from '@code-pushup/utils';
9+
10+
// eslint-disable-next-line unicorn/prefer-top-level-await, unicorn/catch-error-name
11+
export const interpolatedSlugSchema = slugSchema.catch(ctx => {
12+
// allow {projectName} interpolation (invalid slug)
13+
if (
14+
typeof ctx.value === 'string' &&
15+
ctx.issues.length === 1 &&
16+
ctx.issues[0]?.code === 'invalid_format'
17+
) {
18+
// if only regex failed, try if it would pass once we insert known variables
19+
const { success } = slugSchema.safeParse(
20+
interpolate(ctx.value, { projectName: 'example' }),
21+
);
22+
if (success) {
23+
return ctx.value;
24+
}
25+
}
26+
throw new ZodError(ctx.error.issues);
27+
});
28+
29+
export const configPatternsSchema = z.object({
30+
persist: persistConfigSchema.transform(persist => ({
31+
...DEFAULT_PERSIST_CONFIG,
32+
...persist,
33+
})),
34+
upload: uploadConfigSchema
35+
.omit({ organization: true, project: true })
36+
.extend({
37+
organization: interpolatedSlugSchema,
38+
project: interpolatedSlugSchema,
39+
})
40+
.optional(),
41+
});
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { ZodError } from 'zod';
3+
import type { ConfigPatterns } from './models.js';
4+
import { configPatternsSchema, interpolatedSlugSchema } from './schemas.js';
5+
6+
describe('interpolatedSlugSchema', () => {
7+
it('should accept a valid slug', () => {
8+
expect(interpolatedSlugSchema.parse('valid-slug')).toBe('valid-slug');
9+
});
10+
11+
it('should accept a slug with {projectName} interpolation', () => {
12+
expect(interpolatedSlugSchema.parse('{projectName}-slug')).toBe(
13+
'{projectName}-slug',
14+
);
15+
});
16+
17+
it('should reject an invalid slug that cannot be fixed by interpolation', () => {
18+
expect(() => interpolatedSlugSchema.parse('Invalid Slug!')).toThrow(
19+
ZodError,
20+
);
21+
});
22+
23+
it('should reject a non-string value', () => {
24+
expect(() => interpolatedSlugSchema.parse(123)).toThrow(ZodError);
25+
});
26+
});
27+
28+
describe('configPatternsSchema', () => {
29+
it('should accept valid persist and upload configs', () => {
30+
const configPatterns: Required<ConfigPatterns> = {
31+
persist: {
32+
outputDir: '.code-pushup/{projectName}',
33+
filename: 'report',
34+
format: ['json', 'md'],
35+
skipReports: false,
36+
},
37+
upload: {
38+
server: 'https://api.code-pushup.example.com/graphql',
39+
apiKey: 'cp_...',
40+
organization: 'example',
41+
project: '{projectName}',
42+
},
43+
};
44+
expect(configPatternsSchema.parse(configPatterns)).toEqual(configPatterns);
45+
});
46+
47+
it('should accept persist config without upload', () => {
48+
const configPatterns: ConfigPatterns = {
49+
persist: {
50+
outputDir: '.code-pushup/{projectName}',
51+
filename: 'report',
52+
format: ['json', 'md'],
53+
skipReports: false,
54+
},
55+
};
56+
expect(configPatternsSchema.parse(configPatterns)).toEqual(configPatterns);
57+
});
58+
59+
it('fills in default persist values if missing', () => {
60+
expect(
61+
configPatternsSchema.parse({
62+
persist: {
63+
filename: '{projectName}-report',
64+
},
65+
}),
66+
).toEqual<ConfigPatterns>({
67+
persist: {
68+
outputDir: '.code-pushup',
69+
filename: '{projectName}-report',
70+
format: ['json', 'md'],
71+
skipReports: false,
72+
},
73+
});
74+
});
75+
76+
it('should reject if persist is missing', () => {
77+
expect(() => configPatternsSchema.parse({})).toThrow(ZodError);
78+
});
79+
80+
it('should reject if persist has invalid values', () => {
81+
expect(() =>
82+
configPatternsSchema.parse({
83+
persist: {
84+
format: 'json', // should be array
85+
},
86+
}),
87+
).toThrow(ZodError);
88+
});
89+
90+
it('should reject if upload is missing required fields', () => {
91+
expect(() =>
92+
configPatternsSchema.parse({
93+
persist: {
94+
outputDir: '.code-pushup/{projectName}',
95+
filename: 'report',
96+
format: ['json', 'md'],
97+
skipReports: false,
98+
},
99+
upload: {
100+
server: 'https://api.code-pushup.example.com/graphql',
101+
organization: 'example',
102+
project: '{projectName}',
103+
// missing apiKey
104+
},
105+
}),
106+
).toThrow(ZodError);
107+
});
108+
});

packages/ci/src/lib/settings.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { ZodError, z } from 'zod';
2+
import type { ConfigPatterns, Settings } from './models.js';
3+
import { configPatternsSchema } from './schemas.js';
4+
5+
export const DEFAULT_SETTINGS: Settings = {
6+
monorepo: false,
7+
parallel: false,
8+
projects: null,
9+
task: 'code-pushup',
10+
bin: 'npx --no-install code-pushup',
11+
config: null,
12+
directory: process.cwd(),
13+
silent: false,
14+
debug: false,
15+
detectNewIssues: true,
16+
logger: console,
17+
nxProjectsFilter: '--with-target={task}',
18+
skipComment: false,
19+
configPatterns: null,
20+
searchCommits: false,
21+
};
22+
23+
export const MIN_SEARCH_COMMITS = 1;
24+
export const MAX_SEARCH_COMMITS = 100;
25+
26+
export function parseConfigPatternsFromString(
27+
value: string,
28+
): ConfigPatterns | null {
29+
if (!value) {
30+
return null;
31+
}
32+
33+
try {
34+
const json = JSON.parse(value);
35+
return configPatternsSchema.parse(json);
36+
} catch (error) {
37+
if (error instanceof SyntaxError) {
38+
throw new TypeError(
39+
`Invalid JSON value for configPatterns input - ${error.message}`,
40+
);
41+
}
42+
if (error instanceof ZodError) {
43+
throw new TypeError(
44+
`Invalid shape of configPatterns input:\n${z.prettifyError(error)}`,
45+
);
46+
}
47+
throw error;
48+
}
49+
}

0 commit comments

Comments
 (0)