Create preview changes entries

This commit is contained in:
Robert Mosolgo 2020-11-23 07:15:22 -05:00
Родитель 96e4ea9499
Коммит 9d5af1f98d
3 изменённых файлов: 148 добавлений и 30 удалений

Просмотреть файл

@ -2,6 +2,8 @@ const { diff, ChangeType } = require('@graphql-inspector/core')
const { loadSchema } = require('@graphql-tools/load') const { loadSchema } = require('@graphql-tools/load')
const git = require('../../lib/git-utils') const git = require('../../lib/git-utils')
const fs = require('fs') const fs = require('fs')
const yaml = require('js-yaml')
// check for required PAT // check for required PAT
if (!process.env.GITHUB_TOKEN) { if (!process.env.GITHUB_TOKEN) {
console.error('Error! You must have a GITHUB_TOKEN set in an .env file to run this script.') console.error('Error! You must have a GITHUB_TOKEN set in an .env file to run this script.')
@ -20,8 +22,18 @@ async function main() {
const tree = await git.getTree('github', 'github', 'heads/master') const tree = await git.getTree('github', 'github', 'heads/master')
const schemaFileBlob = tree.find(entry => entry.path.includes('config/schema.docs.graphql') && entry.type === 'blob') const schemaFileBlob = tree.find(entry => entry.path.includes('config/schema.docs.graphql') && entry.type === 'blob')
const newSchemaBuffer = await git.getContentsForBlob('github', 'github', schemaFileBlob) const newSchemaBuffer = await git.getContentsForBlob('github', 'github', schemaFileBlob)
const changelogEntry = createChangelogEntry(oldSchemaString, newSchemaBuffer.toString())
const previewsString = fs.readFileSync('data/graphql/graphql_previews.yml')
const previews = yaml.safeLoad(previewsString)
const changelogEntry = createChangelogEntry(oldSchemaString, newSchemaBuffer.toString(), previews)
if (changelogEntry) { if (changelogEntry) {
// Build a `yyyy-mm-dd`-formatted date string
// and tag the changelog entry with it
const today = new Date()
const todayString = String(today.getFullYear()) + '-' + String(today.getMonth() + 1).padStart(2, '0') + '-' + String(today.getDate()).padStart(2, '0')
changelogEntry.date = todayString
const previousChangelogString = fs.readFileSync('lib/graphql/static/changelog.json') const previousChangelogString = fs.readFileSync('lib/graphql/static/changelog.json')
const previousChangelog = JSON.parse(previousChangelogString) const previousChangelog = JSON.parse(previousChangelogString)
// add a new entry to the changelog data // add a new entry to the changelog data
@ -35,7 +47,7 @@ async function main() {
// Compare `oldSchemaString` to `newSchemaString`, and if there are any // Compare `oldSchemaString` to `newSchemaString`, and if there are any
// changes that warrant a changelog entry, return a changelog entry. // changes that warrant a changelog entry, return a changelog entry.
// Otherwise, return null. // Otherwise, return null.
async function createChangelogEntry(oldSchemaString, newSchemaString) { async function createChangelogEntry(oldSchemaString, newSchemaString, previews) {
// Create schema objects out of the strings // Create schema objects out of the strings
const oldSchema = await loadSchema(oldSchemaString) const oldSchema = await loadSchema(oldSchemaString)
const newSchema = await loadSchema(newSchemaString) const newSchema = await loadSchema(newSchemaString)
@ -53,15 +65,10 @@ async function createChangelogEntry(oldSchemaString, newSchemaString) {
} }
}) })
const { schemaChangesToReport, previewChangesToReport } = segmentPreviewChanges(changesToReport) const { schemaChangesToReport, previewChangesToReport } = segmentPreviewChanges(changesToReport, previews)
// If there were any changes, create a changelog entry // If there were any changes, create a changelog entry
if (schemaChangesToReport.length > 0 || previewChangesToReport.length > 0) { if (schemaChangesToReport.length > 0 || previewChangesToReport.length > 0) {
// Build a `yyyy-mm-dd`-formatted date string
const today = new Date()
const todayString = String(today.getFullYear()) + '-' + String(today.getMonth() + 1).padStart(2, '0') + '-' + String(today.getDate()).padStart(2, '0')
const changelogEntry = { const changelogEntry = {
date: todayString,
schemaChanges: [], schemaChanges: [],
previewChanges: [], previewChanges: [],
upcomingChanges: [], upcomingChanges: [],
@ -70,19 +77,19 @@ async function createChangelogEntry(oldSchemaString, newSchemaString) {
const schemaChange = { const schemaChange = {
title: 'The GraphQL schema includes these changes:', title: 'The GraphQL schema includes these changes:',
// Replace single quotes which wrap field/argument/type names with backticks // Replace single quotes which wrap field/argument/type names with backticks
changes: changesToReport.map(function (change) { return change.message.replace(/'([a-zA-Z\. :!]+)'/g, '`$1`') }) changes: cleanMessagesFromChanges(schemaChangesToReport)
} }
changelogEntry.schemaChanges.push(schemaChange) changelogEntry.schemaChanges.push(schemaChange)
// TODO how are these populated? for (const previewTitle in previewChangesToReport) {
// { let previewChanges = previewChangesToReport[previewTitle]
// "title": "The [Checks preview](/graphql/overview/schema-previews#checks-preview) includes these changes:", let cleanTitle = cleanPreviewTitle(previewTitle)
// "changes": [ let entryTitle = "The [" + cleanTitle + "](/graphql/overview/schema-previews#" + previewAnchor(cleanTitle) + ") includes these changes:"
// "Enum value `STALE` was added to enum `CheckConclusionState`", changelogEntry.previewChanges.push({
// "Enum value `SKIPPED` was added to enum `CheckConclusionState`" title: entryTitle,
// ] changes: cleanMessagesFromChanges(previewChanges.changes),
// } })
const previewChanges = [] }
// TODO how are these populated? // TODO how are these populated?
// "upcomingChanges": [ // "upcomingChanges": [
@ -101,12 +108,76 @@ async function createChangelogEntry(oldSchemaString, newSchemaString) {
} }
} }
function segmentPreviewChanges(changesToReport) { // prepare the preview title from github/github source for the docs.
// TODO: read the previews yaml file and // ported from build-changelog-from-markdown
// split the list of changes based on whether the change's path function cleanPreviewTitle(title) {
// (or any "parents" in the change's path) are in a preview. if (title == "UpdateRefsPreview") {
// See: https://github.com/github/graphql-docs/blob/master/lib/graphql_docs/update_internal_developer/change_log.rb#L230 title = "Update refs preview"
return { schemaChangesToReport: changesToReport, previewChangesToReport: [] } } else if (title == "MergeInfoPreview") {
title = "Merge info preview"
} else if (!title.endsWith("preview")) {
title = title + " preview"
}
return title
}
/**
* @param {string} [previewTitle]
* @return {string}
*/
function previewAnchor(previewTitle) {
// ported from https://github.com/github/graphql-docs/blob/master/lib/graphql_docs/update_internal_developer/change_log.rb#L281
return previewTitle
.toLowerCase()
.replace(/ /g, '-')
.replace(/[^\w-]/g, '')
}
function cleanMessagesFromChanges(changes) {
return changes.map(function (change) {
// replace single quotes around graphql names with backticks,
// to match previous behavior from graphql-schema-comparator
return change.message.replace(/'([a-zA-Z\. :!]+)'/g, '`$1`')
})
}
function segmentPreviewChanges(changesToReport, previews) {
// Build a map of `{ path => previewTitle` }
// for easier lookup of change to preview
const pathToPreview = {}
previews.forEach(function (preview) {
preview.toggled_on.forEach(function (path) {
pathToPreview[path] = preview.title
})
})
const schemaChanges = []
const changesByPreview = {}
changesToReport.forEach(function (change) {
// For each change, see if its path _or_ one of its ancestors
// is covered by a preview. If it is, mark this change as belonging to a preview
const pathParts = change.path.split(".")
let testPath = null
let previewTitle = null
let previewChanges = null
while (pathParts.length > 0 && !previewTitle) {
testPath = pathParts.join(".")
previewTitle = pathToPreview[testPath]
// If that path didn't find a match, then we'll
// check the next ancestor.
pathParts.pop()
}
if (previewTitle) {
previewChanges = changesByPreview[previewTitle] || (changesByPreview[previewTitle] = {
title: previewTitle,
changes: []
})
previewChanges.changes.push(change)
} else {
schemaChanges.push(change)
}
})
return { schemaChangesToReport: schemaChanges, previewChangesToReport: changesByPreview }
} }
const CHANGES_TO_REPORT = [ const CHANGES_TO_REPORT = [
@ -169,4 +240,4 @@ const CHANGES_TO_IGNORE = [
module.exports = { createChangelogEntry } module.exports = { createChangelogEntry, cleanPreviewTitle, previewAnchor }

Просмотреть файл

@ -2,8 +2,15 @@
exports[`creating a changelog from old schema and new schema finds a diff of schema changes, upcoming changes, and preview changes 1`] = ` exports[`creating a changelog from old schema and new schema finds a diff of schema changes, upcoming changes, and preview changes 1`] = `
Object { Object {
"date": "2020-11-20", "previewChanges": Array [
"previewChanges": Array [], Object {
"changes": Array [
"Field \`Query.previewField\` changed type from \`PreviewType\` to \`PreviewType!\`",
"Type for argument \`changeTypeArgument\` on field 'PreviewType.field1\` changed from \`Int\` to \`Float'",
],
"title": "The [Test preview](/graphql/overview/schema-previews#test-preview) includes these changes:",
},
],
"schemaChanges": Array [ "schemaChanges": Array [
Object { Object {
"changes": Array [ "changes": Array [

Просмотреть файл

@ -1,8 +1,13 @@
const { createChangelogEntry } = require('../../script/graphql/build-changelog') const yaml = require("js-yaml")
const { createChangelogEntry, cleanPreviewTitle, previewAnchor } = require('../../script/graphql/build-changelog')
describe('creating a changelog from old schema and new schema', () => { describe('creating a changelog from old schema and new schema', () => {
it('finds a diff of schema changes, upcoming changes, and preview changes', async () => { it('finds a diff of schema changes, upcoming changes, and preview changes', async () => {
const oldSchemaString = ` const oldSchemaString = `
type PreviewType {
field1(changeTypeArgument: Int): Int
}
type Query { type Query {
stableField: String stableField: String
removedField: Boolean removedField: Boolean
@ -12,21 +17,39 @@ describe('creating a changelog from old schema and new schema', () => {
argumentMadeRequired: Int argumentMadeRequired: Int
argumentMadeOptional: Int! argumentMadeOptional: Int!
): String ): String
previewField: PreviewType
} }
` `
const newSchemaString = ` const newSchemaString = `
type PreviewType {
field1(changeTypeArgument: Float): Int
}
type Query { type Query {
stableField: String stableField: String
argumentsField( argumentsField(
argumentMadeRequired: Int! argumentMadeRequired: Int!
argumentMadeOptional: Int argumentMadeOptional: Int
): String ): String
previewField: PreviewType!
} }
` `
previews = yaml.safeLoad(`
- title: Test preview
description: This preview is just for test
toggled_by: ':test_preview'
announcement: null
updates: null
toggled_on:
- PreviewType
- Query.previewField
owning_teams:
- '@github/engineering'
`)
const entry = await createChangelogEntry(oldSchemaString, newSchemaString) const entry = await createChangelogEntry(oldSchemaString, newSchemaString, previews)
expect(entry).toMatchSnapshot() expect(entry).toMatchSnapshot()
}) })
@ -36,7 +59,24 @@ describe('creating a changelog from old schema and new schema', () => {
i: Int! i: Int!
}` }`
const nullEntry = await createChangelogEntry(schemaString, schemaString) const nullEntry = await createChangelogEntry(schemaString, schemaString, [])
expect(nullEntry).toBeNull() expect(nullEntry).toBeNull()
}) })
}) })
describe("Preparing preview links", () => {
it("fixes preview names", () => {
// These two are special cases
expect(cleanPreviewTitle("UpdateRefsPreview")).toEqual("Update refs preview")
expect(cleanPreviewTitle("MergeInfoPreview")).toEqual("Merge info preview")
// Previews that don't end in " preview" have it added
expect(cleanPreviewTitle("something interesting")).toEqual("something interesting preview")
// Other things are left as-is
expect(cleanPreviewTitle("nice preview")).toEqual("nice preview")
})
it("creates anchors from preview titles", () => {
expect(previewAnchor("Merge info preview")).toEqual("merge-info-preview")
expect(previewAnchor("some.punct123 preview")).toEqual("somepunct123-preview")
})
})