Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@
"mixpanel": "^0.18.1",
"ora": "^5.4.1",
"package-manager-detector": "^1.3.0",
"semver": "^7.7.2",
"ts-pattern": "catalog:"
},
"peerDependencies": {
"prisma": "catalog:"
},
"devDependencies": {
"@types/better-sqlite3": "^7.6.13",
"@types/semver": "^7.7.0",
"@types/tmp": "catalog:",
"@zenstackhq/eslint-config": "workspace:*",
"@zenstackhq/runtime": "workspace:*",
Expand Down
20 changes: 19 additions & 1 deletion packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Command, CommanderError, Option } from 'commander';
import * as actions from './actions';
import { CliError } from './cli-error';
import { telemetry } from './telemetry';
import { getVersion } from './utils/version-utils';
import { checkNewVersion, getVersion } from './utils/version-utils';

const generateAction = async (options: Parameters<typeof actions.generate>[0]): Promise<void> => {
await telemetry.trackCommand('generate', () => actions.generate(options));
Expand Down Expand Up @@ -51,10 +51,13 @@ function createProgram() {
`schema file (with extension ${schemaExtensions}). Defaults to "zenstack/schema.zmodel" unless specified in package.json.`,
);

const noVersionCheckOption = new Option('--no-version-check', 'do not check for new version');

program
.command('generate')
.description('Run code generation plugins.')
.addOption(schemaOption)
.addOption(noVersionCheckOption)
.addOption(new Option('-o, --output <path>', 'default output directory for code generation'))
.addOption(new Option('--silent', 'suppress all output except errors').default(false))
.action(generateAction);
Expand All @@ -65,6 +68,7 @@ function createProgram() {
migrateCommand
.command('dev')
.addOption(schemaOption)
.addOption(noVersionCheckOption)
.addOption(new Option('-n, --name <name>', 'migration name'))
.addOption(new Option('--create-only', 'only create migration, do not apply'))
.addOption(migrationsOption)
Expand All @@ -76,26 +80,30 @@ function createProgram() {
.addOption(schemaOption)
.addOption(new Option('--force', 'skip the confirmation prompt'))
.addOption(migrationsOption)
.addOption(noVersionCheckOption)
.description('Reset your database and apply all migrations, all data will be lost.')
.action((options) => migrateAction('reset', options));

migrateCommand
.command('deploy')
.addOption(schemaOption)
.addOption(noVersionCheckOption)
.addOption(migrationsOption)
.description('Deploy your pending migrations to your production/staging database.')
.action((options) => migrateAction('deploy', options));

migrateCommand
.command('status')
.addOption(schemaOption)
.addOption(noVersionCheckOption)
.addOption(migrationsOption)
.description('Check the status of your database migrations.')
.action((options) => migrateAction('status', options));

migrateCommand
.command('resolve')
.addOption(schemaOption)
.addOption(noVersionCheckOption)
.addOption(migrationsOption)
.addOption(new Option('--applied <migration>', 'record a specific migration as applied'))
.addOption(new Option('--rolled-back <migration>', 'record a specific migration as rolled back'))
Expand All @@ -108,6 +116,7 @@ function createProgram() {
.command('push')
.description('Push the state from your schema to your database.')
.addOption(schemaOption)
.addOption(noVersionCheckOption)
.addOption(new Option('--accept-data-loss', 'ignore data loss warnings'))
.addOption(new Option('--force-reset', 'force a reset of the database before push'))
.action((options) => dbAction('push', options));
Expand All @@ -116,20 +125,29 @@ function createProgram() {
.command('info')
.description('Get information of installed ZenStack packages.')
.argument('[path]', 'project path', '.')
.addOption(noVersionCheckOption)
.action(infoAction);

program
.command('init')
.description('Initialize an existing project for ZenStack.')
.argument('[path]', 'project path', '.')
.addOption(noVersionCheckOption)
.action(initAction);

program
.command('check')
.description('Check a ZModel schema for syntax or semantic errors.')
.addOption(schemaOption)
.addOption(noVersionCheckOption)
.action(checkAction);

program.hook('preAction', async (_thisCommand, actionCommand) => {
if (actionCommand.getOptionValue('versionCheck')) {
await checkNewVersion();
}
});

return program;
}

Expand Down
37 changes: 37 additions & 0 deletions packages/cli/src/utils/version-utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import colors from 'colors';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import semver from 'semver';

const CHECK_VERSION_TIMEOUT = 2000;
const VERSION_CHECK_TAG = 'next';

export function getVersion() {
try {
Expand All @@ -11,3 +16,35 @@ export function getVersion() {
return undefined;
}
}

export async function checkNewVersion() {
const currVersion = getVersion();
let latestVersion: string;
try {
latestVersion = await getLatestVersion();
} catch {
// noop
return;
}

if (latestVersion && currVersion && semver.gt(latestVersion, currVersion)) {
console.log(`A newer version ${colors.cyan(latestVersion)} is available.`);
}
}

export async function getLatestVersion() {
const fetchResult = await fetch('https://registry.npmjs.org/@zenstackhq/cli', {
headers: { accept: 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*' },
signal: AbortSignal.timeout(CHECK_VERSION_TIMEOUT),
});

if (fetchResult.ok) {
const data: any = await fetchResult.json();
const latestVersion = data?.['dist-tags']?.[VERSION_CHECK_TAG];
if (typeof latestVersion === 'string' && semver.valid(latestVersion)) {
return latestVersion;
}
}

throw new Error('invalid npm registry response');
}
35 changes: 27 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.