mirror of
https://github.com/cypress-io/cypress.git
synced 2026-04-24 07:59:12 -05:00
feat: merging / delegating remote queries to cloud schema (#17875)
Changes: - Pulls down & stitches in the "Cypress Cloud" schema from remote. Currently defaulted to "staging" so we can make quick fixes / iteration there - Add dedicated type checking CircleCI job `check-ts` - Adds [graphcache](https://formidable.com/open-source/urql/docs/graphcache/) to normalize the cache & provide better auto-reactivity when data changes - Removes `LocalProject` / `DashboardProject` in favor of `Project` / `CloudProject` - General cleanup of Vue components' GraphQL fragments - Parallelizes launchpad tests & recording to new Cypress project: https://dashboard.cypress.io/projects/sehy69/runs - Did this b/c tests were frequently timing out, need to figure out the source of this - Basic mocks for remote schema
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"extends": "../.eslintrc.json",
|
||||
"rules": {
|
||||
"no-console": "off"
|
||||
"no-console": "off",
|
||||
"no-empty": "off"
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,34 @@
|
||||
import getenv from 'getenv'
|
||||
|
||||
// Where to fetch the remote "federated" schema TODO: add w/ stitching PR
|
||||
// export const CYPRESS_INTERNAL_CLOUD_ENV = 'production'
|
||||
type Maybe<T> = T | null | undefined
|
||||
|
||||
// Where to fetch the remote "federated" schema. If you have a long-running branch
|
||||
// against a development schema, it's probably easiest to set this manually to "develop"
|
||||
export const CYPRESS_INTERNAL_CLOUD_ENV = getenv('CYPRESS_INTERNAL_CLOUD_ENV', 'staging') as 'production' | 'staging' | 'development'
|
||||
|
||||
process.env.CYPRESS_INTERNAL_CLOUD_ENV = CYPRESS_INTERNAL_CLOUD_ENV
|
||||
|
||||
process.env.CYPRESS_KONFIG_ENV = getenv('CYPRESS_KONFIG_ENV', CYPRESS_INTERNAL_CLOUD_ENV)
|
||||
|
||||
export const CYPRESS_INTERNAL_DEBUG_PORT_STARTUP = getenv.int('CYPRESS_INTERNAL_DEBUG_PORT_STARTUP', 7200)
|
||||
|
||||
export const CYPRESS_INTERNAL_DEBUG_PORT_ELECTRON = getenv.int('CYPRESS_INTERNAL_DEBUG_PORT_ELECTRON', 7201)
|
||||
|
||||
export const CYPRESS_INTERNAL_DEBUG_PORT_CODEGEN = getenv.int('CYPRESS_INTERNAL_DEBUG_PORT_CODEGEN', 7202)
|
||||
|
||||
interface GulpGlobalVals {
|
||||
debug?: Maybe<'--inspect' | '--inspect-brk'>
|
||||
shouldWatch?: boolean
|
||||
}
|
||||
|
||||
const globalVals: GulpGlobalVals = {
|
||||
shouldWatch: true,
|
||||
}
|
||||
|
||||
export function setGulpGlobal<K extends keyof GulpGlobalVals> (k: K, v: GulpGlobalVals[K]) {
|
||||
globalVals[k] = v
|
||||
}
|
||||
|
||||
export function getGulpGlobal<K extends keyof GulpGlobalVals> (k: K): GulpGlobalVals[K] {
|
||||
return globalVals[k]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import gulp from 'gulp'
|
||||
import { autobarrelWatcher } from './tasks/gulpAutobarrel'
|
||||
import { startCypressWatch } from './tasks/gulpCypress'
|
||||
import { graphqlCodegen, graphqlCodegenWatch, nexusCodegen, nexusCodegenWatch } from './tasks/gulpGraphql'
|
||||
import { graphqlCodegen, graphqlCodegenWatch, nexusCodegen, nexusCodegenWatch, printUrqlSchema, syncRemoteGraphQL } from './tasks/gulpGraphql'
|
||||
import { checkTs } from './tasks/gulpTsc'
|
||||
import { viteApp, viteCleanApp, viteCleanLaunchpad, viteLaunchpad } from './tasks/gulpVite'
|
||||
import { makePathMap } from './utils/makePathMap'
|
||||
import { setGulpGlobal } from './gulpConstants'
|
||||
|
||||
gulp.task(
|
||||
'dev',
|
||||
@@ -12,8 +14,7 @@ gulp.task(
|
||||
autobarrelWatcher,
|
||||
|
||||
// Fetch the latest "remote" schema from the Cypress cloud
|
||||
// TODO: with stitching bracnh
|
||||
// fetchCloudSchema,
|
||||
syncRemoteGraphQL,
|
||||
|
||||
gulp.parallel(
|
||||
// Clean the vite apps
|
||||
@@ -37,7 +38,38 @@ gulp.task(
|
||||
),
|
||||
)
|
||||
|
||||
gulp.task(
|
||||
'devNoWatch',
|
||||
gulp.series(
|
||||
async function setupDevNoWatch () {
|
||||
setGulpGlobal('shouldWatch', false)
|
||||
},
|
||||
'dev',
|
||||
),
|
||||
)
|
||||
|
||||
gulp.task(
|
||||
'debug',
|
||||
gulp.series(
|
||||
async function setupDebug () {
|
||||
setGulpGlobal('debug', '--inspect')
|
||||
},
|
||||
'dev',
|
||||
),
|
||||
)
|
||||
|
||||
gulp.task(
|
||||
'debugBrk',
|
||||
gulp.series(
|
||||
async function setupDebugBrk () {
|
||||
setGulpGlobal('debug', '--inspect-brk')
|
||||
},
|
||||
'dev',
|
||||
),
|
||||
)
|
||||
|
||||
gulp.task('buildProd', gulp.series(
|
||||
syncRemoteGraphQL,
|
||||
nexusCodegen,
|
||||
graphqlCodegen,
|
||||
))
|
||||
@@ -62,6 +94,9 @@ gulp.task(
|
||||
// 'debug', // Tim: TODO
|
||||
// )
|
||||
|
||||
gulp.task(checkTs)
|
||||
gulp.task(syncRemoteGraphQL)
|
||||
gulp.task(printUrqlSchema)
|
||||
gulp.task(makePathMap)
|
||||
gulp.task(nexusCodegen)
|
||||
gulp.task(nexusCodegenWatch)
|
||||
|
||||
@@ -4,17 +4,24 @@ import childProcess, { ChildProcess } from 'child_process'
|
||||
import pDefer from 'p-defer'
|
||||
|
||||
import { monorepoPaths } from '../monorepoPaths'
|
||||
import { getGulpGlobal } from '../gulpConstants'
|
||||
|
||||
/**
|
||||
* Starts cypress, but watches the GraphQL files & restarts the server
|
||||
* when any of those change
|
||||
*/
|
||||
export function startCypressWatch () {
|
||||
const watcher = chokidar.watch('src/**/*.{js,ts}', {
|
||||
cwd: monorepoPaths.pkgGraphql,
|
||||
const shouldWatch = getGulpGlobal('shouldWatch')
|
||||
|
||||
const watcher = shouldWatch ? chokidar.watch([
|
||||
'packages/graphql/src/**/*.{js,ts}',
|
||||
'packages/server/lib/graphql/**/*.{js,ts}',
|
||||
], {
|
||||
cwd: monorepoPaths.root,
|
||||
ignored: /\.gen\.ts/,
|
||||
ignoreInitial: true,
|
||||
})
|
||||
}) : null
|
||||
|
||||
let child: ChildProcess | null = null
|
||||
|
||||
let isClosing = false
|
||||
@@ -36,13 +43,19 @@ export function startCypressWatch () {
|
||||
argv.push('--dev')
|
||||
}
|
||||
|
||||
const debugFlag = getGulpGlobal('debug')
|
||||
|
||||
if (debugFlag) {
|
||||
process.env.CYPRESS_INTERNAL_DEV_DEBUG = debugFlag
|
||||
}
|
||||
|
||||
child = childProcess.fork(pathToCli, ['open', ...argv], {
|
||||
stdio: 'inherit',
|
||||
execArgv: [],
|
||||
env: {
|
||||
...process.env,
|
||||
LAUNCHPAD: '1',
|
||||
CYPRESS_INTERNAL_DEV_WATCH: 'true',
|
||||
CYPRESS_INTERNAL_DEV_WATCH: shouldWatch ? 'true' : undefined,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -77,8 +90,10 @@ export function startCypressWatch () {
|
||||
openServer()
|
||||
}
|
||||
|
||||
watcher.on('add', restartServer)
|
||||
watcher.on('change', restartServer)
|
||||
if (shouldWatch) {
|
||||
watcher?.on('add', restartServer)
|
||||
watcher?.on('change', restartServer)
|
||||
}
|
||||
|
||||
openServer()
|
||||
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
// @ts-expect-error - no types
|
||||
import rp from '@cypress/request-promise'
|
||||
import path from 'path'
|
||||
import pDefer from 'p-defer'
|
||||
import chalk from 'chalk'
|
||||
import fs from 'fs-extra'
|
||||
import { buildSchema, introspectionFromSchema } from 'graphql'
|
||||
import { minifyIntrospectionQuery } from '@urql/introspection'
|
||||
|
||||
import { nexusTypegen, watchNexusTypegen } from '../utils/nexusTypegenUtil'
|
||||
import { monorepoPaths } from '../monorepoPaths'
|
||||
import { spawned } from '../utils/childProcessUtils'
|
||||
import { spawn } from 'child_process'
|
||||
import { CYPRESS_INTERNAL_CLOUD_ENV } from '../gulpConstants'
|
||||
|
||||
export async function nexusCodegen () {
|
||||
return nexusTypegen({
|
||||
@@ -75,3 +81,34 @@ export async function graphqlCodegenWatch () {
|
||||
|
||||
return dfd.promise
|
||||
}
|
||||
|
||||
const ENV_MAP = {
|
||||
development: 'http://localhost:3000',
|
||||
staging: 'https://dashboard-staging.cypress.io',
|
||||
production: 'https://dashboard.cypress.io',
|
||||
}
|
||||
|
||||
export async function syncRemoteGraphQL () {
|
||||
if (!ENV_MAP[CYPRESS_INTERNAL_CLOUD_ENV]) {
|
||||
throw new Error(`Expected --env to be one of ${Object.keys(ENV_MAP).join(', ')}`)
|
||||
}
|
||||
|
||||
const body = await rp.get(`${ENV_MAP[CYPRESS_INTERNAL_CLOUD_ENV]}/test-runner-graphql-schema`)
|
||||
|
||||
// TODO(tim): fix
|
||||
await fs.promises.writeFile(path.join(monorepoPaths.pkgGraphql, 'schemas/cloud.graphql'), body)
|
||||
await fs.promises.writeFile(path.join(monorepoPaths.pkgGraphql, 'src/gen/cloud-introspection.gen.json'), JSON.stringify(introspectionFromSchema(buildSchema(body)), null, 2))
|
||||
}
|
||||
|
||||
export async function printUrqlSchema () {
|
||||
const schemaContents = await fs.promises.readFile(path.join(monorepoPaths.pkgGraphql, 'schemas/schema.graphql'), 'utf8')
|
||||
|
||||
const URQL_INTROSPECTION_PATH = path.join(monorepoPaths.pkgFrontendShared, 'src/generated/urql-introspection.gen.ts')
|
||||
|
||||
await fs.ensureDir(path.dirname(URQL_INTROSPECTION_PATH))
|
||||
|
||||
await fs.promises.writeFile(
|
||||
URQL_INTROSPECTION_PATH,
|
||||
`/* eslint-disable */\nexport const urqlSchema = ${JSON.stringify(minifyIntrospectionQuery(introspectionFromSchema(buildSchema(schemaContents))), null, 2)} as const`,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,18 @@ process.on('unhandledRejection', (reason) => {
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
export function getTsPackages (packagesPath: string = ''): Promise<Set<string>> {
|
||||
export async function checkTs () {
|
||||
const [a, b] = await Promise.all([
|
||||
getTsPackages(),
|
||||
getTsPackages('npm'),
|
||||
])
|
||||
|
||||
await buildPackageTsc({
|
||||
tsPackages: new Set([...a, ...b]),
|
||||
})
|
||||
}
|
||||
|
||||
export function getTsPackages (packagesPath: string = 'packages'): Promise<Set<string>> {
|
||||
const dfd = {} as Record<string, any>
|
||||
|
||||
dfd.promise = new Promise((resolve, reject) => {
|
||||
@@ -21,7 +32,7 @@ export function getTsPackages (packagesPath: string = ''): Promise<Set<string>>
|
||||
})
|
||||
|
||||
glob(
|
||||
path.join(monorepoPaths.root, packagesPath, '/packages/*/package.json'),
|
||||
path.join(monorepoPaths.root, packagesPath, '/*/package.json'),
|
||||
(err, packageJsons) => {
|
||||
if (err) {
|
||||
return dfd.reject(err)
|
||||
@@ -40,9 +51,9 @@ export function getTsPackages (packagesPath: string = ''): Promise<Set<string>>
|
||||
const packages = new Set<string>()
|
||||
|
||||
required.forEach((r) => {
|
||||
// Select only packages that have build-ts
|
||||
if (r.scripts && r.scripts['build-ts']) {
|
||||
packages.add(r.name)
|
||||
// Select only packages that have check-ts
|
||||
if (r.scripts && r.scripts['check-ts']) {
|
||||
packages.add(`${packagesPath}/${r.name}`)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -54,7 +65,6 @@ export function getTsPackages (packagesPath: string = ''): Promise<Set<string>>
|
||||
}
|
||||
|
||||
export async function buildPackageTsc ({
|
||||
packagesPath = '',
|
||||
tsPackages = new Set(),
|
||||
onlyPackages,
|
||||
}: {
|
||||
@@ -73,30 +83,28 @@ export async function buildPackageTsc ({
|
||||
const packages = onlyPackages || [...Array.from(tsPackages)]
|
||||
|
||||
for (const pkg of packages) {
|
||||
const cwd = path.join(
|
||||
monorepoPaths.root,
|
||||
`/${pkg.replace(/\@(packages|cypress)\//, '')}`,
|
||||
)
|
||||
|
||||
try {
|
||||
await execAsync('tsc', {
|
||||
cwd: path.join(
|
||||
__dirname,
|
||||
'../../',
|
||||
packagesPath,
|
||||
`/packages/${pkg.replace(/\@(packages|cypress|frontend)\//, '')}`,
|
||||
),
|
||||
})
|
||||
await execAsync('yarn check-ts', { cwd })
|
||||
|
||||
built++
|
||||
console.log(
|
||||
`${chalk.green(`Built`)} ${pkg} ${chalk.magenta(
|
||||
`${chalk.green(`Built`)} ${cwd} ${chalk.magenta(
|
||||
`${built} / ${packages.length}`,
|
||||
)}`,
|
||||
)
|
||||
} catch (e) {
|
||||
console.log(
|
||||
`${chalk.red(`Failed built`)} ${pkg} ${chalk.magenta(
|
||||
`${chalk.red(`Failed building`)} ${cwd} ${chalk.magenta(
|
||||
`${built} / ${packages.length}`,
|
||||
)}`,
|
||||
)
|
||||
|
||||
errors.push({ package: pkg, stdout: e.stdout })
|
||||
errors.push({ package: cwd, stdout: e.stdout })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2018",
|
||||
"moduleResolution": "node",
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
|
||||
@@ -3,6 +3,11 @@ import chalk from 'chalk'
|
||||
import pDefer from 'p-defer'
|
||||
import chokidar from 'chokidar'
|
||||
import _ from 'lodash'
|
||||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
|
||||
import { printUrqlSchema } from '../tasks/gulpGraphql'
|
||||
import { monorepoPaths } from '../monorepoPaths'
|
||||
|
||||
interface NexusTypegenCfg {
|
||||
cwd: string
|
||||
@@ -21,6 +26,8 @@ export async function nexusTypegen (cfg: NexusTypegenCfg) {
|
||||
const dfd = pDefer()
|
||||
|
||||
if (cfg.outputPath) {
|
||||
await fs.ensureDir(path.join(monorepoPaths.pkgGraphql, 'src/gen'))
|
||||
execSync(`touch ${path.join(monorepoPaths.pkgGraphql, 'src/gen/cloud-source-types.gen.ts')}`)
|
||||
execSync(`touch ${cfg.outputPath}`)
|
||||
}
|
||||
|
||||
@@ -51,7 +58,9 @@ export async function nexusTypegen (cfg: NexusTypegenCfg) {
|
||||
|
||||
out.on('error', dfd.reject)
|
||||
|
||||
return dfd.promise
|
||||
return dfd.promise.then(() => {
|
||||
return printUrqlSchema()
|
||||
})
|
||||
}
|
||||
|
||||
let debounced: Record<string, Function> = {}
|
||||
|
||||
Reference in New Issue
Block a user