From 987fe583eeea9cc72cacccbeb7655617502b43ef Mon Sep 17 00:00:00 2001 From: LekoArts Date: Thu, 4 Jul 2024 13:14:50 +0200 Subject: [PATCH 01/35] move destination discovery earlier into CLI and pass destination around Also force verdaccio usage if destination uses workspaces --- src/cli.ts | 41 ++++++++++++++++------- src/types.ts | 4 +++ src/utils/initial-setup.ts | 55 +++++++++++++++++++++++++------ src/verdaccio/index.ts | 9 ++--- src/verdaccio/install-packages.ts | 19 +++-------- src/verdaccio/publish-package.ts | 2 +- src/watcher.ts | 13 ++++---- 7 files changed, 96 insertions(+), 47 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index bcd848b..b41caba 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -7,7 +7,7 @@ import { detectPackageManager } from 'nypm' import { getConfig } from './utils/config' import { logger } from './utils/logger' import { commands } from './commands' -import { checkDirHasPackageJson, findWorkspacesInSource, getDestinationPackages, getPackageNamesToFilePath, getPackages } from './utils/initial-setup' +import { checkDirHasPackageJson, findWorkspacesInDestination, findWorkspacesInSource, getDestinationPackageNamesToFilePath, getDestinationPackages, getPackageNamesToFilePath, getPackages } from './utils/initial-setup' import type { CliArguments, Destination, Source } from './types' import { CLI_NAME } from './constants' import { watcher } from './watcher' @@ -58,17 +58,33 @@ ${JSON.stringify(seccoConfig, null, 2)}`) checkDirHasPackageJson() + const destinationPath = process.cwd() + const { source: sourceConfig } = seccoConfig - const { hasWorkspaces, workspaces } = findWorkspacesInSource(sourceConfig.path) - const pm = await detectPackageManager(sourceConfig.path, { includeParentDirs: false }) - logger.debug(`Detected package manager in source: ${pm?.name}`) - logger.debug(`Source has workspaces: ${hasWorkspaces}`) + const { hasWorkspaces: sourceHasWorkspaces, workspaces: sourceWorkspaces } = findWorkspacesInSource(sourceConfig.path) + const { hasWorkspaces: destinationHasWorkspaces, workspaces: destinationWorkspaces } = findWorkspacesInDestination(destinationPath) + + const pmSource = await detectPackageManager(sourceConfig.path, { includeParentDirs: false }) + const pmDestination = await detectPackageManager(destinationPath, { includeParentDirs: false }) + + if (!pmDestination) { + logger.fatal(`Failed to detect package manager in ${destinationPath} + +If you have control over the destination, manually add the "packageManager" key to its \`package.json\` file.`) + process.exit() + } + + logger.debug(`Detected package manager in source: ${pmSource?.name}`) + logger.debug(`Detected package manager in destination: ${pmDestination?.name}`) + logger.debug(`Source has workspaces: ${sourceHasWorkspaces}`) + logger.debug(`Destination has workspaces: ${destinationHasWorkspaces}`) - const sourcePackages = getPackages(sourceConfig.path, workspaces) - logger.debug(`Found ${sourcePackages.length} packages in source.`) + const sourcePackages = getPackages(sourceConfig.path, sourceWorkspaces) + logger.debug(`Found ${sourcePackages.length} ${sourcePackages.length === 1 ? 'package' : 'packages'} in source.`) const packageNamesToFilePath = getPackageNamesToFilePath() - const destinationPackages = getDestinationPackages(sourcePackages) - logger.debug(`Found ${destinationPackages.length} destination packages.`) + const destinationPackageNamesToFilePath = getDestinationPackageNamesToFilePath() + const destinationPackages = getDestinationPackages(sourcePackages, destinationWorkspaces) + logger.debug(`Found ${destinationPackages.length} ${destinationPackages.length === 1 ? 'package' : 'packages'} in destination.`) if (!argv?.packageNames && destinationPackages.length === 0) { logger.error(`You haven't got any source dependencies in your current \`package.json\`. @@ -87,14 +103,17 @@ If you only want to use \`${CLI_NAME}\` you'll need to add the dependencies to y const source: Source = { ...sourceConfig, - hasWorkspaces, + hasWorkspaces: sourceHasWorkspaces, packages: sourcePackages, packageNamesToFilePath, - pm, + pm: pmSource, } const destination: Destination = { packages: destinationPackages, + hasWorkspaces: destinationHasWorkspaces, + destinationPackageNamesToFilePath, + pm: pmDestination, } watcher(source, destination, argv.packageNames, { scanOnce: argv.scanOnce, forceVerdaccio: argv.forceVerdaccio, verbose: argv.verbose }) diff --git a/src/types.ts b/src/types.ts index a8bf014..deca1e7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,6 +3,7 @@ import type { Config } from './utils/config' export type PackageNames = Array export type PackageNamesToFilePath = Map +export type DestinationPackageNamesToFilePath = Map export type SourcePackages = Array export type DestinationPackages = Array export type DepTree = Record> @@ -24,7 +25,10 @@ export type Source = Config['source'] & { } export interface Destination { + hasWorkspaces: boolean packages: DestinationPackages + destinationPackageNamesToFilePath: DestinationPackageNamesToFilePath + pm: PackageManager } export interface PackageJson { diff --git a/src/utils/initial-setup.ts b/src/utils/initial-setup.ts index c127fb8..873d743 100644 --- a/src/utils/initial-setup.ts +++ b/src/utils/initial-setup.ts @@ -31,6 +31,15 @@ export function findWorkspacesInSource(sourcePath: Source['path']) { } } +export function findWorkspacesInDestination(destinationPath: string) { + const workspaces = findWorkspaces(destinationPath) + + return { + hasWorkspaces: Boolean(workspaces), + workspaces, + } +} + export function hasConfigFile() { const configPath = join(currentDir, CONFIG_FILE_NAME) return fs.existsSync(configPath) @@ -41,6 +50,7 @@ export function isPrivate(pkgJson: PackageJson) { } const packageNameToFilePath = new Map() +const destinationPackageNameToFilePath = new Map() /** * Returns a map (package name to absolute file path) of packages inside the source repository @@ -49,6 +59,13 @@ export function getPackageNamesToFilePath() { return packageNameToFilePath } +/** + * Returns a map (package name to absolute file path) of packages inside the destination repository + */ +export function getDestinationPackageNamesToFilePath() { + return destinationPackageNameToFilePath +} + /** * Go through the source folder and get all names of packages. * @@ -98,17 +115,35 @@ export function getPackages(sourcePath: Source['path'], workspaces: ReturnType, devDependencies?: Record }>(fs.readFileSync(join(currentDir, 'package.json'), 'utf-8')) +export function getDestinationPackages(sourcePackages: SourcePackages, workspaces: ReturnType['workspaces']) { + if (!workspaces) { + const destPkgJson = destr<{ dependencies?: Record, devDependencies?: Record, name: string }>(fs.readFileSync(join(currentDir, 'package.json'), 'utf-8')) - if (!destPkgJson) - return [] + if (!destPkgJson) + return [] + + destinationPackageNameToFilePath.set(destPkgJson.name, currentDir) - // Intersect sourcePackages with destination dependencies to get list of packages that are used - const destinationPackages = intersection( - sourcePackages, - Object.keys(merge({}, destPkgJson.dependencies, destPkgJson.devDependencies)), - ) + // Intersect sourcePackages with destination dependencies to get list of packages that are used + return intersection( + sourcePackages, + Object.keys(merge({}, destPkgJson.dependencies, destPkgJson.devDependencies)), + ) + } - return destinationPackages + if (workspaces.length > 0) { + return workspaces.map((workspace) => { + const absolutePath = workspace.location + const pkgJson = workspace.package + + destinationPackageNameToFilePath.set(pkgJson.name, absolutePath) + + return intersection( + sourcePackages, + Object.keys(merge({}, pkgJson.dependencies, pkgJson.devDependencies)), + ) + }).flat() + } + + return [] } diff --git a/src/verdaccio/index.ts b/src/verdaccio/index.ts index 3fccb9e..bf7db26 100644 --- a/src/verdaccio/index.ts +++ b/src/verdaccio/index.ts @@ -4,7 +4,7 @@ import { customAlphabet } from 'nanoid/non-secure' import fs from 'fs-extra' import { intersection } from 'lodash-es' import { logger } from '../utils/logger' -import type { DestinationPackages, PackageNamesToFilePath, Source } from '../types' +import type { Destination, PackageNamesToFilePath, Source } from '../types' import { VERDACCIO_CONFIG } from './verdaccio-config' import { publishPackage } from './publish-package' import { installPackages } from './install-packages' @@ -43,13 +43,13 @@ async function startVerdaccio() { export interface PublishPackagesAndInstallArgs { packagesToPublish: Array - destinationPackages: DestinationPackages packageNamesToFilePath: PackageNamesToFilePath ignorePackageJsonChanges: (packageName: string, contentArray: Array) => () => void source: Source + destination: Destination } -export async function publishPackagesAndInstall({ packageNamesToFilePath, destinationPackages, ignorePackageJsonChanges, packagesToPublish, source }: PublishPackagesAndInstallArgs) { +export async function publishPackagesAndInstall({ packageNamesToFilePath, ignorePackageJsonChanges, packagesToPublish, source, destination }: PublishPackagesAndInstallArgs) { await startVerdaccio() const versionPostfix = `${Date.now()}-${nanoid()}` @@ -66,10 +66,11 @@ export async function publishPackagesAndInstall({ packageNamesToFilePath, destin }) } - const packagesToInstall = intersection(packagesToPublish, destinationPackages) + const packagesToInstall = intersection(packagesToPublish, destination.packages) await installPackages({ packagesToInstall, newlyPublishedPackageVersions, + destination, }) } diff --git a/src/verdaccio/install-packages.ts b/src/verdaccio/install-packages.ts index d53444b..7d90ba9 100644 --- a/src/verdaccio/install-packages.ts +++ b/src/verdaccio/install-packages.ts @@ -1,33 +1,24 @@ import process from 'node:process' -import { detectPackageManager } from 'nypm' import { logger } from '../utils/logger' import type { PromisifiedSpawnArgs } from '../utils/promisified-spawn' import { promisifiedSpawn } from '../utils/promisified-spawn' +import type { Destination } from '../types' import { getAddDependenciesCmd } from './add-dependencies' import { REGISTRY_URL } from './verdaccio-config' interface InstallPackagesArgs { packagesToInstall: Array newlyPublishedPackageVersions: Record + destination: Destination } -export async function installPackages({ newlyPublishedPackageVersions, packagesToInstall }: InstallPackagesArgs) { - const cwd = process.cwd() - const pm = await detectPackageManager(cwd, { includeParentDirs: false }) - logger.debug(`Detected package manager in destination: ${pm?.name}`) - - if (!pm) { - logger.fatal(`Failed to detect package manager in ${cwd} - -If you have control over the destination, manually add the "packageManager" key to its \`package.json\` file.`) - process.exit() - } +export async function installPackages({ newlyPublishedPackageVersions, packagesToInstall, destination }: InstallPackagesArgs) { + const { pm } = destination + const { name, majorVersion } = pm const listOfPackagesToInstall = packagesToInstall.map(p => ` - ${p}`).join('\n') logger.log(`Installing packages from local registry:\n${listOfPackagesToInstall}`) - const { name, majorVersion } = pm - let externalRegistry = false let env: NodeJS.ProcessEnv = {} diff --git a/src/verdaccio/publish-package.ts b/src/verdaccio/publish-package.ts index 307f195..0cd9b17 100644 --- a/src/verdaccio/publish-package.ts +++ b/src/verdaccio/publish-package.ts @@ -60,7 +60,7 @@ function adjustPackageJson({ sourcePkgJsonPath, packageName, packageNamesToFileP } } -type PublishPackageArgs = Omit & { +type PublishPackageArgs = Omit & { packageName: string versionPostfix: string } diff --git a/src/watcher.ts b/src/watcher.ts index 821e47e..0ae11df 100644 --- a/src/watcher.ts +++ b/src/watcher.ts @@ -36,17 +36,16 @@ interface PrivateCopyPathArgs extends CopyPathArgs { export async function watcher(source: Source, destination: Destination, packages: PackageNames | undefined, options: WatcherOptions) { const { packageNamesToFilePath, packages: sourcePackages } = source - const { packages: destinationPackages } = destination + const { packages: destinationPackages, hasWorkspaces: destinationHasWorkspaces } = destination const { verbose: isVerbose, scanOnce } = options let { forceVerdaccio } = options setDefaultSpawnStdio(isVerbose ? 'inherit' : 'ignore') - if (false) { - // Current logic of copying files from source to destination doesn't work yet with workspaces (inside destination), so force verdaccio usage for now. - // TODO(feature): Support workspaces in destination - // Reuse find-workspaces package to find all workspaces in destination + // Current logic of copying files from source to destination doesn't work yet with workspaces (inside destination), so force verdaccio usage for now. + if (destinationHasWorkspaces && !forceVerdaccio) { forceVerdaccio = true + logger.info('Workspaces detected in destination. Automatically enabling \`--force-verdaccio\` flag.') } let afterPackageInstallation = false @@ -147,9 +146,9 @@ export async function watcher(source: Source, destination: Destination, packages await publishPackagesAndInstall({ packagesToPublish: allPackagesToWatch, packageNamesToFilePath, - destinationPackages, ignorePackageJsonChanges, source, + destination, }) } else { @@ -288,9 +287,9 @@ export async function watcher(source: Source, destination: Destination, packages await publishPackagesAndInstall({ packagesToPublish: Array.from(packagesToPublish), packageNamesToFilePath, - destinationPackages, ignorePackageJsonChanges, source, + destination, }) packagesToPublish.clear() From 488cae8d687378a46fce4ba75dbd226c0d4f6469 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Thu, 4 Jul 2024 15:32:55 +0200 Subject: [PATCH 02/35] switch destPkg from Map to Set --- src/cli.ts | 6 +++--- src/types.ts | 4 ++-- src/utils/initial-setup.ts | 26 +++++++++++++++++--------- src/watcher.ts | 3 ++- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index b41caba..3ce80a7 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -7,7 +7,7 @@ import { detectPackageManager } from 'nypm' import { getConfig } from './utils/config' import { logger } from './utils/logger' import { commands } from './commands' -import { checkDirHasPackageJson, findWorkspacesInDestination, findWorkspacesInSource, getDestinationPackageNamesToFilePath, getDestinationPackages, getPackageNamesToFilePath, getPackages } from './utils/initial-setup' +import { checkDirHasPackageJson, findWorkspacesInDestination, findWorkspacesInSource, getAbsolutePathsForDestinationPackages, getDestinationPackages, getPackageNamesToFilePath, getPackages } from './utils/initial-setup' import type { CliArguments, Destination, Source } from './types' import { CLI_NAME } from './constants' import { watcher } from './watcher' @@ -82,8 +82,8 @@ If you have control over the destination, manually add the "packageManager" key const sourcePackages = getPackages(sourceConfig.path, sourceWorkspaces) logger.debug(`Found ${sourcePackages.length} ${sourcePackages.length === 1 ? 'package' : 'packages'} in source.`) const packageNamesToFilePath = getPackageNamesToFilePath() - const destinationPackageNamesToFilePath = getDestinationPackageNamesToFilePath() const destinationPackages = getDestinationPackages(sourcePackages, destinationWorkspaces) + const absolutePathsForDestinationPackages = getAbsolutePathsForDestinationPackages() logger.debug(`Found ${destinationPackages.length} ${destinationPackages.length === 1 ? 'package' : 'packages'} in destination.`) if (!argv?.packageNames && destinationPackages.length === 0) { @@ -112,7 +112,7 @@ If you only want to use \`${CLI_NAME}\` you'll need to add the dependencies to y const destination: Destination = { packages: destinationPackages, hasWorkspaces: destinationHasWorkspaces, - destinationPackageNamesToFilePath, + absolutePathsForDestinationPackages, pm: pmDestination, } diff --git a/src/types.ts b/src/types.ts index deca1e7..7dc2c72 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,7 +3,7 @@ import type { Config } from './utils/config' export type PackageNames = Array export type PackageNamesToFilePath = Map -export type DestinationPackageNamesToFilePath = Map +export type AbsolutePathsForDestinationPackages = Set export type SourcePackages = Array export type DestinationPackages = Array export type DepTree = Record> @@ -27,7 +27,7 @@ export type Source = Config['source'] & { export interface Destination { hasWorkspaces: boolean packages: DestinationPackages - destinationPackageNamesToFilePath: DestinationPackageNamesToFilePath + absolutePathsForDestinationPackages: AbsolutePathsForDestinationPackages pm: PackageManager } diff --git a/src/utils/initial-setup.ts b/src/utils/initial-setup.ts index 873d743..d93fab8 100644 --- a/src/utils/initial-setup.ts +++ b/src/utils/initial-setup.ts @@ -50,7 +50,7 @@ export function isPrivate(pkgJson: PackageJson) { } const packageNameToFilePath = new Map() -const destinationPackageNameToFilePath = new Map() +const absolutePathsForDestinationPackages = new Set() /** * Returns a map (package name to absolute file path) of packages inside the source repository @@ -62,8 +62,8 @@ export function getPackageNamesToFilePath() { /** * Returns a map (package name to absolute file path) of packages inside the destination repository */ -export function getDestinationPackageNamesToFilePath() { - return destinationPackageNameToFilePath +export function getAbsolutePathsForDestinationPackages() { + return absolutePathsForDestinationPackages } /** @@ -122,13 +122,17 @@ export function getDestinationPackages(sourcePackages: SourcePackages, workspace if (!destPkgJson) return [] - destinationPackageNameToFilePath.set(destPkgJson.name, currentDir) - // Intersect sourcePackages with destination dependencies to get list of packages that are used - return intersection( + const deps = intersection( sourcePackages, Object.keys(merge({}, destPkgJson.dependencies, destPkgJson.devDependencies)), ) + + if (deps.length > 0) { + absolutePathsForDestinationPackages.add(currentDir) + } + + return deps } if (workspaces.length > 0) { @@ -136,12 +140,16 @@ export function getDestinationPackages(sourcePackages: SourcePackages, workspace const absolutePath = workspace.location const pkgJson = workspace.package - destinationPackageNameToFilePath.set(pkgJson.name, absolutePath) - - return intersection( + const deps = intersection( sourcePackages, Object.keys(merge({}, pkgJson.dependencies, pkgJson.devDependencies)), ) + + if (deps.length > 0) { + absolutePathsForDestinationPackages.add(absolutePath) + } + + return deps }).flat() } diff --git a/src/watcher.ts b/src/watcher.ts index 0ae11df..80f51d1 100644 --- a/src/watcher.ts +++ b/src/watcher.ts @@ -42,7 +42,8 @@ export async function watcher(source: Source, destination: Destination, packages setDefaultSpawnStdio(isVerbose ? 'inherit' : 'ignore') - // Current logic of copying files from source to destination doesn't work yet with workspaces (inside destination), so force verdaccio usage for now. + // Current logic of copying files from source to destination doesn't work with workspaces (inside destination), so force verdaccio usage for now. + // TODO: Implement file copying logic for workspaces in destination if (destinationHasWorkspaces && !forceVerdaccio) { forceVerdaccio = true logger.info('Workspaces detected in destination. Automatically enabling \`--force-verdaccio\` flag.') From 80a1ae7d7bf28ef88dc4069943a64724aa85beb0 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Fri, 5 Jul 2024 09:37:17 +0200 Subject: [PATCH 03/35] improve error --- src/utils/initial-setup.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/utils/initial-setup.ts b/src/utils/initial-setup.ts index d93fab8..8abbef5 100644 --- a/src/utils/initial-setup.ts +++ b/src/utils/initial-setup.ts @@ -78,7 +78,17 @@ export function getAbsolutePathsForDestinationPackages() { export function getPackages(sourcePath: Source['path'], workspaces: ReturnType['workspaces']) { // If workspaces is an empty Array or null, it means it's not a monorepo if (!workspaces) { - const pkgJsonPath = fs.readFileSync(join(sourcePath, 'package.json'), 'utf-8') + let pkgJsonPath = '' + + try { + pkgJsonPath = fs.readFileSync(join(sourcePath, 'package.json'), 'utf-8') + } + catch (e) { + logger.fatal(`Couldn't find package.json in ${sourcePath}. Make sure that the source.path inside \`${CONFIG_FILE_NAME}\` is correct.`) + + process.exit() + } + const pkgJson = destr(pkgJsonPath) if (pkgJson?.name) { From bc0da27c4ab381fd24ce7d40f046637c568c9e3d Mon Sep 17 00:00:00 2001 From: LekoArts Date: Fri, 5 Jul 2024 09:38:29 +0200 Subject: [PATCH 04/35] update tests --- integration/__tests__/kitchen-sink.ts | 10 +-- src/utils/__tests__/initial-setup.ts | 100 ++++++++++++++++++-------- 2 files changed, 76 insertions(+), 34 deletions(-) diff --git a/integration/__tests__/kitchen-sink.ts b/integration/__tests__/kitchen-sink.ts index 0890b90..ed37b44 100644 --- a/integration/__tests__/kitchen-sink.ts +++ b/integration/__tests__/kitchen-sink.ts @@ -1,7 +1,7 @@ import type { Application } from '../models/application' import { presets } from '../presets' -describe.sequential('mode: sequential', () => { +describe.sequential('kitchen sink (single package)', () => { let app: Application beforeAll(async () => { @@ -29,8 +29,8 @@ describe.sequential('mode: sequential', () => { it('verbose should be enabled through --verbose flag', () => { const [exitCode, logs] = app.cli(['--verbose', '--scan-once']) - logs.should.contain('[debug] Found 1 packages in source.') - logs.should.contain('[debug] Found 1 destination packages.') + logs.should.contain('[debug] Found 1 package in source.') + logs.should.contain('[debug] Found 1 package in destination.') expect(exitCode).toBe(0) }) @@ -38,8 +38,8 @@ describe.sequential('mode: sequential', () => { it('verbose should be enabled through VERBOSE env var', () => { const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) - logs.should.contain('[debug] Found 1 packages in source.') - logs.should.contain('[debug] Found 1 destination packages.') + logs.should.contain('[debug] Found 1 package in source.') + logs.should.contain('[debug] Found 1 package in destination.') expect(exitCode).toBe(0) }) diff --git a/src/utils/__tests__/initial-setup.ts b/src/utils/__tests__/initial-setup.ts index 7d00bc2..774798d 100644 --- a/src/utils/__tests__/initial-setup.ts +++ b/src/utils/__tests__/initial-setup.ts @@ -3,55 +3,97 @@ import fs from 'fs-extra' import type { Mock } from 'vitest' import { vi } from 'vitest' import { logger } from '../logger' -import { checkDirHasPackageJson, getDestinationPackages, getPackageNamesToFilePath, getPackages, isPrivate } from '../initial-setup' +import { checkDirHasPackageJson, getAbsolutePathsForDestinationPackages, getDestinationPackages, getPackageNamesToFilePath, getPackages, isPrivate } from '../initial-setup' describe('getDestinationPackages', () => { - it('returns an empty array if destination package.json is missing', () => { - vi.spyOn(fs, 'readFileSync').mockReturnValueOnce(undefined as any) + describe('single package', () => { + it('returns an empty array if destination package.json is missing', () => { + vi.spyOn(fs, 'readFileSync').mockReturnValueOnce(undefined as any) - const result = getDestinationPackages(['package1', 'package2']) + const result = getDestinationPackages(['package1', 'package2'], null) - expect(result).toEqual([]) - }) + expect(result).toEqual([]) + }) - it('returns an empty array if sourcePackages is empty', () => { - vi.spyOn(fs, 'readFileSync').mockReturnValueOnce('{}') + it('returns an empty array if sourcePackages is empty', () => { + vi.spyOn(fs, 'readFileSync').mockReturnValueOnce('{}') - const result = getDestinationPackages([]) + const result = getDestinationPackages([], null) - expect(result).toEqual([]) - }) + expect(result).toEqual([]) + }) - it('returns an empty array if there are no matching dependencies', () => { - vi.spyOn(fs, 'readFileSync').mockReturnValueOnce('{"dependencies": {"package3": "^1.0.0"}}') + it('returns an empty array if there are no matching dependencies', () => { + vi.spyOn(fs, 'readFileSync').mockReturnValueOnce('{"dependencies": {"package3": "^1.0.0"}}') - const result = getDestinationPackages(['package1', 'package2']) + const result = getDestinationPackages(['package1', 'package2'], null) - expect(result).toEqual([]) - }) + expect(result).toEqual([]) + }) - it('returns an array of matching dependencies', () => { - vi.spyOn(fs, 'readFileSync').mockReturnValueOnce('{"dependencies": {"package1": "^1.0.0", "package3": "^1.0.0"}}') + it('returns an array of matching dependencies', () => { + vi.spyOn(fs, 'readFileSync').mockReturnValueOnce('{"dependencies": {"package1": "^1.0.0", "package3": "^1.0.0"}}') - const result = getDestinationPackages(['package1', 'package2']) + const result = getDestinationPackages(['package1', 'package2'], null) - expect(result).toEqual(['package1']) - }) + expect(result).toEqual(['package1']) + }) + + it('returns an array of matching devDependencies', () => { + vi.spyOn(fs, 'readFileSync').mockReturnValueOnce('{"devDependencies": {"package2": "^1.0.0", "package3": "^1.0.0"}}') + + const result = getDestinationPackages(['package1', 'package2'], null) - it('returns an array of matching devDependencies', () => { - vi.spyOn(fs, 'readFileSync').mockReturnValueOnce('{"devDependencies": {"package2": "^1.0.0", "package3": "^1.0.0"}}') + expect(result).toEqual(['package2']) + }) + + it('returns an array of matching dependencies and devDependencies', () => { + vi.spyOn(fs, 'readFileSync').mockReturnValueOnce('{"dependencies": {"package1": "^1.0.0", "package3": "^1.0.0"}, "devDependencies": {"package2": "^1.0.0"}}') + + const result = getDestinationPackages(['package1', 'package2'], null) - const result = getDestinationPackages(['package1', 'package2']) + expect(result).toEqual(['package1', 'package2']) + }) - expect(result).toEqual(['package2']) + it('sets the pkg name + path in destinationPackageNameToFilePath Map if intersection', () => { + vi.spyOn(fs, 'readFileSync').mockReturnValueOnce('{"name": "package2", "dependencies": {"package1": "^1.0.0"}}') + + getDestinationPackages(['package1'], null) + + expect(getAbsolutePathsForDestinationPackages().has(process.cwd())).toBe(true) + }) }) + describe('workspaces', () => { + it('returns an empty array if no workspaces are found', () => { + const result = getDestinationPackages(['package1', 'package2'], []) + + expect(result).toEqual([]) + }) + + it('returns an empty array if there are no matching dependencies', () => { + const result = getDestinationPackages(['package1', 'package2'], [{ location: 'location-package3', package: { name: 'package3', dependencies: { package4: '^1.0.0' } } }]) + + expect(result).toEqual([]) + }) + + it('returns an array of matching dependencies', () => { + const result = getDestinationPackages(['package1', 'package2'], [{ location: 'location-package3', package: { name: 'package3', dependencies: { package1: '^1.0.0' } } }, { location: 'location-package4', package: { name: 'package4', dependencies: { package2: '^1.0.0' } } }]) + + expect(result).toEqual(['package1', 'package2']) + }) - it('returns an array of matching dependencies and devDependencies', () => { - vi.spyOn(fs, 'readFileSync').mockReturnValueOnce('{"dependencies": {"package1": "^1.0.0", "package3": "^1.0.0"}, "devDependencies": {"package2": "^1.0.0"}}') + it('returns an array of matching devDependencies', () => { + const result = getDestinationPackages(['package1', 'package2'], [{ location: 'location-package3', package: { name: 'package3', devDependencies: { package1: '^1.0.0' } } }, { location: 'location-package4', package: { name: 'package4', devDependencies: { package2: '^1.0.0' } } }]) - const result = getDestinationPackages(['package1', 'package2']) + expect(result).toEqual(['package1', 'package2']) + }) - expect(result).toEqual(['package1', 'package2']) + it('sets the pkg name + path in destinationPackageNameToFilePath Map if intersection', () => { + getDestinationPackages(['package1', 'package2'], [{ location: 'location-package3', package: { name: 'package3', dependencies: { package1: '^1.0.0' } } }, { location: 'location-package4', package: { name: 'package4', devDependencies: { package2: '^1.0.0' } } }]) + + expect(getAbsolutePathsForDestinationPackages().has('location-package3')).toBe(true) + expect(getAbsolutePathsForDestinationPackages().has('location-package4')).toBe(true) + }) }) }) From 5a0fc11c8f20a94f189037dc94e59ad58a680904 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Fri, 5 Jul 2024 09:38:53 +0200 Subject: [PATCH 05/35] add last bit of workspaces functionality --- src/utils/__tests__/set-npm-tag-in-deps.ts | 131 +++++++++++++++++++++ src/utils/set-npm-tag-in-deps.ts | 55 +++++++++ src/verdaccio/add-dependencies.ts | 14 ++- src/verdaccio/install-packages.ts | 33 ++++-- 4 files changed, 224 insertions(+), 9 deletions(-) create mode 100644 src/utils/__tests__/set-npm-tag-in-deps.ts create mode 100644 src/utils/set-npm-tag-in-deps.ts diff --git a/src/utils/__tests__/set-npm-tag-in-deps.ts b/src/utils/__tests__/set-npm-tag-in-deps.ts new file mode 100644 index 0000000..4d02d70 --- /dev/null +++ b/src/utils/__tests__/set-npm-tag-in-deps.ts @@ -0,0 +1,131 @@ +import { adjustDeps, setNpmTagInDeps } from '../set-npm-tag-in-deps' + +const TEST_VERSION = '1.0.0-test-09833' + +describe('adjustDeps', () => { + it('should return false if the dependencies object is empty', () => { + const result = adjustDeps({ deps: {}, packagesToInstall: ['package1', 'package2'], newlyPublishedPackageVersions: {} }) + expect(result).toBe(false) + }) + + it('should update the versions of packages to be installed', () => { + const deps = { + package1: '1.0.0', + package2: '2.0.0', + package3: '3.0.0', + } + const result = adjustDeps({ deps, packagesToInstall: ['package1', 'package3'], newlyPublishedPackageVersions: { package1: TEST_VERSION, package3: TEST_VERSION } }) + + expect(result).toBe(true) + expect(deps.package1).toBe(TEST_VERSION) + expect(deps.package2).toBe('2.0.0') + expect(deps.package3).toBe(TEST_VERSION) + }) + + it('should return false if no packages to install are found in the dependencies object', () => { + const deps = { + package1: '1.0.0', + package2: '2.0.0', + package3: '3.0.0', + } + const result = adjustDeps({ deps, packagesToInstall: ['package4', 'package5'], newlyPublishedPackageVersions: {} }) + + expect(result).toBe(false) + expect(deps.package1).toBe('1.0.0') + expect(deps.package2).toBe('2.0.0') + expect(deps.package3).toBe('3.0.0') + }) +}) + +describe('setNpmTagInDeps', () => { + it(`should update dependencies with ${TEST_VERSION}`, () => { + const packageJson = { + dependencies: { + 'package-1': '^1.0.0', + 'react': '^18.0.0', + }, + devDependencies: { + 'package-2': '^1.0.0', + 'vitest': '^1.0.0', + }, + peerDependencies: { + 'package-1': '^1.0.0', + 'react': '^18.0.0', + }, + } + const { updatedPkgJson, changed } = setNpmTagInDeps({ packageJson, packagesToInstall: ['package-1', 'package-2'], newlyPublishedPackageVersions: { 'package-1': TEST_VERSION, 'package-2': TEST_VERSION } }) + + expect(updatedPkgJson.dependencies).toEqual({ + 'package-1': TEST_VERSION, + 'react': '^18.0.0', + }) + expect(updatedPkgJson.devDependencies).toEqual({ + 'package-2': TEST_VERSION, + 'vitest': '^1.0.0', + }) + expect(updatedPkgJson.peerDependencies).toEqual({ + 'package-1': TEST_VERSION, + 'react': '^18.0.0', + }) + + expect(changed).toBe(true) + }) + + it('should not update unrelated dependencies', () => { + const packageJson = { + dependencies: { + 'package-1': '^1.0.0', + 'react': '^18.0.0', + }, + devDependencies: { + 'package-2': '^1.0.0', + }, + peerDependencies: { + 'package-1': '^1.0.0', + 'react': '^18.0.0', + }, + } + const { updatedPkgJson, changed } = setNpmTagInDeps({ packageJson, packagesToInstall: ['package-1'], newlyPublishedPackageVersions: { 'package-1': TEST_VERSION } }) + + expect(updatedPkgJson.dependencies).toEqual({ + 'package-1': TEST_VERSION, + 'react': '^18.0.0', + }) + expect(updatedPkgJson.devDependencies).toEqual({ + 'package-2': '^1.0.0', + }) + expect(updatedPkgJson.peerDependencies).toEqual({ + 'package-1': TEST_VERSION, + 'react': '^18.0.0', + }) + + expect(changed).toBe(true) + }) + + it('should not change pkgJson if no packages to install are found', () => { + const packageJson = { + dependencies: { + react: '^18.0.0', + }, + devDependencies: { + 'package-2': '^1.0.0', + }, + peerDependencies: { + react: '^18.0.0', + }, + } + const { updatedPkgJson, changed } = setNpmTagInDeps({ packageJson, packagesToInstall: ['package-1'], newlyPublishedPackageVersions: { 'package-1': TEST_VERSION } }) + + expect(updatedPkgJson.dependencies).toEqual({ + react: '^18.0.0', + }) + expect(updatedPkgJson.devDependencies).toEqual({ + 'package-2': '^1.0.0', + }) + expect(updatedPkgJson.peerDependencies).toEqual({ + react: '^18.0.0', + }) + + expect(changed).toBe(false) + }) +}) diff --git a/src/utils/set-npm-tag-in-deps.ts b/src/utils/set-npm-tag-in-deps.ts new file mode 100644 index 0000000..656fe97 --- /dev/null +++ b/src/utils/set-npm-tag-in-deps.ts @@ -0,0 +1,55 @@ +import type { PackageJson } from '../types' + +interface SetNpmTagInDepsArgs { + packageJson: PackageJson + packagesToInstall: Array + newlyPublishedPackageVersions: Record +} + +interface AdjustDepsArgs { + deps: PackageJson['dependencies'] | PackageJson['devDependencies'] | PackageJson['peerDependencies'] + packagesToInstall: Array + newlyPublishedPackageVersions: Record +} + +/** + * Traverse the dependencies object and adjust the versions of the packages that are to be installed. + * Use the newlyPublishedPackageVersions object so that the local registry is used for those dependencies. + * + * @returns {boolean} Whether the dependencies object was changed + */ +export function adjustDeps({ deps, packagesToInstall, newlyPublishedPackageVersions }: AdjustDepsArgs) { + if (!deps) + return false + + let changed = false + + Object.keys(deps).forEach((depName) => { + if (packagesToInstall.includes(depName)) { + deps[depName] = newlyPublishedPackageVersions[depName] + changed = true + } + }) + + return changed +} + +/** + * When the destination uses workspaces, the dependencies/devDependencies/peerDependencies versions of source packages need to be changed to the newly published package versions. + * Once this work is done, a mere `npm install` will install the packages from the local registry. + */ +export function setNpmTagInDeps({ packageJson, packagesToInstall, newlyPublishedPackageVersions }: SetNpmTagInDepsArgs) { + // Make a new object to avoid mutating the original package.json + const pkgJson = { ...packageJson } + let changed = false + + // Adjust all dependencies. If any of them are changed, `changed` should be set to true + changed = adjustDeps({ deps: pkgJson.dependencies, packagesToInstall, newlyPublishedPackageVersions }) || changed + changed = adjustDeps({ deps: pkgJson.devDependencies, packagesToInstall, newlyPublishedPackageVersions }) || changed + changed = adjustDeps({ deps: pkgJson.peerDependencies, packagesToInstall, newlyPublishedPackageVersions }) || changed + + return { + updatedPkgJson: pkgJson, + changed, + } +} diff --git a/src/verdaccio/add-dependencies.ts b/src/verdaccio/add-dependencies.ts index 4bfea6c..dd204b2 100644 --- a/src/verdaccio/add-dependencies.ts +++ b/src/verdaccio/add-dependencies.ts @@ -2,8 +2,6 @@ import type { PackageManager, PackageManagerName } from 'nypm' import type { PromisifiedSpawnArgs } from '../utils/promisified-spawn' import { REGISTRY_URL } from './verdaccio-config' -// TODO(feature): Handle workspaces - interface GetAddDependenciesCmdArgs { packages: Array pm: PackageManager @@ -11,6 +9,12 @@ interface GetAddDependenciesCmdArgs { env?: NodeJS.ProcessEnv } +interface GetInstallCmdArgs { + pm: PackageManager + externalRegistry?: boolean + env?: NodeJS.ProcessEnv +} + const installMap: Record = { npm: 'install', pnpm: 'add', @@ -30,3 +34,9 @@ export function getAddDependenciesCmd({ packages, pm, externalRegistry = false, return commands } + +export function getInstallCmd({ pm, externalRegistry = false, env = {} }: GetInstallCmdArgs) { + const commands: PromisifiedSpawnArgs = [pm.command, ['install', !externalRegistry ? `--registry=${REGISTRY_URL}` : null].filter(Boolean), { env }] + + return commands +} diff --git a/src/verdaccio/install-packages.ts b/src/verdaccio/install-packages.ts index 7d90ba9..8dedd36 100644 --- a/src/verdaccio/install-packages.ts +++ b/src/verdaccio/install-packages.ts @@ -1,9 +1,14 @@ import process from 'node:process' +import fs from 'fs-extra' +import { destr } from 'destr' +import { join } from 'pathe' import { logger } from '../utils/logger' import type { PromisifiedSpawnArgs } from '../utils/promisified-spawn' import { promisifiedSpawn } from '../utils/promisified-spawn' -import type { Destination } from '../types' -import { getAddDependenciesCmd } from './add-dependencies' +import type { Destination, PackageJson } from '../types' +import { getAbsolutePathsForDestinationPackages } from '../utils/initial-setup' +import { setNpmTagInDeps } from '../utils/set-npm-tag-in-deps' +import { getAddDependenciesCmd, getInstallCmd } from './add-dependencies' import { REGISTRY_URL } from './verdaccio-config' interface InstallPackagesArgs { @@ -13,7 +18,7 @@ interface InstallPackagesArgs { } export async function installPackages({ newlyPublishedPackageVersions, packagesToInstall, destination }: InstallPackagesArgs) { - const { pm } = destination + const { pm, hasWorkspaces } = destination const { name, majorVersion } = pm const listOfPackagesToInstall = packagesToInstall.map(p => ` - ${p}`).join('\n') @@ -22,8 +27,8 @@ export async function installPackages({ newlyPublishedPackageVersions, packagesT let externalRegistry = false let env: NodeJS.ProcessEnv = {} - // The combination of name and majorVersion allows us to detect yarn 3 - if (name === 'yarn' && majorVersion === '3') + // Yarn Berry + if (name === 'yarn' && (majorVersion === '3' || majorVersion === '4')) externalRegistry = true // TODO(feature): Handle externalRegistry case by detecting yarn 2/3 and modify yarn config // We need to set programatically: @@ -37,8 +42,22 @@ export async function installPackages({ newlyPublishedPackageVersions, packagesT let installCmd!: PromisifiedSpawnArgs - if (false) { - // TODO(feature): Support workspace in destination repository + if (hasWorkspaces) { + const absolutePaths = getAbsolutePathsForDestinationPackages() + + absolutePaths.forEach((absPath) => { + const pkgJsonPath = join(absPath, 'package.json') + const packageJson = destr(fs.readFileSync(pkgJsonPath, 'utf8')) + const { changed, updatedPkgJson } = setNpmTagInDeps({ packageJson, packagesToInstall, newlyPublishedPackageVersions }) + + if (changed) { + logger.debug(`Adjusting dependencies in ${pkgJsonPath} to use newly published versions.`) + + fs.outputJSONSync(pkgJsonPath, updatedPkgJson, { spaces: 2 }) + } + }) + + installCmd = getInstallCmd({ pm, externalRegistry, env }) } else { const packages = packagesToInstall.map((p) => { From e7874bd8b3020d830030e07e87e609914c9426d6 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Fri, 5 Jul 2024 10:25:44 +0200 Subject: [PATCH 06/35] add changeset --- .changeset/silver-oranges-wink.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/silver-oranges-wink.md diff --git a/.changeset/silver-oranges-wink.md b/.changeset/silver-oranges-wink.md new file mode 100644 index 0000000..cc20c90 --- /dev/null +++ b/.changeset/silver-oranges-wink.md @@ -0,0 +1,7 @@ +--- +"secco": minor +--- + +You can now use secco inside destinations that are set up with [workspaces](https://docs.npmjs.com/cli/v10/using-npm/workspaces). It should work for all supported package managers (npm, yarn, pnpm, bun). + +Please note: secco will automatically use the `--force-verdaccio` flag when inside a workspaces project. From f6773e76cd2e13e33b8a692951fd9834568efcea Mon Sep 17 00:00:00 2001 From: LekoArts Date: Fri, 5 Jul 2024 10:29:22 +0200 Subject: [PATCH 07/35] update jsdoc --- src/utils/initial-setup.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/initial-setup.ts b/src/utils/initial-setup.ts index 8abbef5..7777517 100644 --- a/src/utils/initial-setup.ts +++ b/src/utils/initial-setup.ts @@ -53,14 +53,15 @@ const packageNameToFilePath = new Map() const absolutePathsForDestinationPackages = new Set() /** - * Returns a map (package name to absolute file path) of packages inside the source repository + * Returns a Map (package name to absolute file path) of packages inside the source repository */ export function getPackageNamesToFilePath() { return packageNameToFilePath } /** - * Returns a map (package name to absolute file path) of packages inside the destination repository + * Returns a Set of absolute paths to packages inside destination that use source packages. + * Will be later used to only modify the package.json files that are actually using the source packages. */ export function getAbsolutePathsForDestinationPackages() { return absolutePathsForDestinationPackages From c653222583f581f0924e2f22854ea65b79cbb99d Mon Sep 17 00:00:00 2001 From: LekoArts Date: Sun, 7 Jul 2024 20:54:14 +0200 Subject: [PATCH 08/35] update integration tests --- integration/__tests__/kitchen-sink.ts | 58 -------- integration/__tests__/scan-once.ts | 134 ++++++++++++++++++ integration/fixtures/index.ts | 1 + .../destination/fixture.pnpm-workspace.yaml | 2 + .../destination/package.json | 7 + .../destination/packages/destination/bin.mjs | 7 + .../packages/destination/package.json | 12 ++ .../source/package.json | 5 + .../say-hello-world-workspaces/index.mjs | 7 + .../say-hello-world-workspaces/package.json | 6 + .../source/pnpm-workspace.yaml | 2 + integration/presets.ts | 6 + 12 files changed, 189 insertions(+), 58 deletions(-) delete mode 100644 integration/__tests__/kitchen-sink.ts create mode 100644 integration/__tests__/scan-once.ts create mode 100644 integration/fixtures/kitchen-sink-workspaces/destination/fixture.pnpm-workspace.yaml create mode 100644 integration/fixtures/kitchen-sink-workspaces/destination/package.json create mode 100644 integration/fixtures/kitchen-sink-workspaces/destination/packages/destination/bin.mjs create mode 100644 integration/fixtures/kitchen-sink-workspaces/destination/packages/destination/package.json create mode 100644 integration/fixtures/kitchen-sink-workspaces/source/package.json create mode 100644 integration/fixtures/kitchen-sink-workspaces/source/packages/say-hello-world-workspaces/index.mjs create mode 100644 integration/fixtures/kitchen-sink-workspaces/source/packages/say-hello-world-workspaces/package.json create mode 100644 integration/fixtures/kitchen-sink-workspaces/source/pnpm-workspace.yaml diff --git a/integration/__tests__/kitchen-sink.ts b/integration/__tests__/kitchen-sink.ts deleted file mode 100644 index ed37b44..0000000 --- a/integration/__tests__/kitchen-sink.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type { Application } from '../models/application' -import { presets } from '../presets' - -describe.sequential('kitchen sink (single package)', () => { - let app: Application - - beforeAll(async () => { - app = await presets.kitchenSink.commit() - }) - - afterAll(async () => { - await app.cleanup() - }) - - it('should run Verdaccio with --force-verdaccio', () => { - const [exitCode, logs] = app.cli(['--scan-once', '--force-verdaccio'], { verbose: true }) - - logs.should.contain('[log] [Verdaccio] Starting server...') - logs.should.contain('[log] [Verdaccio] Started successfully!') - logs.should.contain('[log] Publishing `say-hello-world@0.0.2-secco-') - logs.should.contain('[log] Published `say-hello-world@0.0.2-secco-') - logs.should.contain(`[debug] Detected package manager in destination: ${app.packageManager.split('@')[0]}`) - logs.should.contain('[log] Installing packages from local registry:') - logs.should.contain('[success] Installation finished successfully!') - - expect(exitCode).toBe(0) - }) - - it('verbose should be enabled through --verbose flag', () => { - const [exitCode, logs] = app.cli(['--verbose', '--scan-once']) - - logs.should.contain('[debug] Found 1 package in source.') - logs.should.contain('[debug] Found 1 package in destination.') - - expect(exitCode).toBe(0) - }) - - it('verbose should be enabled through VERBOSE env var', () => { - const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) - - logs.should.contain('[debug] Found 1 package in source.') - logs.should.contain('[debug] Found 1 package in destination.') - - expect(exitCode).toBe(0) - }) - - it('should copy files on consecutive runs', () => { - const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) - - logs.should.not.contain('[log] [Verdaccio] Starting server...') - logs.should.not.contain('[success] Installation finished successfully!') - logs.should.contain('[log] Copied `index.mjs` to `node_modules/say-hello-world/index.mjs`') - logs.should.contain('[log] Copied `package.json` to `node_modules/say-hello-world/package.json`') - logs.should.contain('[info] Copied 2 files. Exiting...') - - expect(exitCode).toBe(0) - }) -}) diff --git a/integration/__tests__/scan-once.ts b/integration/__tests__/scan-once.ts new file mode 100644 index 0000000..0acfdd6 --- /dev/null +++ b/integration/__tests__/scan-once.ts @@ -0,0 +1,134 @@ +import fs from 'fs-extra' +import { join } from 'pathe' +import type { Application } from '../models/application' +import { presets } from '../presets' + +async function renamePnpmWorkspaceFixture(app: Application) { + const fixture = join(app.dir, 'destination', 'fixture.pnpm-workspace.yaml') + const tmpWorkspaceYaml = join(app.dir, 'destination', 'pnpm-workspace.yaml') + + await fs.rename(fixture, tmpWorkspaceYaml) + + return () => fs.rename(tmpWorkspaceYaml, fixture) +} + +describe.sequential('scan-once', () => { + describe.sequential('single package', () => { + let app: Application + + beforeAll(async () => { + app = await presets.kitchenSink.commit() + }) + + afterAll(async () => { + await app.cleanup() + }) + + it('should run Verdaccio with --force-verdaccio', () => { + const [exitCode, logs] = app.cli(['--scan-once', '--force-verdaccio'], { verbose: true }) + + logs.should.contain('[log] [Verdaccio] Starting server...') + logs.should.contain('[log] [Verdaccio] Started successfully!') + logs.should.contain('[log] Publishing `say-hello-world@0.0.2-secco-') + logs.should.contain('[log] Published `say-hello-world@0.0.2-secco-') + logs.should.contain(`[debug] Detected package manager in destination: ${app.packageManager.split('@')[0]}`) + logs.should.contain('[log] Installing packages from local registry:') + logs.should.contain('[success] Installation finished successfully!') + + expect(exitCode).toBe(0) + }) + + it('verbose should be enabled through --verbose flag', () => { + const [exitCode, logs] = app.cli(['--verbose', '--scan-once']) + + logs.should.contain('[debug] Found 1 package in source.') + logs.should.contain('[debug] Found 1 package in destination.') + + expect(exitCode).toBe(0) + }) + + it('verbose should be enabled through VERBOSE env var', () => { + const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) + + logs.should.contain('[debug] Found 1 package in source.') + logs.should.contain('[debug] Found 1 package in destination.') + + expect(exitCode).toBe(0) + }) + + it('should copy files on consecutive runs', () => { + const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) + + logs.should.not.contain('[log] [Verdaccio] Starting server...') + logs.should.not.contain('[success] Installation finished successfully!') + logs.should.contain('[log] Copied `index.mjs` to `node_modules/say-hello-world/index.mjs`') + logs.should.contain('[log] Copied `package.json` to `node_modules/say-hello-world/package.json`') + logs.should.contain('[info] Copied 2 files. Exiting...') + + expect(exitCode).toBe(0) + }) + }) + + describe.sequential('workspaces', () => { + let app: Application + let restorePnpmFixture: () => Promise + + beforeAll(async () => { + app = await presets.kitchenSinkWorkspaces.commit() + + if (process.env.INTEGRATION_PM_NAME === 'pnpm') { + restorePnpmFixture = await renamePnpmWorkspaceFixture(app) + } + }) + + afterAll(async () => { + await app.cleanup() + + if (process.env.INTEGRATION_PM_NAME === 'pnpm') { + await restorePnpmFixture() + } + }) + + it('should run Verdaccio with --force-verdaccio', () => { + const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) + + logs.should.contain('[log] [Verdaccio] Starting server...') + logs.should.contain('[log] [Verdaccio] Started successfully!') + logs.should.contain('[log] Publishing `say-hello-world-workspaces@1.0.0-secco-') + logs.should.contain('[log] Published `say-hello-world-workspaces@1.0.0-secco-') + logs.should.contain(`[debug] Detected package manager in destination: ${app.packageManager.split('@')[0]}`) + logs.should.contain('[log] Installing packages from local registry:') + logs.should.contain('[success] Installation finished successfully!') + + expect(exitCode).toBe(0) + }) + + it('verbose should be enabled through --verbose flag', () => { + const [exitCode, logs] = app.cli(['--verbose', '--scan-once']) + + logs.should.contain('[debug] Found 1 package in source.') + logs.should.contain('[debug] Found 1 package in destination.') + + expect(exitCode).toBe(0) + }) + + it('verbose should be enabled through VERBOSE env var', () => { + const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) + + logs.should.contain('[debug] Found 1 package in source.') + logs.should.contain('[debug] Found 1 package in destination.') + + expect(exitCode).toBe(0) + }) + + it('should use Verdaccio on consecutive runs', () => { + const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) + + logs.should.contain('[log] [Verdaccio] Starting server...') + logs.should.contain('[success] Installation finished successfully!') + logs.should.contain('[info] Copied 0 files. Exiting...') + + expect(exitCode).toBe(0) + }) + }) +}) diff --git a/integration/fixtures/index.ts b/integration/fixtures/index.ts index 005332c..36e9363 100644 --- a/integration/fixtures/index.ts +++ b/integration/fixtures/index.ts @@ -7,6 +7,7 @@ import { resolve } from 'pathe' */ export const fixtures = { 'kitchen-sink': resolve(__dirname, 'kitchen-sink'), + 'kitchen-sink-workspaces': resolve(__dirname, 'kitchen-sink-workspaces'), } as const export type Fixture = keyof typeof fixtures diff --git a/integration/fixtures/kitchen-sink-workspaces/destination/fixture.pnpm-workspace.yaml b/integration/fixtures/kitchen-sink-workspaces/destination/fixture.pnpm-workspace.yaml new file mode 100644 index 0000000..18ec407 --- /dev/null +++ b/integration/fixtures/kitchen-sink-workspaces/destination/fixture.pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - 'packages/*' diff --git a/integration/fixtures/kitchen-sink-workspaces/destination/package.json b/integration/fixtures/kitchen-sink-workspaces/destination/package.json new file mode 100644 index 0000000..50f79f8 --- /dev/null +++ b/integration/fixtures/kitchen-sink-workspaces/destination/package.json @@ -0,0 +1,7 @@ +{ + "name": "kitchen-sink-workspaces-destination", + "private": true, + "workspaces": [ + "packages/*" + ] +} diff --git a/integration/fixtures/kitchen-sink-workspaces/destination/packages/destination/bin.mjs b/integration/fixtures/kitchen-sink-workspaces/destination/packages/destination/bin.mjs new file mode 100644 index 0000000..2da06e4 --- /dev/null +++ b/integration/fixtures/kitchen-sink-workspaces/destination/packages/destination/bin.mjs @@ -0,0 +1,7 @@ +import { sayHelloWorld } from 'say-hello-world' + +function run() { + sayHelloWorld() +} + +run() diff --git a/integration/fixtures/kitchen-sink-workspaces/destination/packages/destination/package.json b/integration/fixtures/kitchen-sink-workspaces/destination/packages/destination/package.json new file mode 100644 index 0000000..d310fc1 --- /dev/null +++ b/integration/fixtures/kitchen-sink-workspaces/destination/packages/destination/package.json @@ -0,0 +1,12 @@ +{ + "name": "destination", + "version": "1.0.0", + "private": true, + "main": "bin.mjs", + "scripts": { + "start": "node bin.mjs" + }, + "dependencies": { + "say-hello-world-workspaces": "^1.0.0" + } +} diff --git a/integration/fixtures/kitchen-sink-workspaces/source/package.json b/integration/fixtures/kitchen-sink-workspaces/source/package.json new file mode 100644 index 0000000..96b0d97 --- /dev/null +++ b/integration/fixtures/kitchen-sink-workspaces/source/package.json @@ -0,0 +1,5 @@ +{ + "name": "kitchen-sink-workspaces-source", + "private": true, + "packageManager": "pnpm@9.4.0" +} diff --git a/integration/fixtures/kitchen-sink-workspaces/source/packages/say-hello-world-workspaces/index.mjs b/integration/fixtures/kitchen-sink-workspaces/source/packages/say-hello-world-workspaces/index.mjs new file mode 100644 index 0000000..0e31129 --- /dev/null +++ b/integration/fixtures/kitchen-sink-workspaces/source/packages/say-hello-world-workspaces/index.mjs @@ -0,0 +1,7 @@ +function sayHelloWorld() { + // eslint-disable-next-line no-console + console.log('Hello World!') +} +export { + sayHelloWorld, +} diff --git a/integration/fixtures/kitchen-sink-workspaces/source/packages/say-hello-world-workspaces/package.json b/integration/fixtures/kitchen-sink-workspaces/source/packages/say-hello-world-workspaces/package.json new file mode 100644 index 0000000..ad47c9a --- /dev/null +++ b/integration/fixtures/kitchen-sink-workspaces/source/packages/say-hello-world-workspaces/package.json @@ -0,0 +1,6 @@ +{ + "name": "say-hello-world-workspaces", + "version": "1.0.0", + "packageManager": "pnpm@9.4.0", + "main": "index.mjs" +} diff --git a/integration/fixtures/kitchen-sink-workspaces/source/pnpm-workspace.yaml b/integration/fixtures/kitchen-sink-workspaces/source/pnpm-workspace.yaml new file mode 100644 index 0000000..18ec407 --- /dev/null +++ b/integration/fixtures/kitchen-sink-workspaces/source/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - 'packages/*' diff --git a/integration/presets.ts b/integration/presets.ts index 32a57d6..17ee5e8 100644 --- a/integration/presets.ts +++ b/integration/presets.ts @@ -22,6 +22,12 @@ const kitchenSink = applicationConfig() .setTemplate(fixtures['kitchen-sink']) .setPackageManager(constants.INTEGRATION_PM_NAME, constants.INTEGRATION_PM_VERSION) +const kitchenSinkWorkspaces = applicationConfig() + .setName('kitchen-sink-workspaces') + .setTemplate(fixtures['kitchen-sink-workspaces']) + .setPackageManager(constants.INTEGRATION_PM_NAME, constants.INTEGRATION_PM_VERSION) + export const presets = { kitchenSink, + kitchenSinkWorkspaces, } as const From 94c884535b5d77bf457e1608350b72e872abdab3 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Mon, 8 Jul 2024 10:45:43 +0200 Subject: [PATCH 09/35] add VERDACCIO_PORT env var --- src/verdaccio/index.ts | 1 + src/verdaccio/verdaccio-config.ts | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/verdaccio/index.ts b/src/verdaccio/index.ts index bf7db26..ed04a41 100644 --- a/src/verdaccio/index.ts +++ b/src/verdaccio/index.ts @@ -15,6 +15,7 @@ async function startVerdaccio() { let resolved = false logger.log('[Verdaccio] Starting server...') + logger.debug(`[Verdaccio] Port: ${VERDACCIO_CONFIG.port}`) // Clear Verdaccio storage fs.removeSync(VERDACCIO_CONFIG.storage as string) diff --git a/src/verdaccio/verdaccio-config.ts b/src/verdaccio/verdaccio-config.ts index 6c7a845..5d72303 100644 --- a/src/verdaccio/verdaccio-config.ts +++ b/src/verdaccio/verdaccio-config.ts @@ -1,12 +1,24 @@ +/* eslint-disable node/prefer-global/process */ +/* eslint-disable ts/no-namespace */ import os from 'node:os' import { join } from 'pathe' import type { Config as VerdaccioConfig } from '@verdaccio/types' import { CLI_NAME } from '../constants' +declare global { + namespace NodeJS { + interface ProcessEnv { + VERDACCIO_PORT?: string + } + } +} + +const PORT = Number.parseInt(process.env.VERDACCIO_PORT || '') || 4873 // Default + // @ts-expect-error: Verdaccio's types are wrong export const VERDACCIO_CONFIG: VerdaccioConfig = { storage: join(os.tmpdir(), 'verdaccio', 'storage'), - port: 4873, // Default + port: PORT, max_body_size: '100mb', web: { enable: true, From 826400f845e26f8a117ed873e4de4ab7127cc20f Mon Sep 17 00:00:00 2001 From: LekoArts Date: Mon, 8 Jul 2024 10:46:04 +0200 Subject: [PATCH 10/35] update tests --- integration/__tests__/missing-information.ts | 13 +++-- integration/__tests__/scan-once.ts | 54 +++----------------- 2 files changed, 14 insertions(+), 53 deletions(-) diff --git a/integration/__tests__/missing-information.ts b/integration/__tests__/missing-information.ts index 0c2f619..86d25cc 100644 --- a/integration/__tests__/missing-information.ts +++ b/integration/__tests__/missing-information.ts @@ -3,7 +3,7 @@ import { SeccoCLI } from '../helpers/invoke-cli' const missingSourcePackagesLocation = join(__dirname, '..', 'fixtures', 'missing-source-packages') -describe('missing .seccorc', () => { +describe('missing information', () => { it('should display error when no .seccorc or env var is found', () => { const [exitCode, logs] = SeccoCLI().setFixture('empty').invoke(['']) @@ -11,9 +11,7 @@ describe('missing .seccorc', () => { logs.should.contain('Please run `secco init` to create a new `.seccorc` file.') expect(exitCode).toBe(0) }) -}) -describe('missing package.json', () => { it('should display error when no package.json is found', () => { const [exitCode, logs] = SeccoCLI().setFixture('existing-config-file').invoke(['']) @@ -21,9 +19,7 @@ describe('missing package.json', () => { logs.should.contain('Current directory must contain a `package.json` file.') expect(exitCode).toBe(0) }) -}) -describe('missing source packages', () => { it('should display error when no source package is found in package.json', () => { const [exitCode, logs] = SeccoCLI().setFixture('missing-source-packages').setEnv({ SECCO_SOURCE_PATH: missingSourcePackagesLocation }).invoke(['']) @@ -31,4 +27,11 @@ describe('missing source packages', () => { logs.should.contain(`If you only want to use \`secco\` you'll need to add the dependencies to your \`package.json\`.`) expect(exitCode).toBe(0) }) + + it('should display error when source.path is incorrect', () => { + const [exitCode, logs] = SeccoCLI().setFixture('missing-source-packages').setEnv({ SECCO_SOURCE_PATH: '/Users/secco' }).invoke(['']) + + logs.should.contain(`[fatal] Couldn't find package.json in /Users/secco`) + expect(exitCode).toBe(0) + }) }) diff --git a/integration/__tests__/scan-once.ts b/integration/__tests__/scan-once.ts index 0acfdd6..3dc5437 100644 --- a/integration/__tests__/scan-once.ts +++ b/integration/__tests__/scan-once.ts @@ -18,6 +18,8 @@ describe.sequential('scan-once', () => { beforeAll(async () => { app = await presets.kitchenSink.commit() + + process.env.VERDACCIO_PORT = '4873' }) afterAll(async () => { @@ -25,7 +27,7 @@ describe.sequential('scan-once', () => { }) it('should run Verdaccio with --force-verdaccio', () => { - const [exitCode, logs] = app.cli(['--scan-once', '--force-verdaccio'], { verbose: true }) + const [exitCode, logs] = app.cli(['--scan-once', '--force-verdaccio', '--verbose']) logs.should.contain('[log] [Verdaccio] Starting server...') logs.should.contain('[log] [Verdaccio] Started successfully!') @@ -38,24 +40,6 @@ describe.sequential('scan-once', () => { expect(exitCode).toBe(0) }) - it('verbose should be enabled through --verbose flag', () => { - const [exitCode, logs] = app.cli(['--verbose', '--scan-once']) - - logs.should.contain('[debug] Found 1 package in source.') - logs.should.contain('[debug] Found 1 package in destination.') - - expect(exitCode).toBe(0) - }) - - it('verbose should be enabled through VERBOSE env var', () => { - const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) - - logs.should.contain('[debug] Found 1 package in source.') - logs.should.contain('[debug] Found 1 package in destination.') - - expect(exitCode).toBe(0) - }) - it('should copy files on consecutive runs', () => { const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) @@ -79,6 +63,8 @@ describe.sequential('scan-once', () => { if (process.env.INTEGRATION_PM_NAME === 'pnpm') { restorePnpmFixture = await renamePnpmWorkspaceFixture(app) } + + process.env.VERDACCIO_PORT = '4874' }) afterAll(async () => { @@ -89,7 +75,7 @@ describe.sequential('scan-once', () => { } }) - it('should run Verdaccio with --force-verdaccio', () => { + it('should work (with Verdaccio by default)', () => { const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) logs.should.contain('[log] [Verdaccio] Starting server...') @@ -102,33 +88,5 @@ describe.sequential('scan-once', () => { expect(exitCode).toBe(0) }) - - it('verbose should be enabled through --verbose flag', () => { - const [exitCode, logs] = app.cli(['--verbose', '--scan-once']) - - logs.should.contain('[debug] Found 1 package in source.') - logs.should.contain('[debug] Found 1 package in destination.') - - expect(exitCode).toBe(0) - }) - - it('verbose should be enabled through VERBOSE env var', () => { - const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) - - logs.should.contain('[debug] Found 1 package in source.') - logs.should.contain('[debug] Found 1 package in destination.') - - expect(exitCode).toBe(0) - }) - - it('should use Verdaccio on consecutive runs', () => { - const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) - - logs.should.contain('[log] [Verdaccio] Starting server...') - logs.should.contain('[success] Installation finished successfully!') - logs.should.contain('[info] Copied 0 files. Exiting...') - - expect(exitCode).toBe(0) - }) }) }) From f7da2e5576e271ab73b2173f19de2e3e26ff05cc Mon Sep 17 00:00:00 2001 From: LekoArts Date: Mon, 8 Jul 2024 13:03:34 +0200 Subject: [PATCH 11/35] update test --- integration/__tests__/scan-once.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/integration/__tests__/scan-once.ts b/integration/__tests__/scan-once.ts index 3dc5437..d776c2a 100644 --- a/integration/__tests__/scan-once.ts +++ b/integration/__tests__/scan-once.ts @@ -8,8 +8,6 @@ async function renamePnpmWorkspaceFixture(app: Application) { const tmpWorkspaceYaml = join(app.dir, 'destination', 'pnpm-workspace.yaml') await fs.rename(fixture, tmpWorkspaceYaml) - - return () => fs.rename(tmpWorkspaceYaml, fixture) } describe.sequential('scan-once', () => { @@ -55,13 +53,12 @@ describe.sequential('scan-once', () => { describe.sequential('workspaces', () => { let app: Application - let restorePnpmFixture: () => Promise beforeAll(async () => { app = await presets.kitchenSinkWorkspaces.commit() if (process.env.INTEGRATION_PM_NAME === 'pnpm') { - restorePnpmFixture = await renamePnpmWorkspaceFixture(app) + await renamePnpmWorkspaceFixture(app) } process.env.VERDACCIO_PORT = '4874' @@ -69,10 +66,6 @@ describe.sequential('scan-once', () => { afterAll(async () => { await app.cleanup() - - if (process.env.INTEGRATION_PM_NAME === 'pnpm') { - await restorePnpmFixture() - } }) it('should work (with Verdaccio by default)', () => { From 626e79f6bac4465f71c9ebf4eac353cae7319d33 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 08:38:02 +0200 Subject: [PATCH 12/35] Modify test to speed up (revert me) --- .github/workflows/ci.yml | 8 -------- .github/workflows/integration-testing.yml | 12 ------------ 2 files changed, 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d27707..7b0503e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,14 +14,6 @@ jobs: build-cli: name: Build CLI uses: ./.github/workflows/build-cli.yml - size-check: - name: Compressed Size - needs: build-cli - uses: ./.github/workflows/size-check.yml - unit-testing: - name: Unit Testing - needs: build-cli - uses: ./.github/workflows/unit-testing.yml integration-testing: name: Integration Testing needs: build-cli diff --git a/.github/workflows/integration-testing.yml b/.github/workflows/integration-testing.yml index 6b348fa..a50d253 100644 --- a/.github/workflows/integration-testing.yml +++ b/.github/workflows/integration-testing.yml @@ -8,21 +8,9 @@ jobs: strategy: matrix: pm: - - name: npm - displayName: npm - version: 10.8.1 - - name: pnpm - displayName: pnpm - version: 9.4.0 - - name: yarn - displayName: yarn (classic) - version: 1.22.22 - name: yarn displayName: yarn (berry) version: 4.3.1 - - name: bun - displayName: bun - version: 1.1.17 name: ${{ matrix.pm.displayName }} steps: - name: Checkout From 3edb0f03afd72f4d1368e8fa38dbad29c8ef6f63 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 08:55:54 +0200 Subject: [PATCH 13/35] rename --- .../packages/{destination => destination-workspaces}/bin.mjs | 0 .../{destination => destination-workspaces}/package.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename integration/fixtures/kitchen-sink-workspaces/destination/packages/{destination => destination-workspaces}/bin.mjs (100%) rename integration/fixtures/kitchen-sink-workspaces/destination/packages/{destination => destination-workspaces}/package.json (83%) diff --git a/integration/fixtures/kitchen-sink-workspaces/destination/packages/destination/bin.mjs b/integration/fixtures/kitchen-sink-workspaces/destination/packages/destination-workspaces/bin.mjs similarity index 100% rename from integration/fixtures/kitchen-sink-workspaces/destination/packages/destination/bin.mjs rename to integration/fixtures/kitchen-sink-workspaces/destination/packages/destination-workspaces/bin.mjs diff --git a/integration/fixtures/kitchen-sink-workspaces/destination/packages/destination/package.json b/integration/fixtures/kitchen-sink-workspaces/destination/packages/destination-workspaces/package.json similarity index 83% rename from integration/fixtures/kitchen-sink-workspaces/destination/packages/destination/package.json rename to integration/fixtures/kitchen-sink-workspaces/destination/packages/destination-workspaces/package.json index d310fc1..4f3f9b5 100644 --- a/integration/fixtures/kitchen-sink-workspaces/destination/packages/destination/package.json +++ b/integration/fixtures/kitchen-sink-workspaces/destination/packages/destination-workspaces/package.json @@ -1,5 +1,5 @@ { - "name": "destination", + "name": "destination-workspaces", "version": "1.0.0", "private": true, "main": "bin.mjs", From a9b1ba07395406b7b25b825cf166428f37bb326b Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 08:56:01 +0200 Subject: [PATCH 14/35] debug --- integration/__tests__/scan-once.ts | 4 ++++ src/verdaccio/add-dependencies.ts | 4 ++-- src/watcher.ts | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/integration/__tests__/scan-once.ts b/integration/__tests__/scan-once.ts index d776c2a..877a0a5 100644 --- a/integration/__tests__/scan-once.ts +++ b/integration/__tests__/scan-once.ts @@ -41,6 +41,8 @@ describe.sequential('scan-once', () => { it('should copy files on consecutive runs', () => { const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) + logs.logOutput() + logs.should.not.contain('[log] [Verdaccio] Starting server...') logs.should.not.contain('[success] Installation finished successfully!') logs.should.contain('[log] Copied `index.mjs` to `node_modules/say-hello-world/index.mjs`') @@ -71,6 +73,8 @@ describe.sequential('scan-once', () => { it('should work (with Verdaccio by default)', () => { const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) + logs.logOutput() + logs.should.contain('[log] [Verdaccio] Starting server...') logs.should.contain('[log] [Verdaccio] Started successfully!') logs.should.contain('[log] Publishing `say-hello-world-workspaces@1.0.0-secco-') diff --git a/src/verdaccio/add-dependencies.ts b/src/verdaccio/add-dependencies.ts index dd204b2..da4c4d0 100644 --- a/src/verdaccio/add-dependencies.ts +++ b/src/verdaccio/add-dependencies.ts @@ -30,13 +30,13 @@ const exactMap: Record = { } export function getAddDependenciesCmd({ packages, pm, externalRegistry = false, env = {} }: GetAddDependenciesCmdArgs) { - const commands: PromisifiedSpawnArgs = [pm.command, [installMap[pm.name], ...packages, exactMap[pm.name], !externalRegistry ? `--registry=${REGISTRY_URL}` : null].filter(Boolean), { env }] + const commands: PromisifiedSpawnArgs = [pm.command, [installMap[pm.name], ...packages, exactMap[pm.name], '--verbose', !externalRegistry ? `--registry=${REGISTRY_URL}` : null].filter(Boolean), { env }] return commands } export function getInstallCmd({ pm, externalRegistry = false, env = {} }: GetInstallCmdArgs) { - const commands: PromisifiedSpawnArgs = [pm.command, ['install', !externalRegistry ? `--registry=${REGISTRY_URL}` : null].filter(Boolean), { env }] + const commands: PromisifiedSpawnArgs = [pm.command, ['install', !externalRegistry ? `--registry=${REGISTRY_URL}` : null, '--verbose'].filter(Boolean), { env }] return commands } diff --git a/src/watcher.ts b/src/watcher.ts index 80f51d1..9557df9 100644 --- a/src/watcher.ts +++ b/src/watcher.ts @@ -237,6 +237,8 @@ export async function watcher(source: Source, destination: Destination, packages ignoredPackageJson, }) + console.log({ nodeModulesFilePath, packageName, sourcePackages, packageNamesToFilePath, isInitialScan, ignoredPackageJson }) + if (isInitialScan) { // checkDepsChanges can do async GET requests to unpkg.com. We need to make sure that we wait for those requests before attempting to install the dependencies. waitFor.add(didDependenciesChangePromise) @@ -244,6 +246,8 @@ export async function watcher(source: Source, destination: Destination, packages const { didDepsChange, pkgNotInstalled } = await didDependenciesChangePromise + console.log({ didDepsChange, pkgNotInstalled }) + if (pkgNotInstalled) anyPackageNotInstalled = true From 45118efd3659751eb4babce5f9de9e2ef7878c71 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 08:58:53 +0200 Subject: [PATCH 15/35] update --- src/verdaccio/add-dependencies.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/verdaccio/add-dependencies.ts b/src/verdaccio/add-dependencies.ts index da4c4d0..dd204b2 100644 --- a/src/verdaccio/add-dependencies.ts +++ b/src/verdaccio/add-dependencies.ts @@ -30,13 +30,13 @@ const exactMap: Record = { } export function getAddDependenciesCmd({ packages, pm, externalRegistry = false, env = {} }: GetAddDependenciesCmdArgs) { - const commands: PromisifiedSpawnArgs = [pm.command, [installMap[pm.name], ...packages, exactMap[pm.name], '--verbose', !externalRegistry ? `--registry=${REGISTRY_URL}` : null].filter(Boolean), { env }] + const commands: PromisifiedSpawnArgs = [pm.command, [installMap[pm.name], ...packages, exactMap[pm.name], !externalRegistry ? `--registry=${REGISTRY_URL}` : null].filter(Boolean), { env }] return commands } export function getInstallCmd({ pm, externalRegistry = false, env = {} }: GetInstallCmdArgs) { - const commands: PromisifiedSpawnArgs = [pm.command, ['install', !externalRegistry ? `--registry=${REGISTRY_URL}` : null, '--verbose'].filter(Boolean), { env }] + const commands: PromisifiedSpawnArgs = [pm.command, ['install', !externalRegistry ? `--registry=${REGISTRY_URL}` : null].filter(Boolean), { env }] return commands } From 62ac34250cf614179e3dd0de618b4ae4d33ec057 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 09:06:12 +0200 Subject: [PATCH 16/35] test --- .github/workflows/integration-testing.yml | 3 +++ integration/__tests__/scan-once.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/integration-testing.yml b/.github/workflows/integration-testing.yml index a50d253..dd3ab49 100644 --- a/.github/workflows/integration-testing.yml +++ b/.github/workflows/integration-testing.yml @@ -8,6 +8,9 @@ jobs: strategy: matrix: pm: + - name: yarn + displayName: yarn (classic) + version: 1.22.22 - name: yarn displayName: yarn (berry) version: 4.3.1 diff --git a/integration/__tests__/scan-once.ts b/integration/__tests__/scan-once.ts index 877a0a5..06688e6 100644 --- a/integration/__tests__/scan-once.ts +++ b/integration/__tests__/scan-once.ts @@ -53,7 +53,7 @@ describe.sequential('scan-once', () => { }) }) - describe.sequential('workspaces', () => { + describe.sequential.skip('workspaces', () => { let app: Application beforeAll(async () => { From ad8ec9afef7ab782328d02683a08185bf37f04bd Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 09:21:55 +0200 Subject: [PATCH 17/35] actual good fixes --- integration/models/application.ts | 2 +- src/utils/check-deps-changes.ts | 3 ++- src/verdaccio/install-packages.ts | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/integration/models/application.ts b/integration/models/application.ts index e1274db..63f9149 100644 --- a/integration/models/application.ts +++ b/integration/models/application.ts @@ -29,7 +29,7 @@ export function application(config: ApplicationConfig, isolatedDir: string) { cleanup: async () => { logger.log(`Cleaning up...`) - await rm(isolatedDir, { recursive: true, force: true }) + // await rm(isolatedDir, { recursive: true, force: true }) }, } diff --git a/src/utils/check-deps-changes.ts b/src/utils/check-deps-changes.ts index 510bce4..646b8d6 100644 --- a/src/utils/check-deps-changes.ts +++ b/src/utils/check-deps-changes.ts @@ -43,7 +43,8 @@ export async function checkDepsChanges(args: CheckDependencyChangesArgs) { try { // The package might already be installed (e.g. the "latest" version) // nodeModulesFilePath might not exist, but this is okay since we catch the resulting error - nodeModulePkgJson = destr(fs.readFileSync(args.nodeModulesFilePath, 'utf8')) + const nodeModulePkgJsonString = await fs.readFile(args.nodeModulesFilePath, 'utf8') + nodeModulePkgJson = destr(nodeModulePkgJsonString) } catch { pkgNotInstalled = true diff --git a/src/verdaccio/install-packages.ts b/src/verdaccio/install-packages.ts index 12bfb78..7e7ee51 100644 --- a/src/verdaccio/install-packages.ts +++ b/src/verdaccio/install-packages.ts @@ -34,6 +34,7 @@ export async function installPackages({ newlyPublishedPackageVersions, packagesT await execa`yarn config set npmRegistryServer ${REGISTRY_URL}` await execa`yarn config set unsafeHttpWhitelist --json ["localhost"]` + await execa`yarn config set nodeLinker node-modules` } if (name === 'bun') { From b2b0cffffdfc7b1cc129feb21a480e7ccc8d3e72 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 09:24:01 +0200 Subject: [PATCH 18/35] Revert "Modify test to speed up (revert me)" This reverts commit 626e79f6bac4465f71c9ebf4eac353cae7319d33. --- .github/workflows/ci.yml | 8 ++++++++ .github/workflows/integration-testing.yml | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b0503e..1d27707 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,14 @@ jobs: build-cli: name: Build CLI uses: ./.github/workflows/build-cli.yml + size-check: + name: Compressed Size + needs: build-cli + uses: ./.github/workflows/size-check.yml + unit-testing: + name: Unit Testing + needs: build-cli + uses: ./.github/workflows/unit-testing.yml integration-testing: name: Integration Testing needs: build-cli diff --git a/.github/workflows/integration-testing.yml b/.github/workflows/integration-testing.yml index dd3ab49..7b96e9b 100644 --- a/.github/workflows/integration-testing.yml +++ b/.github/workflows/integration-testing.yml @@ -8,12 +8,24 @@ jobs: strategy: matrix: pm: + - name: npm + displayName: npm + version: 10.8.1 + - name: pnpm + displayName: pnpm + version: 9.4.0 + - name: yarn + displayName: yarn (classic) + version: 1.22.22 - name: yarn displayName: yarn (classic) version: 1.22.22 - name: yarn displayName: yarn (berry) version: 4.3.1 + - name: bun + displayName: bun + version: 1.1.17 name: ${{ matrix.pm.displayName }} steps: - name: Checkout From 1ae28919e4e9bd1cad7626e1c6443094b070af8c Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 09:24:46 +0200 Subject: [PATCH 19/35] Revert "debug" This reverts commit a9b1ba07395406b7b25b825cf166428f37bb326b. --- integration/__tests__/scan-once.ts | 4 ---- src/watcher.ts | 4 ---- 2 files changed, 8 deletions(-) diff --git a/integration/__tests__/scan-once.ts b/integration/__tests__/scan-once.ts index 06688e6..2d6dbc5 100644 --- a/integration/__tests__/scan-once.ts +++ b/integration/__tests__/scan-once.ts @@ -41,8 +41,6 @@ describe.sequential('scan-once', () => { it('should copy files on consecutive runs', () => { const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) - logs.logOutput() - logs.should.not.contain('[log] [Verdaccio] Starting server...') logs.should.not.contain('[success] Installation finished successfully!') logs.should.contain('[log] Copied `index.mjs` to `node_modules/say-hello-world/index.mjs`') @@ -73,8 +71,6 @@ describe.sequential('scan-once', () => { it('should work (with Verdaccio by default)', () => { const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) - logs.logOutput() - logs.should.contain('[log] [Verdaccio] Starting server...') logs.should.contain('[log] [Verdaccio] Started successfully!') logs.should.contain('[log] Publishing `say-hello-world-workspaces@1.0.0-secco-') diff --git a/src/watcher.ts b/src/watcher.ts index 9557df9..80f51d1 100644 --- a/src/watcher.ts +++ b/src/watcher.ts @@ -237,8 +237,6 @@ export async function watcher(source: Source, destination: Destination, packages ignoredPackageJson, }) - console.log({ nodeModulesFilePath, packageName, sourcePackages, packageNamesToFilePath, isInitialScan, ignoredPackageJson }) - if (isInitialScan) { // checkDepsChanges can do async GET requests to unpkg.com. We need to make sure that we wait for those requests before attempting to install the dependencies. waitFor.add(didDependenciesChangePromise) @@ -246,8 +244,6 @@ export async function watcher(source: Source, destination: Destination, packages const { didDepsChange, pkgNotInstalled } = await didDependenciesChangePromise - console.log({ didDepsChange, pkgNotInstalled }) - if (pkgNotInstalled) anyPackageNotInstalled = true From 93d6e5c8daeeaa5bda60d12b9cdb809996232291 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 09:25:41 +0200 Subject: [PATCH 20/35] revert more --- integration/__tests__/scan-once.ts | 2 +- integration/models/application.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integration/__tests__/scan-once.ts b/integration/__tests__/scan-once.ts index 2d6dbc5..d776c2a 100644 --- a/integration/__tests__/scan-once.ts +++ b/integration/__tests__/scan-once.ts @@ -51,7 +51,7 @@ describe.sequential('scan-once', () => { }) }) - describe.sequential.skip('workspaces', () => { + describe.sequential('workspaces', () => { let app: Application beforeAll(async () => { diff --git a/integration/models/application.ts b/integration/models/application.ts index 63f9149..e1274db 100644 --- a/integration/models/application.ts +++ b/integration/models/application.ts @@ -29,7 +29,7 @@ export function application(config: ApplicationConfig, isolatedDir: string) { cleanup: async () => { logger.log(`Cleaning up...`) - // await rm(isolatedDir, { recursive: true, force: true }) + await rm(isolatedDir, { recursive: true, force: true }) }, } From 452ebf59831ac9c57671f37940ae600e4b482cde Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 09:28:02 +0200 Subject: [PATCH 21/35] update --- .github/workflows/integration-testing.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/integration-testing.yml b/.github/workflows/integration-testing.yml index 7b96e9b..6b348fa 100644 --- a/.github/workflows/integration-testing.yml +++ b/.github/workflows/integration-testing.yml @@ -17,9 +17,6 @@ jobs: - name: yarn displayName: yarn (classic) version: 1.22.22 - - name: yarn - displayName: yarn (classic) - version: 1.22.22 - name: yarn displayName: yarn (berry) version: 4.3.1 From efeb31f6f7700ece818bc7494a50630082900423 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 10:02:32 +0200 Subject: [PATCH 22/35] put isolated dir into github_env --- integration/models/application-config.ts | 9 +++++++++ integration/presets.ts | 10 ---------- src/types.ts | 13 +++++++++++++ src/verdaccio/verdaccio-config.ts | 10 +--------- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/integration/models/application-config.ts b/integration/models/application-config.ts index 3eda4f1..717f2d8 100644 --- a/integration/models/application-config.ts +++ b/integration/models/application-config.ts @@ -1,10 +1,13 @@ +/* eslint-disable node/prefer-global/process */ import { cp, mkdtemp, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import fs from 'fs-extra' import { basename, join } from 'pathe' +import { execaSync } from 'execa' import { CONFIG_FILE_NAME } from '../../src/constants' import { createLogger } from '../helpers/logger' import { packageManager as rootPackageManager } from '../../package.json' +import { isTruthy } from '../../src/utils/is-truthy' import { application } from './application' export type ApplicationConfig = ReturnType @@ -50,6 +53,12 @@ export function applicationConfig() { const isolatedDir = await mkdtemp(join(tmpdir(), `secco-${name}-`)) + if (isTruthy(process.env.CI) && isTruthy(process.env.GITHUB_ACTIONS)) { + // Make isolatedDir available to other GitHub Actions steps + logger.log('Setting INTEGRATION_ISOLATED_DIR environment variable') + execaSync(`echo "INTEGRATION_ISOLATED_DIR=${isolatedDir}" >> $GITHUB_ENV`) + } + logger.log(`Copying template "${basename(template)}" to "${isolatedDir}"`) await cp(template, isolatedDir, { recursive: true }) diff --git a/integration/presets.ts b/integration/presets.ts index 17ee5e8..79ae138 100644 --- a/integration/presets.ts +++ b/integration/presets.ts @@ -1,4 +1,3 @@ -/* eslint-disable ts/no-namespace */ /* eslint-disable node/prefer-global/process */ import { applicationConfig } from './models/application-config' import { fixtures } from './fixtures' @@ -8,15 +7,6 @@ const constants = { INTEGRATION_PM_VERSION: process.env.INTEGRATION_PM_VERSION, } -declare global { - namespace NodeJS { - interface ProcessEnv { - INTEGRATION_PM_NAME?: 'npm' | 'pnpm' | 'yarn' | 'bun' - INTEGRATION_PM_VERSION?: string - } - } -} - const kitchenSink = applicationConfig() .setName('kitchen-sink') .setTemplate(fixtures['kitchen-sink']) diff --git a/src/types.ts b/src/types.ts index 7dc2c72..0b4f118 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,4 @@ +/* eslint-disable ts/no-namespace */ import type { PackageManager } from 'nypm' import type { Config } from './utils/config' @@ -40,3 +41,15 @@ export interface PackageJson { peerDependencies?: Record workspaces?: Array | { packages: Array } } + +declare global { + namespace NodeJS { + interface ProcessEnv { + INTEGRATION_PM_NAME?: 'npm' | 'pnpm' | 'yarn' | 'bun' + INTEGRATION_PM_VERSION?: string + VERDACCIO_PORT?: string + CI?: string + GITHUB_ACTIONS?: string + } + } +} diff --git a/src/verdaccio/verdaccio-config.ts b/src/verdaccio/verdaccio-config.ts index 5d72303..2436852 100644 --- a/src/verdaccio/verdaccio-config.ts +++ b/src/verdaccio/verdaccio-config.ts @@ -1,18 +1,10 @@ /* eslint-disable node/prefer-global/process */ -/* eslint-disable ts/no-namespace */ + import os from 'node:os' import { join } from 'pathe' import type { Config as VerdaccioConfig } from '@verdaccio/types' import { CLI_NAME } from '../constants' -declare global { - namespace NodeJS { - interface ProcessEnv { - VERDACCIO_PORT?: string - } - } -} - const PORT = Number.parseInt(process.env.VERDACCIO_PORT || '') || 4873 // Default // @ts-expect-error: Verdaccio's types are wrong From 27290c7571be9b291317d54e603913ae4c423626 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 10:14:47 +0200 Subject: [PATCH 23/35] use glob --- .github/workflows/integration-testing.yml | 7 +++++++ integration/models/application-config.ts | 9 --------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/integration-testing.yml b/.github/workflows/integration-testing.yml index 6b348fa..e770fe7 100644 --- a/.github/workflows/integration-testing.yml +++ b/.github/workflows/integration-testing.yml @@ -41,3 +41,10 @@ jobs: env: INTEGRATION_PM_NAME: ${{ matrix.pm.name }} INTEGRATION_PM_VERSION: ${{ matrix.pm.version }} + - name: Upload isolated dirs + if: ${{ cancelled() || failure() }} + uses: actions/upload-artifact@v4 + with: + name: isolated-dirs-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.pm.name }} + path: /tmp/secco-**/**/* + retention-days: 1 diff --git a/integration/models/application-config.ts b/integration/models/application-config.ts index 717f2d8..3eda4f1 100644 --- a/integration/models/application-config.ts +++ b/integration/models/application-config.ts @@ -1,13 +1,10 @@ -/* eslint-disable node/prefer-global/process */ import { cp, mkdtemp, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import fs from 'fs-extra' import { basename, join } from 'pathe' -import { execaSync } from 'execa' import { CONFIG_FILE_NAME } from '../../src/constants' import { createLogger } from '../helpers/logger' import { packageManager as rootPackageManager } from '../../package.json' -import { isTruthy } from '../../src/utils/is-truthy' import { application } from './application' export type ApplicationConfig = ReturnType @@ -53,12 +50,6 @@ export function applicationConfig() { const isolatedDir = await mkdtemp(join(tmpdir(), `secco-${name}-`)) - if (isTruthy(process.env.CI) && isTruthy(process.env.GITHUB_ACTIONS)) { - // Make isolatedDir available to other GitHub Actions steps - logger.log('Setting INTEGRATION_ISOLATED_DIR environment variable') - execaSync(`echo "INTEGRATION_ISOLATED_DIR=${isolatedDir}" >> $GITHUB_ENV`) - } - logger.log(`Copying template "${basename(template)}" to "${isolatedDir}"`) await cp(template, isolatedDir, { recursive: true }) From f3a675ada9104947a4f482a7bdd4374a15ef8582 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 10:15:01 +0200 Subject: [PATCH 24/35] revert me --- .github/workflows/ci.yml | 8 -------- .github/workflows/integration-testing.yml | 12 ------------ 2 files changed, 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d27707..7b0503e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,14 +14,6 @@ jobs: build-cli: name: Build CLI uses: ./.github/workflows/build-cli.yml - size-check: - name: Compressed Size - needs: build-cli - uses: ./.github/workflows/size-check.yml - unit-testing: - name: Unit Testing - needs: build-cli - uses: ./.github/workflows/unit-testing.yml integration-testing: name: Integration Testing needs: build-cli diff --git a/.github/workflows/integration-testing.yml b/.github/workflows/integration-testing.yml index e770fe7..7400a48 100644 --- a/.github/workflows/integration-testing.yml +++ b/.github/workflows/integration-testing.yml @@ -8,21 +8,9 @@ jobs: strategy: matrix: pm: - - name: npm - displayName: npm - version: 10.8.1 - - name: pnpm - displayName: pnpm - version: 9.4.0 - - name: yarn - displayName: yarn (classic) - version: 1.22.22 - name: yarn displayName: yarn (berry) version: 4.3.1 - - name: bun - displayName: bun - version: 1.1.17 name: ${{ matrix.pm.displayName }} steps: - name: Checkout From 7340f518eb867211989946c36b95ddfd740d08ff Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 10:27:09 +0200 Subject: [PATCH 25/35] update --- .github/workflows/integration-testing.yml | 2 +- eslint.config.js | 1 + integration/models/application-config.ts | 3 ++- integration/presets.ts | 1 - src/types.ts | 1 + src/verdaccio/verdaccio-config.ts | 2 -- 6 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/integration-testing.yml b/.github/workflows/integration-testing.yml index 7400a48..3e90278 100644 --- a/.github/workflows/integration-testing.yml +++ b/.github/workflows/integration-testing.yml @@ -34,5 +34,5 @@ jobs: uses: actions/upload-artifact@v4 with: name: isolated-dirs-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.pm.name }} - path: /tmp/secco-**/**/* + path: ${{ runner.temp }}/secco-**/**/* retention-days: 1 diff --git a/eslint.config.js b/eslint.config.js index 87e77bb..becba39 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -30,6 +30,7 @@ export default antfu( }, ], 'ts/array-type': ['error', { default: 'generic' }], + 'node/prefer-global/process': 'off', }, }, ) diff --git a/integration/models/application-config.ts b/integration/models/application-config.ts index 3eda4f1..7022bfe 100644 --- a/integration/models/application-config.ts +++ b/integration/models/application-config.ts @@ -48,7 +48,8 @@ export function applicationConfig() { commit: async () => { logger.log(`Creating application "${name}"`) - const isolatedDir = await mkdtemp(join(tmpdir(), `secco-${name}-`)) + const tempDir = process.env.RUNNER_TEMP || tmpdir() + const isolatedDir = await mkdtemp(join(tempDir, `secco-${name}-`)) logger.log(`Copying template "${basename(template)}" to "${isolatedDir}"`) await cp(template, isolatedDir, { recursive: true }) diff --git a/integration/presets.ts b/integration/presets.ts index 79ae138..14bba9d 100644 --- a/integration/presets.ts +++ b/integration/presets.ts @@ -1,4 +1,3 @@ -/* eslint-disable node/prefer-global/process */ import { applicationConfig } from './models/application-config' import { fixtures } from './fixtures' diff --git a/src/types.ts b/src/types.ts index 0b4f118..9e80fb1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -50,6 +50,7 @@ declare global { VERDACCIO_PORT?: string CI?: string GITHUB_ACTIONS?: string + RUNNER_TEMP?: string } } } diff --git a/src/verdaccio/verdaccio-config.ts b/src/verdaccio/verdaccio-config.ts index 2436852..6b68081 100644 --- a/src/verdaccio/verdaccio-config.ts +++ b/src/verdaccio/verdaccio-config.ts @@ -1,5 +1,3 @@ -/* eslint-disable node/prefer-global/process */ - import os from 'node:os' import { join } from 'pathe' import type { Config as VerdaccioConfig } from '@verdaccio/types' From 8cec6101fd2816c6997918769a34215ba73f3dc1 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 10:29:36 +0200 Subject: [PATCH 26/35] update --- .github/workflows/integration-testing.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integration-testing.yml b/.github/workflows/integration-testing.yml index 3e90278..6cabdc1 100644 --- a/.github/workflows/integration-testing.yml +++ b/.github/workflows/integration-testing.yml @@ -29,10 +29,10 @@ jobs: env: INTEGRATION_PM_NAME: ${{ matrix.pm.name }} INTEGRATION_PM_VERSION: ${{ matrix.pm.version }} - - name: Upload isolated dirs + - name: Upload temp dir (optional) if: ${{ cancelled() || failure() }} uses: actions/upload-artifact@v4 with: - name: isolated-dirs-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.pm.name }} - path: ${{ runner.temp }}/secco-**/**/* + name: temp-dir-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.pm.name }} + path: ${{ runner.temp }} retention-days: 1 From 12fa378f8a58ff838d9d4ea57e402d3be99782e5 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 10:33:15 +0200 Subject: [PATCH 27/35] update --- .github/workflows/integration-testing.yml | 2 +- integration/models/application.ts | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/integration-testing.yml b/.github/workflows/integration-testing.yml index 6cabdc1..492bf0b 100644 --- a/.github/workflows/integration-testing.yml +++ b/.github/workflows/integration-testing.yml @@ -34,5 +34,5 @@ jobs: uses: actions/upload-artifact@v4 with: name: temp-dir-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.pm.name }} - path: ${{ runner.temp }} + path: ${{ runner.temp }}/secco-**/**/* retention-days: 1 diff --git a/integration/models/application.ts b/integration/models/application.ts index e1274db..b948337 100644 --- a/integration/models/application.ts +++ b/integration/models/application.ts @@ -3,6 +3,7 @@ import { join } from 'pathe' import type { InvokeResult } from '../helpers/invoke-cli' import { SeccoCLI } from '../helpers/invoke-cli' import { createLogger } from '../helpers/logger' +import { isTruthy } from '../../src/utils/is-truthy' import type { ApplicationConfig } from './application-config' export type Application = ReturnType @@ -27,6 +28,11 @@ export function application(config: ApplicationConfig, isolatedDir: string) { }).invoke(args) }, cleanup: async () => { + if (isTruthy(process.env.CI)) { + logger.log(`Skipping cleanup in CI environment`) + return + } + logger.log(`Cleaning up...`) await rm(isolatedDir, { recursive: true, force: true }) From 4b4e87341d80a66c438af42a634ec6ab2a2d06f5 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 10:50:08 +0200 Subject: [PATCH 28/35] debug --- integration/__tests__/scan-once.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/integration/__tests__/scan-once.ts b/integration/__tests__/scan-once.ts index d776c2a..f5846a3 100644 --- a/integration/__tests__/scan-once.ts +++ b/integration/__tests__/scan-once.ts @@ -71,6 +71,8 @@ describe.sequential('scan-once', () => { it('should work (with Verdaccio by default)', () => { const [exitCode, logs] = app.cli(['--scan-once'], { verbose: true }) + logs.logOutput() + logs.should.contain('[log] [Verdaccio] Starting server...') logs.should.contain('[log] [Verdaccio] Started successfully!') logs.should.contain('[log] Publishing `say-hello-world-workspaces@1.0.0-secco-') From 4ea89305986074ed3e3611b4773cd22ed59e6fff Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 10:50:43 +0200 Subject: [PATCH 29/35] fix VERBOSE bug --- .changeset/beige-rings-brake.md | 5 +++++ src/cli.ts | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .changeset/beige-rings-brake.md diff --git a/.changeset/beige-rings-brake.md b/.changeset/beige-rings-brake.md new file mode 100644 index 0000000..629286e --- /dev/null +++ b/.changeset/beige-rings-brake.md @@ -0,0 +1,5 @@ +--- +"secco": patch +--- + +Correctly set internal "verbose" value when VERBOSE env var is set diff --git a/src/cli.ts b/src/cli.ts index 3ce80a7..c616d57 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -47,8 +47,9 @@ const parser = yargsInstace async function run() { const argv: CliArguments = await parser + const verbose = argv.verbose || isTruthy(process.env.VERBOSE) - if (argv.verbose || isTruthy(process.env.VERBOSE)) + if (verbose) logger.level = 4 const seccoConfig = getConfig() @@ -116,7 +117,7 @@ If you only want to use \`${CLI_NAME}\` you'll need to add the dependencies to y pm: pmDestination, } - watcher(source, destination, argv.packageNames, { scanOnce: argv.scanOnce, forceVerdaccio: argv.forceVerdaccio, verbose: argv.verbose }) + watcher(source, destination, argv.packageNames, { scanOnce: argv.scanOnce, forceVerdaccio: argv.forceVerdaccio, verbose }) } run() From 57a3eb91d10c9f64061baa5119cf9eda63bb8d3f Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 10:55:17 +0200 Subject: [PATCH 30/35] try config option --- src/verdaccio/install-packages.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/verdaccio/install-packages.ts b/src/verdaccio/install-packages.ts index 7e7ee51..5c30a2f 100644 --- a/src/verdaccio/install-packages.ts +++ b/src/verdaccio/install-packages.ts @@ -34,7 +34,10 @@ export async function installPackages({ newlyPublishedPackageVersions, packagesT await execa`yarn config set npmRegistryServer ${REGISTRY_URL}` await execa`yarn config set unsafeHttpWhitelist --json ["localhost"]` + // secco tries to look at node_modules paths, so Yarn plug'n'play is not suitable await execa`yarn config set nodeLinker node-modules` + // In pull requests the hardened mode would be enabled, breaking the installation + await execa`yarn config set enableHardenedMode false` } if (name === 'bun') { From bdf592d1d70b6c436ba6dbd701a10d7c2e827e30 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 10:58:44 +0200 Subject: [PATCH 31/35] try config option --- src/verdaccio/install-packages.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/verdaccio/install-packages.ts b/src/verdaccio/install-packages.ts index 5c30a2f..80a7cea 100644 --- a/src/verdaccio/install-packages.ts +++ b/src/verdaccio/install-packages.ts @@ -36,8 +36,9 @@ export async function installPackages({ newlyPublishedPackageVersions, packagesT await execa`yarn config set unsafeHttpWhitelist --json ["localhost"]` // secco tries to look at node_modules paths, so Yarn plug'n'play is not suitable await execa`yarn config set nodeLinker node-modules` - // In pull requests the hardened mode would be enabled, breaking the installation + // In pull requests these values would be enabled, breaking the installation await execa`yarn config set enableHardenedMode false` + await execa`yarn config set enableImmutableInstalls false` } if (name === 'bun') { From 1a57ceb9346d3f51cc8929dbbdcc06f3d45ee54e Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 11:03:59 +0200 Subject: [PATCH 32/35] revert debug stuff --- .github/workflows/ci.yml | 8 ++++++++ .github/workflows/integration-testing.yml | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b0503e..1d27707 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,14 @@ jobs: build-cli: name: Build CLI uses: ./.github/workflows/build-cli.yml + size-check: + name: Compressed Size + needs: build-cli + uses: ./.github/workflows/size-check.yml + unit-testing: + name: Unit Testing + needs: build-cli + uses: ./.github/workflows/unit-testing.yml integration-testing: name: Integration Testing needs: build-cli diff --git a/.github/workflows/integration-testing.yml b/.github/workflows/integration-testing.yml index 492bf0b..c3dcdfe 100644 --- a/.github/workflows/integration-testing.yml +++ b/.github/workflows/integration-testing.yml @@ -8,9 +8,21 @@ jobs: strategy: matrix: pm: + - name: npm + displayName: npm + version: 10.8.1 + - name: pnpm + displayName: pnpm + version: 9.4.0 + - name: yarn + displayName: yarn (classic) + version: 1.22.22 - name: yarn displayName: yarn (berry) version: 4.3.1 + - name: bun + displayName: bun + version: 1.1.17 name: ${{ matrix.pm.displayName }} steps: - name: Checkout From db8af60be2829614cf30f09b39cd8e1657b7ce3b Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 11:13:14 +0200 Subject: [PATCH 33/35] rename to SECCO_VERDACCIO_PORT --- .changeset/fluffy-fans-explain.md | 5 +++++ integration/__tests__/scan-once.ts | 4 ++-- src/types.ts | 2 +- src/verdaccio/verdaccio-config.ts | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 .changeset/fluffy-fans-explain.md diff --git a/.changeset/fluffy-fans-explain.md b/.changeset/fluffy-fans-explain.md new file mode 100644 index 0000000..3ef239f --- /dev/null +++ b/.changeset/fluffy-fans-explain.md @@ -0,0 +1,5 @@ +--- +"secco": minor +--- + +Add SECCO_VERDACCIO_PORT environment variable. You can use this to change the default port (4873) when secco uses Verdaccio. diff --git a/integration/__tests__/scan-once.ts b/integration/__tests__/scan-once.ts index f5846a3..e640bd0 100644 --- a/integration/__tests__/scan-once.ts +++ b/integration/__tests__/scan-once.ts @@ -17,7 +17,7 @@ describe.sequential('scan-once', () => { beforeAll(async () => { app = await presets.kitchenSink.commit() - process.env.VERDACCIO_PORT = '4873' + process.env.SECCO_VERDACCIO_PORT = '4873' }) afterAll(async () => { @@ -61,7 +61,7 @@ describe.sequential('scan-once', () => { await renamePnpmWorkspaceFixture(app) } - process.env.VERDACCIO_PORT = '4874' + process.env.SECCO_VERDACCIO_PORT = '4874' }) afterAll(async () => { diff --git a/src/types.ts b/src/types.ts index 9e80fb1..0a452f9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -47,7 +47,7 @@ declare global { interface ProcessEnv { INTEGRATION_PM_NAME?: 'npm' | 'pnpm' | 'yarn' | 'bun' INTEGRATION_PM_VERSION?: string - VERDACCIO_PORT?: string + SECCO_VERDACCIO_PORT?: string CI?: string GITHUB_ACTIONS?: string RUNNER_TEMP?: string diff --git a/src/verdaccio/verdaccio-config.ts b/src/verdaccio/verdaccio-config.ts index 6b68081..7c59c5d 100644 --- a/src/verdaccio/verdaccio-config.ts +++ b/src/verdaccio/verdaccio-config.ts @@ -3,7 +3,7 @@ import { join } from 'pathe' import type { Config as VerdaccioConfig } from '@verdaccio/types' import { CLI_NAME } from '../constants' -const PORT = Number.parseInt(process.env.VERDACCIO_PORT || '') || 4873 // Default +const PORT = Number.parseInt(process.env.SECCO_VERDACCIO_PORT || '') || 4873 // Default // @ts-expect-error: Verdaccio's types are wrong export const VERDACCIO_CONFIG: VerdaccioConfig = { From a3efe2ec1b9b9f247a6ecf4245113c8271e3908c Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 11:16:53 +0200 Subject: [PATCH 34/35] update docs --- README.md | 2 +- docs/src/content/docs/guide/features.md | 2 +- docs/src/content/docs/reference/config.md | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 545b668..9e4d5b8 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ secco solves these problems and streamlines the process of local package testing - **Link Multiple Projects.** secco reads the `.seccorc` file to make the connection between destination and source. This allows you to use `secco` with as many source folders as you wish. - **npm, yarn, pnpm, and bun support.** You can use any of these package managers in your source and destination projects. - **Watch and CI mode.** By default, secco starts a watch task. But you can also only run it once, enabling CI End-To-End testing use cases. -- **Workspaces (in source).** Your source folder can be a monorepo using workspaces. +- **Workspaces.** Your source & destination folders can be a monorepo using workspaces. lekoarts.de diff --git a/docs/src/content/docs/guide/features.md b/docs/src/content/docs/guide/features.md index 8cb8ab1..4d77361 100644 --- a/docs/src/content/docs/guide/features.md +++ b/docs/src/content/docs/guide/features.md @@ -9,4 +9,4 @@ secco boasts a lot of features to enable you to test local changes more quickly. - **Link Multiple Projects.** secco reads the `.seccorc` file to make the connection between destination and source. This allows you to use `secco` with as many source folders as you wish. - **npm, yarn, pnpm, and bun support.** You can use any of these package managers in your source and destination projects. - **Watch and CI mode.** By default, secco starts a watch task. But you can also only run it once, enabling CI End-To-End testing use cases. -- **Workspaces (in source).** Your source folder can be a monorepo using workspaces. +- **Workspaces.** Your source & destination folders can be a monorepo using workspaces. diff --git a/docs/src/content/docs/reference/config.md b/docs/src/content/docs/reference/config.md index 3be4793..b72b2b2 100644 --- a/docs/src/content/docs/reference/config.md +++ b/docs/src/content/docs/reference/config.md @@ -35,3 +35,10 @@ Equivalent to `source.path`. - **Default:** `false` Equivalent to the [`--verbose`](/reference/flags/#--verbose) flag. + +### `SECCO_VERDACCIO_PORT` + +- **Type:** `string` +- **Default:** `4873` + +Configure the port that secco's internal Verdaccio instance uses. From 2f170f4802d6f3aa6f01aaf90b8a4991a5efe602 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 9 Jul 2024 11:19:24 +0200 Subject: [PATCH 35/35] update changesets --- .changeset/beige-rings-brake.md | 2 +- .changeset/cool-actors-visit.md | 2 +- .changeset/fluffy-fans-explain.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.changeset/beige-rings-brake.md b/.changeset/beige-rings-brake.md index 629286e..f0ce1f5 100644 --- a/.changeset/beige-rings-brake.md +++ b/.changeset/beige-rings-brake.md @@ -2,4 +2,4 @@ "secco": patch --- -Correctly set internal "verbose" value when VERBOSE env var is set +Correctly display additional information e.g. during `npm install` when `VERBOSE` env var is set diff --git a/.changeset/cool-actors-visit.md b/.changeset/cool-actors-visit.md index 4faea9f..a2d9515 100644 --- a/.changeset/cool-actors-visit.md +++ b/.changeset/cool-actors-visit.md @@ -2,4 +2,4 @@ "secco": minor --- -Support Yarn Berry (currently v3 & v4) by modyfing the .yarnrc.yml file inside the destination before trying to install packages from the local Verdaccio registry +Support Yarn Berry (currently v3 & v4) by modyfing the `.yarnrc.yml` file inside the destination before trying to install packages from the local Verdaccio registry diff --git a/.changeset/fluffy-fans-explain.md b/.changeset/fluffy-fans-explain.md index 3ef239f..eaf2d0d 100644 --- a/.changeset/fluffy-fans-explain.md +++ b/.changeset/fluffy-fans-explain.md @@ -2,4 +2,4 @@ "secco": minor --- -Add SECCO_VERDACCIO_PORT environment variable. You can use this to change the default port (4873) when secco uses Verdaccio. +Add `SECCO_VERDACCIO_PORT` environment variable. You can use this to change the default port (`4873`) when secco uses Verdaccio.