Skip to content

Commit 9d5af1f

Browse files
committed
Create preview changes entries
1 parent 96e4ea9 commit 9d5af1f

File tree

3 files changed

+148
-30
lines changed

3 files changed

+148
-30
lines changed

script/graphql/build-changelog.js

Lines changed: 96 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ const { diff, ChangeType } = require('@graphql-inspector/core')
22
const { loadSchema } = require('@graphql-tools/load')
33
const git = require('../../lib/git-utils')
44
const fs = require('fs')
5+
const yaml = require('js-yaml')
6+
57
// check for required PAT
68
if (!process.env.GITHUB_TOKEN) {
79
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() {
2022
const tree = await git.getTree('github', 'github', 'heads/master')
2123
const schemaFileBlob = tree.find(entry => entry.path.includes('config/schema.docs.graphql') && entry.type === 'blob')
2224
const newSchemaBuffer = await git.getContentsForBlob('github', 'github', schemaFileBlob)
23-
const changelogEntry = createChangelogEntry(oldSchemaString, newSchemaBuffer.toString())
25+
26+
const previewsString = fs.readFileSync('data/graphql/graphql_previews.yml')
27+
const previews = yaml.safeLoad(previewsString)
28+
29+
const changelogEntry = createChangelogEntry(oldSchemaString, newSchemaBuffer.toString(), previews)
2430
if (changelogEntry) {
31+
// Build a `yyyy-mm-dd`-formatted date string
32+
// and tag the changelog entry with it
33+
const today = new Date()
34+
const todayString = String(today.getFullYear()) + '-' + String(today.getMonth() + 1).padStart(2, '0') + '-' + String(today.getDate()).padStart(2, '0')
35+
changelogEntry.date = todayString
36+
2537
const previousChangelogString = fs.readFileSync('lib/graphql/static/changelog.json')
2638
const previousChangelog = JSON.parse(previousChangelogString)
2739
// add a new entry to the changelog data
@@ -35,7 +47,7 @@ async function main() {
3547
// Compare `oldSchemaString` to `newSchemaString`, and if there are any
3648
// changes that warrant a changelog entry, return a changelog entry.
3749
// Otherwise, return null.
38-
async function createChangelogEntry(oldSchemaString, newSchemaString) {
50+
async function createChangelogEntry(oldSchemaString, newSchemaString, previews) {
3951
// Create schema objects out of the strings
4052
const oldSchema = await loadSchema(oldSchemaString)
4153
const newSchema = await loadSchema(newSchemaString)
@@ -53,15 +65,10 @@ async function createChangelogEntry(oldSchemaString, newSchemaString) {
5365
}
5466
})
5567

56-
const { schemaChangesToReport, previewChangesToReport } = segmentPreviewChanges(changesToReport)
68+
const { schemaChangesToReport, previewChangesToReport } = segmentPreviewChanges(changesToReport, previews)
5769
// If there were any changes, create a changelog entry
5870
if (schemaChangesToReport.length > 0 || previewChangesToReport.length > 0) {
59-
// Build a `yyyy-mm-dd`-formatted date string
60-
const today = new Date()
61-
const todayString = String(today.getFullYear()) + '-' + String(today.getMonth() + 1).padStart(2, '0') + '-' + String(today.getDate()).padStart(2, '0')
62-
6371
const changelogEntry = {
64-
date: todayString,
6572
schemaChanges: [],
6673
previewChanges: [],
6774
upcomingChanges: [],
@@ -70,19 +77,19 @@ async function createChangelogEntry(oldSchemaString, newSchemaString) {
7077
const schemaChange = {
7178
title: 'The GraphQL schema includes these changes:',
7279
// Replace single quotes which wrap field/argument/type names with backticks
73-
changes: changesToReport.map(function (change) { return change.message.replace(/'([a-zA-Z\. :!]+)'/g, '`$1`') })
80+
changes: cleanMessagesFromChanges(schemaChangesToReport)
7481
}
7582
changelogEntry.schemaChanges.push(schemaChange)
7683

77-
// TODO how are these populated?
78-
// {
79-
// "title": "The [Checks preview](/graphql/overview/schema-previews#checks-preview) includes these changes:",
80-
// "changes": [
81-
// "Enum value `STALE` was added to enum `CheckConclusionState`",
82-
// "Enum value `SKIPPED` was added to enum `CheckConclusionState`"
83-
// ]
84-
// }
85-
const previewChanges = []
84+
for (const previewTitle in previewChangesToReport) {
85+
let previewChanges = previewChangesToReport[previewTitle]
86+
let cleanTitle = cleanPreviewTitle(previewTitle)
87+
let entryTitle = "The [" + cleanTitle + "](/graphql/overview/schema-previews#" + previewAnchor(cleanTitle) + ") includes these changes:"
88+
changelogEntry.previewChanges.push({
89+
title: entryTitle,
90+
changes: cleanMessagesFromChanges(previewChanges.changes),
91+
})
92+
}
8693

8794
// TODO how are these populated?
8895
// "upcomingChanges": [
@@ -101,12 +108,76 @@ async function createChangelogEntry(oldSchemaString, newSchemaString) {
101108
}
102109
}
103110

104-
function segmentPreviewChanges(changesToReport) {
105-
// TODO: read the previews yaml file and
106-
// split the list of changes based on whether the change's path
107-
// (or any "parents" in the change's path) are in a preview.
108-
// See: https://github.com/github/graphql-docs/blob/master/lib/graphql_docs/update_internal_developer/change_log.rb#L230
109-
return { schemaChangesToReport: changesToReport, previewChangesToReport: [] }
111+
// prepare the preview title from github/github source for the docs.
112+
// ported from build-changelog-from-markdown
113+
function cleanPreviewTitle(title) {
114+
if (title == "UpdateRefsPreview") {
115+
title = "Update refs preview"
116+
} else if (title == "MergeInfoPreview") {
117+
title = "Merge info preview"
118+
} else if (!title.endsWith("preview")) {
119+
title = title + " preview"
120+
}
121+
return title
122+
}
123+
124+
/**
125+
* @param {string} [previewTitle]
126+
* @return {string}
127+
*/
128+
function previewAnchor(previewTitle) {
129+
// ported from https://github.com/github/graphql-docs/blob/master/lib/graphql_docs/update_internal_developer/change_log.rb#L281
130+
return previewTitle
131+
.toLowerCase()
132+
.replace(/ /g, '-')
133+
.replace(/[^\w-]/g, '')
134+
}
135+
136+
function cleanMessagesFromChanges(changes) {
137+
return changes.map(function (change) {
138+
// replace single quotes around graphql names with backticks,
139+
// to match previous behavior from graphql-schema-comparator
140+
return change.message.replace(/'([a-zA-Z\. :!]+)'/g, '`$1`')
141+
})
142+
}
143+
144+
function segmentPreviewChanges(changesToReport, previews) {
145+
// Build a map of `{ path => previewTitle` }
146+
// for easier lookup of change to preview
147+
const pathToPreview = {}
148+
previews.forEach(function (preview) {
149+
preview.toggled_on.forEach(function (path) {
150+
pathToPreview[path] = preview.title
151+
})
152+
})
153+
const schemaChanges = []
154+
const changesByPreview = {}
155+
156+
changesToReport.forEach(function (change) {
157+
// For each change, see if its path _or_ one of its ancestors
158+
// is covered by a preview. If it is, mark this change as belonging to a preview
159+
const pathParts = change.path.split(".")
160+
let testPath = null
161+
let previewTitle = null
162+
let previewChanges = null
163+
while (pathParts.length > 0 && !previewTitle) {
164+
testPath = pathParts.join(".")
165+
previewTitle = pathToPreview[testPath]
166+
// If that path didn't find a match, then we'll
167+
// check the next ancestor.
168+
pathParts.pop()
169+
}
170+
if (previewTitle) {
171+
previewChanges = changesByPreview[previewTitle] || (changesByPreview[previewTitle] = {
172+
title: previewTitle,
173+
changes: []
174+
})
175+
previewChanges.changes.push(change)
176+
} else {
177+
schemaChanges.push(change)
178+
}
179+
})
180+
return { schemaChangesToReport: schemaChanges, previewChangesToReport: changesByPreview }
110181
}
111182

112183
const CHANGES_TO_REPORT = [
@@ -169,4 +240,4 @@ const CHANGES_TO_IGNORE = [
169240

170241

171242

172-
module.exports = { createChangelogEntry }
243+
module.exports = { createChangelogEntry, cleanPreviewTitle, previewAnchor }

tests/graphql/__snapshots__/build-changelog-test.js.snap

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@
22

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

tests/graphql/build-changelog-test.js

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
const { createChangelogEntry } = require('../../script/graphql/build-changelog')
1+
const yaml = require("js-yaml")
2+
const { createChangelogEntry, cleanPreviewTitle, previewAnchor } = require('../../script/graphql/build-changelog')
23

34
describe('creating a changelog from old schema and new schema', () => {
45
it('finds a diff of schema changes, upcoming changes, and preview changes', async () => {
56
const oldSchemaString = `
7+
type PreviewType {
8+
field1(changeTypeArgument: Int): Int
9+
}
10+
611
type Query {
712
stableField: String
813
removedField: Boolean
@@ -12,21 +17,39 @@ describe('creating a changelog from old schema and new schema', () => {
1217
argumentMadeRequired: Int
1318
argumentMadeOptional: Int!
1419
): String
20+
previewField: PreviewType
1521
}
1622
`
1723

1824
const newSchemaString = `
25+
type PreviewType {
26+
field1(changeTypeArgument: Float): Int
27+
}
28+
1929
type Query {
2030
stableField: String
2131
argumentsField(
2232
argumentMadeRequired: Int!
2333
argumentMadeOptional: Int
2434
): String
35+
previewField: PreviewType!
2536
}
2637
`
2738

39+
previews = yaml.safeLoad(`
40+
- title: Test preview
41+
description: This preview is just for test
42+
toggled_by: ':test_preview'
43+
announcement: null
44+
updates: null
45+
toggled_on:
46+
- PreviewType
47+
- Query.previewField
48+
owning_teams:
49+
- '@github/engineering'
50+
`)
2851

29-
const entry = await createChangelogEntry(oldSchemaString, newSchemaString)
52+
const entry = await createChangelogEntry(oldSchemaString, newSchemaString, previews)
3053
expect(entry).toMatchSnapshot()
3154
})
3255

@@ -36,7 +59,24 @@ describe('creating a changelog from old schema and new schema', () => {
3659
i: Int!
3760
}`
3861

39-
const nullEntry = await createChangelogEntry(schemaString, schemaString)
62+
const nullEntry = await createChangelogEntry(schemaString, schemaString, [])
4063
expect(nullEntry).toBeNull()
4164
})
4265
})
66+
67+
describe("Preparing preview links", () => {
68+
it("fixes preview names", () => {
69+
// These two are special cases
70+
expect(cleanPreviewTitle("UpdateRefsPreview")).toEqual("Update refs preview")
71+
expect(cleanPreviewTitle("MergeInfoPreview")).toEqual("Merge info preview")
72+
// Previews that don't end in " preview" have it added
73+
expect(cleanPreviewTitle("something interesting")).toEqual("something interesting preview")
74+
// Other things are left as-is
75+
expect(cleanPreviewTitle("nice preview")).toEqual("nice preview")
76+
})
77+
78+
it("creates anchors from preview titles", () => {
79+
expect(previewAnchor("Merge info preview")).toEqual("merge-info-preview")
80+
expect(previewAnchor("some.punct123 preview")).toEqual("somepunct123-preview")
81+
})
82+
})

0 commit comments

Comments
 (0)