monecromanci 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -0
- package/dist/assets/azure-pipelines.yml +33 -0
- package/dist/assets/build-templates/01-preparation.mjs +174 -0
- package/dist/assets/build-templates/01-preparation.yml +51 -0
- package/dist/assets/build-templates/02-quality-control.mjs +32 -0
- package/dist/assets/build-templates/02-quality-control.yml +24 -0
- package/dist/assets/build-templates/03-package-apps.mjs +573 -0
- package/dist/assets/build-templates/03-package-apps.yml +37 -0
- package/dist/assets/build-templates/04-publish-libs.mjs +155 -0
- package/dist/assets/build-templates/04-publish-libs.yml +20 -0
- package/dist/assets/build-templates/05-publish-documentation.mjs +88 -0
- package/dist/assets/build-templates/05-publish-documentation.yml +20 -0
- package/dist/assets/build-templates/06-summary.mjs +463 -0
- package/dist/assets/build-templates/06-summary.yml +4 -0
- package/dist/assets/build-templates/README.md +69 -0
- package/dist/assets/build-templates/lib/_h.mjs +299 -0
- package/dist/assets/build-templates/lib/context.mjs +359 -0
- package/dist/assets/build-templates/lib/nx.mjs +254 -0
- package/dist/assets/eslint.config.mjs +306 -0
- package/dist/assets/github/workflows/ci.yml +67 -0
- package/dist/assets/scripts/clean-config.mjs +35 -0
- package/dist/assets/scripts/generate-dist-package.mjs +205 -0
- package/dist/assets/scripts/next-build.mjs +37 -0
- package/dist/cli.js +2466 -0
- package/dist/cli.js.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# MoNecromanCI
|
|
2
|
+
|
|
3
|
+
> **MO**no(repo) + **NECROMAN**cy + **CI**. The CLI command is `monecromanci` (short alias `mnci`).
|
|
4
|
+
|
|
5
|
+
An interactive CLI that **summons**, **conjures**, **raises** and **validates** NX
|
|
6
|
+
monorepos — Node + TypeScript, Jest, ESLint, real VSCode `.ts` debugging, and a
|
|
7
|
+
complete CI pipeline (Azure DevOps **and/or** GitHub Actions), with near-zero
|
|
8
|
+
per-project configuration.
|
|
9
|
+
|
|
10
|
+
It generates nine project kinds and keeps every repo's tool-owned config in sync:
|
|
11
|
+
|
|
12
|
+
| Kind | What you get |
|
|
13
|
+
| ----------------- | ----------------------------------------------------------------------- |
|
|
14
|
+
| `internal-lib` | Source-resolved (`main → src/index.ts`) so you step into it while debugging and "find references" works across libs. |
|
|
15
|
+
| `publishable-lib` | Published via `nx release`; `dist/package.json` gets **real, resolved dependencies** even though all deps live in the root. |
|
|
16
|
+
| `cli-tool` | A publishable lib that also ships a bundled `bin` (esbuild + shebang). |
|
|
17
|
+
| `function-app` | Azure Functions v4, `.configurations/{dev,uat,prod}.json`, `clean:config` whitespace-strip, attach-debugging. |
|
|
18
|
+
| `node-app` | A framework-agnostic TS HTTP server (node:http) you extend with Express/Koa/Fastify/Nest/…; built, dependency-traced and zipped like a function app. |
|
|
19
|
+
| `react-app` | Vite with `dev`/`uat`/`prod` builds (`dist-dev`/`dist-uat`/`dist-prod`) and browser debugging. |
|
|
20
|
+
| `vue-app` | Vue 3 + Vite, same multi-env builds. |
|
|
21
|
+
| `svelte-app` | Svelte 5 + Vite, same multi-env builds. |
|
|
22
|
+
| `nextjs-app` | Full-stack Next.js (App Router). Per-env builds assemble `dist-<env>` in **server** (standalone) or **static-export** mode (`NEXT_OUTPUT`). |
|
|
23
|
+
|
|
24
|
+
## Commands
|
|
25
|
+
|
|
26
|
+
Every command keeps its plain name and gains a necromancy-themed alias (in the
|
|
27
|
+
comment below) — use whichever reads better to you:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
monecromanci new [name] # summon · scaffold a brand-new monorepo (prompts: CI provider, registry, scope, …)
|
|
31
|
+
monecromanci add [type] # conjure · internal-lib | publishable-lib | cli-tool | function-app | node-app | react-app | vue-app | svelte-app | nextjs-app
|
|
32
|
+
monecromanci doctor [--fix] # raise · detect (and with --fix, repair) tool-owned config drift
|
|
33
|
+
monecromanci update # ascend · doctor --fix + re-stamp the template version
|
|
34
|
+
monecromanci validate [--all] # ritual · run lint/test/build locally (nx affected; --all = run-many) before pushing to CI
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`new` is fully scriptable: `monecromanci new demo --yes --ci github --registry github-packages --owner acme`.
|
|
38
|
+
|
|
39
|
+
## CI providers & registry (chosen per repo)
|
|
40
|
+
|
|
41
|
+
`new` prompts for a **CI provider** — `azure`, `github`, or `both` — and a **package
|
|
42
|
+
registry** — Azure Artifacts, GitHub Packages, or public npm (defaulting to match
|
|
43
|
+
the CI). The `.build-templates/*.mjs` are the single engine for **both** providers;
|
|
44
|
+
only a thin wrapper differs: `azure-pipelines.yml` and/or `.github/workflows/ci.yml`.
|
|
45
|
+
The `.npmrc`, each publishable project's `publishConfig`, and the nx-release docs
|
|
46
|
+
are generated to match the registry.
|
|
47
|
+
|
|
48
|
+
## What's centralised
|
|
49
|
+
|
|
50
|
+
One root `package.json` holds **all** dependencies. The root owns `nx.json`
|
|
51
|
+
(with `nx release`), `tsconfig.base.json`, `tsconfig.jest.json`, a Jest preset
|
|
52
|
+
factory, a **non-type-checked** ESLint flat config (standard/no-semi + @stylistic
|
|
53
|
+
+ unicorn + React + Jest + TSDoc + JSON/JSONC/JSON5 + YAML + Markdown), the
|
|
54
|
+
`.code-workspace`, and a vendored CI pipeline. Per-project config is 2–4 tiny files.
|
|
55
|
+
|
|
56
|
+
## Debugging (works on the TypeScript, including into libs)
|
|
57
|
+
|
|
58
|
+
The `.code-workspace` ships **breakpoint-capable** configs at the workspace top
|
|
59
|
+
level (where VSCode actually reads them): "Debug Jest (current file)"
|
|
60
|
+
(`--runInBand`, `resolveSourceMapLocations: null`, source maps on), a Function/Node
|
|
61
|
+
App attach config (`:9229`), and browser configs for Vite (`:5173`) and Next.js
|
|
62
|
+
(`:3000`) — plus the `Orta.vscode-jest` extension for per-test Debug lenses.
|
|
63
|
+
|
|
64
|
+
## Developing MoNecromanCI
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
npm install --legacy-peer-deps # ESLint 10 leads some plugins' peer ranges
|
|
68
|
+
npm run build # tsup bundle + copy assets to dist/
|
|
69
|
+
npm run lint
|
|
70
|
+
npm test
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Verify the generated output end-to-end
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
node dist/cli.js new demo --yes --registry npm --lib helpers
|
|
77
|
+
cd demo
|
|
78
|
+
node ../dist/cli.js add nextjs-app web # or any other kind
|
|
79
|
+
npm install
|
|
80
|
+
npm run lint && npm test && npm run build
|
|
81
|
+
node ../dist/cli.js validate # (ritual) nx affected -t lint test build
|
|
82
|
+
# In VSCode: open demo.code-workspace, set a breakpoint in a *.test.ts, run
|
|
83
|
+
# "Debug Jest (current file)" → it should pause on the breakpoint.
|
|
84
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: monorepo-ci-$(Date:yyyyMMdd)$(Rev:.r)
|
|
2
|
+
|
|
3
|
+
# Generated by MoNecromanCI. Re-sync the build-templates with 'monecromanci doctor'.
|
|
4
|
+
trigger:
|
|
5
|
+
branches:
|
|
6
|
+
include: [dev, development, uat, master, main]
|
|
7
|
+
paths:
|
|
8
|
+
exclude: [docs/**, "**/*.md"]
|
|
9
|
+
|
|
10
|
+
pr:
|
|
11
|
+
branches:
|
|
12
|
+
include: [dev, development, uat, master, main]
|
|
13
|
+
paths:
|
|
14
|
+
exclude: [docs/**, "**/*.md"]
|
|
15
|
+
|
|
16
|
+
pool:
|
|
17
|
+
name: AzurePipelineManagedPool-Windows
|
|
18
|
+
demands:
|
|
19
|
+
- npm
|
|
20
|
+
|
|
21
|
+
variables:
|
|
22
|
+
# Set to an Azure Resource Manager service connection to publish TypeDoc to a
|
|
23
|
+
# Storage blob; leave empty to skip documentation publishing.
|
|
24
|
+
- name: docsAzureSubscription
|
|
25
|
+
value: ""
|
|
26
|
+
|
|
27
|
+
steps:
|
|
28
|
+
- template: .build-templates/01-preparation.yml
|
|
29
|
+
- template: .build-templates/02-quality-control.yml
|
|
30
|
+
- template: .build-templates/03-package-apps.yml
|
|
31
|
+
- template: .build-templates/04-publish-libs.yml
|
|
32
|
+
- template: .build-templates/05-publish-documentation.yml
|
|
33
|
+
- template: .build-templates/06-summary.yml
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Step 01 — Preparation.
|
|
5
|
+
*
|
|
6
|
+
* Resolves the git range, the Nx affected project set, classifies every project
|
|
7
|
+
* from its tags, and writes a reusable context manifest consumed by every later
|
|
8
|
+
* step. Also emits the pipeline variables used for step gating and prints a
|
|
9
|
+
* human-readable execution plan so a run can be understood at a glance.
|
|
10
|
+
*
|
|
11
|
+
* Run locally with `npm run pipeline:plan` (after `npm ci`) to preview the plan
|
|
12
|
+
* without pushing.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import process from 'node:process'
|
|
16
|
+
import {
|
|
17
|
+
banner,
|
|
18
|
+
log,
|
|
19
|
+
section,
|
|
20
|
+
setBuildNumber,
|
|
21
|
+
setVariables,
|
|
22
|
+
table,
|
|
23
|
+
writeJson,
|
|
24
|
+
} from './lib/_h.mjs'
|
|
25
|
+
import {
|
|
26
|
+
buildContextManifest,
|
|
27
|
+
CONTEXT_FILE_PATH,
|
|
28
|
+
describeProjectAction,
|
|
29
|
+
describeProjectType,
|
|
30
|
+
enrichProject,
|
|
31
|
+
} from './lib/context.mjs'
|
|
32
|
+
import {
|
|
33
|
+
resolveAffectedProjects,
|
|
34
|
+
resolveAllProjects,
|
|
35
|
+
resolveBaseCommit,
|
|
36
|
+
resolveEffectiveBranchName,
|
|
37
|
+
resolveHeadCommit,
|
|
38
|
+
resolveProjectMetadata,
|
|
39
|
+
} from './lib/nx.mjs'
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Resolves and enriches every project in the workspace.
|
|
43
|
+
*
|
|
44
|
+
* @param {string[]} projectNames All Nx project names.
|
|
45
|
+
* @param {Set<string>} affectedSet The affected project names.
|
|
46
|
+
* @param {string} branchName The effective branch name.
|
|
47
|
+
* @returns {Array<Record<string, any>>} Returns enriched project data.
|
|
48
|
+
*/
|
|
49
|
+
function resolvePipelineProjects (projectNames, affectedSet, branchName) {
|
|
50
|
+
const projects = []
|
|
51
|
+
|
|
52
|
+
for (const projectName of [...projectNames].sort((left, right) => left.localeCompare(right))) {
|
|
53
|
+
const metadata = resolveProjectMetadata(projectName)
|
|
54
|
+
if (!metadata) {
|
|
55
|
+
log(`[projects] Skipping ${projectName}: no Nx metadata resolved`)
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
projects.push(enrichProject(metadata, affectedSet.has(projectName), branchName))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return projects
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Resolves the build number from releasable project versions.
|
|
67
|
+
*
|
|
68
|
+
* @param {Record<string, any>} context The context manifest.
|
|
69
|
+
* @returns {string} Returns the build number.
|
|
70
|
+
*/
|
|
71
|
+
function resolveBuildNumber (context) {
|
|
72
|
+
const releasable = context.projects
|
|
73
|
+
.filter(project => !project.ignored)
|
|
74
|
+
.filter(project => project.type.functionApp || project.type.reactApp || project.type.externalPackage)
|
|
75
|
+
.filter(project => project.affected && project.version)
|
|
76
|
+
|
|
77
|
+
if (releasable.length === 0) {
|
|
78
|
+
return process.env.BUILD_BUILDNUMBER || 'monorepo-no-affected'
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return releasable.map(project => `${project.name}_${project.version}`).join('-')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Builds the pipeline variables emitted for step gating.
|
|
86
|
+
*
|
|
87
|
+
* @param {Record<string, any>} context The context manifest.
|
|
88
|
+
* @returns {{name: string, value: string}[]} Returns the pipeline variables.
|
|
89
|
+
*/
|
|
90
|
+
function buildPipelineVariables (context) {
|
|
91
|
+
const variables = [
|
|
92
|
+
{ name: 'MONOREPO_CONTEXT_FILE', value: CONTEXT_FILE_PATH },
|
|
93
|
+
{ name: 'NX_BASE', value: context.baseCommit },
|
|
94
|
+
{ name: 'NX_HEAD', value: context.headCommit },
|
|
95
|
+
{ name: 'HAS_AFFECTED', value: String(context.hasAffected) },
|
|
96
|
+
{ name: 'AFFECTED_PROJECTS', value: context.affectedProjects.join(',') },
|
|
97
|
+
{ name: 'FUNCTION_APPS', value: context.groups.functionApps.join(',') },
|
|
98
|
+
{ name: 'NODE_APPS', value: context.groups.nodeApps.join(',') },
|
|
99
|
+
{ name: 'REACT_APPS', value: context.groups.reactApps.join(',') },
|
|
100
|
+
{ name: 'INTERNAL_PACKAGES', value: context.groups.internalPackages.join(',') },
|
|
101
|
+
{ name: 'EXTERNAL_PACKAGES', value: context.groups.externalPackages.join(',') },
|
|
102
|
+
{ name: 'IGNORED_PROJECTS', value: context.groups.ignoredProjects.join(',') },
|
|
103
|
+
{ name: 'HAS_FUNCTION_APPS', value: String(context.groups.functionApps.length > 0) },
|
|
104
|
+
{ name: 'HAS_NODE_APPS', value: String(context.groups.nodeApps.length > 0) },
|
|
105
|
+
{ name: 'HAS_REACT_APPS', value: String(context.groups.reactApps.length > 0) },
|
|
106
|
+
{ name: 'HAS_INTERNAL_PACKAGES', value: String(context.groups.internalPackages.length > 0) },
|
|
107
|
+
{ name: 'HAS_PUBLISHABLE_LIBS', value: String(context.groups.externalPackages.length > 0) },
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
for (const project of context.projects) {
|
|
111
|
+
variables.push({ name: `HAS_${project.sanitizedName}`, value: String(project.affected && !project.ignored) })
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return variables
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Prints the execution plan derived from the context manifest.
|
|
119
|
+
*
|
|
120
|
+
* @param {Record<string, any>} context The context manifest.
|
|
121
|
+
*/
|
|
122
|
+
function printExecutionPlan (context) {
|
|
123
|
+
section('Execution plan')
|
|
124
|
+
|
|
125
|
+
table(context.projects.map(project => ({
|
|
126
|
+
project: project.name,
|
|
127
|
+
type: describeProjectType(project),
|
|
128
|
+
affected: project.affected && !project.ignored ? 'yes' : 'no',
|
|
129
|
+
version: project.version || 'n/a',
|
|
130
|
+
action: project.affected && !project.ignored ? describeProjectAction(project) : '—',
|
|
131
|
+
})))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Runs preparation discovery and emits the reusable pipeline context.
|
|
136
|
+
*/
|
|
137
|
+
function main () {
|
|
138
|
+
banner('[01] Preparation — resolving monorepo context')
|
|
139
|
+
log(`Platform: ${process.platform}, cwd: ${process.cwd()}`)
|
|
140
|
+
|
|
141
|
+
const branchName = resolveEffectiveBranchName()
|
|
142
|
+
const headCommit = resolveHeadCommit()
|
|
143
|
+
const baseCommit = resolveBaseCommit(headCommit)
|
|
144
|
+
log(`Branch: ${branchName || '(unknown)'}`)
|
|
145
|
+
log(`Range: ${baseCommit} .. ${headCommit}`)
|
|
146
|
+
|
|
147
|
+
const affectedProjects = resolveAffectedProjects(baseCommit, headCommit)
|
|
148
|
+
const affectedSet = new Set(affectedProjects)
|
|
149
|
+
log(`Affected (${affectedProjects.length}): ${affectedProjects.join(', ') || '(none)'}`)
|
|
150
|
+
|
|
151
|
+
const projectNames = resolveAllProjects()
|
|
152
|
+
log(`All projects (${projectNames.length}): ${projectNames.join(', ')}`)
|
|
153
|
+
|
|
154
|
+
const projects = resolvePipelineProjects(projectNames, affectedSet, branchName)
|
|
155
|
+
const context = buildContextManifest({ baseCommit, branchName, headCommit, projects })
|
|
156
|
+
|
|
157
|
+
writeJson(CONTEXT_FILE_PATH, context)
|
|
158
|
+
log(`Context written: ${CONTEXT_FILE_PATH}`)
|
|
159
|
+
|
|
160
|
+
printExecutionPlan(context)
|
|
161
|
+
|
|
162
|
+
const buildNumber = resolveBuildNumber(context)
|
|
163
|
+
setBuildNumber(buildNumber)
|
|
164
|
+
log(`Build number: ${buildNumber}`)
|
|
165
|
+
|
|
166
|
+
section('Pipeline variables')
|
|
167
|
+
setVariables(buildPipelineVariables(context))
|
|
168
|
+
|
|
169
|
+
banner(context.hasAffected
|
|
170
|
+
? `[01] Preparation complete — ${context.affectedProjects.length} affected project(s)`
|
|
171
|
+
: '[01] Preparation complete — no affected projects, downstream steps will skip')
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
main()
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
steps:
|
|
2
|
+
- checkout: self
|
|
3
|
+
fetchDepth: 0
|
|
4
|
+
persistCredentials: true
|
|
5
|
+
|
|
6
|
+
- task: PowerShell@2
|
|
7
|
+
displayName: "[01] Fetch all refs for affected detection"
|
|
8
|
+
inputs:
|
|
9
|
+
targetType: inline
|
|
10
|
+
script: |
|
|
11
|
+
Write-Host "=== Fetching all refs and tags ===" -ForegroundColor Cyan
|
|
12
|
+
git fetch --all --prune --tags
|
|
13
|
+
Write-Host "=== Recent commits ===" -ForegroundColor Cyan
|
|
14
|
+
git log --oneline -10
|
|
15
|
+
|
|
16
|
+
- task: UseNode@1
|
|
17
|
+
displayName: "[01] Use Node.js 24"
|
|
18
|
+
inputs:
|
|
19
|
+
version: 24.x
|
|
20
|
+
|
|
21
|
+
- task: PowerShell@2
|
|
22
|
+
displayName: "[01] Ensure npm cache directory exists"
|
|
23
|
+
inputs:
|
|
24
|
+
targetType: inline
|
|
25
|
+
script: New-Item -ItemType Directory -Path "$(Pipeline.Workspace)/.npm" -Force | Out-Null
|
|
26
|
+
|
|
27
|
+
- task: Cache@2
|
|
28
|
+
displayName: "[01] Restore npm cache"
|
|
29
|
+
inputs:
|
|
30
|
+
key: npm | "$(Agent.OS)" | $(Build.SourcesDirectory)/package-lock.json
|
|
31
|
+
restoreKeys: |
|
|
32
|
+
npm | "$(Agent.OS)"
|
|
33
|
+
path: $(Pipeline.Workspace)/.npm
|
|
34
|
+
|
|
35
|
+
# Authenticate against the registries declared in the repo's .npmrc. This
|
|
36
|
+
# injects credentials so the plain `npm ci` below works for any feed/registry.
|
|
37
|
+
- task: npmAuthenticate@0
|
|
38
|
+
displayName: "[01] Authenticate npm registry"
|
|
39
|
+
inputs:
|
|
40
|
+
workingFile: .npmrc
|
|
41
|
+
|
|
42
|
+
# Dependencies are installed BEFORE affected detection so Nx computes the
|
|
43
|
+
# project graph with the workspace's pinned Nx version (never a downloaded one).
|
|
44
|
+
- script: npm ci
|
|
45
|
+
displayName: "[01] Install monorepo dependencies"
|
|
46
|
+
workingDirectory: $(Build.SourcesDirectory)
|
|
47
|
+
env:
|
|
48
|
+
HUSKY: 0
|
|
49
|
+
|
|
50
|
+
- script: node .build-templates/01-preparation.mjs
|
|
51
|
+
displayName: "[01] Resolve context, affected projects and execution plan"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Step 02 — Quality control.
|
|
5
|
+
*
|
|
6
|
+
* Runs lint, test and build for the affected projects over the resolved git
|
|
7
|
+
* range. Kept as a Node step (rather than a repo `qa` npm script) so the
|
|
8
|
+
* template stays self-contained — a consuming repo only needs the standard Nx
|
|
9
|
+
* `lint`/`test`/`build` targets, defined either in `project.json` or via the
|
|
10
|
+
* `@nx/*` inference plugins.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import process from 'node:process'
|
|
14
|
+
import { banner, shellEscape } from './lib/_h.mjs'
|
|
15
|
+
import { runNxInherit } from './lib/nx.mjs'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Runs lint, test and build for the affected project set.
|
|
19
|
+
*/
|
|
20
|
+
function main () {
|
|
21
|
+
banner('[02] Quality control — lint, test and build affected projects')
|
|
22
|
+
|
|
23
|
+
const base = process.env.NX_BASE || ''
|
|
24
|
+
const head = process.env.NX_HEAD || ''
|
|
25
|
+
const range = base && head ? ` --base=${shellEscape(base)} --head=${shellEscape(head)}` : ''
|
|
26
|
+
|
|
27
|
+
runNxInherit(`affected -t lint,test,build${range} --outputStyle=static`)
|
|
28
|
+
|
|
29
|
+
banner('[02] Quality control complete')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
main()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
steps:
|
|
2
|
+
- script: node .build-templates/02-quality-control.mjs
|
|
3
|
+
displayName: "[02] Run affected projects lint, test and build"
|
|
4
|
+
condition: and(succeeded(), eq(variables['HAS_AFFECTED'], 'true'))
|
|
5
|
+
|
|
6
|
+
- task: PublishTestResults@2
|
|
7
|
+
displayName: "[02] Publish affected projects test results"
|
|
8
|
+
condition: and(succeededOrFailed(), eq(variables['HAS_AFFECTED'], 'true'))
|
|
9
|
+
inputs:
|
|
10
|
+
testResultsFormat: JUnit
|
|
11
|
+
testResultsFiles: "**/coverage/test-results.xml"
|
|
12
|
+
searchFolder: $(Build.SourcesDirectory)
|
|
13
|
+
mergeTestResults: true
|
|
14
|
+
failTaskOnFailedTests: true
|
|
15
|
+
testRunTitle: Affected Tests
|
|
16
|
+
|
|
17
|
+
- task: PublishCodeCoverageResults@2
|
|
18
|
+
displayName: "[02] Publish affected projects coverage results"
|
|
19
|
+
condition: and(succeededOrFailed(), eq(variables['HAS_AFFECTED'], 'true'))
|
|
20
|
+
inputs:
|
|
21
|
+
summaryFileLocation: "$(Build.SourcesDirectory)/**/coverage/cobertura-coverage.xml"
|
|
22
|
+
pathToSources: $(Build.SourcesDirectory)
|
|
23
|
+
reportDirectory: "$(Build.SourcesDirectory)/**/coverage"
|
|
24
|
+
failIfCoverageEmpty: false
|