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 git = require('../../lib/git-utils')
const fs = require('fs')
const yaml = require('js-yaml')
// check for required PAT
if (!process.env.GITHUB_TOKEN) {
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 schemaFileBlob = tree.find(entry => entry.path.includes('config/schema.docs.graphql') && entry.type === 'blob')
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) {
// 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 previousChangelog = JSON.parse(previousChangelogString)
// add a new entry to the changelog data
@ -35,7 +47,7 @@ async function main() {
// Compare `oldSchemaString` to `newSchemaString`, and if there are any
// changes that warrant a changelog entry, return a changelog entry.
// Otherwise, return null.
async function createChangelogEntry(oldSchemaString, newSchemaString) {
async function createChangelogEntry(oldSchemaString, newSchemaString, previews) {
// Create schema objects out of the strings
const oldSchema = await loadSchema(oldSchemaString)
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 (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 = {
date: todayString,
schemaChanges: [],
previewChanges: [],
upcomingChanges: [],
@ -70,19 +77,19 @@ async function createChangelogEntry(oldSchemaString, newSchemaString) {
const schemaChange = {
title: 'The GraphQL schema includes these changes:',
// 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)
// TODO how are these populated?
// {
// "title": "The [Checks preview](/graphql/overview/schema-previews#checks-preview) includes these changes:",
// "changes": [
// "Enum value `STALE` was added to enum `CheckConclusionState`",
// "Enum value `SKIPPED` was added to enum `CheckConclusionState`"
// ]
// }
const previewChanges = []
for (const previewTitle in previewChangesToReport) {
let previewChanges = previewChangesToReport[previewTitle]
let cleanTitle = cleanPreviewTitle(previewTitle)
let entryTitle = "The [" + cleanTitle + "](/graphql/overview/schema-previews#" + previewAnchor(cleanTitle) + ") includes these changes:"
changelogEntry.previewChanges.push({
title: entryTitle,
changes: cleanMessagesFromChanges(previewChanges.changes),
})
}
// TODO how are these populated?
// "upcomingChanges": [
@ -101,12 +108,76 @@ async function createChangelogEntry(oldSchemaString, newSchemaString) {
}
}
function segmentPreviewChanges(changesToReport) {
// TODO: read the previews yaml file and
// split the list of changes based on whether the change's path
// (or any "parents" in the change's path) are in a preview.
// See: https://github.com/github/graphql-docs/blob/master/lib/graphql_docs/update_internal_developer/change_log.rb#L230
return { schemaChangesToReport: changesToReport, previewChangesToReport: [] }
// prepare the preview title from github/github source for the docs.
// ported from build-changelog-from-markdown
function cleanPreviewTitle(title) {
if (title == "UpdateRefsPreview") {
title = "Update refs preview"
} 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 = [
@ -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`] = `
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 [
Object {
"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', () => {
it('finds a diff of schema changes, upcoming changes, and preview changes', async () => {
const oldSchemaString = `
type PreviewType {
field1(changeTypeArgument: Int): Int
}
type Query {
stableField: String
removedField: Boolean
@ -12,21 +17,39 @@ describe('creating a changelog from old schema and new schema', () => {
argumentMadeRequired: Int
argumentMadeOptional: Int!
): String
previewField: PreviewType
}
`
const newSchemaString = `
type PreviewType {
field1(changeTypeArgument: Float): Int
}
type Query {
stableField: String
argumentsField(
argumentMadeRequired: Int!
argumentMadeOptional: Int
): 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()
})
@ -36,7 +59,24 @@ describe('creating a changelog from old schema and new schema', () => {
i: Int!
}`
const nullEntry = await createChangelogEntry(schemaString, schemaString)
const nullEntry = await createChangelogEntry(schemaString, schemaString, [])
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")
})
})