@rtorcato/repo-tooling 4.6.0 → 4.7.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/dist/base/fixers.js +2 -1
- package/dist/cli/commands/doctor.js +2 -2
- package/dist/cli/generators/brand.js +23 -0
- package/dist/cli/generators/docs-site.js +6 -0
- package/dist/cli/generators/github-actions.js +2 -2
- package/dist/languages/js/ci.js +20 -1
- package/dist/languages/js/fixers.js +2 -1
- package/package.json +2 -2
package/dist/base/fixers.js
CHANGED
|
@@ -15,7 +15,7 @@ import path from 'node:path';
|
|
|
15
15
|
import chalk from 'chalk';
|
|
16
16
|
import fs from 'fs-extra';
|
|
17
17
|
import { installAgentRules, installAiSetup } from '../cli/generators/agent-rules.js';
|
|
18
|
-
import { addReadmeBanner, generateBrand, renderBrand, resolveBrandMeta, } from '../cli/generators/brand.js';
|
|
18
|
+
import { addReadmeBanner, generateBrand, renderBrand, resolveBrandMeta, syncBrandToDocs, } from '../cli/generators/brand.js';
|
|
19
19
|
import { generateCommunityHealth } from '../cli/generators/community-health.js';
|
|
20
20
|
import { generateCommitlintConfig } from '../cli/generators/git.js';
|
|
21
21
|
import { generateCodeowners, generateEditorConfig } from '../cli/generators/misc.js';
|
|
@@ -274,6 +274,7 @@ export const BASE_FIXERS = [
|
|
|
274
274
|
const tagline = lock?.rules?.brand?.tagline;
|
|
275
275
|
const filesWritten = await generateBrand(pkg, targetDir, tagline);
|
|
276
276
|
filesWritten.push(...((await renderBrand(targetDir)) ?? []));
|
|
277
|
+
filesWritten.push(...(await syncBrandToDocs(targetDir)));
|
|
277
278
|
const { name } = await resolveBrandMeta(pkg, targetDir, tagline);
|
|
278
279
|
const readme = await addReadmeBanner(targetDir, name);
|
|
279
280
|
if (readme && !filesWritten.includes(readme))
|
|
@@ -2,7 +2,7 @@ import path from 'node:path';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import fs from 'fs-extra';
|
|
4
4
|
import { renderGitHubWorkflow } from '../../base/ci.js';
|
|
5
|
-
import { githubJobs, scriptsOf } from '../../languages/js/ci.js';
|
|
5
|
+
import { githubJobs, hasBin, scriptsOf } from '../../languages/js/ci.js';
|
|
6
6
|
import { inferProjectConfig } from '../../languages/js/fixers.js';
|
|
7
7
|
import { checkNpmTrustedPublisher, checkPublishJob, findNpmPublishJob, NPM_OIDC_CHECK, } from '../../languages/js/npm-trust.js';
|
|
8
8
|
import { PERL_GIT_HOOKS, runPerlChecks } from '../../languages/perl/checks.js';
|
|
@@ -391,7 +391,7 @@ export async function runDoctor(dir) {
|
|
|
391
391
|
},
|
|
392
392
|
// Same `scripts` gating the fixer applies, or the workflow doctor
|
|
393
393
|
// compares against would reference steps the fixer never writes (#364).
|
|
394
|
-
presetWorkflow: renderGitHubWorkflow(githubJobs(inferProjectConfig(pkg), { scripts: scriptsOf(pkg) })),
|
|
394
|
+
presetWorkflow: renderGitHubWorkflow(githubJobs(inferProjectConfig(pkg), { scripts: scriptsOf(pkg), bin: hasBin(pkg) })),
|
|
395
395
|
language,
|
|
396
396
|
codeqlLanguages: languageModule.codeqlLanguages,
|
|
397
397
|
})));
|
|
@@ -402,6 +402,29 @@ export async function renderBrand(targetDir) {
|
|
|
402
402
|
}
|
|
403
403
|
return written;
|
|
404
404
|
}
|
|
405
|
+
/** brand/ file → docs-site static/img file. The ico and card PNG exist only once rendered. */
|
|
406
|
+
const DOCS_ASSETS = ['favicon.svg', 'favicon.ico', 'social-card.png'];
|
|
407
|
+
/**
|
|
408
|
+
* Copy the brand favicon and social card into the docs site's `static/img`
|
|
409
|
+
* (#680). Copy-if-missing, and a no-op without `apps/docs`, so `fix brand` and
|
|
410
|
+
* `fix docs-site` reach the same tree in either order — each calls it.
|
|
411
|
+
*/
|
|
412
|
+
export async function syncBrandToDocs(targetDir) {
|
|
413
|
+
if (!(await fs.pathExists(path.join(targetDir, 'apps', 'docs'))))
|
|
414
|
+
return [];
|
|
415
|
+
const img = path.join('apps', 'docs', 'static', 'img');
|
|
416
|
+
const written = [];
|
|
417
|
+
for (const name of DOCS_ASSETS) {
|
|
418
|
+
const src = path.join(targetDir, 'brand', name);
|
|
419
|
+
const dest = path.join(targetDir, img, name);
|
|
420
|
+
if (!(await fs.pathExists(src)) || (await fs.pathExists(dest)))
|
|
421
|
+
continue;
|
|
422
|
+
await fs.ensureDir(path.dirname(dest));
|
|
423
|
+
await fs.copyFile(src, dest);
|
|
424
|
+
written.push(path.join(img, name));
|
|
425
|
+
}
|
|
426
|
+
return written;
|
|
427
|
+
}
|
|
405
428
|
export const BANNER_START = '<!-- js-tooling:banner:start -->';
|
|
406
429
|
export const BANNER_END = '<!-- js-tooling:banner:end -->';
|
|
407
430
|
/** The README `<picture>` banner, mobile variant under 640px, as a delimited block. */
|
|
@@ -6,6 +6,7 @@ import { coverageUploadWorkflow } from '../../base/checks.js';
|
|
|
6
6
|
import { jsBadgeAudience } from '../../languages/js/checks.js';
|
|
7
7
|
import { copyPreset, PRESETS } from '../utils/copy-preset.js';
|
|
8
8
|
import { buildBadgeRow, parseRepository } from './badges.js';
|
|
9
|
+
import { syncBrandToDocs } from './brand.js';
|
|
9
10
|
import { DOCS_SITE_BUILDS, mergeAllowBuilds } from './pnpm-workspace.js';
|
|
10
11
|
import { inferSubpathsFromExports } from './treeshake.js';
|
|
11
12
|
/**
|
|
@@ -176,6 +177,7 @@ ${typedocPlugins}\t\t[
|
|
|
176
177
|
\t],
|
|
177
178
|
|
|
178
179
|
\tthemeConfig: {
|
|
180
|
+
\t\timage: 'img/social-card.png',
|
|
179
181
|
\t\tcolorMode: {
|
|
180
182
|
\t\t\tdefaultMode: 'dark',
|
|
181
183
|
\t\t\trespectPrefersColorScheme: true,
|
|
@@ -427,6 +429,10 @@ export async function generateDocsSite(pkg, targetDir, options = {}) {
|
|
|
427
429
|
if (w)
|
|
428
430
|
written.push(w);
|
|
429
431
|
}
|
|
432
|
+
// static/img always exists (the config points at img/favicon.ico); the brand
|
|
433
|
+
// assets go in when brand/ has them (#680).
|
|
434
|
+
await fs.ensureDir(path.join(targetDir, DOCS_APP, 'static', 'img'));
|
|
435
|
+
written.push(...(await syncBrandToDocs(targetDir)));
|
|
430
436
|
const ws = await ensureWorkspace(targetDir);
|
|
431
437
|
if (ws)
|
|
432
438
|
written.push(ws);
|
|
@@ -31,7 +31,7 @@ export const CI_WORKFLOW = '.github/workflows/ci.yml';
|
|
|
31
31
|
* scripts itself as part of the same scaffold.
|
|
32
32
|
* @returns the files actually written, relative to targetDir.
|
|
33
33
|
*/
|
|
34
|
-
export async function generateGitHubActions(config, targetDir, { overwrite = false, scripts } = {}) {
|
|
34
|
+
export async function generateGitHubActions(config, targetDir, { overwrite = false, scripts, bin, } = {}) {
|
|
35
35
|
const workflowsDir = path.join(targetDir, '.github', 'workflows');
|
|
36
36
|
await fs.ensureDir(workflowsDir);
|
|
37
37
|
// This is the JS path specifically. Swift (#287) renders its own workflow
|
|
@@ -40,7 +40,7 @@ export async function generateGitHubActions(config, targetDir, { overwrite = fal
|
|
|
40
40
|
// shared entry point would mean inventing a fake config to pass in. Both
|
|
41
41
|
// paths meet at renderGitHubWorkflow() in src/base/ci.ts, which is the seam
|
|
42
42
|
// that actually matters.
|
|
43
|
-
const workflow = renderGitHubWorkflow(githubJobs(config, { scripts }));
|
|
43
|
+
const workflow = renderGitHubWorkflow(githubJobs(config, { scripts, bin }));
|
|
44
44
|
const ciPath = path.join(workflowsDir, 'ci.yml');
|
|
45
45
|
const existing = (await fs.pathExists(ciPath)) ? await fs.readFile(ciPath, 'utf-8') : null;
|
|
46
46
|
const filesWritten = [];
|
package/dist/languages/js/ci.js
CHANGED
|
@@ -73,6 +73,10 @@ const DEPENDENCIES_JOB = {
|
|
|
73
73
|
- name: 📦 Install dependencies
|
|
74
74
|
run: pnpm install --frozen-lockfile`,
|
|
75
75
|
};
|
|
76
|
+
/** `JobOptions.bin` for a real repo. */
|
|
77
|
+
export function hasBin(pkg) {
|
|
78
|
+
return Boolean(pkg?.bin);
|
|
79
|
+
}
|
|
76
80
|
/**
|
|
77
81
|
* `JobOptions.scripts` for a real repo. A package.json with no `scripts` block
|
|
78
82
|
* yields `{}` — "this repo has no scripts", which gates every step off —
|
|
@@ -160,6 +164,21 @@ export function githubJobs(config, opts = {}) {
|
|
|
160
164
|
const publint = config.publint
|
|
161
165
|
? '\n - name: 🔍 Validate package with publint\n run: pnpm exec publint --strict\n'
|
|
162
166
|
: '';
|
|
167
|
+
// npm's own `bin[…] invalid and removed` warning and a bin pnpm never
|
|
168
|
+
// links both hide until the tarball is installed somewhere else (#693).
|
|
169
|
+
const packSmoke = opts.bin
|
|
170
|
+
? `
|
|
171
|
+
- name: 📦 Smoke-test the packed tarball
|
|
172
|
+
run: |
|
|
173
|
+
set -e
|
|
174
|
+
BIN=$(node -p "const p=require('./package.json');typeof p.bin==='string'?p.name.split('/').pop():Object.keys(p.bin)[0]")
|
|
175
|
+
TARBALL="$PWD/$(npm pack --silent | tail -n1)"
|
|
176
|
+
tar -tzf "$TARBALL" | grep -q . || { echo "::error::npm pack produced an empty tarball"; exit 1; }
|
|
177
|
+
SMOKE=$(mktemp -d)
|
|
178
|
+
cd "$SMOKE" && npm init -y >/dev/null && npm install "$TARBALL"
|
|
179
|
+
./node_modules/.bin/"$BIN" --version
|
|
180
|
+
`
|
|
181
|
+
: '';
|
|
163
182
|
jobs.push({
|
|
164
183
|
id: 'build',
|
|
165
184
|
needs: ['dependencies'],
|
|
@@ -167,7 +186,7 @@ export function githubJobs(config, opts = {}) {
|
|
|
167
186
|
|
|
168
187
|
- name: 🏗️ Build project
|
|
169
188
|
run: pnpm build
|
|
170
|
-
${attw}${publint}
|
|
189
|
+
${attw}${publint}${packSmoke}
|
|
171
190
|
- name: 📦 Upload build artifacts
|
|
172
191
|
uses: actions/upload-artifact@v7
|
|
173
192
|
with:
|
|
@@ -9,7 +9,7 @@ import { generateGitLabCI } from '../../cli/generators/gitlab-ci.js';
|
|
|
9
9
|
import { GH_WORKFLOWS, generateGhWorkflow } from '../../cli/generators/github-workflows.js';
|
|
10
10
|
import { BIOME_CONFIG, generateBiomeConfig, generateESLintConfig, generatePrettierConfig, } from '../../cli/generators/linting.js';
|
|
11
11
|
import { alignNodeVersion, ensureEnginesNode, ensurePackageManager, generateKnipConfig, generateNvmrc, generateSizeLimitConfig, generateVscodeExtensions, } from '../../cli/generators/misc.js';
|
|
12
|
-
import { scriptsOf } from './ci.js';
|
|
12
|
+
import { hasBin, scriptsOf } from './ci.js';
|
|
13
13
|
import { NPM_OIDC_CHECK, NPM_TRUST_FIXER } from './npm-trust.js';
|
|
14
14
|
import { usesPnpm } from './checks.js';
|
|
15
15
|
import { SIZE_LIMIT_SCRIPTS, SIZE_LIMIT_VERSION, composeVerifyScriptFromPkg, ensureScripts, } from '../../cli/generators/package-json.js';
|
|
@@ -451,6 +451,7 @@ export const FIXERS = [
|
|
|
451
451
|
overwrite: result.check === 'GitHub Actions',
|
|
452
452
|
// Only reference scripts this repo actually has (#364).
|
|
453
453
|
scripts: scriptsOf(pkg),
|
|
454
|
+
bin: hasBin(pkg),
|
|
454
455
|
});
|
|
455
456
|
// `pnpm/action-setup` is emitted without a `version:` input, so without
|
|
456
457
|
// this every job dies at setup with "No pnpm version is specified".
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rtorcato/repo-tooling",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"description": "One CLI to scaffold, audit and fix your repo's whole toolchain — linting, tests, commits, releases & CI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -248,7 +248,7 @@
|
|
|
248
248
|
"@next/eslint-plugin-next": "^16.3.2",
|
|
249
249
|
"@playwright/test": "^1.62.1",
|
|
250
250
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
251
|
-
"@rtorcato/repo-ai": "^3.1.
|
|
251
|
+
"@rtorcato/repo-ai": "^3.1.2",
|
|
252
252
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
253
253
|
"@semantic-release/exec": "^7.1.0",
|
|
254
254
|
"@semantic-release/github": "^12.0.9",
|