@@ -2,6 +2,8 @@ const { diff, ChangeType } = require('@graphql-inspector/core')
22const { loadSchema } = require ( '@graphql-tools/load' )
33const git = require ( '../../lib/git-utils' )
44const fs = require ( 'fs' )
5+ const yaml = require ( 'js-yaml' )
6+
57// check for required PAT
68if ( ! 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 - z A - 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 - z A - 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
112183const CHANGES_TO_REPORT = [
@@ -169,4 +240,4 @@ const CHANGES_TO_IGNORE = [
169240
170241
171242
172- module . exports = { createChangelogEntry }
243+ module . exports = { createChangelogEntry, cleanPreviewTitle , previewAnchor }
0 commit comments