Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 38 additions & 3 deletions dev-packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ Options:

## coverageReport

The `coverageReport` command can be used to create a full nyc test coverage report for a lerna/yarn mono repository.
The `coverageReport` command can be used to create a full nyc test coverage report for a pnpm/yarn mono repository.
The package manager is auto-detected from the repository (pnpm-workspace.yaml/pnpm-lock.yaml vs. yarn.lock).
Individual coverage reports for each package are created and then combined to a full report.

```console
Expand Down Expand Up @@ -142,14 +143,15 @@ Commands:
version [options] <versionType> [customVersion] Set the version of all packages in a GLSP repository
prepare [options] <versionType> [customVersion] Prepare a new release for a GLSP component (version bump, changelog, PR
creation ...)
publish [options] <distTag> Publish all workspace packages of a GLSP repository
help [command] display help for command
```

### version

Command to bump the version of all packages in a GLSP repository.
Similar to "lerna version" this bumps the version of all workspace packages.
In addition, external GLSP dependencies are considered and bumped as well.
This bumps the version of all workspace packages (the root `package.json` version is the source of truth).
In addition, external GLSP dependencies are considered and bumped as well; `workspace:` ranges are preserved.
The glsp repository type ("glsp-client", "glsp-server-node" etc.) is auto detected from the given repository path.
If the command is invoked in a non-GLSP repository it will fail.

Expand Down Expand Up @@ -199,6 +201,39 @@ Options:
-h, --help display help for command
```

### publish

Publishes all (public) workspace packages of a GLSP repository (replaces `lerna publish`).
The package manager is auto-detected: pnpm-based repositories publish via `pnpm publish -r`, while
not-yet-migrated yarn/lerna-based repositories fall back to the legacy `lerna publish`.

- `next`: applies a canary version (`<root-version>.<commits-since-last-tag>`, e.g. `2.8.0-next.42`) to all
workspace packages and publishes them under the `next` dist-tag. Requires the full git history
(`fetch-depth: 0` in CI) to derive the commit count.
- `latest`: publishes the current package versions under the `latest` dist-tag. Packages whose version
already exists on the registry are skipped.

For pnpm repositories publishing is delegated to `pnpm publish -r`, so `workspace:` dependency ranges are
rewritten to exact versions; in both cases npm provenance/trusted publishing (`NPM_CONFIG_PROVENANCE`) is
preserved. `--dry-run` is only supported for pnpm-based repositories.

```console
$ glsp releng publish -h
Usage: glsp releng publish [options] <distTag>

Publish all workspace packages of a GLSP repository (pnpm: `pnpm publish`, yarn/lerna: `lerna publish`)

Arguments:
distTag The npm dist-tag to publish under (choices: "next", "latest")

