Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(@graphql-hive/cli): json output #6122

Draft
wants to merge 129 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
129 commits
Select commit Hold shift + click to select a range
7dc21e7
feat(@graphql-hive/cli): json flag for json output
jasonkuhrt Dec 12, 2024
de6c0dc
Merge branch 'main' into feat/cli-json-flag
jasonkuhrt Dec 13, 2024
b5f78b4
work
jasonkuhrt Dec 13, 2024
31eb025
Merge branch 'main' into feat/cli-json-flag
jasonkuhrt Dec 13, 2024
27e8811
not this PR
jasonkuhrt Dec 13, 2024
195e466
not this pr
jasonkuhrt Dec 13, 2024
3328e20
not this pr
jasonkuhrt Dec 13, 2024
f81c69c
undo
jasonkuhrt Dec 13, 2024
2423a50
unused
jasonkuhrt Dec 13, 2024
d4bd1bf
progress, introduce envelope
jasonkuhrt Dec 13, 2024
35da5f6
more commands
jasonkuhrt Dec 13, 2024
fbc286e
refactor
jasonkuhrt Dec 13, 2024
6a4a46b
clarify log methods
jasonkuhrt Dec 13, 2024
1e3c14f
validate
jasonkuhrt Dec 13, 2024
8f68b79
Merge branch 'main' into feat/cli-json-flag
jasonkuhrt Dec 13, 2024
c9628bb
feedback: remove unused command
jasonkuhrt Dec 16, 2024
528bad9
feedback: remove unused command reset
jasonkuhrt Dec 16, 2024
e8b65ec
feedback: remove unused commands get, delete
jasonkuhrt Dec 16, 2024
fe15732
Merge branch 'main' into feat/cli-json-flag
jasonkuhrt Dec 16, 2024
0d5d33f
feedback: switch to typebox
jasonkuhrt Dec 16, 2024
333095c
doc
jasonkuhrt Dec 16, 2024
79f7763
refactor
jasonkuhrt Dec 16, 2024
1fbbcc7
todo
jasonkuhrt Dec 16, 2024
cf6074c
todo
jasonkuhrt Dec 16, 2024
f8986a6
work
jasonkuhrt Dec 16, 2024
754526c
fix
jasonkuhrt Dec 16, 2024
321325d
lint
jasonkuhrt Dec 16, 2024
923c422
remove command-wide try-catch
jasonkuhrt Dec 16, 2024
032ff42
fixes
jasonkuhrt Dec 16, 2024
ba14c2d
refactor
jasonkuhrt Dec 16, 2024
ad7e65c
finish check
jasonkuhrt Dec 16, 2024
d4f1b88
fix err msg
jasonkuhrt Dec 16, 2024
392ad0f
lint
jasonkuhrt Dec 16, 2024
6b9aba5
fix
jasonkuhrt Dec 16, 2024
af8721b
start tackling failures
jasonkuhrt Dec 16, 2024
def2053
single output
jasonkuhrt Dec 17, 2024
d631f8b
refactor
jasonkuhrt Dec 17, 2024
219faf5
refactor
jasonkuhrt Dec 17, 2024
58b62b8
progress
jasonkuhrt Dec 17, 2024
dee2049
build updates
jasonkuhrt Dec 17, 2024
2221fa0
fix import paths
jasonkuhrt Dec 17, 2024
6d498eb
lint
jasonkuhrt Dec 17, 2024
b258db2
no ci check for now
jasonkuhrt Dec 17, 2024
97c46dd
prettier
jasonkuhrt Dec 17, 2024
9439aa7
Merge branch 'main' into feat/cli-json-flag
jasonkuhrt Dec 17, 2024
612aaa4
schema to own dir
jasonkuhrt Dec 17, 2024
7c1823e
refactor
jasonkuhrt Dec 17, 2024
3ccd617
look at fragments
jasonkuhrt Dec 17, 2024
a45f5d6
output helper
jasonkuhrt Dec 17, 2024
84833da
try schema match hive
jasonkuhrt Dec 17, 2024
2b8c7a8
more
jasonkuhrt Dec 17, 2024
888657a
work
jasonkuhrt Dec 17, 2024
87fe7c7
file
jasonkuhrt Dec 17, 2024
4d5644f
publish command
jasonkuhrt Dec 17, 2024
7dffe2e
helper methods for data only
jasonkuhrt Dec 18, 2024
b33e865
runResult all
jasonkuhrt Dec 18, 2024
5796094
refacotr
jasonkuhrt Dec 18, 2024
247cf5c
prettier
jasonkuhrt Dec 18, 2024
35efce1
improve jsdoc
jasonkuhrt Dec 18, 2024
b0e966e
rename
jasonkuhrt Dec 18, 2024
45af397
Merge branch 'main' into feat/cli-json-flag
jasonkuhrt Dec 18, 2024
e15c9cf
work
jasonkuhrt Dec 18, 2024
4861b9e
fix
jasonkuhrt Dec 18, 2024
eb0b1e1
no only
jasonkuhrt Dec 18, 2024
609d286
refactor
jasonkuhrt Dec 18, 2024
1fa5079
no only
jasonkuhrt Dec 18, 2024
4b3675f
titles
jasonkuhrt Dec 18, 2024
3010a25
refactor
jasonkuhrt Dec 18, 2024
58d3c70
lint
jasonkuhrt Dec 18, 2024
9d0fc87
refactor cli tests to use inferred hive cli library
jasonkuhrt Dec 18, 2024
31cdc7c
lint
jasonkuhrt Dec 18, 2024
76eb192
no empty
jasonkuhrt Dec 18, 2024
5489204
support multi flags
jasonkuhrt Dec 19, 2024
8b23d0e
do not repeat dev interface
jasonkuhrt Dec 19, 2024
77db99a
rename
jasonkuhrt Dec 19, 2024
59b066c
rest
jasonkuhrt Dec 19, 2024
2e675fd
undo test refactors
jasonkuhrt Dec 19, 2024
438941c
Merge branch 'main' into feat/cli-json-flag
jasonkuhrt Dec 19, 2024
ee1dcc7
vitest fixtures return
jasonkuhrt Dec 19, 2024
044835a
type fix
jasonkuhrt Dec 19, 2024
ba94eea
tmpfile util
jasonkuhrt Dec 19, 2024
cd30c27
wire up json
jasonkuhrt Dec 19, 2024
90afcf7
snapshot system
jasonkuhrt Dec 19, 2024
d9b114d
all tests snapping
jasonkuhrt Dec 19, 2024
728af1f
clean
jasonkuhrt Dec 19, 2024
8cf2893
no ansi
jasonkuhrt Dec 19, 2024
764fb20
first pass of json output
jasonkuhrt Dec 19, 2024
64d60d8
json error output
jasonkuhrt Dec 19, 2024
028cfde
more json error output
jasonkuhrt Dec 19, 2024
4fb7f07
simplify
jasonkuhrt Dec 19, 2024
07fa446
optional
jasonkuhrt Dec 19, 2024
5d62044
Merge branch 'main' into feat/cli-json-flag
jasonkuhrt Dec 19, 2024
a503c12
no stacks
jasonkuhrt Dec 19, 2024
35e299e
type failure or success
jasonkuhrt Dec 19, 2024
fd1a85c
no __typename
jasonkuhrt Dec 19, 2024
c35833d
Merge branch 'main' into feat/cli-json-flag
jasonkuhrt Dec 19, 2024
07b9f97
encode type field
jasonkuhrt Dec 19, 2024
5049eb3
simplify test
jasonkuhrt Dec 19, 2024
b774dcf
try fix
jasonkuhrt Dec 19, 2024
b3b63d2
try fix
jasonkuhrt Dec 20, 2024
f7e3ef0
reduce methods
jasonkuhrt Dec 20, 2024
5444780
unused
jasonkuhrt Dec 20, 2024
af41791
try again
jasonkuhrt Dec 20, 2024
48973fb
try again
jasonkuhrt Dec 20, 2024
f1f6b9f
try one more thing
jasonkuhrt Dec 20, 2024
7943697
no parameters stuff
jasonkuhrt Dec 20, 2024
f6b12a4
format
jasonkuhrt Dec 20, 2024
c7c629e
try quick cli tests in ci
jasonkuhrt Dec 20, 2024
1cd5734
try ci run again
jasonkuhrt Dec 20, 2024
ada91d0
log settings
jasonkuhrt Dec 20, 2024
ade8ad3
pnpm pach fml
jasonkuhrt Dec 20, 2024
ad3024b
repatch
jasonkuhrt Dec 20, 2024
1f2f2eb
repatch
jasonkuhrt Dec 20, 2024
c8dbfaf
repatch
jasonkuhrt Dec 20, 2024
ed962e7
repatch
jasonkuhrt Dec 20, 2024
3d6b2e7
repatch
jasonkuhrt Dec 20, 2024
ae88819
repatch
jasonkuhrt Dec 20, 2024
66d174c
repatch
jasonkuhrt Dec 20, 2024
d2bfa72
more debug
jasonkuhrt Dec 20, 2024
dae187d
repatch
jasonkuhrt Dec 20, 2024
6663144
repatch
jasonkuhrt Dec 20, 2024
45362ae
found it?
jasonkuhrt Dec 20, 2024
71d1bd0
restore
jasonkuhrt Dec 20, 2024
bda183b
restore
jasonkuhrt Dec 20, 2024
33e5025
lint
jasonkuhrt Dec 20, 2024
aaf34ac
update vitest for no flicker watch mode
jasonkuhrt Dec 20, 2024
2457c33
restore old patch
jasonkuhrt Dec 20, 2024
c5be266
rename types
jasonkuhrt Dec 21, 2024
0b7aff4
lockfile
jasonkuhrt Dec 21, 2024
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: 0 additions & 2 deletions .github/workflows/tests-integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ jobs:

