Skip to content

Commit

Permalink
Merge pull request #3061 from artsy/wip_check_gql_breaking
Browse files Browse the repository at this point in the history
Adds a graphql validation script to force deploys
  • Loading branch information
orta authored Dec 13, 2018
2 parents fbb42f5 + e59b7b3 commit 2cc25bc
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 88 deletions.
3 changes: 3 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ jobs:
docker:
- image: artsy/hokusai:0.4.5
steps:
- run:
name: Validated Schemas
command: node scripts/validate_schemas.js
- add_ssh_keys
- checkout
- setup_remote_docker
Expand Down
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
language: node_js
node_js: 10
script: yarn danger ci
58 changes: 50 additions & 8 deletions dangerfile.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,52 @@
import { warn, danger } from 'danger'

// Warn about creating new JS files
const jsFiles = danger.git.created_files.filter(f => f.endsWith('.js'))
if (jsFiles.length) {
const files = danger.github.utils.fileLinks(jsFiles)
warn(
`Please don't include .js files, we want to be using TypeScript found: ${files}.`
import { warn, danger } from "danger"
import { getBreakingChanges } from "./scripts/validate_schemas"
import { BreakingChange } from "graphql"

// tslint:disable-next-line:no-default-export
export default async () => {
// Warn about creating new JS files
const jsFiles = danger.git.created_files.filter(
f => f.includes("src") && f.endsWith(".js")
)
if (jsFiles.length) {
const files = danger.github.utils.fileLinks(jsFiles)
warn(
`Please don't include .js files, we want to be using TypeScript found: ${files}.`
)
}

// Breaking change check for Metaphysics production when deploying
if (danger.github.pr.base.ref === "release") {
const breakingChanges = await getBreakingChanges()
const bc = breakingChanges
const typesMissing = bc.filter(b => b.type === "TYPE_REMOVED")
const fieldsMissing = bc.filter(b => b.type === "FIELD_REMOVED")
const unionOptionsMissing = bc.filter(
b => b.type === "TYPE_REMOVED_FROM_UNION"
)
const fieldChanged = bc.filter(b => b.type === "FIELD_CHANGED_KIND")

const descriptions = {
"Missing types": typesMissing,
"Fields missing": fieldsMissing,
"Fields changed": fieldChanged,
"Union types-mismatch": unionOptionsMissing,
}

if (bc.length) {
fail(
`Metaphysics production does not have a compatible schema for force's GraphQL usage, please deploy metaphysics to production and re-run Travis CI.`
)
}

Object.keys(descriptions).forEach(key => {
const breakingChanges: BreakingChange[] = descriptions[key]
if (breakingChanges.length) {
const fields = breakingChanges.map(
b => "`" + b.description.split(" ")[0] + "`"
)
fail(`${key}: ${danger.utils.sentence(fields)}`)
}
})
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@
"cache-loader": "^1.2.2",
"coffee-loader": "^0.8.0",
"core-js": "^2.5.7",
"danger": "^4.0.1",
"danger": "^6.0.2",
"electron": "1.7.16",
"enzyme": "^3.4.4",
"enzyme-adapter-react-16": "^1.5.0",
Expand Down
81 changes: 81 additions & 0 deletions scripts/validate_schemas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// @ts-check

// Grab the schema for reaction out of the node_modules
// to see if it is a subset of the production schema
//
// Used both by Danger during the deploy PR, and also
// before the deployment on circle
//
const { readFileSync } = require("fs")
const {
introspectionQuery,
buildClientSchema,
printSchema,
buildSchema,
findBreakingChanges,
} = require("graphql")

const fetch = require("isomorphic-fetch")
const metaphysicsProd = "https://metaphysics-production.artsy.net/"

const downloadProductionSchema = async endpoint => {
const postBody = {
query: introspectionQuery,
operationName: "IntrospectionQuery",
}

const response = await fetch(endpoint, {
method: "POST",
body: JSON.stringify(postBody),
headers: {
"Content-Type": "application/json",
},
})
const { data } = await response.json()
// commentDescriptions is hidden
// @ts-ignore
return printSchema(buildClientSchema(data), { commentDescriptions: true })
}

const downloadGitHubReaction = async release => {
const response = await fetch(
`https://github.com/artsy/reaction/raw/v${release}/data/schema.graphql`
)

const body = await response.text()
return body
}

const getBreakingChanges = async () => {
const packageJSON = JSON.parse(
readFileSync(__dirname + "/../package.json", "utf8")
)
const reactionVersion = packageJSON["dependencies"]["@artsy/reaction"]
const reactionSchema = await downloadGitHubReaction(reactionVersion)
const metaphyicsSchema = await downloadProductionSchema(metaphysicsProd)
return findBreakingChanges(
buildSchema(metaphyicsSchema),
buildSchema(reactionSchema)
)
}

module.exports = {
getBreakingChanges,
}

// @ts-ignore
if (require.main === module) {
// When this is being called as a script via `node scripts/validate_schemas.js`
getBreakingChanges().then(changes => {
if (changes.length) {
process.exitCode = 1
console.error(
"Failing due to breaking changes between Force and Metaphysics Production\n\n"
)
console.error(changes)
console.error(
"\n\nYou should deploy metaphysics production, and re-deploy force"
)
}
})
}
Loading

0 comments on commit 2cc25bc

Please sign in to comment.