diff --git a/.changeset/silly-swans-serve.md b/.changeset/silly-swans-serve.md new file mode 100644 index 000000000..02c3f6e39 --- /dev/null +++ b/.changeset/silly-swans-serve.md @@ -0,0 +1,5 @@ +--- +'skuba': patch +--- + +template/\*-rest-api: Switch distroless image from `nodejs:18` to `nodejs18-debian11` diff --git a/.changeset/thirty-bananas-tickle.md b/.changeset/thirty-bananas-tickle.md new file mode 100644 index 000000000..3ec9c62b5 --- /dev/null +++ b/.changeset/thirty-bananas-tickle.md @@ -0,0 +1,5 @@ +--- +'skuba': minor +--- + +format, lint: Add `pnpm-lock.yaml` to `.prettierignore` diff --git a/.changeset/wild-plums-join.md b/.changeset/wild-plums-join.md new file mode 100644 index 000000000..7ad07fbf5 --- /dev/null +++ b/.changeset/wild-plums-join.md @@ -0,0 +1,7 @@ +--- +'skuba': minor +--- + +format, lint: Switch distroless image from `nodejs` to `nodejs-debian11` + +`skuba format` and `skuba lint` will now automatically switch your `gcr.io/distroless/nodejs:18` image to `gcr.io/distroless/nodejs18-debian11`. This is now the [recommended](https://github.com/GoogleContainerTools/distroless/blob/main/nodejs/README.md) base image for Node.js. diff --git a/.prettierignore b/.prettierignore index f79f754a9..1b5cb4b78 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,6 +4,7 @@ /.gantry/**/*.yml gantry*.yaml gantry*.yml +pnpm-lock.yaml # end managed by skuba /integration/base/ diff --git a/docs/deep-dives/arm64.md b/docs/deep-dives/arm64.md index 65a6e46b4..002f2222e 100644 --- a/docs/deep-dives/arm64.md +++ b/docs/deep-dives/arm64.md @@ -54,11 +54,11 @@ Replace the relevant `--platform` values in your Dockerfile(s), then ensure that you run your builds on AMD64 hardware: ```diff -- FROM --platform=arm64 gcr.io/distroless/nodejs:18 AS runtime -+ FROM --platform=${BUILDPLATFORM:-amd64} gcr.io/distroless/nodejs:18 AS runtime +- FROM --platform=arm64 gcr.io/distroless/nodejs18-debian11 AS runtime ++ FROM --platform=${BUILDPLATFORM:-amd64} gcr.io/distroless/nodejs18-debian11 AS runtime -- FROM --platform=${BUILDPLATFORM:-arm64} gcr.io/distroless/nodejs:18 AS runtime -+ FROM --platform=${BUILDPLATFORM:-amd64} gcr.io/distroless/nodejs:18 AS runtime +- FROM --platform=${BUILDPLATFORM:-arm64} gcr.io/distroless/nodejs18-debian11 AS runtime ++ FROM --platform=${BUILDPLATFORM:-amd64} gcr.io/distroless/nodejs18-debian11 AS runtime ``` For a [Gantry] service, modify the `cpuArchitecture` property in your `gantry.build.yml` and `gantry.apply.yml` resource files: diff --git a/src/cli/configure/analysis/__snapshots__/project.test.ts.snap b/src/cli/configure/analysis/__snapshots__/project.test.ts.snap index fdfc9f29e..e0739b827 100644 --- a/src/cli/configure/analysis/__snapshots__/project.test.ts.snap +++ b/src/cli/configure/analysis/__snapshots__/project.test.ts.snap @@ -90,6 +90,7 @@ yarn-error.log /.gantry/**/*.yml gantry*.yaml gantry*.yml +pnpm-lock.yaml # end managed by skuba ", "operation": "A", diff --git a/src/cli/configure/patchDockerfile.test.ts b/src/cli/configure/patchDockerfile.test.ts new file mode 100644 index 000000000..a78198eac --- /dev/null +++ b/src/cli/configure/patchDockerfile.test.ts @@ -0,0 +1,44 @@ +import memfs, { vol } from 'memfs'; + +import { tryPatchDockerfile } from './patchDockerfile'; + +jest.mock('fs-extra', () => memfs); + +const volToJson = () => vol.toJSON(process.cwd(), undefined, true); + +beforeEach(jest.clearAllMocks); +beforeEach(() => vol.reset()); + +const dockerfile = ` +ARG BASE_IMAGE +ARG BASE_TAG + +FROM --platform=\${BUILDPLATFORM:-<%- platformName %>} gcr.io/distroless/nodejs:18 AS runtime + +WORKDIR /workdir +`; + +it('patches an existing Dockerfile', async () => { + vol.fromJSON({ Dockerfile: dockerfile }); + + await expect(tryPatchDockerfile()).resolves.toBeUndefined(); + + expect(volToJson()).toMatchInlineSnapshot(` +{ + "Dockerfile": " +ARG BASE_IMAGE +ARG BASE_TAG + +FROM --platform=\${BUILDPLATFORM:-<%- platformName %>} gcr.io/distroless/nodejs18-debian11 AS runtime + +WORKDIR /workdir +", +} +`); +}); + +it('ignores when a Dockerfile is missing', async () => { + vol.fromJSON({}); + + await expect(tryPatchDockerfile()).resolves.toBeUndefined(); +}); diff --git a/src/cli/configure/patchDockerfile.ts b/src/cli/configure/patchDockerfile.ts new file mode 100644 index 000000000..2d0320a5c --- /dev/null +++ b/src/cli/configure/patchDockerfile.ts @@ -0,0 +1,38 @@ +import { inspect } from 'util'; + +import fs from 'fs-extra'; + +import { log } from '../../utils/logging'; + +import { createDestinationFileReader } from './analysis/project'; + +const DOCKERFILE_FILENAME = 'Dockerfile'; + +const VERSION_REGEX = /gcr.io\/distroless\/nodejs:(16|18|20)/g; +const VERSION_DEBIAN_REPLACE = 'gcr.io/distroless/nodejs$1-debian11'; + +const patchDockerfile = async (dir: string) => { + const readFile = createDestinationFileReader(dir); + + const maybeDockerfile = await readFile(DOCKERFILE_FILENAME); + + if (!maybeDockerfile) { + return; + } + + const patched = maybeDockerfile.replaceAll( + VERSION_REGEX, + VERSION_DEBIAN_REPLACE, + ); + + await fs.promises.writeFile(DOCKERFILE_FILENAME, patched); +}; + +export const tryPatchDockerfile = async (dir = process.cwd()) => { + try { + await patchDockerfile(dir); + } catch (err) { + log.warn('Failed to patch Dockerfile.'); + log.subtle(inspect(err)); + } +}; diff --git a/src/cli/format.ts b/src/cli/format.ts index 5f6aca18d..5c6071b50 100644 --- a/src/cli/format.ts +++ b/src/cli/format.ts @@ -6,6 +6,7 @@ import { createLogger, log } from '../utils/logging'; import { runESLint } from './adapter/eslint'; import { runPrettier } from './adapter/prettier'; import { tryAddEmptyExports } from './configure/addEmptyExports'; +import { tryPatchDockerfile } from './configure/patchDockerfile'; import { tryPatchRenovateConfig } from './configure/patchRenovateConfig'; import { tryPatchServerListener } from './configure/patchServerListener'; import { tryRefreshIgnoreFiles } from './configure/refreshIgnoreFiles'; @@ -14,6 +15,7 @@ export const format = async (args = process.argv.slice(2)): Promise => { await Promise.all([ tryAddEmptyExports(), tryPatchRenovateConfig(), + tryPatchDockerfile(), tryPatchServerListener(), tryRefreshIgnoreFiles(), ]); diff --git a/template/base/_.prettierignore b/template/base/_.prettierignore index 58c1da8f1..e0ff288a5 100644 --- a/template/base/_.prettierignore +++ b/template/base/_.prettierignore @@ -4,4 +4,5 @@ /.gantry/**/*.yml gantry*.yaml gantry*.yml +pnpm-lock.yaml # end managed by skuba diff --git a/template/express-rest-api/Dockerfile b/template/express-rest-api/Dockerfile index 3182d3c9f..e9238b20e 100644 --- a/template/express-rest-api/Dockerfile +++ b/template/express-rest-api/Dockerfile @@ -17,7 +17,7 @@ RUN yarn build ### -FROM --platform=${BUILDPLATFORM:-<%- platformName %>} gcr.io/distroless/nodejs:18 AS runtime +FROM --platform=${BUILDPLATFORM:-<%- platformName %>} gcr.io/distroless/nodejs18-debian11 AS runtime WORKDIR /workdir diff --git a/template/koa-rest-api/Dockerfile b/template/koa-rest-api/Dockerfile index b476c8999..eb1ba7c53 100644 --- a/template/koa-rest-api/Dockerfile +++ b/template/koa-rest-api/Dockerfile @@ -17,7 +17,7 @@ RUN yarn build ### -FROM --platform=${BUILDPLATFORM:-<%- platformName %>} gcr.io/distroless/nodejs:18 AS runtime +FROM --platform=${BUILDPLATFORM:-<%- platformName %>} gcr.io/distroless/nodejs18-debian11 AS runtime WORKDIR /workdir