From 1325c3fea10589bf9a073f1d15cba2c3297b04aa Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Sun, 6 Aug 2023 23:25:50 +0000 Subject: [PATCH] chore(deps-dev): bump megalinter docker tag to v7 MegaLinter recently added support for arrays to `*_CLI_EXECUTABLE` in v7.0.0. Leverage this feature to run the version of ESLint installed in our project via Yarn in lieu of the version of ESLint in the MegaLinter Docker image via npm. This avoids many complications that arise from ESLint being unable to access Yarn dependencies and allows us to stop running ESLint in project mode as a workaround. Reorder pre-commit hooks so that Yarn hooks precede MegaLinter hooks since `yarn install` must now be run before MegaLinter so that ESLint can run. Disable newly added linter ts-standard since we use Prettier, which is incompatible with StandardJS. Apply new Prettier v3 formatting. Configure newly added link checker Lychee to ignore email addresses to prevent false positives on GitHub Actions, which contain "@" symbols. Increase network request retry limit from the default of 3 to 4 to avoid false positives. | datasource | package | from | to | | ---------- | -------------------------------- | ------- | ------ | | docker | oxsecurity/megalinter-javascript | v6.22.2 | v7.2.1 | --- .mega-linter.yaml | 8 +++---- .pre-commit-config.yaml | 8 +++---- lychee.toml | 3 +++ src/arbitraries/util.ts | 6 +++--- src/docker.test.ts | 46 +++++++++++++++++++++-------------------- src/docker.ts | 8 +++---- src/integration.test.ts | 24 ++++++++++----------- src/main.test.ts | 2 +- src/post.test.ts | 2 +- src/util.test.ts | 12 +++++------ src/util.ts | 2 +- 11 files changed, 63 insertions(+), 58 deletions(-) create mode 100644 lychee.toml diff --git a/.mega-linter.yaml b/.mega-linter.yaml index 9e0ec722..c7c0e278 100644 --- a/.mega-linter.yaml +++ b/.mega-linter.yaml @@ -1,6 +1,8 @@ APPLY_FIXES: all CLEAR_REPORT_FOLDER: true DEFAULT_BRANCH: main +DISABLE_LINTERS: + - TYPESCRIPT_STANDARD # We use Prettier instead. FAIL_IF_MISSING_LINTER_IN_FLAVOR: true FILTER_REGEX_EXCLUDE: \.pnp\.(c|loader\.m)js|\.yarn|dist FORMATTERS_DISABLE_ERRORS: false @@ -8,8 +10,7 @@ PRINT_ALPACA: false SHOW_ELAPSED_TIME: true CREDENTIALS_SECRETLINT_ARGUMENTS: --secretlintignore .gitignore JAVASCRIPT_DEFAULT_STYLE: prettier -# Work around https://github.com/oxsecurity/megalinter/issues/1572. -JAVASCRIPT_ES_CLI_LINT_MODE: project +JAVASCRIPT_ES_CLI_EXECUTABLE: [node, .yarn/releases/yarn-3.6.1.cjs, run, eslint] JAVASCRIPT_ES_CONFIG_FILE: LINTER_DEFAULT JSON_PRETTIER_FILE_EXTENSIONS: - .json @@ -22,6 +23,5 @@ SPELL_CSPELL_PRE_COMMANDS: - command: npm install @cspell/dict-win32@2.0.1 continue_if_failed: false TYPESCRIPT_DEFAULT_STYLE: prettier -# Work around https://github.com/oxsecurity/megalinter/issues/1572. -TYPESCRIPT_ES_CLI_LINT_MODE: project +TYPESCRIPT_ES_CLI_EXECUTABLE: [node, .yarn/releases/yarn-3.6.1.cjs, run, eslint] TYPESCRIPT_ES_CONFIG_FILE: LINTER_DEFAULT diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 81ded23d..0cc0d859 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,15 +26,15 @@ repos: - id: poetry-lock - id: poetry-install - id: pre-commit-install - - id: megalinter-incremental - args: &megalinter-args [--flavor, javascript, --release, v6.22.2] - - id: megalinter-full - args: *megalinter-args - id: yarn-install - id: yarn-dedupe - id: yarn-audit - id: yarn-build - id: yarn-test + - id: megalinter-incremental + args: &megalinter-args [--flavor, javascript, --release, v7.2.1] + - id: megalinter-full + args: *megalinter-args ## Markdown - repo: https://github.com/frnmst/md-toc diff --git a/lychee.toml b/lychee.toml new file mode 100644 index 00000000..1e914b07 --- /dev/null +++ b/lychee.toml @@ -0,0 +1,3 @@ +exclude_mail = true + +max_retries = 4 diff --git a/src/arbitraries/util.ts b/src/arbitraries/util.ts index 314a19d4..9eb64c7e 100644 --- a/src/arbitraries/util.ts +++ b/src/arbitraries/util.ts @@ -22,8 +22,8 @@ export const dockerImages = (): Arbitrary => uniqueArray( stringOf( fullUnicode().filter((char: string): boolean => char !== "\n"), - { minLength: 1 } - ) + { minLength: 1 }, + ), ); export const platform = (): Arbitrary => @@ -37,7 +37,7 @@ export const platform = (): Arbitrary => */ export const uniquePair = ( arrArbA: Arbitrary, - arrArbB: Arbitrary + arrArbB: Arbitrary, ): Arbitrary<[T[], T[]]> => tuple(arrArbA, arrArbB).map(([arrayA, arrayB]: [T[], T[]]): [T[], T[]] => { const setA = new Set(arrayA); diff --git a/src/docker.test.ts b/src/docker.test.ts index 1dfc1066..8d309b50 100644 --- a/src/docker.test.ts +++ b/src/docker.test.ts @@ -15,7 +15,7 @@ jest.mock("@actions/core"); jest.unstable_mockModule( "./util.js", - (): Util => ({ execBashCommand: jest.fn() }) + (): Util => ({ execBashCommand: jest.fn() }), ); const cache = jest.mocked(await import("@actions/cache")); @@ -40,11 +40,11 @@ const assertCalledInOrder = ( currentMock.mock.invocationCallOrder[callCount] ?? fail(`Mock function ${index} was called too few times: ${callCount}.`) ); - } + }, ); const sortedCallOrders = [...callOrders].sort( - (a: number, b: number): number => a - b + (a: number, b: number): number => a - b, ); expect(callOrders).toStrictEqual(sortedCallOrders); }; @@ -56,16 +56,16 @@ describe("Docker images", (): void => { expect(core.saveState).nthCalledWith<[string, boolean]>( 1, docker.CACHE_HIT, - cacheHit + cacheHit, ); expect(core.setOutput).lastCalledWith(docker.CACHE_HIT, cacheHit); if (cacheHit) { expect(util.execBashCommand).lastCalledWith( - `docker load --input ${docker.DOCKER_IMAGES_PATH}` + `docker load --input ${docker.DOCKER_IMAGES_PATH}`, ); } else { expect(util.execBashCommand).lastCalledWith( - 'docker image list --format "{{ .Repository }}:{{ .Tag }}"' + 'docker image list --format "{{ .Repository }}:{{ .Tag }}"', ); } expect(util.execBashCommand).toHaveBeenCalledTimes(1); @@ -74,7 +74,7 @@ describe("Docker images", (): void => { const mockedLoadDockerImages = async ( key: string, cacheHit: boolean, - images = "" + images = "", ): Promise => { core.getInput.mockReturnValue(key); cache.restoreCache.mockResolvedValueOnce(cacheHit ? key : undefined); @@ -86,7 +86,7 @@ describe("Docker images", (): void => { const assertSaveDockerImages = ( cacheHit: boolean, - readOnly = false + readOnly = false, ): void => { expect(core.getInput).nthCalledWith<[string, InputOptions]>(1, "key", { required: true, @@ -99,7 +99,7 @@ describe("Docker images", (): void => { expect(core.info).nthCalledWith<[string]>(1, "Listing Docker images."); expect(util.execBashCommand).nthCalledWith<[string]>( 1, - 'docker image list --format "{{ .Repository }}:{{ .Tag }}"' + 'docker image list --format "{{ .Repository }}:{{ .Tag }}"', ); } } @@ -110,7 +110,7 @@ describe("Docker images", (): void => { cacheHit: boolean, readOnly: boolean, preexistingImages: string[], - newImages: string[] + newImages: string[], ): Promise => { core.getInput.mockReturnValueOnce(key); core.getState.mockReturnValueOnce(cacheHit.toString()); @@ -134,7 +134,7 @@ describe("Docker images", (): void => { const assertSaveCacheHit = (key: string): void => { expect(core.info).lastCalledWith( - `Cache hit occurred on the primary key ${key}, not saving cache.` + `Cache hit occurred on the primary key ${key}, not saving cache.`, ); assertCacheNotSaved(); }; @@ -142,7 +142,7 @@ describe("Docker images", (): void => { const assertSaveReadOnly = (key: string): void => { expect(core.info).lastCalledWith( `Cache miss occurred on the primary key ${key}. ` + - "Not saving cache as read-only option was selected." + "Not saving cache as read-only option was selected.", ); assertCacheNotSaved(); }; @@ -156,10 +156,12 @@ describe("Docker images", (): void => { const assertSaveCacheMiss = (key: string, newImages: string[]): void => { expect(core.info).lastCalledWith( "Images present before restore step will be skipped; only new images " + - "will be saved." + "will be saved.", ); expect(util.execBashCommand).lastCalledWith( - `docker save --output ${docker.DOCKER_IMAGES_PATH} ${newImages.join(" ")}` + `docker save --output ${docker.DOCKER_IMAGES_PATH} ${newImages.join( + " ", + )}`, ); expect(cache.saveCache).lastCalledWith([docker.DOCKER_IMAGES_PATH], key); @@ -174,7 +176,7 @@ describe("Docker images", (): void => { core.getState, util.execBashCommand, util.execBashCommand, - cache.saveCache + cache.saveCache, ); }; @@ -207,9 +209,9 @@ describe("Docker images", (): void => { assertCalledInOrder( core.getInput, cache.restoreCache, - util.execBashCommand + util.execBashCommand, ); - } + }, ); testProp( @@ -221,10 +223,10 @@ describe("Docker images", (): void => { expect(core.info).lastCalledWith( "Recording preexisting Docker images. These include standard images " + - "pre-cached by GitHub Actions when Docker is run as root." + "pre-cached by GitHub Actions when Docker is run as root.", ); expect(core.saveState).lastCalledWith(docker.DOCKER_IMAGES_LIST, images); - } + }, ); testProp( @@ -239,7 +241,7 @@ describe("Docker images", (): void => { key: string, cacheHit: boolean, readOnly: boolean, - [preexistingImages, newImages]: [string[], string[]] + [preexistingImages, newImages]: [string[], string[]], ): Promise => { jest.clearAllMocks(); await mockedSaveDockerImages( @@ -247,7 +249,7 @@ describe("Docker images", (): void => { cacheHit, readOnly, preexistingImages, - newImages + newImages, ); if (cacheHit) { @@ -267,6 +269,6 @@ describe("Docker images", (): void => { ["my-key", false, true, [["preexisting-image"], ["new-image"]]], ["my-key", true, false, [["preexisting-image"], ["new-image"]]], ], - } + }, ); }); diff --git a/src/docker.ts b/src/docker.ts index e3d32cf7..92a5b024 100644 --- a/src/docker.ts +++ b/src/docker.ts @@ -20,7 +20,7 @@ const loadDockerImages = async (): Promise => { } else { info( "Recording preexisting Docker images. These include standard images " + - "pre-cached by GitHub Actions when Docker is run as root." + "pre-cached by GitHub Actions when Docker is run as root.", ); const dockerImages = await execBashCommand(LIST_COMMAND); saveState(DOCKER_IMAGES_LIST, dockerImages); @@ -34,7 +34,7 @@ const saveDockerImages = async (): Promise => { } else if (getInput("read-only") === "true") { info( `Cache miss occurred on the primary key ${key}. Not saving cache as ` + - "read-only option was selected." + "read-only option was selected.", ); } else { const preexistingImages = getState(DOCKER_IMAGES_LIST).split("\n"); @@ -42,14 +42,14 @@ const saveDockerImages = async (): Promise => { const images = await execBashCommand(LIST_COMMAND); const imagesList = images.split("\n"); const newImages = imagesList.filter( - (image: string): boolean => !preexistingImages.includes(image) + (image: string): boolean => !preexistingImages.includes(image), ); if (newImages.length === 0) { info("No Docker images to save"); } else { info( "Images present before restore step will be skipped; only new images " + - "will be saved." + "will be saved.", ); const newImagesArgs = newImages.join(" "); const cmd = `docker save --output ${DOCKER_IMAGES_PATH} ${newImagesArgs}`; diff --git a/src/integration.test.ts b/src/integration.test.ts index 3e7f996a..b260eb0b 100644 --- a/src/integration.test.ts +++ b/src/integration.test.ts @@ -48,14 +48,14 @@ describe("Integration Test", (): void => { const key = getKey(paths, primaryKey); inMemoryCache.set(key, primaryKey); return Promise.resolve(0); - } + }, ); cache.restoreCache.mockImplementation( (paths: string[], primaryKey: string): Promise => { const key = getKey(paths, primaryKey); return Promise.resolve(inMemoryCache.get(key)); - } + }, ); core.getState.mockImplementation((key: string): string => { @@ -100,14 +100,14 @@ describe("Integration Test", (): void => { infoCallNum: number, execCallNum: number, command: string, - output: ConsoleOutput + output: ConsoleOutput, ): void => { expect(core.info).nthCalledWith<[string]>(infoCallNum, command); expect(nodeUtil.promisify).nthCalledWith<[typeof exec]>(execCallNum, exec); expect(execMock).nthCalledWith<[string, ExecOptions]>( execCallNum, command, - EXEC_OPTIONS + EXEC_OPTIONS, ); expect(core.info).nthCalledWith<[string]>(infoCallNum + 1, output.stdout); expect(core.error).nthCalledWith<[string]>(execCallNum, output.stderr); @@ -117,7 +117,7 @@ describe("Integration Test", (): void => { const assertLoadDockerImages = ( cacheHit: boolean, listStderr: string, - loadOutput: ConsoleOutput + loadOutput: ConsoleOutput, ): void => { expect(core.getInput).nthCalledWith<[string, InputOptions]>(1, "key", { required: true, @@ -130,7 +130,7 @@ describe("Integration Test", (): void => { expect(core.info).nthCalledWith<[string]>( 1, "Recording preexisting Docker images. These include standard images " + - "pre-cached by GitHub Actions when Docker is run as root." + "pre-cached by GitHub Actions when Docker is run as root.", ); const listOutput = joinOutput(dockerImages, listStderr); assertExecBashCommand(2, 1, LIST_COMMAND, listOutput); @@ -140,7 +140,7 @@ describe("Integration Test", (): void => { const assertSaveCacheHit = (key: string): void => { expect(core.info).lastCalledWith( - `Cache hit occurred on the primary key ${key}, not saving cache.` + `Cache hit occurred on the primary key ${key}, not saving cache.`, ); expect(execMock).not.toHaveBeenCalled(); expect(core.setFailed).not.toHaveBeenCalled(); @@ -149,7 +149,7 @@ describe("Integration Test", (): void => { const assertSaveCacheMiss = ( listStderr: string, - saveOutput: ConsoleOutput + saveOutput: ConsoleOutput, ): void => { expect(core.getInput).lastCalledWith("read-only"); expect(core.info).nthCalledWith<[string]>(1, "Listing Docker images."); @@ -158,7 +158,7 @@ describe("Integration Test", (): void => { expect(core.info).nthCalledWith<[string]>( 4, "Images present before restore step will be skipped; only new images " + - "will be saved." + "will be saved.", ); const saveCommand = `docker save --output ${docker.DOCKER_IMAGES_PATH} test-docker-image:v2`; assertExecBashCommand(5, 2, saveCommand, saveOutput); @@ -168,7 +168,7 @@ describe("Integration Test", (): void => { cacheHit: boolean, key: string, listStderr: string, - saveOutput: ConsoleOutput + saveOutput: ConsoleOutput, ): void => { expect(core.getInput).nthCalledWith<[string, InputOptions]>(1, "key", { required: true, @@ -184,7 +184,7 @@ describe("Integration Test", (): void => { async ( key: string, listStderr: string, - otherOutput: ConsoleOutput + otherOutput: ConsoleOutput, ): Promise => { jest.clearAllMocks(); inMemoryCache = new Map(); @@ -216,6 +216,6 @@ describe("Integration Test", (): void => { await docker.saveDockerImages(); // Expect cache not to have been saved on cache hit. assertSaveDockerImages(true, key, listStderr, otherOutput); - } + }, ); }); diff --git a/src/main.test.ts b/src/main.test.ts index ec073516..060443ae 100644 --- a/src/main.test.ts +++ b/src/main.test.ts @@ -7,7 +7,7 @@ jest.unstable_mockModule( "./docker.js", (): Partial => ({ loadDockerImages: jest.fn(), - }) + }), ); const docker = jest.mocked(await import("./docker.js")); diff --git a/src/post.test.ts b/src/post.test.ts index 6fd80595..8358a80a 100644 --- a/src/post.test.ts +++ b/src/post.test.ts @@ -7,7 +7,7 @@ jest.unstable_mockModule( "./docker.js", (): Partial => ({ saveDockerImages: jest.fn(), - }) + }), ); const docker = jest.mocked(await import("./docker.js")); diff --git a/src/util.test.ts b/src/util.test.ts index 0ff1ec7e..ecf95d95 100644 --- a/src/util.test.ts +++ b/src/util.test.ts @@ -21,11 +21,11 @@ describe("Util", (): void => { command: string, error: Error | null, platform: NodeJS.Platform, - output: ConsoleOutput + output: ConsoleOutput, ): Promise => { const execMock = jest.fn( (): Promise => - error ? Promise.reject(error) : Promise.resolve(output) + error ? Promise.reject(error) : Promise.resolve(output), ); nodeUtil.promisify.mockReturnValueOnce(execMock); @@ -48,7 +48,7 @@ describe("Util", (): void => { async ( command: string, platform: NodeJS.Platform, - output: ConsoleOutput + output: ConsoleOutput, ): Promise => { jest.clearAllMocks(); const stdout = await mockedExec(command, null, platform, output); @@ -62,7 +62,7 @@ describe("Util", (): void => { ["sample Linux command", "linux", { stdout: "", stderr: "" }], ["sample Windows command", "win32", { stdout: "", stderr: "" }], ], - } + }, ); testProp( @@ -72,7 +72,7 @@ describe("Util", (): void => { command: string, errorMessage: string, platform: NodeJS.Platform, - output: ConsoleOutput + output: ConsoleOutput, ): Promise => { jest.clearAllMocks(); const error = new Error(errorMessage); @@ -88,7 +88,7 @@ describe("Util", (): void => { ["sample Linux command", "", "linux", { stdout: "", stderr: "" }], ["sample Windows command", "", "win32", { stdout: "", stderr: "" }], ], - } + }, ); }); }); diff --git a/src/util.ts b/src/util.ts index 33a1c2f2..2acf9068 100644 --- a/src/util.ts +++ b/src/util.ts @@ -10,7 +10,7 @@ interface ConsoleOutput { const execBashCommand = async ( command: string, - platform: NodeJS.Platform = process.platform + platform: NodeJS.Platform = process.platform, ): Promise => { info(command); const execAsPromised = promisify(exec);