Skip to content

Commit

Permalink
Replace keystone-next prototype with keystone-next dev and a config o…
Browse files Browse the repository at this point in the history
…ption to determine if migrations should be used or not (#5163)

* Replace keystone-next prototype with keystone-next dev and a config option to determine if migrations should be used or not

* Fix test utils

* Update cypress test

* Fix a thing

* Fix the tests

* Make smoke tests run dev

* Update purple-swans-sort.md

* Apply suggestions from code review

Co-authored-by: Tim Leslie <[email protected]>

Co-authored-by: Tim Leslie <[email protected]>
  • Loading branch information
emmatown and timleslie authored Mar 21, 2021
1 parent 66955da commit b37cbff
Show file tree
Hide file tree
Showing 20 changed files with 76 additions and 53 deletions.
6 changes: 6 additions & 0 deletions .changeset/purple-swans-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@keystone-next/keystone': major
'@keystone-next/types': minor
---

Added `db.useMigrations` option to replace using `keystone-next dev` and `keystone-next prototype` depending on what kind of migration strategy you want to use. If you were previously using `keystone-next dev`, you should set `db.useMigrations` to true in your config and continue using `keystone-next dev`. If you were previously using `keystone-next prototype`, you should now use `keystone-next dev`.
7 changes: 7 additions & 0 deletions .changeset/tame-frogs-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@keystone-next/types': major
'@keystone-next/keystone': major
'@keystone-next/test-utils-legacy': patch
---

Replaced `MigrationMode` type with `MigrationAction` that `createSystem` and `createKeystone` now accept.
4 changes: 2 additions & 2 deletions docs-next/cypress/integration/copy-link.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ describe('on click of a link icon', () => {
.then(text => expect(text).to.equal('http://localhost:8000/guides/cli#run'));
});
it('changes the URL to the specified anchor href', () => {
cy.get('#keystone-next-prototype-default a').click();
cy.get('#keystone-next-dev-default a').click();
return cy.window().then(win => {
expect(win.location.href).to.equal(
'http://localhost:8000/guides/cli#keystone-next-prototype-default'
'http://localhost:8000/guides/cli#keystone-next-dev-default'
);
});
});
Expand Down
4 changes: 4 additions & 0 deletions docs-next/pages/apis/config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Advanced configuration:
- `enableLogging` (default: `false`): Enable logging from the Prisma client.
- `getPrismaPath` (default: `() => '.keystone/prisma'` ): Set the location of the generated Prisma schema and client.
- `getDbSchemaName` (default: `() => 'public'` ): Set the schema named used in the database.
- `useMigrations` (default: `false`): Determines whether to use migrations or automatically force-update the database with the latest schema and potentially lose data.

The functions for `getPrismaPath` and `getDbSchemaName` are each provided with the generated Prisma schema as a `string` in the `{ prismaSchema }` argument.

Expand All @@ -86,6 +87,7 @@ export default config({
enableLogging: true,
getPrismaPath: ({ prismaSchema }) => '.prisma',
getDbSchemaName: ({ prismaSchema }) => 'prisma',
useMigrations: true,
},
/* ... */
});
Expand All @@ -100,6 +102,7 @@ Advanced configuration:

- `enableLogging` (default: `false`): Enable logging from the Prisma client.
- `getPrismaPath` (default: `() => '.keystone/prisma'` ): Set the location of the generated Prisma schema and client.
- `useMigrations` (default: `false`): Determines whether to use migrations or automatically force-update the database with the latest schema and potentially lose data.

The function for `getPrismaPath` is provided with the generated Prisma schema as a `string` in the `{ prismaSchema }` argument.

Expand All @@ -112,6 +115,7 @@ export default config({
// Optional advanced configuration
enableLogging: true,
getPrismaPath: ({ prismaSchema }) => '.prisma',
useMigrations: true,
},
/* ... */
});
Expand Down
25 changes: 10 additions & 15 deletions docs-next/pages/guides/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ Usage
$ keystone-next [command]
Commands
Run
prototype (default) start the project in prototyping mode
dev start the project in development mode
dev (default) start the project in development mode
start start the project in production mode
Build
build build the project (must be done before using start)
Expand All @@ -26,25 +25,17 @@ The CLI supports commands in three categories; Run, Build and Migrate.
### Run