Options:
-v, --verbose Enable verbose (debug) log output (default: false)
-r, --repoDir <repoDir> Path to the component repository (default: "<cwd>")
--dry-run Derive versions and run `pnpm publish` in dry-run mode without applying changes (default: false)
--registry <url> Publish to a custom npm registry (e.g. a local verdaccio for testing)
-h, --help display help for command
```

## repo

Multi-repository workspace management for GLSP development.
Expand Down
16 changes: 10 additions & 6 deletions dev-packages/cli/src/commands/coverage-report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ import {
PackageHelper,
baseCommand,
cd,
detectPackageManager,
exec,
execAsync,
execBinCommand,
findFiles,
getYarnWorkspacePackages,
getWorkspacePackages,
moveFile,
runScriptCommand,
validateDirectory
} from '../util';

Expand All @@ -42,7 +45,7 @@ export const CoverageReportCommand = baseCommand() //
.action(generateCoverageReport);

/**
* Generates and aggregates an 'nyc' coverage report for lerna/yarn mono repositories.
* Generates and aggregates an 'nyc' coverage report for pnpm/yarn mono repositories.
* First, individual reports for each package are generated. Then, they are aggregated into one combined HTML report.
* @param options configuration options
*/
Expand All @@ -56,9 +59,10 @@ export async function generateCoverageReport(options: CoverageCmdOptions): Promi
}

export function validateAndRetrievePackages(options: CoverageCmdOptions): PackageHelper[] {
exec('yarn nyc -h', { silent: true, errorMsg: 'Nyc is not installed!' });
const pm = detectPackageManager(options.projectRoot);
exec(execBinCommand(pm, 'nyc -h'), { silent: true, errorMsg: 'Nyc is not installed!' });

const workspacePackages = getYarnWorkspacePackages(options.projectRoot, true);
const workspacePackages = getWorkspacePackages(options.projectRoot, true);

const rootPackage = workspacePackages.pop()!;
if (!rootPackage.hasScript(options.coverageScript)) {
Expand All @@ -71,7 +75,7 @@ export function validateAndRetrievePackages(options: CoverageCmdOptions): Packag

export async function collectPackageReportFiles(packages: PackageHelper[], options: CoverageCmdOptions): Promise<string[]> {
LOGGER.info('Create individual package coverage reports');
await execAsync(`yarn ${options.coverageScript}`, { silent: false });
await execAsync(runScriptCommand(detectPackageManager(options.projectRoot), options.coverageScript), { silent: false });
const reports: string[] = packages.flatMap(pkg => findFiles(pkg.location, '**/coverage-final.json'));
LOGGER.info(`Collected ${reports.length} coverage reports from ${packages.length} packages`);
return reports;
Expand Down Expand Up @@ -102,7 +106,7 @@ async function combineReports(reportFiles: string[], options: CoverageCmdOptions
});

// Generate report
await execAsync('yarn nyc report --reporter html', { silent: false });
await execAsync(execBinCommand(detectPackageManager(options.projectRoot), 'nyc report --reporter html'), { silent: false });

// Restore nyc configs (if any)
tempFiles.forEach(config => moveFile(config, config.substring(1)));
Expand Down
35 changes: 35 additions & 0 deletions dev-packages/cli/src/commands/releng/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,41 @@ export function isNextVersion(version: string): boolean {
return version.endsWith('-next') || version.endsWith('.SNAPSHOT');
}

export interface CanaryVersion {
/** The base version from the root package.json, e.g. `2.8.0-next` */
base: string;
/** The most recent git tag */
lastTag: string;
/** The number of commits since {@link lastTag} */
commitCount: number;
/** The derived canary version, e.g. `2.8.0-next.42` */
version: string;
}

/**
* Derives a canary version for `next` publishing (replacement for `lerna publish --canary`).
* The version is the root package version suffixed with the number of commits since the last
* git tag, e.g. `2.8.0-next.42`. Requires the full git history (fetch-depth: 0 in CI).
* @param repoDir The root path of the repository
*/
export function deriveCanaryVersion(repoDir: string): CanaryVersion {
const base = getVersionFromPackage(repoDir);
let lastTag: string;
try {
lastTag = exec('git describe --tags --abbrev=0', { cwd: repoDir, silent: true }).trim();
} catch (error) {
throw new Error(
`Could not determine the last git tag in '${repoDir}'.` +
' Deriving a canary version requires at least one tag and the full git history (fetch-depth: 0 in CI).'
);
}
const commitCount = Number.parseInt(exec(`git rev-list --count ${lastTag}..HEAD`, { cwd: repoDir, silent: true }).trim(), 10);
if (Number.isNaN(commitCount)) {
throw new Error(`Could not determine the number of commits since tag '${lastTag}' in '${repoDir}'.`);
}
return { base, lastTag, commitCount, version: `${base}.${commitCount}` };
}

export async function checkIfNpmVersionIsNew(pckgName: string, newVersion: string): Promise<void> {
LOGGER.debug(`Check that the release version is new i.e. does not exist on npm: ${newVersion}`);

Expand Down
18 changes: 13 additions & 5 deletions dev-packages/cli/src/commands/releng/prepare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ import {
commitChanges,
createBranch,
deleteFile,
detectPackageManager,
exec,
execAsync,
getDefaultBranch,
getYarnWorkspacePackages,
getWorkspacePackages,
installCommand,
replaceInFile,
runScriptCommand,
validateGitDirectory,
writeFile
} from '../../util';
Expand Down Expand Up @@ -92,7 +95,7 @@ export async function prepareRelease(options: PrepareReleaseOptions): Promise<vo
}
cd(options.repoDir);
if (GLSPRepo.isNpmRepo(options.repo)) {
options.workspacePackages = getYarnWorkspacePackages(options.repoDir, true);
options.workspacePackages = getWorkspacePackages(options.repoDir, true);
}
if (options.check) {
checkPreconditions(options);
Expand Down Expand Up @@ -153,9 +156,14 @@ async function build(options: PrepareReleaseOptions): Promise<void> {
}

async function buildNpm(options: PrepareReleaseOptions): Promise<void> {
LOGGER.info('Install & Build with yarn');
await execAsync('yarn', { silent: false, cwd: options.repoDir, errorMsg: 'Yarn build failed' });
LOGGER.debug('Yarn build succeeded');
const pm = detectPackageManager(options.repoDir);
LOGGER.info(`Install & Build with ${pm}`);
await execAsync(installCommand(pm), { silent: false, cwd: options.repoDir, errorMsg: `${pm} install failed` });
if (pm === 'pnpm') {
// bare `yarn` triggers the root prepare/build script implicitly; with pnpm we build explicitly
await execAsync(runScriptCommand(pm, '--if-present build'), { silent: false, cwd: options.repoDir, errorMsg: 'Build failed' });
}
LOGGER.debug('Build succeeded');
}

async function buildJavaServer(options: PrepareReleaseOptions): Promise<void> {
Expand Down
Loading