From 15bff1b955e82c137a1a64993738421d3da1fdf9 Mon Sep 17 00:00:00 2001 From: Paul Armstrong Date: Sat, 9 Mar 2024 18:30:36 -0800 Subject: [PATCH] feat(core): add `one change show` (#663) **Problem:** While it's easy to inspect the files in the `.changes` folder(s), it would be more helpful to have a command that could list and preview the changes together, including dependencies that need versioning. **Solution:** Adds `one change show` to preview versions and changelogs. Minimal for now, ripe for making prettier. **Related issues:** Fixes #656 **Checklist:** - [x] Added or updated tests - [x] Added or updated documentation - [x] Ensured the pre-commit hooks ran successfully --- docs/src/content/docs/core/changes.mdx | 18 ++++- .../content/docs/plugins/docgen/example.mdx | 18 ++++- .../onerepo/.changes/002-curvy-donkeys-cut.md | 5 ++ .../src/core/changes/__tests__/show.test.ts | 20 ++++++ modules/onerepo/src/core/changes/index.ts | 3 + modules/onerepo/src/core/changes/show.ts | 72 +++++++++++++++++++ .../src/core/changes/utils/changelog.ts | 2 +- .../src/core/changes/utils/get-versionable.ts | 11 +-- 8 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 modules/onerepo/.changes/002-curvy-donkeys-cut.md create mode 100644 modules/onerepo/src/core/changes/__tests__/show.test.ts create mode 100644 modules/onerepo/src/core/changes/show.ts diff --git a/docs/src/content/docs/core/changes.mdx b/docs/src/content/docs/core/changes.mdx index 6547aa48..ea05ba46 100644 --- a/docs/src/content/docs/core/changes.mdx +++ b/docs/src/content/docs/core/changes.mdx @@ -207,7 +207,7 @@ Eventually, we will need to publish one or more Workspaces to our registry for u ## Commands {/* start-auto-generated-from-cli-changes */} -{/* @generated SignedSource<<3b021d1d2d6206998de70671056ad043>> */} +{/* @generated SignedSource<> */} ### `one change` @@ -326,6 +326,22 @@ one change publish --all --- +#### `one change show` + +Preview the next versions and changelogs for Workspaces. + +```sh +one change show +``` + +| Option | Type | Description | +| ------------------ | --------------------------------------- | ---------------------------------------- | +| `--all, -a` | `boolean` | Run across all workspaces | +| `--format` | `"json"`, `"plain"`, default: `"plain"` | Choose how the results will be returned. | +| `--workspaces, -w` | `array` | List of Workspace names to run against | + +--- + #### `one change snapshot` Aliases: `one change snap` diff --git a/docs/src/content/docs/plugins/docgen/example.mdx b/docs/src/content/docs/plugins/docgen/example.mdx index a32baed1..6cc04061 100644 --- a/docs/src/content/docs/plugins/docgen/example.mdx +++ b/docs/src/content/docs/plugins/docgen/example.mdx @@ -13,7 +13,7 @@ The following content is auto-generated using the [official documentation plugin ::: {/* start-auto-generated-from-cli */} -{/* @generated SignedSource<<953a87825b511982869a1afa4ab8557b>> */} +{/* @generated SignedSource<<08893ba03b34cdb9ab05787ac817a439>> */} ## `one` @@ -205,6 +205,22 @@ one change publish --all --- +#### `one change show` + +Preview the next versions and changelogs for Workspaces. + +```sh +one change show +``` + +| Option | Type | Description | +| ------------------ | --------------------------------------- | ---------------------------------------- | +| `--all, -a` | `boolean` | Run across all workspaces | +| `--format` | `"json"`, `"plain"`, default: `"plain"` | Choose how the results will be returned. | +| `--workspaces, -w` | `array` | List of Workspace names to run against | + +--- + #### `one change snapshot` Aliases: `one change snap` diff --git a/modules/onerepo/.changes/002-curvy-donkeys-cut.md b/modules/onerepo/.changes/002-curvy-donkeys-cut.md new file mode 100644 index 00000000..4f089b9d --- /dev/null +++ b/modules/onerepo/.changes/002-curvy-donkeys-cut.md @@ -0,0 +1,5 @@ +--- +type: minor +--- + +Adds `one change show` to help preview changes in Workspaces" diff --git a/modules/onerepo/src/core/changes/__tests__/show.test.ts b/modules/onerepo/src/core/changes/__tests__/show.test.ts new file mode 100644 index 00000000..636ee655 --- /dev/null +++ b/modules/onerepo/src/core/changes/__tests__/show.test.ts @@ -0,0 +1,20 @@ +import path from 'node:path'; +import { getCommand } from '@onerepo/test-cli'; +import { getGraph } from '@onerepo/graph'; +import * as show from '../show'; + +const graph = getGraph(path.join(__dirname, '__fixtures__/with-entries')); +const { run } = getCommand(show, graph); + +describe('show changes', () => { + test('adds change files', async () => { + vi.spyOn(process.stdout, 'write').mockReturnValue(true); + await run('-w tacos --format json'); + + expect(process.stdout.write).toHaveBeenCalledWith( + expect.stringContaining( + '{"cheese":{"type":"minor","version":"2.1.0","entries":[{"type":"minor","content":"A minor change\\n"', + ), + ); + }); +}); diff --git a/modules/onerepo/src/core/changes/index.ts b/modules/onerepo/src/core/changes/index.ts index 8fe588ee..e2236d81 100644 --- a/modules/onerepo/src/core/changes/index.ts +++ b/modules/onerepo/src/core/changes/index.ts @@ -2,6 +2,7 @@ import type { Plugin } from '../../types'; import * as Add from './add'; import * as Migrate from './migrate'; import * as Publish from './publish'; +import * as Show from './show'; import * as Snapshot from './snapshot'; import * as Verify from './verify'; import * as Version from './version'; @@ -13,6 +14,7 @@ export const changes: Plugin = function codeowners(config) { const add = visitor(Add); const migrate = visitor(Migrate); const publish = visitor(Publish); + const show = visitor(Show); const snapshot = visitor(Snapshot); const verify = visitor(Verify); const version = visitor(Version); @@ -40,6 +42,7 @@ export const changes: Plugin = function codeowners(config) { migrate.handler, ) .command(publish.command, publish.description, publish.builder, publish.handler) + .command(show.command, show.description, show.builder, show.handler) .command(snapshot.command, snapshot.description, snapshot.builder, snapshot.handler) .command(verify.command, verify.description, verify.builder, verify.handler) .command(version.command, version.description, version.builder, version.handler) diff --git a/modules/onerepo/src/core/changes/show.ts b/modules/onerepo/src/core/changes/show.ts new file mode 100644 index 00000000..a7b9c2bf --- /dev/null +++ b/modules/onerepo/src/core/changes/show.ts @@ -0,0 +1,72 @@ +import cliui from 'cliui'; +import pc from 'picocolors'; +import type { WithWorkspaces } from '@onerepo/builders'; +import { withWorkspaces } from '@onerepo/builders'; +import type { Workspace } from '@onerepo/graph'; +import type { Builder, Handler } from '@onerepo/yargs'; +import type { VersionPlan } from './utils'; +import { buildChangelog, getVersionable } from './utils'; + +export const command = ['show']; + +export const description = 'Preview the next versions and changelogs for Workspaces.'; + +type Argv = WithWorkspaces & { + format: 'json' | 'plain'; +}; + +export const builder: Builder = (yargs) => + withWorkspaces(yargs.usage(`$0 ${command[0]}`)).option('format', { + type: 'string', + choices: ['json', 'plain'] as const, + default: 'plain' as const, + description: 'Choose how the results will be returned.', + }); + +export const handler: Handler = async (argv, { getWorkspaces, graph }) => { + const { format } = argv; + + const workspaces = await getWorkspaces(); + + const versionPlans = await getVersionable(graph); + + const deps = graph.dependencies(workspaces, true); + + const toVersion: Array = Array.from(versionPlans.keys()).filter((ws) => deps.includes(ws)); + + if (format === 'json') { + process.stdout.write( + JSON.stringify( + toVersion.reduce( + (memo, ws) => { + memo[ws.name] = versionPlans.get(ws)!; + return memo; + }, + {} as Record, + ), + ) + '\n', + ); + return; + } + + const ui = cliui({ width: Math.min(160, process.stdout.columns) }); + for (const ws of toVersion) { + const plan = versionPlans.get(ws)!; + ui.div( + { text: pc.cyan(pc.bold(pc.underline(ws.name))), padding: [1, 0, 0, 0] }, + { text: `${ws.version} ${pc.dim('→')} ${pc.bold(plan.version)}`, padding: [1, 0, 0, 0] }, + ); + + const deps = graph.dependencies(ws); + const depPlans = new Map(); + for (const dep of deps) { + const depPlan = versionPlans.get(dep); + if (depPlan) { + depPlans.set(dep, depPlan); + } + } + + ui.div({ text: await buildChangelog(plan, depPlans), padding: [0, 2, 0, 2] }); + } + process.stdout.write(ui.toString() + '\n'); +}; diff --git a/modules/onerepo/src/core/changes/utils/changelog.ts b/modules/onerepo/src/core/changes/utils/changelog.ts index ea1cebb5..c851463e 100644 --- a/modules/onerepo/src/core/changes/utils/changelog.ts +++ b/modules/onerepo/src/core/changes/utils/changelog.ts @@ -7,7 +7,7 @@ import type { ChangeEntry, ReleaseType, VersionPlan } from './get-versionable'; type Formatting = Required['changes']>['formatting']; -async function buildChangelog( +export async function buildChangelog( plan: VersionPlan, dependencies: Map, formatting: Formatting = {}, diff --git a/modules/onerepo/src/core/changes/utils/get-versionable.ts b/modules/onerepo/src/core/changes/utils/get-versionable.ts index eea33ba3..b539b9c2 100644 --- a/modules/onerepo/src/core/changes/utils/get-versionable.ts +++ b/modules/onerepo/src/core/changes/utils/get-versionable.ts @@ -118,10 +118,13 @@ async function getVersionPlan( runDry: true, }); - const logs = rawLogs.split('\u0000').map((str) => { - const [ref, subject] = str.split('\u0003\u0002'); - return { ref, subject }; - }); + const logs = rawLogs + .split('\u0000') + .map((str) => { + const [ref, subject] = str.split('\u0003\u0002'); + return { ref, subject }; + }) + .filter(({ subject }) => !!subject); const version = options.snapshot ? `0.0.0-${options.identifier}-${options.snapshot}`