Skip to content

Commit

Permalink
feat: Support workspaces in destination (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
LekoArts authored Jul 9, 2024
1 parent 1593131 commit d4d8ef9
Show file tree
Hide file tree
Showing 37 changed files with 630 additions and 165 deletions.
5 changes: 5 additions & 0 deletions .changeset/beige-rings-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"secco": patch
---

Correctly display additional information e.g. during `npm install` when `VERBOSE` env var is set
2 changes: 1 addition & 1 deletion .changeset/cool-actors-visit.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 5 additions & 0 deletions .changeset/fluffy-fans-explain.md
Original file line number Diff line number Diff line change
@@ -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.
7 changes: 7 additions & 0 deletions .changeset/silver-oranges-wink.md
Original file line number Diff line number Diff line change
@@ -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.
7 changes: 7 additions & 0 deletions .github/workflows/integration-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,10 @@ jobs:
env:
INTEGRATION_PM_NAME: ${{ matrix.pm.name }}
INTEGRATION_PM_VERSION: ${{ matrix.pm.version }}
- name: Upload temp dir (optional)
if: ${{ cancelled() || failure() }}
uses: actions/upload-artifact@v4
with:
name: temp-dir-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.pm.name }}
path: ${{ runner.temp }}/secco-**/**/*
retention-days: 1
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<a href="https://www.lekoarts.de?utm_source=secco">
<img alt="lekoarts.de" src="https://img.shields.io/badge/-website-blue">
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/guide/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
7 changes: 7 additions & 0 deletions docs/src/content/docs/reference/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default antfu(
},
],
'ts/array-type': ['error', { default: 'generic' }],
'node/prefer-global/process': 'off',
},
},
)
58 changes: 0 additions & 58 deletions integration/__tests__/kitchen-sink.ts

This file was deleted.

13 changes: 8 additions & 5 deletions integration/__tests__/missing-information.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,35 @@ 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([''])

logs.should.contain('No `.seccorc` file found in')
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([''])

logs.should.contain('No `package.json` found in')
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([''])

logs.should.contain(`You haven't got any source dependencies in your current \`package.json\`.`)
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)
})
})
87 changes: 87 additions & 0 deletions integration/__tests__/scan-once.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
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)
}

describe.sequential('scan-once', () => {
describe.sequential('single package', () => {
let app: Application

beforeAll(async () => {
app = await presets.kitchenSink.commit()

process.env.SECCO_VERDACCIO_PORT = '4873'
})

afterAll(async () => {
await app.cleanup()
})

it('should run Verdaccio with --force-verdaccio', () => {
const [exitCode, logs] = app.cli(['--scan-once', '--force-verdaccio', '--verbose'])

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('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

beforeAll(async () => {
app = await presets.kitchenSinkWorkspaces.commit()

if (process.env.INTEGRATION_PM_NAME === 'pnpm') {
await renamePnpmWorkspaceFixture(app)
}

process.env.SECCO_VERDACCIO_PORT = '4874'
})

afterAll(async () => {
await app.cleanup()
})

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-')
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)
})
})
})
1 change: 1 addition & 0 deletions integration/fixtures/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
packages:
- 'packages/*'
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "kitchen-sink-workspaces-destination",
"private": true,
"workspaces": [
"packages/*"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { sayHelloWorld } from 'say-hello-world'

function run() {
sayHelloWorld()
}

run()
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "destination-workspaces",
"version": "1.0.0",
"private": true,
"main": "bin.mjs",
"scripts": {
"start": "node bin.mjs"
},
"dependencies": {
"say-hello-world-workspaces": "^1.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "kitchen-sink-workspaces-source",
"private": true,
"packageManager": "pnpm@9.4.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
function sayHelloWorld() {
// eslint-disable-next-line no-console
console.log('Hello World!')
}
export {
sayHelloWorld,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "say-hello-world-workspaces",
"version": "1.0.0",
"packageManager": "pnpm@9.4.0",
"main": "index.mjs"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
packages:
- 'packages/*'
3 changes: 2 additions & 1 deletion integration/models/application-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 })
Expand Down
6 changes: 6 additions & 0 deletions integration/models/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof application>
Expand All @@ -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 })
Expand Down
17 changes: 6 additions & 11 deletions integration/presets.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/* eslint-disable ts/no-namespace */
/* eslint-disable node/prefer-global/process */
import { applicationConfig } from './models/application-config'
import { fixtures } from './fixtures'

Expand All @@ -8,20 +6,17 @@ 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'])
.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
Loading

0 comments on commit d4d8ef9

Please sign in to comment.