The **run** commands are used to prepare and then start your Keystone server.
Keystone can be run in three different modes; **prototyping**, **dev** and **production**.
These different modes support different phases of your project life-cycle.
The different modes differ in how they interact with your database and with your Admin UI application.
Keystone can be run in two different modes; **dev** and **production**.

Note: All the **run** commands expect to find a module called `keystone.ts` with a default export which returns a system configuration `config()` from `@keystone-next/keystone/schema`.
See the [System Configuration API](../apis/config) for details on how to configure a Keystone system.

#### keystone-next prototype (default)
#### keystone-next dev (default)

In **prototyping** mode, Keystone will try its hardest to put your database into a state which is consistent with your schema.
This might require Keystone to delete data in your database.
This mode of operation should only be used when you are first getting started with Keystone and are not yet working with real data.
In prototyping mode you can quickly change your schema and immediately see the changes reflected in your database and Admin UI when you restart.
In **dev** mode, Keystone will start the dev server and update your database is a different way depending on `db.useMigrations`:

#### keystone-next dev

**Dev** mode is identical to **prototyping** mode when using the `knex` of `mongoose` adapters.
When using the `prisma` adapter, Keystone will use Prisma's migration framework to generate and locally apply migrations when you start your system.
If you are using `prisma` and your database already includes migrations then you must use **dev** mode, and not **prototyping** mode.
- If `db.useMigrations` is `false` (the default), Keystone will use Prisma Migrate to update your database so that it matches your schema. It may lose data while updating your database so you should only use this mode in initial development.
- If `db.useMigrations` is `true`, Keystone will use Prisma Migrate to apply any existing migrations and prompt you to create a migration if there was a change to your schema.

#### keystone-next start

Expand All @@ -69,6 +60,8 @@ You generally shouldn't need to use `keystone-next reset` directly unless you wa

#### keystone-next generate

> keystone-next generate is only usable when db.useMigrations is set to true in your config
This command will generate a migration based on the current state of your database and your Keystone schema.
The generated migration artefact should be added to your repository so that it can be shared with other developers and deployed in production.
You should generally use `keystone-next dev` instead of `keystone-next generate` because it will prompt you to create a migration when you need to.
Expand All @@ -81,6 +74,8 @@ You only need to use `keystone-next generate` when you want to generate an empty

#### keystone-next deploy

> keystone-next deploy is only usable when db.useMigrations is set to true in your config
This command will apply any migrations in the migrations directory.
It should be used in production to apply migrations before starting the server.

