diff --git a/.eslintignore b/.eslintignore index b1d80557..0aec16d9 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,3 @@ site -.eslintrc.js +.eslintrc.cjs build_utils \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8bee4e9f..45a22c69 100644 --- a/.gitignore +++ b/.gitignore @@ -10,9 +10,6 @@ node_modules/ # Coverage coverage -# Transpiled files -build/ - # Optional npm cache directory .npm diff --git a/build_utils/eslintrc.js b/build_utils/eslintrc.cjs similarity index 98% rename from build_utils/eslintrc.js rename to build_utils/eslintrc.cjs index dacc41fe..26d254e4 100644 --- a/build_utils/eslintrc.js +++ b/build_utils/eslintrc.cjs @@ -40,6 +40,7 @@ module.exports = { 'single', { avoidEscape: true, allowTemplateLiterals: false }, ], + 'import/extensions': 'off', 'dot-location': 'warn', 'no-trailing-spaces': 'warn', 'no-multi-spaces': 'warn', @@ -101,9 +102,6 @@ module.exports = { } ], 'jest/valid-describe': ['off'], - 'import/extensions': ['error', 'never', { - 'json': 'always', - }], 'no-restricted-imports': ['error', { patterns: ['**/dist/**', 'src/*'] }], // copied from https://github.com/airbnb/javascript/blob/master/packages/eslint-config-airbnb-base/rules/style.js#L334 // removed rule about generators/iterators since es2019 natively supports them diff --git a/packages/cli-common/.eslintignore b/packages/cli-common/.eslintignore index 4a74d128..33bffeaa 100644 --- a/packages/cli-common/.eslintignore +++ b/packages/cli-common/.eslintignore @@ -1,3 +1,3 @@ -/.eslintrc.js +/.eslintrc.cjs /dist /build.mjs \ No newline at end of file diff --git a/packages/cli-common/.eslintrc.cjs b/packages/cli-common/.eslintrc.cjs new file mode 100644 index 00000000..1a4bf217 --- /dev/null +++ b/packages/cli-common/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('../../build_utils/eslintrc.cjs') \ No newline at end of file diff --git a/packages/cli-common/.eslintrc.js b/packages/cli-common/.eslintrc.js deleted file mode 100644 index de29daef..00000000 --- a/packages/cli-common/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../build_utils/eslintrc.js') \ No newline at end of file diff --git a/packages/cli-common/package.json b/packages/cli-common/package.json index a0a8151e..bcbfd29b 100644 --- a/packages/cli-common/package.json +++ b/packages/cli-common/package.json @@ -22,8 +22,8 @@ "@jest/globals": "29.7.0", "@types/lodash-es": "^4.17.12", "@types/node": "18", - "@typescript-eslint/eslint-plugin": "6.10.0", - "@typescript-eslint/parser": "6.10.0", + "@typescript-eslint/eslint-plugin": "6.14.0", + "@typescript-eslint/parser": "6.14.0", "eslint": "^8.36.0", "eslint-config-airbnb": "^19.0.4", "eslint-plugin-import": "^2.27.5", diff --git a/packages/cli-common/src/lib/plugins/model.ts b/packages/cli-common/src/lib/plugins/model.ts index 220c0dca..89253e72 100644 --- a/packages/cli-common/src/lib/plugins/model.ts +++ b/packages/cli-common/src/lib/plugins/model.ts @@ -1,6 +1,6 @@ import { FlagProps } from '@oclif/core/lib/interfaces/parser.js' import { Topic } from '@oclif/core/lib/interfaces' -import { Command, Interfaces } from '@oclif/core' +import { Command } from '@oclif/core' import { ComposeModel, config as coreConfig } from '@preevy/core' import { PluginInitContext } from './context.js' import { HookFuncs, HooksListeners } from '../hooks.js' diff --git a/packages/cli/.eslintrc.cjs b/packages/cli/.eslintrc.cjs new file mode 100644 index 00000000..652c451c --- /dev/null +++ b/packages/cli/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('../../build_utils/eslintrc.cjs') diff --git a/packages/cli/.eslintrc.js b/packages/cli/.eslintrc.js deleted file mode 100644 index 25256f88..00000000 --- a/packages/cli/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../build_utils/eslintrc.js') diff --git a/packages/cli/.gitignore b/packages/cli/.gitignore index 64a06d27..0e2ac517 100644 --- a/packages/cli/.gitignore +++ b/packages/cli/.gitignore @@ -11,7 +11,7 @@ node_modules/ coverage # Transpiled files -build/ +/build/ # Optional npm cache directory .npm diff --git a/packages/cli/package.json b/packages/cli/package.json index e509ae65..2aaf5795 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -51,8 +51,8 @@ "@types/lodash-es": "^4.17.12", "@types/node": "18", "@types/shell-escape": "^0.2.1", - "@typescript-eslint/eslint-plugin": "6.10.0", - "@typescript-eslint/parser": "6.10.0", + "@typescript-eslint/eslint-plugin": "6.14.0", + "@typescript-eslint/parser": "6.14.0", "eslint": "^8.36.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-oclif": "^4", diff --git a/packages/cli/src/commands/profile/cp.ts b/packages/cli/src/commands/profile/cp.ts index baee6a89..9d9b51d2 100644 --- a/packages/cli/src/commands/profile/cp.ts +++ b/packages/cli/src/commands/profile/cp.ts @@ -1,5 +1,5 @@ import { Flags, ux } from '@oclif/core' -import inquirer from 'inquirer' +import * as inquirer from '@inquirer/prompts' import { BaseCommand, text } from '@preevy/cli-common' import { LocalProfilesConfig } from '@preevy/core' import { loadProfileConfig } from '../../profile-command.js' @@ -13,16 +13,10 @@ const validateFsType = (fsType: string) => { return fsType } -const chooseTargetAlias = async (defaultAlias: string) => ( - await inquirer.prompt<{ targetAlias: string }>([ - { - type: 'input', - name: 'targetAlias', - message: 'Target profile name', - default: defaultAlias, - }, - ]) -).targetAlias +const chooseTargetAlias = async (defaultAlias: string) => await inquirer.input({ + message: 'Target profile name', + default: defaultAlias, +}) // eslint-disable-next-line no-use-before-define export default class CopyProfile extends BaseCommand { diff --git a/packages/cli/src/commands/profile/link.ts b/packages/cli/src/commands/profile/link.ts index b836331c..8ccfa18d 100644 --- a/packages/cli/src/commands/profile/link.ts +++ b/packages/cli/src/commands/profile/link.ts @@ -1,6 +1,6 @@ import { link, Org, localFs, profileStore, TokenExpiredError, getLivecycleTokensFromLocalFs } from '@preevy/core' import { Flags, ux } from '@oclif/core' -import inquirer from 'inquirer' +import * as inquirer from '@inquirer/prompts' import ProfileCommand from '../../profile-command.js' import { LC_API_URL } from '../../defaults.js' @@ -43,14 +43,13 @@ export default class Link extends ProfileCommand { if (orgs.length === 1) { return orgs[0] } - const selection = await inquirer.prompt<{org: string}>({ type: 'list', - name: 'org', + org = await inquirer.select({ message: 'Choose the organization to link the profile to', choices: orgs.map(o => ({ name: o.name, value: o.slug, - })) }) - org = selection.org + })), + }) } const orgInfo = orgs.find(o => o.slug === org) if (!orgInfo) { diff --git a/packages/cli/src/fs.ts b/packages/cli/src/fs.ts index 2494e100..237c748f 100644 --- a/packages/cli/src/fs.ts +++ b/packages/cli/src/fs.ts @@ -63,7 +63,9 @@ export const chooseFs: Record = { }) => { const region = await inquirerAutoComplete({ message: 'S3 bucket region', - source: async input => S3_REGIONS.filter(r => !input || r.includes(input.toLowerCase())).map(value => ({ value })), + source: async input => S3_REGIONS + .filter(r => !input || r.includes(input.toLowerCase())) + .map(value => ({ value })), default: driver?.name === 'lightsail' && S3_REGIONS.includes(driver.flags.region as string) ? driver.flags.region as string : 'us-east-1', diff --git a/packages/common/.eslintignore b/packages/common/.eslintignore index 4a74d128..33bffeaa 100644 --- a/packages/common/.eslintignore +++ b/packages/common/.eslintignore @@ -1,3 +1,3 @@ -/.eslintrc.js +/.eslintrc.cjs /dist /build.mjs \ No newline at end of file diff --git a/packages/common/.eslintrc.cjs b/packages/common/.eslintrc.cjs new file mode 100644 index 00000000..1a4bf217 --- /dev/null +++ b/packages/common/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('../../build_utils/eslintrc.cjs') \ No newline at end of file diff --git a/packages/common/.eslintrc.js b/packages/common/.eslintrc.js deleted file mode 100644 index de29daef..00000000 --- a/packages/common/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../build_utils/eslintrc.js') \ No newline at end of file diff --git a/packages/common/package.json b/packages/common/package.json index 2bb6f25b..f0938e45 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -27,8 +27,8 @@ "@types/node": "18", "@types/shell-escape": "^0.2.1", "@types/ssh2": "^1.11.8", - "@typescript-eslint/eslint-plugin": "6.10.0", - "@typescript-eslint/parser": "6.10.0", + "@typescript-eslint/eslint-plugin": "6.14.0", + "@typescript-eslint/parser": "6.14.0", "esbuild": "^0.19.9", "eslint": "^8.36.0", "husky": "^8.0.0", diff --git a/packages/compose-tunnel-agent/.eslintignore b/packages/compose-tunnel-agent/.eslintignore index 4a74d128..33bffeaa 100644 --- a/packages/compose-tunnel-agent/.eslintignore +++ b/packages/compose-tunnel-agent/.eslintignore @@ -1,3 +1,3 @@ -/.eslintrc.js +/.eslintrc.cjs /dist /build.mjs \ No newline at end of file diff --git a/packages/compose-tunnel-agent/.eslintrc.cjs b/packages/compose-tunnel-agent/.eslintrc.cjs new file mode 100644 index 00000000..1a4bf217 --- /dev/null +++ b/packages/compose-tunnel-agent/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('../../build_utils/eslintrc.cjs') \ No newline at end of file diff --git a/packages/compose-tunnel-agent/.eslintrc.js b/packages/compose-tunnel-agent/.eslintrc.js deleted file mode 100644 index de29daef..00000000 --- a/packages/compose-tunnel-agent/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../build_utils/eslintrc.js') \ No newline at end of file diff --git a/packages/compose-tunnel-agent/index.ts b/packages/compose-tunnel-agent/index.ts index e848f280..7e737c29 100644 --- a/packages/compose-tunnel-agent/index.ts +++ b/packages/compose-tunnel-agent/index.ts @@ -3,7 +3,7 @@ import path from 'path' import Docker from 'dockerode' import { rimraf } from 'rimraf' import { pino } from 'pino' -import { default as pinoPrettyModule } from 'pino-pretty' +import pinoPrettyModule from 'pino-pretty' import { requiredEnv, formatPublicKey, diff --git a/packages/compose-tunnel-agent/package.json b/packages/compose-tunnel-agent/package.json index 936d6dd7..12703bbb 100644 --- a/packages/compose-tunnel-agent/package.json +++ b/packages/compose-tunnel-agent/package.json @@ -42,8 +42,8 @@ "@types/node-fetch": "^2.6.3", "@types/shell-escape": "^0.2.1", "@types/ssh2": "^1.11.8", - "@typescript-eslint/eslint-plugin": "6.10.0", - "@typescript-eslint/parser": "6.10.0", + "@typescript-eslint/eslint-plugin": "6.14.0", + "@typescript-eslint/parser": "6.14.0", "esbuild": "^0.19.9", "eslint": "^8.36.0", "husky": "^8.0.0", diff --git a/packages/compose-tunnel-agent/src/api-server/index.test.ts b/packages/compose-tunnel-agent/src/api-server/index.test.ts index 6d425bda..34b450ee 100644 --- a/packages/compose-tunnel-agent/src/api-server/index.test.ts +++ b/packages/compose-tunnel-agent/src/api-server/index.test.ts @@ -2,10 +2,10 @@ import { AddressInfo } from 'node:net' import { describe, expect, beforeAll, afterAll, jest, it } from '@jest/globals' import { ChildProcess, spawn, exec } from 'child_process' import { pino } from 'pino' -import { default as pinoPrettyModule } from 'pino-pretty' +import pinoPrettyModule from 'pino-pretty' import Dockerode from 'dockerode' import { inspect, promisify } from 'node:util' -import { default as waitForExpectModule } from 'wait-for-expect' +import waitForExpectModule from 'wait-for-expect' import WebSocket from 'ws' import stripAnsi from 'strip-ansi' import { createApp } from './index.js' diff --git a/packages/core/.eslintignore b/packages/core/.eslintignore index 4a74d128..33bffeaa 100644 --- a/packages/core/.eslintignore +++ b/packages/core/.eslintignore @@ -1,3 +1,3 @@ -/.eslintrc.js +/.eslintrc.cjs /dist /build.mjs \ No newline at end of file diff --git a/packages/core/.eslintrc.cjs b/packages/core/.eslintrc.cjs new file mode 100644 index 00000000..1a4bf217 --- /dev/null +++ b/packages/core/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('../../build_utils/eslintrc.cjs') \ No newline at end of file diff --git a/packages/core/.eslintrc.js b/packages/core/.eslintrc.js deleted file mode 100644 index de29daef..00000000 --- a/packages/core/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../build_utils/eslintrc.js') \ No newline at end of file diff --git a/packages/core/package.json b/packages/core/package.json index a5f2d0b3..4fc31b12 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -56,8 +56,8 @@ "@types/sshpk": "^1.17.1", "@types/tar": "^6.1.4", "@types/tar-stream": "^2.2.2", - "@typescript-eslint/eslint-plugin": "6.10.0", - "@typescript-eslint/parser": "6.10.0", + "@typescript-eslint/eslint-plugin": "6.14.0", + "@typescript-eslint/parser": "6.14.0", "eslint": "^8.36.0", "eslint-config-airbnb": "^19.0.4", "eslint-plugin-import": "^2.27.5", diff --git a/packages/core/src/build/image-tag.test.ts b/packages/core/src/build/image-tag.test.ts new file mode 100644 index 00000000..fc7ad283 --- /dev/null +++ b/packages/core/src/build/image-tag.test.ts @@ -0,0 +1,96 @@ +import { beforeEach, describe, it, expect } from '@jest/globals' +import { GitContext } from '../git.js' +import { FsReader } from '../store/index.js' +import { MockFunctions, mockFunction } from '../test-helpers.js' +import { ImageTagCalculator, imageTagCalculator } from './image-tag.js' + +describe('imageTagCalculator', () => { + const dockerfileContents = 'FROM my-image' + + type PartialGitContext = Pick + + let mockReader: MockFunctions + beforeEach(() => { + mockReader = { read: mockFunction() } + }) + + let mockGitContextCreator: () => PartialGitContext + let mockGitContext: MockFunctions + beforeEach(() => { + mockGitContext = { + commit: mockFunction(async () => 'abcdef'), + localChanges: mockFunction(async () => ''), + } + mockGitContextCreator = mockFunction<() => PartialGitContext>(() => mockGitContext) + }) + + describe('sanity', () => { + let c: ImageTagCalculator + let tag: string + + beforeEach(async () => { + c = imageTagCalculator({ + gitContext: mockGitContextCreator, + fsReader: mockReader, + }) + + tag = await c({ + context: '/context', + dockerfile: 'Dockerfile', + }) + }) + + it('should call the commit function correctly', async () => { + expect(mockGitContext.commit).toHaveBeenCalledWith({ short: true }) + }) + + it('should generate a tag', async () => { + expect(tag).toMatch(/^[a-z0-9-]+$/) + }) + + it('should generate the same tag for the same build', async () => { + expect(await c({ + context: '/context', + dockerfile: 'Dockerfile', + })).toEqual(tag) + }) + + it('should generate the same tag if the dockerfile path changed', async () => { + expect(await c({ + context: '/context', + dockerfile: 'Dockerfile.a', + })).toEqual(tag) + }) + + it('should generate a different tag if the context changed', async () => { + expect(await c({ + context: '/context/a', + dockerfile: 'Dockerfile', + })).not.toEqual(tag) + }) + + it('should generate a different tag if the dockerfile contents changed', async () => { + mockReader.read.mockResolvedValue(Buffer.from(`${dockerfileContents}a`)) + expect(await c({ + context: '/context/a', + dockerfile: 'Dockerfile', + })).not.toEqual(tag) + }) + + it('should generate a different tag if the build args changed', async () => { + expect(await c({ + context: '/context/a', + dockerfile: 'Dockerfile', + args: { foo: 'bar' }, + })).not.toEqual(tag) + }) + + it('should generate a different tag if the build target changed', async () => { + expect(await c({ + context: '/context/a', + dockerfile: 'Dockerfile', + target: 'dev', + })).not.toEqual(tag) + }) + }) +}) diff --git a/packages/core/src/build/image-tag.ts b/packages/core/src/build/image-tag.ts new file mode 100644 index 00000000..2b742e5c --- /dev/null +++ b/packages/core/src/build/image-tag.ts @@ -0,0 +1,68 @@ +import crypto from 'crypto' +import path from 'path' +import url from 'url' +import { memoize } from 'lodash-es' +import { ComposeBuild } from '../compose/index.js' +import { GitContext } from '../git.js' +import { FsReader } from '../store/index.js' +import { tryParseUrl } from '../url.js' +import { randomString } from '../strings.js' + +export type ImageTagCalculator = (build: ComposeBuild) => Promise + +export const imageTagCalculator = ({ gitContext, fsReader }: { + gitContext: (dir: string) => Pick + fsReader: FsReader +}) => { + const calcGitHash = async (dir: string) => { + const gitCtx = gitContext(dir) + const commitHash = await gitCtx.commit({ short: true }) + if (!commitHash) { + return undefined + } + const localChanges = await gitCtx.localChanges() + return localChanges + ? [commitHash, crypto.createHash('sha1').update(localChanges).digest('hex').substring(0, 8)].join('-') + : commitHash + } + + const memoizedCalcGitHash = memoize(calcGitHash) + + const readDockerfileContents = async ({ context, ...build }: ComposeBuild) => { + if ('dockerfile_inline' in build) { + return build.dockerfile_inline + } + + const { dockerfile } = build + + const contextUrl = tryParseUrl(context) + + if (!contextUrl) { + return await fsReader.read(path.resolve(context, dockerfile)) + } + + if (contextUrl.protocol.startsWith('file:')) { + return await fsReader.read(path.resolve(url.fileURLToPath(context), dockerfile)) + } + + return undefined // TODO: attempt to fetch remote URL + } + + return async (build: ComposeBuild) => { + const contextGitHash = await memoizedCalcGitHash(build.context) + if (!contextGitHash) { + return randomString.lowercaseNumeric(8) + } + + const hash = crypto.createHash('sha1').update(build.context).update(build.target ?? '') + + const dockerfileContents = await readDockerfileContents(build) + hash.update(dockerfileContents ?? '') + + if (build.args) { + hash.update(JSON.stringify(build.args)) + } + + return [contextGitHash, hash.digest().toString('hex').substring(0, 8)].join('-') + } +} diff --git a/packages/core/src/build/index.test.ts b/packages/core/src/build/index.test.ts new file mode 100644 index 00000000..9814310c --- /dev/null +++ b/packages/core/src/build/index.test.ts @@ -0,0 +1,361 @@ +import { describe, it, expect, beforeEach, jest } from '@jest/globals' +import { ImageRegistry, generateBuild, parseRegistry } from './index.js' +import { ComposeModel, ComposeService } from '../compose/index.js' +import { ImageTagCalculator } from './image-tag.js' +import { mockFunction } from '../test-helpers.js' + +describe('build', () => { + const ECR_BASE_REGISTRY = '123456789.dkr.ecr.us-east-1.amazonaws.com' + const ECR_REPO = 'my-repo' + const ECR_REPO2 = 'my-repo2' + + describe('parseRegistry', () => { + describe('when given an ECR-style registry', () => { + const REGISTRY = `${ECR_BASE_REGISTRY}/${ECR_REPO}` + + describe('when given singleName=false', () => { + let result: ImageRegistry + + beforeEach(() => { + result = parseRegistry({ registry: REGISTRY, singleName: false }) + }) + it('should not return a singleName', () => { + expect(result).toEqual({ registry: REGISTRY }) + }) + }) + + describe('when given singleName=string', () => { + let result: ImageRegistry + + beforeEach(() => { + result = parseRegistry({ registry: REGISTRY, singleName: ECR_REPO2 }) + }) + it('should return the given singleName', () => { + expect(result).toEqual({ registry: REGISTRY, singleName: ECR_REPO2 }) + }) + }) + + describe('when given singleName=undefined', () => { + let result: ImageRegistry + + beforeEach(() => { + result = parseRegistry({ registry: REGISTRY, singleName: undefined }) + }) + it('should auto-detect the singleName', () => { + expect(result).toEqual({ registry: ECR_BASE_REGISTRY, singleName: ECR_REPO }) + }) + }) + }) + + describe('when given an non-ECR-style registry', () => { + const REGISTRY = 'my-registry' + + describe('when given singleName=false', () => { + let result: ImageRegistry + + beforeEach(() => { + result = parseRegistry({ registry: REGISTRY, singleName: false }) + }) + it('should not return a singleName', () => { + expect(result).toEqual({ registry: REGISTRY }) + }) + }) + + describe('when given singleName=string', () => { + let result: ImageRegistry + + beforeEach(() => { + result = parseRegistry({ registry: REGISTRY, singleName: ECR_REPO2 }) + }) + it('should return the given singleName', () => { + expect(result).toEqual({ registry: REGISTRY, singleName: ECR_REPO2 }) + }) + }) + + describe('when given singleName=undefined', () => { + let result: ImageRegistry + + beforeEach(() => { + result = parseRegistry({ registry: REGISTRY, singleName: undefined }) + }) + it('should not return a singleName', () => { + expect(result).toEqual({ registry: REGISTRY }) + }) + }) + }) + }) + + describe('generateBuild', () => { + let result: Awaited> + let bakeArgs: string[] + + let mockTagCalculator: jest.MockedFunction + beforeEach(() => { + mockTagCalculator = mockFunction(async () => 'abcdef') + }) + + describe('sanity', () => { + beforeEach(async () => { + result = await generateBuild({ + imageTagCalculator: mockTagCalculator, + composeModel: { + name: 'my-project', + services: { + frontend: { + build: { + context: '/context', + dockerfile: 'x/Dockerfile', + target: 'dev', + }, + environment: { + FOO: 'bar', + }, + }, + db: { + image: 'mydb', + }, + }, + }, + buildSpec: { + builder: 'my-builder', + cacheFromRegistry: true, + noCache: false, + registry: { registry: 'my-registry' }, + }, + machineDockerPlatform: 'linux/amd64', + }) + + bakeArgs = result.createBakeArgs('my-file.yaml') + }) + + it('should return a correct build model', () => { + expect(result.buildModel).toEqual({ + name: 'my-project', + services: { + frontend: { + build: { + context: '/context', + dockerfile: 'x/Dockerfile', + target: 'dev', + cache_from: [ + 'my-registry/preevy-my-project-frontend:latest', + 'my-registry/preevy-my-project-frontend:abcdef', + ], + cache_to: [ + 'type=registry,ref=my-registry/preevy-my-project-frontend:latest,mode=max,oci-mediatypes=true,image-manifest=true', + ], + tags: [ + 'my-registry/preevy-my-project-frontend:latest', + 'my-registry/preevy-my-project-frontend:abcdef', + ], + }, + image: 'my-registry/preevy-my-project-frontend:abcdef', + }, + }, + } as ComposeModel) + }) + + it('should return the correct bake args', () => { + expect(bakeArgs).toEqual([ + '-f', 'my-file.yaml', + '--push', + '--builder=my-builder', + '--set=*.platform=linux/amd64', + ]) + }) + + it('should transform the deploy model correctly', () => { + expect(result.deployModel).toEqual({ + name: 'my-project', + services: { + frontend: { + build: { + context: '/context', + dockerfile: 'x/Dockerfile', + target: 'dev', + }, + image: 'my-registry/preevy-my-project-frontend:abcdef', + environment: { + FOO: 'bar', + }, + }, + db: { + image: 'mydb', + }, + }, + } as ComposeModel) + }) + }) + + describe('ECR-style registry', () => { + beforeEach(async () => { + result = await generateBuild({ + imageTagCalculator: mockTagCalculator, + composeModel: { + name: 'my-project', + services: { + frontend: { + build: { + context: '/context', + dockerfile_inline: 'bla', + target: 'dev', + }, + environment: { + FOO: 'bar', + }, + }, + db: { + image: 'mydb', + }, + }, + }, + buildSpec: { + builder: 'my-builder', + cacheFromRegistry: true, + noCache: false, + registry: { registry: 'my-registry', singleName: 'my-repo' }, + }, + machineDockerPlatform: 'linux/amd64', + }) + }) + + it('should return a correct build model', () => { + expect(result.buildModel.services?.frontend).toMatchObject({ + build: { + cache_from: [ + 'my-registry/my-repo:preevy-my-project-frontend-latest', + 'my-registry/my-repo:preevy-my-project-frontend-abcdef', + ], + cache_to: [ + 'type=registry,ref=my-registry/my-repo:preevy-my-project-frontend-latest,mode=max,oci-mediatypes=true,image-manifest=true', + ], + tags: [ + 'my-registry/my-repo:preevy-my-project-frontend-latest', + 'my-registry/my-repo:preevy-my-project-frontend-abcdef', + ], + }, + image: 'my-registry/my-repo:preevy-my-project-frontend-abcdef', + } as ComposeService) + }) + + it('should transform the deploy model correctly', () => { + expect(result.deployModel.services?.frontend).toMatchObject({ + image: 'my-registry/my-repo:preevy-my-project-frontend-abcdef', + }) + }) + }) + + describe('when no registry is given', () => { + beforeEach(async () => { + result = await generateBuild({ + imageTagCalculator: mockTagCalculator, + composeModel: { + name: 'my-project', + services: { + frontend: { + build: { + context: '/context', + dockerfile: 'x/Dockerfile', + target: 'dev', + }, + environment: { + FOO: 'bar', + }, + }, + db: { + image: 'mydb', + }, + }, + }, + buildSpec: { + builder: 'my-builder', + cacheFromRegistry: true, + noCache: false, + registry: undefined, + }, + machineDockerPlatform: 'linux/amd64', + }) + + bakeArgs = result.createBakeArgs('my-file.yaml') + }) + + it('should return a correct build model', () => { + expect(result.buildModel.services?.frontend).toMatchObject({ + build: { + tags: [ + 'preevy-my-project-frontend:latest', + 'preevy-my-project-frontend:abcdef', + ], + }, + image: 'preevy-my-project-frontend:abcdef', + } as ComposeService) + }) + + it('should transform the deploy model correctly', () => { + expect(result.deployModel.services?.frontend).toMatchObject({ + image: 'preevy-my-project-frontend:abcdef', + }) + }) + + it('should return the correct bake args', () => { + expect(bakeArgs).toContain('--load') + expect(bakeArgs).not.toContain('--push') + }) + }) + + describe('when buildSpec.cacheFromRegistry=false and an image is given', () => { + beforeEach(async () => { + result = await generateBuild({ + imageTagCalculator: mockTagCalculator, + composeModel: { + name: 'my-project', + services: { + frontend: { + build: { + context: '/context', + dockerfile: 'x/Dockerfile', + target: 'dev', + cache_from: ['cf1', 'cf2'], + cache_to: ['ct1'], + }, + environment: { + FOO: 'bar', + }, + image: 'my-frontend', + }, + db: { + image: 'mydb', + }, + }, + }, + buildSpec: { + builder: 'my-builder', + cacheFromRegistry: false, + noCache: false, + registry: { registry: 'my-registry' }, + }, + machineDockerPlatform: 'linux/amd64', + }) + + bakeArgs = result.createBakeArgs('my-file.yaml') + }) + + it('should return a correct build model', () => { + expect(result.buildModel.services?.frontend).toMatchObject({ + build: { + context: '/context', + dockerfile: 'x/Dockerfile', + target: 'dev', + tags: [ + 'my-registry/preevy-my-project-frontend:latest', + 'my-registry/preevy-my-project-frontend:abcdef', + ], + cache_from: ['cf1', 'cf2'], + cache_to: ['ct1'], + }, + image: 'my-frontend', + }) + }) + }) + }) +}) diff --git a/packages/core/src/build/index.ts b/packages/core/src/build/index.ts new file mode 100644 index 00000000..260564fd --- /dev/null +++ b/packages/core/src/build/index.ts @@ -0,0 +1,113 @@ +import { mapValues, pickBy } from 'lodash-es' +import { ComposeModel } from '../compose/index.js' +import { hasProp } from '../nulls.js' +import { asyncMapValues } from '../async.js' +import { ImageTagCalculator } from './image-tag.js' + +export type ImageRegistry = { registry: string; singleName?: string } + +export type BuildSpec = { + registry?: ImageRegistry + cacheFromRegistry?: boolean + noCache?: boolean + builder?: string +} + +const ecrRegex = /^(?[0-9]+\.dkr\.ecr\.[^.]+\.*\.amazonaws\.com)\/(?.+)/ + +export const parseRegistry = ( + { registry, singleName }: { registry: string; singleName: undefined | string | false }, +): ImageRegistry => { + if (singleName === undefined) { + const match = ecrRegex.exec(registry) + if (match) { + return match.groups as { registry: string; singleName: string } + } + } + return { registry, singleName: typeof singleName === 'string' ? singleName : undefined } +} + +type ImageRefFactory = ({ image, tag }: { image: string; tag: string }) => string + +const plainImageRefFactory: ImageRefFactory = ({ image, tag }) => `${image}:${tag}` + +const registryImageRefFactory = ({ registry, singleName }: ImageRegistry): ImageRefFactory => ( + singleName + ? ({ image, tag }) => `${registry}/${singleName}:${image}-${tag}` + : ({ image, tag }) => `${registry}/${image}:${tag}` +) + +export const generateBuild = async ({ + composeModel, + buildSpec, + machineDockerPlatform, + imageTagCalculator, +}: { + composeModel: ComposeModel + buildSpec: BuildSpec + machineDockerPlatform: string + imageTagCalculator: ImageTagCalculator +}) => { + const imageRef = buildSpec.registry + ? registryImageRefFactory(buildSpec.registry) + : plainImageRefFactory + + const imageRefForService = (service: string, tag: string) => imageRef({ + image: `preevy-${composeModel.name}-${service}`, + tag, + }) + + const services = await asyncMapValues( + pickBy(composeModel.services ?? {}, hasProp('build')), + async ({ build, image }, serviceName) => { + const latestImage = imageRefForService(serviceName, 'latest') + const serviceTagSuffix = await imageTagCalculator(build) + const thisImage = imageRefForService(serviceName, serviceTagSuffix) + + const cacheFrom = build.cache_from ?? [] + const cacheTo = build.cache_to ?? [] + const tags = build.tags ?? [] + + if (buildSpec.registry && buildSpec.cacheFromRegistry) { + cacheTo.push(`type=registry,ref=${latestImage},mode=max,oci-mediatypes=true,image-manifest=true`) + cacheFrom.push(latestImage) + cacheFrom.push(thisImage) + } + + tags.push(latestImage) + tags.push(thisImage) + + return { + image: image ?? thisImage, + build: { + ...build, + tags, + cache_from: cacheFrom, + cache_to: cacheTo, + }, + } + }, + ) + + const buildModel: ComposeModel = { name: composeModel.name, services } + + const createBakeArgs = (modelFilename: string) => [ + '-f', modelFilename, + ...buildSpec.registry ? ['--push'] : ['--load'], + ...buildSpec.builder ? [`--builder=${buildSpec.builder}`] : [], + ...buildSpec.noCache ? ['--no-cache'] : [], + `--set=*.platform=${machineDockerPlatform}`, + ] + + const deployModel: ComposeModel = { + ...composeModel, + services: { + ...mapValues(composeModel.services, (service, serviceName) => ({ + ...service, + image: buildModel.services?.[serviceName]?.image ?? service.image, + })), + }, + } + + return { buildModel, createBakeArgs, deployModel } +} diff --git a/packages/core/src/compose/remote.ts b/packages/core/src/compose/remote.ts index ed5b5ab1..f9f4bb23 100644 --- a/packages/core/src/compose/remote.ts +++ b/packages/core/src/compose/remote.ts @@ -108,7 +108,11 @@ const fixModelForRemote = async ( } const matchingVolumeSkipIndex = volumeSkipRes.findIndex(re => re.test(volume.source)) if (matchingVolumeSkipIndex !== -1) { - skippedVolumes.push({ service: serviceName, source: volume.source, matchingRule: volumeSkipList[matchingVolumeSkipIndex] }) + skippedVolumes.push({ + service: serviceName, + source: volume.source, + matchingRule: volumeSkipList[matchingVolumeSkipIndex], + }) return volume } diff --git a/packages/core/src/login.ts b/packages/core/src/login.ts index 59b30fe6..0ea8e44c 100644 --- a/packages/core/src/login.ts +++ b/packages/core/src/login.ts @@ -2,7 +2,7 @@ import * as jose from 'jose' import { z } from 'zod' import open from 'open' -import inquirer from 'inquirer' +import * as inquirer from '@inquirer/prompts' import { VirtualFS, localFs } from './store/index.js' import { Logger } from './log.js' import { withSpinner } from './spinner.js' @@ -165,24 +165,14 @@ export const login = async (dataDir: string, loginUrl: string, lcUrl: string, cl if (postLoginResponse.ok) { const postLoginData = await postLoginResponse.json() as PostLoginResult if (!('currentOrg' in postLoginData)) { - const { - orgName, - associateDomain, - // eslint-disable-next-line no-use-before-define - } = await inquirer.prompt<{ orgName: string; associateDomain: string }>([ - { - type: 'input', - name: 'orgName', - message: 'Select a name for your organization', - default: postLoginData.organizationDomainDetails.name, - }, - { - type: 'confirm', - name: 'associateDomain', - message: `Allow anyone with @${postLoginData.organizationDomainDetails.domain} email domain to join your organization as viewers`, - default: true, - }, - ]) + const orgName = await inquirer.input({ + message: 'Select a name for your organization', + default: postLoginData.organizationDomainDetails.name, + }) + const associateDomain = await inquirer.confirm({ + message: `Allow anyone with @${postLoginData.organizationDomainDetails.domain} email domain to join your organization as viewers`, + default: true, + }) const createOrganizationResponse = await withSpinner( () => fetch( diff --git a/packages/core/src/ssh/client/exec.ts b/packages/core/src/ssh/client/exec.ts index 7a188f06..42e95202 100644 --- a/packages/core/src/ssh/client/exec.ts +++ b/packages/core/src/ssh/client/exec.ts @@ -1,5 +1,5 @@ import ssh2 from 'ssh2' -import { default as isStreamModule } from 'is-stream' +import isStreamModule from 'is-stream' import { orderedOutput } from '@preevy/common' import { ExecResult, CommandExecuter, commandWith, checkResult, execResultFromOrderedOutput } from '../../command-executer.js' import { outputFromStdio } from '../../child-process.js' diff --git a/packages/core/src/ssh/keypair.ts b/packages/core/src/ssh/keypair.ts index e587ad63..59505189 100644 --- a/packages/core/src/ssh/keypair.ts +++ b/packages/core/src/ssh/keypair.ts @@ -1,6 +1,6 @@ import crypto from 'crypto' import { promisify } from 'util' -import { default as sshpkModule } from 'sshpk' +import sshpkModule from 'sshpk' const { parseKey, parsePrivateKey } = sshpkModule diff --git a/packages/core/src/telemetry/emitter.ts b/packages/core/src/telemetry/emitter.ts index 17a15ec1..71c7c5d6 100644 --- a/packages/core/src/telemetry/emitter.ts +++ b/packages/core/src/telemetry/emitter.ts @@ -1,7 +1,7 @@ import os from 'os' import fs from 'fs' import crypto from 'crypto' -import { default as stringifyModule } from 'fast-safe-stringify' +import stringifyModule from 'fast-safe-stringify' import { debounce } from 'lodash-es' import pLimit from 'p-limit' import { inspect } from 'util' diff --git a/packages/core/src/upload-files/walk.ts b/packages/core/src/upload-files/walk.ts index dbdda5be..c56b4b4c 100644 --- a/packages/core/src/upload-files/walk.ts +++ b/packages/core/src/upload-files/walk.ts @@ -1,5 +1,5 @@ import fs from 'fs' -import { default as PQueueModule } from 'p-queue' +import PQueueModule from 'p-queue' import { DirInfo, FileInfo } from './files.js' const PQueue = PQueueModule.default diff --git a/packages/driver-azure/.eslintignore b/packages/driver-azure/.eslintignore index 4a74d128..33bffeaa 100644 --- a/packages/driver-azure/.eslintignore +++ b/packages/driver-azure/.eslintignore @@ -1,3 +1,3 @@ -/.eslintrc.js +/.eslintrc.cjs /dist /build.mjs \ No newline at end of file diff --git a/packages/driver-azure/.eslintrc.cjs b/packages/driver-azure/.eslintrc.cjs new file mode 100644 index 00000000..1a4bf217 --- /dev/null +++ b/packages/driver-azure/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('../../build_utils/eslintrc.cjs') \ No newline at end of file diff --git a/packages/driver-azure/.eslintrc.js b/packages/driver-azure/.eslintrc.js deleted file mode 100644 index de29daef..00000000 --- a/packages/driver-azure/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../build_utils/eslintrc.js') \ No newline at end of file diff --git a/packages/driver-azure/package.json b/packages/driver-azure/package.json index 2cbdfde1..bd734694 100644 --- a/packages/driver-azure/package.json +++ b/packages/driver-azure/package.json @@ -33,8 +33,8 @@ "@types/inquirer-autocomplete-prompt": "^3.0.3", "@types/lodash-es": "^4.17.12", "@types/node": "18", - "@typescript-eslint/eslint-plugin": "6.10.0", - "@typescript-eslint/parser": "6.10.0", + "@typescript-eslint/eslint-plugin": "6.14.0", + "@typescript-eslint/parser": "6.14.0", "eslint": "^8.36.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-oclif": "^4", diff --git a/packages/driver-azure/src/static.ts b/packages/driver-azure/src/static.ts index 075fb6ad..af003a00 100644 --- a/packages/driver-azure/src/static.ts +++ b/packages/driver-azure/src/static.ts @@ -1,6 +1,7 @@ import path from 'path' import url from 'url' +// eslint-disable-next-line no-underscore-dangle const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) export const DIR = path.join(__dirname, '../static') diff --git a/packages/driver-gce/.eslintignore b/packages/driver-gce/.eslintignore index 4a74d128..33bffeaa 100644 --- a/packages/driver-gce/.eslintignore +++ b/packages/driver-gce/.eslintignore @@ -1,3 +1,3 @@ -/.eslintrc.js +/.eslintrc.cjs /dist /build.mjs \ No newline at end of file diff --git a/packages/driver-gce/.eslintrc.cjs b/packages/driver-gce/.eslintrc.cjs new file mode 100644 index 00000000..1a4bf217 --- /dev/null +++ b/packages/driver-gce/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('../../build_utils/eslintrc.cjs') \ No newline at end of file diff --git a/packages/driver-gce/.eslintrc.js b/packages/driver-gce/.eslintrc.js deleted file mode 100644 index de29daef..00000000 --- a/packages/driver-gce/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../build_utils/eslintrc.js') \ No newline at end of file diff --git a/packages/driver-gce/package.json b/packages/driver-gce/package.json index f4f39df4..212ab714 100644 --- a/packages/driver-gce/package.json +++ b/packages/driver-gce/package.json @@ -29,8 +29,8 @@ "@types/inquirer-autocomplete-prompt": "^3.0.3", "@types/lodash-es": "^4.17.12", "@types/node": "18", - "@typescript-eslint/eslint-plugin": "6.10.0", - "@typescript-eslint/parser": "6.10.0", + "@typescript-eslint/eslint-plugin": "6.14.0", + "@typescript-eslint/parser": "6.14.0", "eslint": "^8.36.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-oclif": "^4", diff --git a/packages/driver-gce/src/static.ts b/packages/driver-gce/src/static.ts index 65b3a7a7..47bb8829 100644 --- a/packages/driver-gce/src/static.ts +++ b/packages/driver-gce/src/static.ts @@ -2,6 +2,7 @@ import path from 'path' import fs from 'fs' import url from 'url' +// eslint-disable-next-line no-underscore-dangle const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) export const DIR = path.join(__dirname, '../static') diff --git a/packages/driver-kube-pod/.eslintignore b/packages/driver-kube-pod/.eslintignore index 4a74d128..33bffeaa 100644 --- a/packages/driver-kube-pod/.eslintignore +++ b/packages/driver-kube-pod/.eslintignore @@ -1,3 +1,3 @@ -/.eslintrc.js +/.eslintrc.cjs /dist /build.mjs \ No newline at end of file diff --git a/packages/driver-kube-pod/.eslintrc.cjs b/packages/driver-kube-pod/.eslintrc.cjs new file mode 100644 index 00000000..1a4bf217 --- /dev/null +++ b/packages/driver-kube-pod/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('../../build_utils/eslintrc.cjs') \ No newline at end of file diff --git a/packages/driver-kube-pod/.eslintrc.js b/packages/driver-kube-pod/.eslintrc.js deleted file mode 100644 index de29daef..00000000 --- a/packages/driver-kube-pod/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../build_utils/eslintrc.js') \ No newline at end of file diff --git a/packages/driver-kube-pod/package.json b/packages/driver-kube-pod/package.json index dfc84a2e..ddc4aeb4 100644 --- a/packages/driver-kube-pod/package.json +++ b/packages/driver-kube-pod/package.json @@ -31,8 +31,8 @@ "@types/lodash-es": "^4.17.12", "@types/node": "18", "@types/stream-buffers": "^3.0.4", - "@typescript-eslint/eslint-plugin": "6.10.0", - "@typescript-eslint/parser": "6.10.0", + "@typescript-eslint/eslint-plugin": "6.14.0", + "@typescript-eslint/parser": "6.14.0", "eslint": "^8.36.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-oclif": "^4", diff --git a/packages/driver-kube-pod/src/driver/client/exec/api.ts b/packages/driver-kube-pod/src/driver/client/exec/api.ts index bf8b20b3..97be8fc5 100644 --- a/packages/driver-kube-pod/src/driver/client/exec/api.ts +++ b/packages/driver-kube-pod/src/driver/client/exec/api.ts @@ -1,7 +1,7 @@ import * as k8s from '@kubernetes/client-node' import util from 'util' import retry from 'p-retry' -import { default as isStreamModule } from 'is-stream' +import isStreamModule from 'is-stream' import { Logger } from '@preevy/core' import { Writable } from 'stream' import { ProcessOutputBuffers } from '@preevy/common' diff --git a/packages/driver-kube-pod/src/driver/client/exec/kubectl.ts b/packages/driver-kube-pod/src/driver/client/exec/kubectl.ts index c23be13a..40cdfed0 100644 --- a/packages/driver-kube-pod/src/driver/client/exec/kubectl.ts +++ b/packages/driver-kube-pod/src/driver/client/exec/kubectl.ts @@ -1,4 +1,4 @@ -import { default as isStreamModule } from 'is-stream' +import isStreamModule from 'is-stream' import { ProcessOutputBuffers } from '@preevy/common' import { Readable, Writable } from 'stream' import { ChildProcess, StdioOptions, spawn } from 'child_process' diff --git a/packages/driver-kube-pod/src/index.ts b/packages/driver-kube-pod/src/index.ts index 48d20054..552163e8 100644 --- a/packages/driver-kube-pod/src/index.ts +++ b/packages/driver-kube-pod/src/index.ts @@ -1,4 +1,4 @@ import kubeDocker from './driver/index.js' -export type * from './driver/index.js' +export type * from './driver/index.js' export default kubeDocker diff --git a/packages/driver-kube-pod/src/static.ts b/packages/driver-kube-pod/src/static.ts index b58ad84c..c8723301 100644 --- a/packages/driver-kube-pod/src/static.ts +++ b/packages/driver-kube-pod/src/static.ts @@ -1,9 +1,10 @@ import path from 'path' -import packageJsonImport from '../package.json' assert { type: 'json' } import url from 'url' +import packageJsonImport from '../package.json' assert { type: 'json' } +// eslint-disable-next-line no-underscore-dangle const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) export const DIR = path.join(__dirname, '../static') export const DEFAULT_TEMPLATE = path.join(DIR, './default-template.yaml.njk') -export const packageJson = packageJsonImport \ No newline at end of file +export const packageJson = packageJsonImport diff --git a/packages/driver-lightsail/.eslintignore b/packages/driver-lightsail/.eslintignore index 4a74d128..33bffeaa 100644 --- a/packages/driver-lightsail/.eslintignore +++ b/packages/driver-lightsail/.eslintignore @@ -1,3 +1,3 @@ -/.eslintrc.js +/.eslintrc.cjs /dist /build.mjs \ No newline at end of file diff --git a/packages/driver-lightsail/.eslintrc.cjs b/packages/driver-lightsail/.eslintrc.cjs new file mode 100644 index 00000000..1a4bf217 --- /dev/null +++ b/packages/driver-lightsail/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('../../build_utils/eslintrc.cjs') \ No newline at end of file diff --git a/packages/driver-lightsail/.eslintrc.js b/packages/driver-lightsail/.eslintrc.js deleted file mode 100644 index de29daef..00000000 --- a/packages/driver-lightsail/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../build_utils/eslintrc.js') \ No newline at end of file diff --git a/packages/driver-lightsail/package.json b/packages/driver-lightsail/package.json index c1ed69ec..c6c92603 100644 --- a/packages/driver-lightsail/package.json +++ b/packages/driver-lightsail/package.json @@ -28,8 +28,8 @@ "@types/inquirer-autocomplete-prompt": "^3.0.3", "@types/lodash-es": "^4.17.12", "@types/node": "18", - "@typescript-eslint/eslint-plugin": "6.10.0", - "@typescript-eslint/parser": "6.10.0", + "@typescript-eslint/eslint-plugin": "6.14.0", + "@typescript-eslint/parser": "6.14.0", "eslint": "^8.36.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-oclif": "^4", diff --git a/packages/driver-lightsail/src/static.ts b/packages/driver-lightsail/src/static.ts index 075fb6ad..af003a00 100644 --- a/packages/driver-lightsail/src/static.ts +++ b/packages/driver-lightsail/src/static.ts @@ -1,6 +1,7 @@ import path from 'path' import url from 'url' +// eslint-disable-next-line no-underscore-dangle const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) export const DIR = path.join(__dirname, '../static') diff --git a/packages/plugin-github/.eslintignore b/packages/plugin-github/.eslintignore index 4a74d128..33bffeaa 100644 --- a/packages/plugin-github/.eslintignore +++ b/packages/plugin-github/.eslintignore @@ -1,3 +1,3 @@ -/.eslintrc.js +/.eslintrc.cjs /dist /build.mjs \ No newline at end of file diff --git a/packages/plugin-github/.eslintrc.cjs b/packages/plugin-github/.eslintrc.cjs new file mode 100644 index 00000000..1a4bf217 --- /dev/null +++ b/packages/plugin-github/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('../../build_utils/eslintrc.cjs') \ No newline at end of file diff --git a/packages/plugin-github/.eslintrc.js b/packages/plugin-github/.eslintrc.js deleted file mode 100644 index de29daef..00000000 --- a/packages/plugin-github/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../build_utils/eslintrc.js') \ No newline at end of file diff --git a/packages/plugin-github/package.json b/packages/plugin-github/package.json index 11dc4d70..cfe3914b 100644 --- a/packages/plugin-github/package.json +++ b/packages/plugin-github/package.json @@ -20,8 +20,8 @@ }, "devDependencies": { "@types/nunjucks": "^3.2.2", - "@typescript-eslint/eslint-plugin": "6.10.0", - "@typescript-eslint/parser": "6.10.0", + "@typescript-eslint/eslint-plugin": "6.14.0", + "@typescript-eslint/parser": "6.14.0", "eslint": "^8.36.0", "lint-staged": "^15.2.0", "shx": "^0.3.3", diff --git a/tunnel-server/.eslintrc.cjs b/tunnel-server/.eslintrc.cjs index 4b333975..59c60ff4 100644 --- a/tunnel-server/.eslintrc.cjs +++ b/tunnel-server/.eslintrc.cjs @@ -1,6 +1,6 @@ const deepMerge = require('../build_utils/deep_merge') -module.exports = deepMerge(require('../build_utils/eslintrc'), { +module.exports = deepMerge(require('../build_utils/eslintrc.cjs'), { rules: { 'no-underscore-dangle': [ 'warn', diff --git a/tunnel-server/.gitignore b/tunnel-server/.gitignore index 72d4fea4..74e70484 100644 --- a/tunnel-server/.gitignore +++ b/tunnel-server/.gitignore @@ -10,9 +10,6 @@ node_modules/ # Coverage coverage -# Transpiled files -build/ - # Optional npm cache directory .npm diff --git a/tunnel-server/index.ts b/tunnel-server/index.ts index 5fb50d8c..2683f3e0 100644 --- a/tunnel-server/index.ts +++ b/tunnel-server/index.ts @@ -16,6 +16,7 @@ import { cookieSessionStore } from './src/session' import { claimsSchema } from './src/auth' import { createSshServer } from './src/ssh' +// eslint-disable-next-line no-underscore-dangle const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) const log = pino(appLoggerFromEnv()) diff --git a/tunnel-server/package.json b/tunnel-server/package.json index aa28b8fa..35125b05 100644 --- a/tunnel-server/package.json +++ b/tunnel-server/package.json @@ -38,8 +38,8 @@ "@types/node": "18", "@types/node-fetch": "^2.6.4", "@types/ssh2": "^1.11.8", - "@typescript-eslint/eslint-plugin": "6.10.0", - "@typescript-eslint/parser": "6.10.0", + "@typescript-eslint/eslint-plugin": "6.14.0", + "@typescript-eslint/parser": "6.14.0", "eslint": "^8.36.0", "jest": "29.7.0", "nodemon": "^2.0.20", diff --git a/yarn.lock b/yarn.lock index 5e39a5ad..1e23f7b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4310,16 +4310,16 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@6.10.0": - version "6.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.10.0.tgz#cfe2bd34e26d2289212946b96ab19dcad64b661a" - integrity sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg== +"@typescript-eslint/eslint-plugin@6.14.0": + version "6.14.0" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.14.0.tgz#fc1ab5f23618ba590c87e8226ff07a760be3dd7b" + integrity sha512-1ZJBykBCXaSHG94vMMKmiHoL0MhNHKSVlcHVYZNw+BKxufhqQVTOawNpwwI1P5nIFZ/4jLVop0mcY6mJJDFNaw== dependencies: "@eslint-community/regexpp" "^4.5.1" - "@typescript-eslint/scope-manager" "6.10.0" - "@typescript-eslint/type-utils" "6.10.0" - "@typescript-eslint/utils" "6.10.0" - "@typescript-eslint/visitor-keys" "6.10.0" + "@typescript-eslint/scope-manager" "6.14.0" + "@typescript-eslint/type-utils" "6.14.0" + "@typescript-eslint/utils" "6.14.0" + "@typescript-eslint/visitor-keys" "6.14.0" debug "^4.3.4" graphemer "^1.4.0" ignore "^5.2.4" @@ -4353,15 +4353,15 @@ eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/parser@6.10.0": - version "6.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.10.0.tgz#578af79ae7273193b0b6b61a742a2bc8e02f875a" - integrity sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog== +"@typescript-eslint/parser@6.14.0": + version "6.14.0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.14.0.tgz#a2d6a732e0d2b95c73f6a26ae7362877cc1b4212" + integrity sha512-QjToC14CKacd4Pa7JK4GeB/vHmWFJckec49FR4hmIRf97+KXole0T97xxu9IFiPxVQ1DBWrQ5wreLwAGwWAVQA== dependencies: - "@typescript-eslint/scope-manager" "6.10.0" - "@typescript-eslint/types" "6.10.0" - "@typescript-eslint/typescript-estree" "6.10.0" - "@typescript-eslint/visitor-keys" "6.10.0" + "@typescript-eslint/scope-manager" "6.14.0" + "@typescript-eslint/types" "6.14.0" + "@typescript-eslint/typescript-estree" "6.14.0" + "@typescript-eslint/visitor-keys" "6.14.0" debug "^4.3.4" "@typescript-eslint/parser@^4.31.2": @@ -4390,14 +4390,6 @@ "@typescript-eslint/types" "5.54.0" "@typescript-eslint/visitor-keys" "5.54.0" -"@typescript-eslint/scope-manager@6.10.0": - version "6.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz#b0276118b13d16f72809e3cecc86a72c93708540" - integrity sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg== - dependencies: - "@typescript-eslint/types" "6.10.0" - "@typescript-eslint/visitor-keys" "6.10.0" - "@typescript-eslint/scope-manager@6.12.0": version "6.12.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.12.0.tgz#5833a16dbe19cfbad639d4d33bcca5e755c7044b" @@ -4406,13 +4398,21 @@ "@typescript-eslint/types" "6.12.0" "@typescript-eslint/visitor-keys" "6.12.0" -"@typescript-eslint/type-utils@6.10.0": - version "6.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.10.0.tgz#1007faede067c78bdbcef2e8abb31437e163e2e1" - integrity sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg== +"@typescript-eslint/scope-manager@6.14.0": + version "6.14.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.14.0.tgz#53d24363fdb5ee0d1d8cda4ed5e5321272ab3d48" + integrity sha512-VT7CFWHbZipPncAZtuALr9y3EuzY1b1t1AEkIq2bTXUPKw+pHoXflGNG5L+Gv6nKul1cz1VH8fz16IThIU0tdg== + dependencies: + "@typescript-eslint/types" "6.14.0" + "@typescript-eslint/visitor-keys" "6.14.0" + +"@typescript-eslint/type-utils@6.14.0": + version "6.14.0" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.14.0.tgz#ac9cb5ba0615c837f1a6b172feeb273d36e4f8af" + integrity sha512-x6OC9Q7HfYKqjnuNu5a7kffIYs3No30isapRBJl1iCHLitD8O0lFbRcVGiOcuyN837fqXzPZ1NS10maQzZMKqw== dependencies: - "@typescript-eslint/typescript-estree" "6.10.0" - "@typescript-eslint/utils" "6.10.0" + "@typescript-eslint/typescript-estree" "6.14.0" + "@typescript-eslint/utils" "6.14.0" debug "^4.3.4" ts-api-utils "^1.0.1" @@ -4426,16 +4426,16 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.54.0.tgz#7d519df01f50739254d89378e0dcac504cab2740" integrity sha512-nExy+fDCBEgqblasfeE3aQ3NuafBUxZxgxXcYfzYRZFHdVvk5q60KhCSkG0noHgHRo/xQ/BOzURLZAafFpTkmQ== -"@typescript-eslint/types@6.10.0": - version "6.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.10.0.tgz#f4f0a84aeb2ac546f21a66c6e0da92420e921367" - integrity sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg== - "@typescript-eslint/types@6.12.0": version "6.12.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.12.0.tgz#ffc5297bcfe77003c8b7b545b51c2505748314ac" integrity sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q== +"@typescript-eslint/types@6.14.0": + version "6.14.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.14.0.tgz#935307f7a931016b7a5eb25d494ea3e1f613e929" + integrity sha512-uty9H2K4Xs8E47z3SnXEPRNDfsis8JO27amp2GNCnzGETEW3yTqEIVg5+AI7U276oGF/tw6ZA+UesxeQ104ceA== + "@typescript-eslint/typescript-estree@4.33.0": version "4.33.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" @@ -4462,43 +4462,43 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@6.10.0": - version "6.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz#667381eed6f723a1a8ad7590a31f312e31e07697" - integrity sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg== +"@typescript-eslint/typescript-estree@6.12.0": + version "6.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.12.0.tgz#764ccc32598549e5b48ec99e3b85f89b1385310c" + integrity sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw== dependencies: - "@typescript-eslint/types" "6.10.0" - "@typescript-eslint/visitor-keys" "6.10.0" + "@typescript-eslint/types" "6.12.0" + "@typescript-eslint/visitor-keys" "6.12.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/typescript-estree@6.12.0": - version "6.12.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.12.0.tgz#764ccc32598549e5b48ec99e3b85f89b1385310c" - integrity sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw== +"@typescript-eslint/typescript-estree@6.14.0": + version "6.14.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.14.0.tgz#90c7ddd45cd22139adf3d4577580d04c9189ac13" + integrity sha512-yPkaLwK0yH2mZKFE/bXkPAkkFgOv15GJAUzgUVonAbv0Hr4PK/N2yaA/4XQbTZQdygiDkpt5DkxPELqHguNvyw== dependencies: - "@typescript-eslint/types" "6.12.0" - "@typescript-eslint/visitor-keys" "6.12.0" + "@typescript-eslint/types" "6.14.0" + "@typescript-eslint/visitor-keys" "6.14.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/utils@6.10.0": - version "6.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.10.0.tgz#4d76062d94413c30e402c9b0df8c14aef8d77336" - integrity sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg== +"@typescript-eslint/utils@6.14.0": + version "6.14.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.14.0.tgz#856a9e274367d99ffbd39c48128b93a86c4261e3" + integrity sha512-XwRTnbvRr7Ey9a1NT6jqdKX8y/atWG+8fAIu3z73HSP8h06i3r/ClMhmaF/RGWGW1tHJEwij1uEg2GbEmPYvYg== dependencies: "@eslint-community/eslint-utils" "^4.4.0" "@types/json-schema" "^7.0.12" "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "6.10.0" - "@typescript-eslint/types" "6.10.0" - "@typescript-eslint/typescript-estree" "6.10.0" + "@typescript-eslint/scope-manager" "6.14.0" + "@typescript-eslint/types" "6.14.0" + "@typescript-eslint/typescript-estree" "6.14.0" semver "^7.5.4" "@typescript-eslint/utils@^5.10.0": @@ -4544,14 +4544,6 @@ "@typescript-eslint/types" "5.54.0" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@6.10.0": - version "6.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz#b9eaf855a1ac7e95633ae1073af43d451e8f84e3" - integrity sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg== - dependencies: - "@typescript-eslint/types" "6.10.0" - eslint-visitor-keys "^3.4.1" - "@typescript-eslint/visitor-keys@6.12.0": version "6.12.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.12.0.tgz#5877950de42a0f3344261b7a1eee15417306d7e9" @@ -4560,6 +4552,14 @@ "@typescript-eslint/types" "6.12.0" eslint-visitor-keys "^3.4.1" +"@typescript-eslint/visitor-keys@6.14.0": + version "6.14.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.14.0.tgz#1d1d486581819287de824a56c22f32543561138e" + integrity sha512-fB5cw6GRhJUz03MrROVuj5Zm/Q+XWlVdIsFj+Zb1Hvqouc8t+XP2H5y53QYU/MGtd2dPg6/vJJlhoX3xc2ehfw== + dependencies: + "@typescript-eslint/types" "6.14.0" + eslint-visitor-keys "^3.4.1" + "@ungap/structured-clone@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"