From 6ed693bfac28698c4ac465898b7bf6afa6464b29 Mon Sep 17 00:00:00 2001 From: Aaron Moat <2937187+AaronMoat@users.noreply.github.com> Date: Wed, 7 Feb 2024 13:41:13 +1100 Subject: [PATCH] Manage .dockerignore (#1433) --- .changeset/mighty-brooms-shout.md | 7 + .dockerignore | 1 - src/cli/__snapshots__/format.int.test.ts.snap | 16 +- src/cli/__snapshots__/lint.int.test.ts.snap | 4 +- .../configure/upgrade/patches/7.3.1/index.ts | 8 +- ...eNpmrcOutOfGitignoreManagedSection.test.ts | 169 --------------- ...moveNpmrcOutOfIgnoreManagedSection.test.ts | 205 ++++++++++++++++++ ... => moveNpmrcOutOfIgnoreManagedSection.ts} | 41 ++-- .../internalLints/refreshConfigFiles.test.ts | 89 ++++---- .../lint/internalLints/refreshConfigFiles.ts | 42 ++-- 10 files changed, 318 insertions(+), 264 deletions(-) create mode 100644 .changeset/mighty-brooms-shout.md delete mode 100644 src/cli/configure/upgrade/patches/7.3.1/moveNpmrcOutOfGitignoreManagedSection.test.ts create mode 100644 src/cli/configure/upgrade/patches/7.3.1/moveNpmrcOutOfIgnoreManagedSection.test.ts rename src/cli/configure/upgrade/patches/7.3.1/{moveNpmrcOutOfGitignoreManagedSection.ts => moveNpmrcOutOfIgnoreManagedSection.ts} (68%) diff --git a/.changeset/mighty-brooms-shout.md b/.changeset/mighty-brooms-shout.md new file mode 100644 index 000000000..3032b2c51 --- /dev/null +++ b/.changeset/mighty-brooms-shout.md @@ -0,0 +1,7 @@ +--- +'skuba': minor +--- + +lint: Manage `.dockerignore` + +skuba now manages a section of `.dockerignore` for you, ensuring that the file is up to date with the latest enhancements in skuba. diff --git a/.dockerignore b/.dockerignore index bd18476d0..d428ab05d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -12,7 +12,6 @@ node_modules*/ /tmp*/ .DS_Store -.npmrc npm-debug.log yarn-error.log # end managed by skuba diff --git a/src/cli/__snapshots__/format.int.test.ts.snap b/src/cli/__snapshots__/format.int.test.ts.snap index b285d5be4..8d6e6ca3a 100644 --- a/src/cli/__snapshots__/format.int.test.ts.snap +++ b/src/cli/__snapshots__/format.int.test.ts.snap @@ -15,11 +15,14 @@ Patch skipped: Add keepAliveTimeout to server listener - no listener file found Patch skipped: Move .npmrc out of the .gitignore managed section - no .gitignore file found +Patch skipped: Move .npmrc out of the .dockerignore managed section - no .dockerignore file found + skuba update complete. Refreshed .eslintignore. refresh-config-files Refreshed .gitignore. refresh-config-files Refreshed .prettierignore. refresh-config-files +Refreshed .dockerignore. refresh-config-files Processed skuba lints in s. ESLint @@ -84,11 +87,14 @@ Patch skipped: Add keepAliveTimeout to server listener - no listener file found Patch skipped: Move .npmrc out of the .gitignore managed section - no .gitignore file found +Patch skipped: Move .npmrc out of the .dockerignore managed section - no .dockerignore file found + skuba update complete. Refreshed .eslintignore. refresh-config-files Refreshed .gitignore. refresh-config-files Refreshed .prettierignore. refresh-config-files +Refreshed .dockerignore. refresh-config-files Processed skuba lints in s. ESLint @@ -102,8 +108,10 @@ Prettier Initialising Prettier... Detected project root: Discovering files... -Discovered 9 files. +Discovered 10 files. Formatting files... +.dockerignore + parser: - .eslintignore parser: - .gitignore @@ -146,11 +154,14 @@ Patch skipped: Add keepAliveTimeout to server listener - no listener file found Patch skipped: Move .npmrc out of the .gitignore managed section - no .gitignore file found +Patch skipped: Move .npmrc out of the .dockerignore managed section - no .dockerignore file found + skuba update complete. Refreshed .eslintignore. refresh-config-files Refreshed .gitignore. refresh-config-files Refreshed .prettierignore. refresh-config-files +Refreshed .dockerignore. refresh-config-files Processed skuba lints in s. ESLint @@ -181,11 +192,14 @@ Patch skipped: Add keepAliveTimeout to server listener - no listener file found Patch skipped: Move .npmrc out of the .gitignore managed section - no .gitignore file found +Patch skipped: Move .npmrc out of the .dockerignore managed section - no .dockerignore file found + skuba update complete. Refreshed .eslintignore. refresh-config-files Refreshed .gitignore. refresh-config-files Refreshed .prettierignore. refresh-config-files +Refreshed .dockerignore. refresh-config-files Processed skuba lints in s. ESLint diff --git a/src/cli/__snapshots__/lint.int.test.ts.snap b/src/cli/__snapshots__/lint.int.test.ts.snap index 6affc0f02..dbd0f70a6 100644 --- a/src/cli/__snapshots__/lint.int.test.ts.snap +++ b/src/cli/__snapshots__/lint.int.test.ts.snap @@ -106,8 +106,10 @@ ESLint │ ○ d.js Prettier │ Initialising Prettier... Prettier │ Detected project root: Prettier │ Discovering files... -Prettier │ Discovered 9 files. +Prettier │ Discovered 10 files. Prettier │ Linting files... +Prettier │ .dockerignore +Prettier │ parser: - Prettier │ .eslintignore Prettier │ parser: - Prettier │ .gitignore diff --git a/src/cli/configure/upgrade/patches/7.3.1/index.ts b/src/cli/configure/upgrade/patches/7.3.1/index.ts index 7bd8aec9a..25f0aa947 100644 --- a/src/cli/configure/upgrade/patches/7.3.1/index.ts +++ b/src/cli/configure/upgrade/patches/7.3.1/index.ts @@ -2,7 +2,7 @@ import type { Patches } from '../..'; import { tryPatchRenovateConfig } from '../../../patchRenovateConfig'; import { tryAddEmptyExports } from './addEmptyExports'; -import { tryMoveNpmrcOutOfGitignoreManagedSection } from './moveNpmrcOutOfGitignoreManagedSection'; +import { tryMoveNpmrcOutOfIgnoreManagedSection } from './moveNpmrcOutOfIgnoreManagedSection'; import { tryPatchDockerfile } from './patchDockerfile'; import { tryPatchServerListener } from './patchServerListener'; @@ -25,7 +25,11 @@ export const patches: Patches = [ description: 'Add keepAliveTimeout to server listener', }, { - apply: tryMoveNpmrcOutOfGitignoreManagedSection, + apply: tryMoveNpmrcOutOfIgnoreManagedSection('.gitignore'), description: 'Move .npmrc out of the .gitignore managed section', }, + { + apply: tryMoveNpmrcOutOfIgnoreManagedSection('.dockerignore'), + description: 'Move .npmrc out of the .dockerignore managed section', + }, ]; diff --git a/src/cli/configure/upgrade/patches/7.3.1/moveNpmrcOutOfGitignoreManagedSection.test.ts b/src/cli/configure/upgrade/patches/7.3.1/moveNpmrcOutOfGitignoreManagedSection.test.ts deleted file mode 100644 index 1eaf63842..000000000 --- a/src/cli/configure/upgrade/patches/7.3.1/moveNpmrcOutOfGitignoreManagedSection.test.ts +++ /dev/null @@ -1,169 +0,0 @@ -import fs from 'fs-extra'; - -import * as packageAnalysis from '../../../analysis/package'; -import * as projectAnalysis from '../../../analysis/project'; - -import { tryMoveNpmrcOutOfGitignoreManagedSection } from './moveNpmrcOutOfGitignoreManagedSection'; - -jest - .spyOn(packageAnalysis, 'getDestinationManifest') - .mockResolvedValue({ path: '~/project/package.json' } as any); - -const createDestinationFileReader = jest - .spyOn(projectAnalysis, 'createDestinationFileReader') - .mockReturnValue(() => { - throw new Error('Not implemented!'); - }); - -const writeFile = jest.spyOn(fs.promises, 'writeFile').mockResolvedValue(); - -beforeEach(jest.clearAllMocks); - -describe('tryMoveNpmrcOutOfGitignoreManagedSection', () => { - describe('format mode', () => { - it('moves a .gitignore out', async () => { - createDestinationFileReader.mockReturnValue(() => - Promise.resolve( - `# managed by skuba\nstuff\n.npmrc\nother stuff\n# end managed by skuba`, - ), - ); - - await expect( - tryMoveNpmrcOutOfGitignoreManagedSection('format', '~/project'), - ).resolves.toEqual({ - result: 'apply', - }); - - expect(writeFile.mock.calls.flat().join('\n')).toMatchInlineSnapshot(` - "~/project/.gitignore - # managed by skuba - stuff - other stuff - # end managed by skuba - - # Ignore .npmrc. This is no longer managed by skuba as pnpm projects use a managed .npmrc. - # IMPORTANT: if migrating to pnpm, remove this line and add an .npmrc IN THE SAME COMMIT. - # You can use \`skuba format\` to generate the file or otherwise commit an empty file. - # Doing so will conflict with a local .npmrc and make it more difficult to unintentionally commit auth secrets. - .npmrc - " - `); - }); - - it('should be a no-op if ignored then un-ignored', async () => { - createDestinationFileReader.mockReturnValue(() => - Promise.resolve( - `# managed by skuba\nstuff\n.npmrc\nother stuff\n# end managed by skuba\n!.npmrc`, - ), - ); - - await expect( - tryMoveNpmrcOutOfGitignoreManagedSection('format', '~/project'), - ).resolves.toEqual({ - result: 'skip', - reason: 'not ignored', - }); - - expect(writeFile).not.toHaveBeenCalled(); - }); - - it('should be a no-op if ignored out of managed section', async () => { - createDestinationFileReader.mockReturnValue(() => - Promise.resolve( - `# managed by skuba\nstuff\n.npmrc\nother stuff\n# end managed by skuba\n.npmrc`, - ), - ); - - await expect( - tryMoveNpmrcOutOfGitignoreManagedSection('format', '~/project'), - ).resolves.toEqual({ - result: 'skip', - reason: 'already ignored in unmanaged section', - }); - - expect(writeFile).not.toHaveBeenCalled(); - }); - - it('should be a no-op if not ignored', async () => { - createDestinationFileReader.mockReturnValue(() => - Promise.resolve(`# managed by skuba\nstuff\n# end managed by skuba`), - ); - - await expect( - tryMoveNpmrcOutOfGitignoreManagedSection('format', '~/project'), - ).resolves.toEqual({ - result: 'skip', - reason: 'not ignored', - }); - - expect(writeFile).not.toHaveBeenCalled(); - }); - }); - - describe('lint mode', () => { - it('flags moving a .gitignore out', async () => { - createDestinationFileReader.mockReturnValue(() => - Promise.resolve( - `# managed by skuba\nstuff\n.npmrc\nother stuff\n# end managed by skuba`, - ), - ); - - await expect( - tryMoveNpmrcOutOfGitignoreManagedSection('lint', '~/project'), - ).resolves.toEqual({ - result: 'apply', - }); - - expect(writeFile).not.toHaveBeenCalled(); - }); - - it('should be a no-op if ignored then un-ignored', async () => { - createDestinationFileReader.mockReturnValue(() => - Promise.resolve( - `# managed by skuba\nstuff\n.npmrc\nother stuff\n# end managed by skuba\n!.npmrc`, - ), - ); - - await expect( - tryMoveNpmrcOutOfGitignoreManagedSection('lint', '~/project'), - ).resolves.toEqual({ - result: 'skip', - reason: 'not ignored', - }); - - expect(writeFile).not.toHaveBeenCalled(); - }); - - it('should be a no-op if ignored out of managed section', async () => { - createDestinationFileReader.mockReturnValue(() => - Promise.resolve( - `# managed by skuba\nstuff\n.npmrc\nother stuff\n# end managed by skuba\n.npmrc`, - ), - ); - - await expect( - tryMoveNpmrcOutOfGitignoreManagedSection('lint', '~/project'), - ).resolves.toEqual({ - result: 'skip', - reason: 'already ignored in unmanaged section', - }); - - expect(writeFile).not.toHaveBeenCalled(); - }); - - it('should be a no-op if not ignored', async () => { - createDestinationFileReader.mockReturnValue(() => - Promise.resolve(`# managed by skuba\nstuff\n# end managed by skuba`), - ); - - await expect( - tryMoveNpmrcOutOfGitignoreManagedSection('lint', '~/project'), - ).resolves.toEqual({ - result: 'skip', - reason: 'not ignored', - }); - - expect(writeFile).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/src/cli/configure/upgrade/patches/7.3.1/moveNpmrcOutOfIgnoreManagedSection.test.ts b/src/cli/configure/upgrade/patches/7.3.1/moveNpmrcOutOfIgnoreManagedSection.test.ts new file mode 100644 index 000000000..d8ad850eb --- /dev/null +++ b/src/cli/configure/upgrade/patches/7.3.1/moveNpmrcOutOfIgnoreManagedSection.test.ts @@ -0,0 +1,205 @@ +import fs from 'fs-extra'; + +import * as packageAnalysis from '../../../analysis/package'; +import * as projectAnalysis from '../../../analysis/project'; + +import { tryMoveNpmrcOutOfIgnoreManagedSection } from './moveNpmrcOutOfIgnoreManagedSection'; + +jest + .spyOn(packageAnalysis, 'getDestinationManifest') + .mockResolvedValue({ path: '~/project/package.json' } as any); + +const createDestinationFileReader = jest + .spyOn(projectAnalysis, 'createDestinationFileReader') + .mockReturnValue(() => { + throw new Error('Not implemented!'); + }); + +const writeFile = jest.spyOn(fs.promises, 'writeFile').mockResolvedValue(); + +beforeEach(jest.clearAllMocks); + +describe('tryMoveNpmrcOutOfIgnoreManagedSection', () => { + describe.each(['.gitignore', '.dockerignore'] as const)( + 'with %s', + (fileName) => { + describe('format mode', () => { + it(`moves an .npmrc out`, async () => { + createDestinationFileReader.mockReturnValue(() => + Promise.resolve( + `# managed by skuba\nstuff\n.npmrc\nother stuff\n# end managed by skuba`, + ), + ); + + await expect( + tryMoveNpmrcOutOfIgnoreManagedSection(fileName)( + 'format', + '~/project', + ), + ).resolves.toEqual({ + result: 'apply', + }); + + expect(writeFile.mock.calls.map((c) => c[0])).toEqual([ + `~/project/${fileName}`, + ]); + expect(writeFile.mock.calls.map((c) => c[1]).join('\n')) + .toMatchInlineSnapshot(` + "# managed by skuba + stuff + other stuff + # end managed by skuba + + # Ignore .npmrc. This is no longer managed by skuba as pnpm projects use a managed .npmrc. + # IMPORTANT: if migrating to pnpm, remove this line and add an .npmrc IN THE SAME COMMIT. + # You can use \`skuba format\` to generate the file or otherwise commit an empty file. + # Doing so will conflict with a local .npmrc and make it more difficult to unintentionally commit auth secrets. + .npmrc + " + `); + }); + + it('should be a no-op if ignored then un-ignored', async () => { + createDestinationFileReader.mockReturnValue(() => + Promise.resolve( + `# managed by skuba\nstuff\n.npmrc\nother stuff\n# end managed by skuba\n!.npmrc`, + ), + ); + + await expect( + tryMoveNpmrcOutOfIgnoreManagedSection(fileName)( + 'format', + '~/project', + ), + ).resolves.toEqual({ + result: 'skip', + reason: 'not ignored', + }); + + expect(writeFile).not.toHaveBeenCalled(); + }); + + it('should be a no-op if ignored out of managed section', async () => { + createDestinationFileReader.mockReturnValue(() => + Promise.resolve( + `# managed by skuba\nstuff\n.npmrc\nother stuff\n# end managed by skuba\n.npmrc`, + ), + ); + + await expect( + tryMoveNpmrcOutOfIgnoreManagedSection(fileName)( + 'format', + '~/project', + ), + ).resolves.toEqual({ + result: 'skip', + reason: 'already ignored in unmanaged section', + }); + + expect(writeFile).not.toHaveBeenCalled(); + }); + + it('should be a no-op if not ignored', async () => { + createDestinationFileReader.mockReturnValue(() => + Promise.resolve( + `# managed by skuba\nstuff\n# end managed by skuba`, + ), + ); + + await expect( + tryMoveNpmrcOutOfIgnoreManagedSection(fileName)( + 'format', + '~/project', + ), + ).resolves.toEqual({ + result: 'skip', + reason: 'not ignored', + }); + + expect(writeFile).not.toHaveBeenCalled(); + }); + }); + + describe('lint mode', () => { + it('flags moving an .npmrc out', async () => { + createDestinationFileReader.mockReturnValue(() => + Promise.resolve( + `# managed by skuba\nstuff\n.npmrc\nother stuff\n# end managed by skuba`, + ), + ); + + await expect( + tryMoveNpmrcOutOfIgnoreManagedSection(fileName)( + 'lint', + '~/project', + ), + ).resolves.toEqual({ + result: 'apply', + }); + + expect(writeFile).not.toHaveBeenCalled(); + }); + + it('should be a no-op if ignored then un-ignored', async () => { + createDestinationFileReader.mockReturnValue(() => + Promise.resolve( + `# managed by skuba\nstuff\n.npmrc\nother stuff\n# end managed by skuba\n!.npmrc`, + ), + ); + + await expect( + tryMoveNpmrcOutOfIgnoreManagedSection(fileName)( + 'lint', + '~/project', + ), + ).resolves.toEqual({ + result: 'skip', + reason: 'not ignored', + }); + + expect(writeFile).not.toHaveBeenCalled(); + }); + + it('should be a no-op if ignored out of managed section', async () => { + createDestinationFileReader.mockReturnValue(() => + Promise.resolve( + `# managed by skuba\nstuff\n.npmrc\nother stuff\n# end managed by skuba\n.npmrc`, + ), + ); + + await expect( + tryMoveNpmrcOutOfIgnoreManagedSection(fileName)( + 'lint', + '~/project', + ), + ).resolves.toEqual({ + result: 'skip', + reason: 'already ignored in unmanaged section', + }); + + expect(writeFile).not.toHaveBeenCalled(); + }); + + it('should be a no-op if not ignored', async () => { + createDestinationFileReader.mockReturnValue(() => + Promise.resolve( + `# managed by skuba\nstuff\n# end managed by skuba`, + ), + ); + + await expect( + tryMoveNpmrcOutOfIgnoreManagedSection(fileName)( + 'lint', + '~/project', + ), + ).resolves.toEqual({ + result: 'skip', + reason: 'not ignored', + }); + + expect(writeFile).not.toHaveBeenCalled(); + }); + }); + }, + ); +}); diff --git a/src/cli/configure/upgrade/patches/7.3.1/moveNpmrcOutOfGitignoreManagedSection.ts b/src/cli/configure/upgrade/patches/7.3.1/moveNpmrcOutOfIgnoreManagedSection.ts similarity index 68% rename from src/cli/configure/upgrade/patches/7.3.1/moveNpmrcOutOfGitignoreManagedSection.ts rename to src/cli/configure/upgrade/patches/7.3.1/moveNpmrcOutOfIgnoreManagedSection.ts index 4e737a884..41bc6da8e 100644 --- a/src/cli/configure/upgrade/patches/7.3.1/moveNpmrcOutOfGitignoreManagedSection.ts +++ b/src/cli/configure/upgrade/patches/7.3.1/moveNpmrcOutOfIgnoreManagedSection.ts @@ -17,22 +17,23 @@ const NPMRC_IGNORE_SECTION = ` .npmrc `; -const moveNpmrcOutOfGitignoreManagedSection = async ( +const moveNpmrcOutOfIgnoreManagedSection = async ( mode: 'format' | 'lint', dir: string, + fileName: '.gitignore' | '.dockerignore', ): Promise => { const readFile = createDestinationFileReader(dir); - const gitignore = await readFile('.gitignore'); + const ignoreFile = await readFile(fileName); - if (!gitignore) { - return { result: 'skip', reason: 'no .gitignore file found' }; + if (!ignoreFile) { + return { result: 'skip', reason: `no ${fileName} file found` }; } let isIgnored: { inManaged: boolean } | undefined; let currentlyInManagedSection = false; - for (const line of gitignore.split('\n')) { + for (const line of ignoreFile.split('\n')) { if (line.trim() === '# managed by skuba') { currentlyInManagedSection = true; } else if (line.trim() === '# end managed by skuba') { @@ -60,27 +61,27 @@ const moveNpmrcOutOfGitignoreManagedSection = async ( return { result: 'apply' }; } - const newGitignore = - gitignore + const newIgnoreFile = + ignoreFile .split('\n') .filter((line) => !NPMRC_LINES.includes(line.trim())) .join('\n') .trim() + NPMRC_IGNORE_SECTION; - await fs.promises.writeFile(path.join(dir, '.gitignore'), newGitignore); + await fs.promises.writeFile(path.join(dir, fileName), newIgnoreFile); return { result: 'apply' }; }; -export const tryMoveNpmrcOutOfGitignoreManagedSection = (async ( - mode: 'format' | 'lint', - dir = process.cwd(), -) => { - try { - return await moveNpmrcOutOfGitignoreManagedSection(mode, dir); - } catch (err) { - log.warn('Failed to move .npmrc out of .gitignore managed section.'); - log.subtle(inspect(err)); - return { result: 'skip', reason: 'due to an error' }; - } -}) satisfies PatchFunction; +export const tryMoveNpmrcOutOfIgnoreManagedSection = ( + type: '.gitignore' | '.dockerignore', +) => + (async (mode: 'format' | 'lint', dir = process.cwd()) => { + try { + return await moveNpmrcOutOfIgnoreManagedSection(mode, dir, type); + } catch (err) { + log.warn(`Failed to move .npmrc out of ${type} managed sections.`); + log.subtle(inspect(err)); + return { result: 'skip', reason: 'due to an error' }; + } + }) satisfies PatchFunction; diff --git a/src/cli/lint/internalLints/refreshConfigFiles.test.ts b/src/cli/lint/internalLints/refreshConfigFiles.test.ts index 10d39c17d..301b87bba 100644 --- a/src/cli/lint/internalLints/refreshConfigFiles.test.ts +++ b/src/cli/lint/internalLints/refreshConfigFiles.test.ts @@ -6,7 +6,10 @@ import { log } from '../../../utils/logging'; import { detectPackageManager } from '../../../utils/packageManager'; import * as project from '../../configure/analysis/project'; -import { refreshConfigFiles } from './refreshConfigFiles'; +import { + REFRESHABLE_CONFIG_FILES, + refreshConfigFiles, +} from './refreshConfigFiles'; const stdoutMock = jest.fn(); @@ -53,23 +56,27 @@ describe('refreshConfigFiles', () => { project.createDestinationFileReader, ); - function setupDestinationFiles(data: Record) { + function setupDestinationFiles(data: Record) { + const contents = { + ...Object.fromEntries( + REFRESHABLE_CONFIG_FILES.map((f) => [ + f.name, + `# managed by skuba\nfake content for _${f.name}\n# end managed by skuba\n`, + ]), + ), + ...data, + }; + createDestinationFileReader.mockImplementation( - () => (filename: string) => Promise.resolve(data[filename]), + () => (filename: string) => Promise.resolve(contents[filename]), ); } describe('lint mode', () => { it('should report ok if files are up to date, and output nothing', async () => { setupDestinationFiles({ - '.eslintignore': - '# managed by skuba\nfake content for _.eslintignore\n# end managed by skuba', '.gitignore': '# managed by skuba\nfake content for _.gitignore\n# end managed by skuba\n\nstuff afterwards', - '.prettierignore': - '# managed by skuba\nfake content for _.prettierignore\n# end managed by skuba', - '.npmrc': - '# managed by skuba\nfake content for _.npmrc\n# end managed by skuba', }); await expect(refreshConfigFiles('lint', log)).resolves.toEqual({ @@ -91,8 +98,6 @@ describe('refreshConfigFiles', () => { '# managed by skuba\n# end managed by skuba\n\nstuff afterwards', '.prettierignore': '# managed by skuba\nfake content for _.prettierignore\n# end managed by skuba\nstuff afterwards', - '.npmrc': - '# managed by skuba\nfake content for _.npmrc\n# end managed by skuba', }); await expect(refreshConfigFiles('lint', log)).resolves.toEqual({ @@ -122,12 +127,8 @@ The .gitignore file is out of date. Run \`pnpm exec skuba format\` to update it. it('should flag an .npmrc containing authToken even if otherwise up to date', async () => { setupDestinationFiles({ - '.eslintignore': - '# managed by skuba\nfake content for _.eslintignore\n# end managed by skuba', '.gitignore': '# managed by skuba\nfake content for _.gitignore\n# end managed by skuba\n\nstuff afterwards', - '.prettierignore': - '# managed by skuba\nfake content for _.prettierignore\n# end managed by skuba', '.npmrc': '# managed by skuba\nfake content for _.npmrc\n# end managed by skuba\n//registry.npmjs.org/:_authToken=not-a-real-token', }); @@ -153,12 +154,7 @@ The .npmrc file is out of date. Run \`pnpm exec skuba format\` to update it. ref it('should flag creation of an .npmrc for pnpm projects if missing', async () => { setupDestinationFiles({ - '.eslintignore': - '# managed by skuba\nfake content for _.eslintignore\n# end managed by skuba', - '.gitignore': - '# managed by skuba\nfake content for _.gitignore\n# end managed by skuba', - '.prettierignore': - '# managed by skuba\nfake content for _.prettierignore\n# end managed by skuba', + '.npmrc': undefined, }); await expect(refreshConfigFiles('lint', log)).resolves.toEqual({ @@ -184,12 +180,7 @@ The .npmrc file is out of date. Run \`pnpm exec skuba format\` to update it. ref givenMockPackageManager('yarn'); setupDestinationFiles({ - '.eslintignore': - '# managed by skuba\nfake content for _.eslintignore\n# end managed by skuba', - '.gitignore': - '# managed by skuba\nfake content for _.gitignore\n# end managed by skuba', - '.prettierignore': - '# managed by skuba\nfake content for _.prettierignore\n# end managed by skuba', + '.npmrc': undefined, }); await expect(refreshConfigFiles('lint', log)).resolves.toEqual({ @@ -207,14 +198,8 @@ The .npmrc file is out of date. Run \`pnpm exec skuba format\` to update it. ref describe('format mode', () => { it('should report ok if files are up to date, and write or output nothing', async () => { setupDestinationFiles({ - '.eslintignore': - '# managed by skuba\nfake content for _.eslintignore\n# end managed by skuba', '.gitignore': '# managed by skuba\nfake content for _.gitignore\n# end managed by skuba\n\nstuff afterwards', - '.prettierignore': - '# managed by skuba\nfake content for _.prettierignore\n# end managed by skuba', - '.npmrc': - '# managed by skuba\nfake content for _.npmrc\n# end managed by skuba', }); await expect(refreshConfigFiles('format', log)).resolves.toEqual({ @@ -236,8 +221,6 @@ The .npmrc file is out of date. Run \`pnpm exec skuba format\` to update it. ref '# managed by skuba\n# end managed by skuba\n\nstuff afterwards', '.prettierignore': '# managed by skuba\nfake content for _.prettierignore\n# end managed by skuba\nstuff afterwards', - '.npmrc': - '# managed by skuba\nfake content for _.npmrc\n# end managed by skuba', }); await expect(refreshConfigFiles('format', log)).resolves.toEqual({ @@ -263,12 +246,8 @@ The .npmrc file is out of date. Run \`pnpm exec skuba format\` to update it. ref it('should strip an authToken line from an .npmrc file', async () => { setupDestinationFiles({ - '.eslintignore': - '# managed by skuba\nfake content for _.eslintignore\n# end managed by skuba', '.gitignore': '# managed by skuba\nfake content for _.gitignore\n# end managed by skuba\n\nstuff afterwards', - '.prettierignore': - '# managed by skuba\nfake content for _.prettierignore\n# end managed by skuba', '.npmrc': '# managed by skuba\nfake content for _.npmrc\n# end managed by skuba\n//registry.npmjs.org/:_authToken=not-a-real-token', }); @@ -292,12 +271,7 @@ The .npmrc file is out of date. Run \`pnpm exec skuba format\` to update it. ref it('should create an .npmrc for pnpm projects if missing', async () => { setupDestinationFiles({ - '.eslintignore': - '# managed by skuba\nfake content for _.eslintignore\n# end managed by skuba', - '.gitignore': - '# managed by skuba\nfake content for _.gitignore\n# end managed by skuba', - '.prettierignore': - '# managed by skuba\nfake content for _.prettierignore\n# end managed by skuba', + '.npmrc': undefined, }); await expect(refreshConfigFiles('format', log)).resolves.toEqual({ @@ -321,12 +295,7 @@ Refreshed .npmrc. refresh-config-files givenMockPackageManager('yarn'); setupDestinationFiles({ - '.eslintignore': - '# managed by skuba\nfake content for _.eslintignore\n# end managed by skuba', - '.gitignore': - '# managed by skuba\nfake content for _.gitignore\n# end managed by skuba', - '.prettierignore': - '# managed by skuba\nfake content for _.prettierignore\n# end managed by skuba', + '.npmrc': undefined, }); await expect(refreshConfigFiles('format', log)).resolves.toEqual({ @@ -358,6 +327,24 @@ Refreshed .npmrc. refresh-config-files ); }); + it('should also manage .dockerignore', async () => { + setupDestinationFiles({ + '.dockerignore': + '# managed by skuba\nfake content for _.dockerignore\n# end managed by skuba\nstuff\n!.npmrc\n!/.npmrc\nother stuff', + }); + + await expect(refreshConfigFiles('format', log)).resolves.toEqual({ + ok: true, + fixable: false, + annotations: [], + }); + + expect(writeFile).toHaveBeenCalledWith( + path.join(process.cwd(), '.dockerignore'), + '# managed by skuba\nfake content for _.dockerignore\n# end managed by skuba\nstuff\nother stuff', + ); + }); + it('should not strip !.npmrc if ignored out of the managed file for no good reason', async () => { setupDestinationFiles({ '.gitignore': diff --git a/src/cli/lint/internalLints/refreshConfigFiles.ts b/src/cli/lint/internalLints/refreshConfigFiles.ts index a74d6aca5..47dc6d254 100644 --- a/src/cli/lint/internalLints/refreshConfigFiles.ts +++ b/src/cli/lint/internalLints/refreshConfigFiles.ts @@ -32,29 +32,28 @@ type RefreshableConfigFile = { if?: (packageManager: PackageManagerConfig) => boolean; }; -const REFRESHABLE_CONFIG_FILES: RefreshableConfigFile[] = [ +const removeRedundantNpmrc = (contents: string) => { + const npmrcLines = contents + .split('\n') + .filter((line) => NPMRC_LINES.includes(line.trim())); + + // If we're only left with !.npmrc line we can remove it + // TODO: Consider if we should generalise this + if (npmrcLines.length > 0 && npmrcLines.every((line) => line.includes('!'))) { + return contents + .split('\n') + .filter((line) => !NPMRC_LINES.includes(line.trim())) + .join('\n'); + } + return contents; +}; + +export const REFRESHABLE_CONFIG_FILES: RefreshableConfigFile[] = [ { name: '.eslintignore', type: 'ignore' }, { name: '.gitignore', type: 'ignore', - additionalMapping: (gitignore: string) => { - const npmrcLines = gitignore - .split('\n') - .filter((line) => NPMRC_LINES.includes(line.trim())); - - // If we're only left with !.npmrc line we can remove it - // TODO: Consider if we should generalise this - if ( - npmrcLines.length > 0 && - npmrcLines.every((line) => line.includes('!')) - ) { - return gitignore - .split('\n') - .filter((line) => !NPMRC_LINES.includes(line.trim())) - .join('\n'); - } - return gitignore; - }, + additionalMapping: removeRedundantNpmrc, }, { name: '.prettierignore', type: 'ignore' }, { @@ -64,6 +63,11 @@ const REFRESHABLE_CONFIG_FILES: RefreshableConfigFile[] = [ if: (packageManager: PackageManagerConfig) => packageManager.command === 'pnpm', }, + { + name: '.dockerignore', + type: 'ignore', + additionalMapping: removeRedundantNpmrc, + }, ]; export const refreshConfigFiles = async (