Expand Down
2 changes: 1 addition & 1 deletion examples-next/graphql-api-endpoint/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.2",
"private": true,
"scripts": {
"dev": "keystone-next prototype",
"dev": "keystone-next dev",
"start": "keystone-next start",
"build": "keystone-next build"
},
Expand Down
2 changes: 1 addition & 1 deletion examples-next/sandbox/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"license": "MIT",
"scripts": {
"dev": "keystone-next prototype",
"dev": "keystone-next dev",
"sandbox": "yarn && yarn dev"
},
"dependencies": {
Expand Down
1 change: 0 additions & 1 deletion examples-next/todo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"private": true,
"license": "MIT",
"scripts": {
"prototype": "keystone-next prototype",
"dev": "keystone-next dev",
"start": "keystone-next start",
"build": "keystone-next build",
Expand Down
9 changes: 6 additions & 3 deletions packages-next/keystone/src/lib/createKeystone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import { MongooseAdapter } from '@keystone-next/adapter-mongoose-legacy';
import { KnexAdapter } from '@keystone-next/adapter-knex-legacy';
// @ts-ignore
import { PrismaAdapter } from '@keystone-next/adapter-prisma-legacy';
import type { KeystoneConfig, BaseKeystone, MigrationMode } from '@keystone-next/types';
import type { KeystoneConfig, BaseKeystone, MigrationAction } from '@keystone-next/types';

export function createKeystone(
config: KeystoneConfig,
dotKeystonePath: string,
migrationMode: MigrationMode,
migrationAction: MigrationAction,
prismaClient?: any
) {
// Note: For backwards compatibility we may want to expose
Expand All @@ -31,7 +31,8 @@ export function createKeystone(
} else if (db.adapter === 'prisma_postgresql') {
adapter = new PrismaAdapter({
getPrismaPath: () => path.join(dotKeystonePath, 'prisma'),
migrationMode,
migrationMode:
migrationAction === 'dev' ? (db.useMigrations ? 'dev' : 'prototype') : migrationAction,
prismaClient,
...db,
provider: 'postgresql',
Expand All @@ -45,6 +46,8 @@ export function createKeystone(
adapter = new PrismaAdapter({
getPrismaPath: () => path.join(dotKeystonePath, 'prisma'),
prismaClient,
migrationMode:
migrationAction === 'dev' ? (db.useMigrations ? 'dev' : 'prototype') : migrationAction,
...db,
provider: 'sqlite',
});
Expand Down
6 changes: 3 additions & 3 deletions packages-next/keystone/src/lib/createSystem.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { KeystoneConfig, MigrationMode } from '@keystone-next/types';
import type { KeystoneConfig, MigrationAction } from '@keystone-next/types';

import { createGraphQLSchema } from './createGraphQLSchema';
import { makeCreateContext } from './createContext';
Expand All @@ -7,10 +7,10 @@ import { createKeystone } from './createKeystone';
export function createSystem(
config: KeystoneConfig,
dotKeystonePath: string,
migrationMode: MigrationMode,
migrationAction: MigrationAction,
prismaClient?: any
) {
const keystone = createKeystone(config, dotKeystonePath, migrationMode, prismaClient);
const keystone = createKeystone(config, dotKeystonePath, migrationAction, prismaClient);

const graphQLSchema = createGraphQLSchema(config, keystone, 'public');

Expand Down
8 changes: 3 additions & 5 deletions packages-next/keystone/src/scripts/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import path from 'path';
import meow from 'meow';
import { prototype } from './run/prototype';
import { dev } from './run/dev';
import { start } from './run/start';
import { build } from './build/build';
Expand All @@ -10,7 +9,7 @@ import { reset } from './migrate/reset';

export type StaticPaths = { dotKeystonePath: string; projectAdminPath: string };

const commands = { prototype, dev, start, build, deploy, generate, reset };
const commands = { dev, start, build, deploy, generate, reset };

function cli() {
const { input, help, flags } = meow(
Expand All @@ -19,8 +18,7 @@ function cli() {
$ keystone-next [command]
Commands
Run
prototype (default) start the project in prototyping mode
dev start the project in development mode
dev (default) start the project in development mode
start start the project in production mode
Build
build build the project (must be done before using start)
Expand All @@ -37,7 +35,7 @@ function cli() {
},
}
);
const command = input[0] || 'prototype';
const command = input[0] || 'dev';
if (!isCommand(command)) {
console.log(`${command} is not a command that keystone-next accepts`);
console.log(help);
Expand Down
14 changes: 12 additions & 2 deletions packages-next/keystone/src/scripts/migrate/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@ import { CONFIG_PATH } from '../utils';
import type { StaticPaths } from '..';

export const deploy = async ({ dotKeystonePath }: StaticPaths) => {
console.log('🤞 Migrating Keystone');

const config = initConfig(requireSource(CONFIG_PATH).default);

if (config.db.adapter !== 'prisma_postgresql' && config.db.adapter !== 'prisma_sqlite') {
console.log('keystone-next deploy only supports Prisma');
process.exit(1);
}

if (!config.db.useMigrations) {
console.log('db.useMigrations must be set to true in your config to use keystone-next deploy');
process.exit(1);
}

const keystone = createKeystone(config, dotKeystonePath, 'none');

console.log('✨ Deploying migrations');
await keystone.adapter.deploy(keystone._consolidateRelationships());
console.log('✅ Deployed migrations');
};
12 changes: 12 additions & 0 deletions packages-next/keystone/src/scripts/migrate/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ export const generate = async (
args: CLIOptionsForCreateMigration
) => {
const config = initConfig(requireSource(CONFIG_PATH).default);
if (config.db.adapter !== 'prisma_postgresql' && config.db.adapter !== 'prisma_sqlite') {
console.log('keystone-next generate only supports Prisma');
process.exit(1);
}

if (!config.db.useMigrations) {
console.log(
'db.useMigrations must be set to true in your config to use keystone-next generate'
);
process.exit(1);
}

const { keystone } = createSystem(config, dotKeystonePath, 'none');

await keystone.adapter._generateClient(keystone._consolidateRelationships());
Expand Down
3 changes: 1 addition & 2 deletions packages-next/keystone/src/scripts/migrate/reset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import { CONFIG_PATH } from '../utils';
import type { StaticPaths } from '..';

export const reset = async ({ dotKeystonePath }: StaticPaths) => {
console.log('🤞 Migrating Keystone');

const config = initConfig(requireSource(CONFIG_PATH).default);
const keystone = createKeystone(config, dotKeystonePath, 'none');

console.log('✨ Resetting database');
await keystone.adapter._prepareSchema(keystone._consolidateRelationships());
await keystone.adapter.dropDatabase();
console.log('✅ Database reset');
};
12 changes: 2 additions & 10 deletions packages-next/keystone/src/scripts/run/dev.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import path from 'path';
import express from 'express';
import { generateAdminUI } from '@keystone-next/admin-ui/system';
import { MigrationMode } from '@keystone-next/types';
import { createSystem } from '../../lib/createSystem';
import { initConfig } from '../../lib/initConfig';
import { requireSource } from '../../lib/requireSource';
Expand All @@ -18,22 +17,15 @@ const devLoadingHTMLFilepath = path.join(
'dev-loading.html'
);

export const dev = async (
{ dotKeystonePath, projectAdminPath }: StaticPaths,
migrationMode: MigrationMode = 'dev'
) => {
export const dev = async ({ dotKeystonePath, projectAdminPath }: StaticPaths) => {
console.log('🤞 Starting Keystone');

const server = express();
let expressServer: null | ReturnType<typeof express> = null;

const config = initConfig(requireSource(CONFIG_PATH).default);
const initKeystone = async () => {
const { keystone, graphQLSchema, createContext } = createSystem(
config,
dotKeystonePath,
migrationMode
);
const { keystone, graphQLSchema, createContext } = createSystem(config, dotKeystonePath, 'dev');

console.log('✨ Generating graphQL schema');
await saveSchemaAndTypes(graphQLSchema, keystone, dotKeystonePath);
Expand Down
4 changes: 0 additions & 4 deletions packages-next/keystone/src/scripts/run/prototype.ts

This file was deleted.

2 changes: 2 additions & 0 deletions packages-next/types/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,14 @@ export type DatabaseConfig = DatabaseCommon &
(
| {
adapter: 'prisma_postgresql';
useMigrations?: boolean;
enableLogging?: boolean;
getPrismaPath?: (arg: { prismaSchema: any }) => string;
getDbSchemaName?: (arg: { prismaSchema: any }) => string;
}
| {
adapter: 'prisma_sqlite';
useMigrations?: boolean;
enableLogging?: boolean;
getPrismaPath?: (arg: { prismaSchema: any }) => string;
}
Expand Down
2 changes: 1 addition & 1 deletion packages-next/types/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,4 @@ export function getGqlNames({
};
}

export type MigrationMode = 'none-skip-client-generation' | 'none' | 'dev' | 'prototype';
export type MigrationAction = 'none-skip-client-generation' | 'none' | 'dev';
2 changes: 1 addition & 1 deletion packages/test-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ async function setupFromConfig({
const { keystone, createContext, graphQLSchema } = createSystem(
_config,
path.resolve('.keystone'),
'prototype'
'dev'
);

const app = await createExpressServer(_config, graphQLSchema, createContext, true, '', false);
Expand Down
4 changes: 2 additions & 2 deletions tests/examples-smoke-tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const exampleProjectTests = (
await cleanupKeystoneProcess();
});

async function startKeystone(command: 'start' | 'prototype') {
async function startKeystone(command: 'start' | 'dev') {
let keystoneProcess = execa('yarn', ['keystone-next', command], {
cwd: projectDir,
env: process.env,
Expand Down Expand Up @@ -84,7 +84,7 @@ export const exampleProjectTests = (

if (mode === 'dev') {
test('start keystone in dev', async () => {
await startKeystone('prototype');
await startKeystone('dev');
});
}

Expand Down

1 comment on commit b37cbff

@vercel
Copy link

@vercel vercel bot commented on b37cbff Mar 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.