- name: run integration tests
timeout-minutes: 10
env:
HIVE_DEBUG: 1
Comment on lines -74 to -75
Copy link
Member Author

Choose a reason for hiding this comment

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

This causes stack traces to be rendered by OClif which breaks our CLI snapshot tests.

run: |
VITEST_MAX_THREADS=${{ steps.cpu-cores.outputs.count }} pnpm --filter integration-tests test:integration --shard=${{ matrix.shardIndex }}/3

Expand Down
10 changes: 9 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"search.exclude": {
"**/dist": true,
"**/pnpm-lock.yaml": true,
"**/*.tsbuildinfo": true
},
"commands.commands": [
{
"command": "terminals.runTerminals",
Expand Down Expand Up @@ -28,5 +33,8 @@
["cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"],
["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"],
["clsx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
]
],
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
jasonkuhrt marked this conversation as resolved.
Show resolved Hide resolved
}
2 changes: 1 addition & 1 deletion codegen.mts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const config: CodegenConfig = {
},
// CLI
'./packages/libraries/cli/src/gql/': {
documents: ['./packages/libraries/cli/src/(commands|helpers)/**/*.ts'],
documents: ['./packages/libraries/cli/src/(commands|fragments)/**/*.ts'],
preset: 'client',
plugins: [],
config: {
Expand Down
6 changes: 4 additions & 2 deletions integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
"@aws-sdk/client-s3": "3.693.0",
"@esm2cjs/execa": "6.1.1-cjs.1",
"@graphql-hive/apollo": "workspace:*",
"@graphql-hive/cli": "workspace:*",
Copy link
Member Author

Choose a reason for hiding this comment

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

It would be great to get our coverage to not be child-process based because each CLI invocation is taking 1-2 seconds which is terrible. Ideally we can reserve child process spawning tests for only a select few cases that really need that. I briefly played around with that while waiting for CI a few times. Currently this dep remains unused, we could remove.

"@graphql-hive/core": "workspace:*",
"@graphql-typed-document-node/core": "3.2.0",
"@hive/rate-limit": "workspace:*",
"@hive/schema": "workspace:*",
"@hive/server": "workspace:*",
"@hive/storage": "workspace:*",
"@sinclair/typebox": "^0.34.12",
Copy link
Member Author

@jasonkuhrt jasonkuhrt Dec 20, 2024

Choose a reason for hiding this comment

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

Having worked with this library for the first time for this PR via prompt from @n1ru4l, I can now confirm I like it.

"@trpc/client": "10.45.2",
"@trpc/server": "10.45.2",
"@types/async-retry": "1.4.8",
Expand All @@ -34,9 +36,9 @@
"human-id": "4.1.1",
"ioredis": "5.4.1",
"slonik": "30.4.4",
"strip-ansi": "7.1.0",
"strip-ansi": "6.0.1",
Copy link
Member Author

Choose a reason for hiding this comment

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

For our CLI output snapshot serializer.

"tslib": "2.8.1",
"vitest": "2.0.5",
"vitest": "2.1.8",
Copy link
Member Author

Choose a reason for hiding this comment

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

Thought this might be the version that improves terminal watch mode flickering. Doesn't seem to have worked but no matter, upgrade vitest all the same.

"zod": "3.23.8"
}
}
45 changes: 26 additions & 19 deletions integration-tests/testkit/cli.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,27 @@
import { randomUUID } from 'node:crypto';
import { writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
import { resolve } from 'node:path';
import { execaCommand } from '@esm2cjs/execa';
import { fetchLatestSchema, fetchLatestValidSchema } from './flow';
import { generateTmpFile } from './fs';
import { getServiceHost } from './utils';

const binPath = resolve(__dirname, '../../packages/libraries/cli/bin/run');
const cliDir = resolve(__dirname, '../../packages/libraries/cli');

async function generateTmpFile(content: string, extension: string) {
const dir = tmpdir();
const fileName = randomUUID();
const filepath = join(dir, `${fileName}.${extension}`);

await writeFile(filepath, content, 'utf-8');

return filepath;
}

async function exec(cmd: string) {
const outout = await execaCommand(`${binPath} ${cmd}`, {
export async function exec(cmd: string) {
const result = await execaCommand(`${binPath} ${cmd}`, {
shell: true,
env: {
OCLIF_CLI_CUSTOM_PATH: cliDir,
NODE_OPTIONS: '--no-deprecation',
},
});

if (outout.failed) {
throw new Error(outout.stderr);
if (result.failed) {
throw new Error('CLI execution marked as "failed".', { cause: result.stderr });
Copy link
Member Author

Choose a reason for hiding this comment

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

While trying to understand where stack traces and errors were coming from, I adjusted this to be more explicit.

}

return outout.stdout;
return result.stdout;
}

export async function schemaPublish(args: string[]) {
Expand Down Expand Up @@ -70,6 +59,8 @@ async function dev(args: string[]) {
);
}

export type CLI = ReturnType<typeof createCLI>;

export function createCLI(tokens: { readwrite: string; readonly: string }) {
let publishCount = 0;

Expand All @@ -81,6 +72,7 @@ export function createCLI(tokens: { readwrite: string; readonly: string }) {
expect: expectedStatus,
legacy_force,
legacy_acceptBreakingChanges,
json,
}: {
sdl: string;
commit?: string;
Expand All @@ -90,6 +82,7 @@ export function createCLI(tokens: { readwrite: string; readonly: string }) {
legacy_force?: boolean;
legacy_acceptBreakingChanges?: boolean;
expect: 'latest' | 'latest-composable' | 'ignored' | 'rejected';
json?: boolean;
}): Promise<string> {
const publishName = ` #${++publishCount}`;
const commit = randomUUID();
Expand All @@ -106,6 +99,7 @@ export function createCLI(tokens: { readwrite: string; readonly: string }) {
...(metadata ? ['--metadata', await generateTmpFile(JSON.stringify(metadata), 'json')] : []),
...(legacy_force ? ['--force'] : []),
...(legacy_acceptBreakingChanges ? ['--experimental_acceptBreakingChanges'] : []),
...(json ? ['--json'] : []),
await generateTmpFile(sdl, 'graphql'),
]);

Expand Down Expand Up @@ -177,15 +171,18 @@ export function createCLI(tokens: { readwrite: string; readonly: string }) {
sdl,
serviceName,
expect: expectedStatus,
json,
}: {
sdl: string;
serviceName?: string;
expect: 'approved' | 'rejected';
json?: boolean;
}): Promise<string> {
const cmd = schemaCheck([
'--registry.accessToken',
tokens.readonly,
...(serviceName ? ['--service', serviceName] : []),
...(json ? ['--json'] : []),
await generateTmpFile(sdl, 'graphql'),
]);

Expand All @@ -199,11 +196,19 @@ export function createCLI(tokens: { readwrite: string; readonly: string }) {
async function deleteCommand({
serviceName,
expect: expectedStatus,
json,
}: {
serviceName?: string;
json?: boolean;
expect: 'latest' | 'latest-composable' | 'rejected';
}): Promise<string> {
const cmd = schemaDelete(['--token', tokens.readwrite, '--confirm', serviceName ?? '']);
const cmd = schemaDelete([
'--token',
tokens.readwrite,
'--confirm',
serviceName ?? '',
...(json ? ['--json'] : []),
]);

const before = {
latest: await fetchLatestSchema(tokens.readonly).then(r => r.expectNoGraphQLErrors()),
Expand Down Expand Up @@ -259,11 +264,13 @@ export function createCLI(tokens: { readwrite: string; readonly: string }) {
url: string;
sdl: string;
}>;
json?: boolean;
remote: boolean;
write?: string;
useLatestVersion?: boolean;
}) {
return dev([
...(input.json ? ['--json'] : []),
...(input.remote
? [
'--remote',
Expand Down
27 changes: 27 additions & 0 deletions integration-tests/testkit/fs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { randomUUID } from 'node:crypto';
import { readFile, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

export function tmpFile(extension: string) {
const dir = tmpdir();
const fileName = randomUUID();
const filepath = join(dir, `${fileName}.${extension}`);

return {
filepath,
read() {
return readFile(filepath, 'utf-8');
},
};
}

export async function generateTmpFile(content: string, extension: string) {
const dir = tmpdir();
const fileName = randomUUID();
const filepath = join(dir, `${fileName}.${extension}`);

await writeFile(filepath, content, 'utf-8');

return filepath;
}
6 changes: 6 additions & 0 deletions integration-tests/testkit/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ import { UpdateSchemaPolicyForOrganization, UpdateSchemaPolicyForProject } from
import { collect, CollectedOperation, legacyCollect } from './usage';
import { generateUnique } from './utils';

export type Seed = ReturnType<typeof initSeed>;
export type OwnerSeed = Awaited<ReturnType<Seed['createOwner']>>;
export type OrgSeed = Awaited<ReturnType<OwnerSeed['createOrg']>>;
export type ProjectSeed = Awaited<ReturnType<OrgSeed['createProject']>>;
export type TargetAccessTokenSeed = Awaited<ReturnType<ProjectSeed['createTargetAccessToken']>>;

export function initSeed() {
const pg = {
user: ensureEnv('POSTGRES_USER'),
Expand Down
102 changes: 102 additions & 0 deletions integration-tests/testkit/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* This module uses Vitest's fixture system to make common usage patterns
* of our testkit easily consumable in test cases. @see https://vitest.dev/guide/test-context.html#test-extend
*/

import { test as testBase } from 'vitest';
import { CLI, createCLI } from './cli';
import { ProjectType } from './gql/graphql';
import { initSeed, OrgSeed, OwnerSeed, ProjectSeed, Seed, TargetAccessTokenSeed } from './seed';

interface Context {
seed: Seed;
owner: OwnerSeed;
org: OrgSeed;
//
// "single" branch
//
projectSingle: ProjectSeed;
targetAccessTokenSingle: TargetAccessTokenSeed;
cliSingle: CLI;
//
// "federation" branch
//
projectFederation: ProjectSeed;
targetAccessTokenFederation: TargetAccessTokenSeed;
cliFederation: CLI;
//
// "stitching" branch
//
projectStitching: ProjectSeed;
targetAccessTokenStitching: TargetAccessTokenSeed;
cliStitching: CLI;
}

export const test = testBase.extend<Context>({
seed: async ({}, use) => {
const seed = await initSeed();
await use(seed);
},
owner: async ({ seed }, use) => {
const owner = await seed.createOwner();
await use(owner);
},
org: async ({ owner }, use) => {
const org = await owner.createOrg();
await use(org);
},
//
// "single" branch
//
projectSingle: async ({ org }, use) => {
const project = await org.createProject(ProjectType.Single);
await use(project);
},
targetAccessTokenSingle: async ({ projectSingle }, use) => {
const targetAccessToken = await projectSingle.createTargetAccessToken({});
await use(targetAccessToken);
},
cliSingle: async ({ targetAccessTokenSingle }, use) => {
const cli = createCLI({
readwrite: targetAccessTokenSingle.secret,
readonly: targetAccessTokenSingle.secret,
});
await use(cli);
},
//
// "federation" branch
//
projectFederation: async ({ org }, use) => {
const project = await org.createProject(ProjectType.Federation);
await use(project);
},
targetAccessTokenFederation: async ({ projectFederation }, use) => {
const targetAccessToken = await projectFederation.createTargetAccessToken({});
await use(targetAccessToken);
},
cliFederation: async ({ targetAccessTokenFederation }, use) => {
const cli = createCLI({
readwrite: targetAccessTokenFederation.secret,
readonly: targetAccessTokenFederation.secret,
});
await use(cli);
},
//
// "stitching" branch
//
projectStitching: async ({ org }, use) => {
const project = await org.createProject(ProjectType.Stitching);
await use(project);
},
targetAccessTokenStitching: async ({ projectStitching }, use) => {
const targetAccessToken = await projectStitching.createTargetAccessToken({});
await use(targetAccessToken);
},
cliStitching: async ({ targetAccessTokenStitching }, use) => {
const cli = createCLI({
readwrite: targetAccessTokenStitching.secret,
readonly: targetAccessTokenStitching.secret,
});
await use(cli);
},
});
1 change: 1 addition & 0 deletions integration-tests/tests/cli/__snapshot_serializers__/_.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './cli-output';
1 change: 1 addition & 0 deletions integration-tests/tests/cli/__snapshot_serializers__/__.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as SnapshotSerializers from './_';
Loading
Loading