1
0
mirror of synced 2025-12-21 02:46:50 -05:00
Files
docs/script/graphql/build-changelog.js
2020-11-20 17:02:21 -05:00

173 lines
6.6 KiB
JavaScript

const { diff, ChangeType } = require('@graphql-inspector/core')
const { loadSchema } = require('@graphql-tools/load')
const git = require('../../lib/git-utils')
const fs = require('fs')
// 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.')
process.exit(1)
}
// main()
async function main() {
// Load the previous schema from this repo
// TODO -- how to make sure that this script runs _before_ this artifact is updated?
// Maybe hook into the existing `update-files` script instead of being a stand-alone script.
const oldSchemaString = fs.readFileSync('data/graphql/schema.docs.graphql').toString()
// Load the latest schema from github/github
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())
if (changelogEntry) {
const previousChangelogString = fs.readFileSync('lib/graphql/static/changelog.json')
const previousChangelog = JSON.parse(previousChangelogString)
// add a new entry to the changelog data
previousChangelog.unshift(changelogEntry)
// rewrite the updated changelog
fs.writeFileSync('lib/graphql/static/changelog.json', JSON.stringify(previousChangelog, null, 2))
}
}
// 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) {
// Create schema objects out of the strings
const oldSchema = await loadSchema(oldSchemaString)
const newSchema = await loadSchema(newSchemaString)
// Generate changes between the two schemas
const changes = diff(oldSchema, newSchema)
const changesToReport = []
changes.forEach(function (change) {
if (CHANGES_TO_REPORT.includes(change.type)) {
changesToReport.push(change)
} else if (CHANGES_TO_IGNORE.includes(change.type)) {
// Do nothing
} else {
throw "This change type should be added to CHANGES_TO_REPORT or CHANGES_TO_IGNORE: " + change.type
}
})
const { schemaChangesToReport, previewChangesToReport } = segmentPreviewChanges(changesToReport)
// 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: [],
}
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`') })
}
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 = []
// TODO how are these populated?
// "upcomingChanges": [
// {
// "title": "The following changes will be made to the schema:",
// "changes": [
// "On member `Issue.timeline`: `timeline` will be removed. Use Issue.timelineItems instead. **Effective 2020-10-01**.",
// "On member `PullRequest.timeline`: `timeline` will be removed. Use PullRequest.timelineItems instead. **Effective 2020-10-01**."
// ]
// }
// ]
const upcomingChanges = []
return changelogEntry
} else {
return null
}
}
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: [] }
}
const CHANGES_TO_REPORT = [
ChangeType.FieldArgumentDefaultChanged,
ChangeType.FieldArgumentTypeChanged,
ChangeType.EnumValueRemoved,
ChangeType.EnumValueAdded,
ChangeType.FieldRemoved,
ChangeType.FieldAdded,
ChangeType.FieldTypeChanged,
ChangeType.FieldArgumentAdded,
ChangeType.FieldArgumentRemoved,
ChangeType.ObjectTypeInterfaceAdded,
ChangeType.ObjectTypeInterfaceRemoved,
ChangeType.InputFieldRemoved,
ChangeType.InputFieldAdded,
ChangeType.InputFieldDefaultValueChanged,
ChangeType.InputFieldTypeChanged,
ChangeType.TypeRemoved,
ChangeType.TypeAdded,
ChangeType.TypeKindChanged,
ChangeType.UnionMemberRemoved,
ChangeType.UnionMemberAdded,
ChangeType.SchemaQueryTypeChanged,
ChangeType.SchemaMutationTypeChanged,
ChangeType.SchemaSubscriptionTypeChanged,
]
const CHANGES_TO_IGNORE = [
ChangeType.FieldArgumentDescriptionChanged,
ChangeType.DirectiveRemoved,
ChangeType.DirectiveAdded,
ChangeType.DirectiveDescriptionChanged,
ChangeType.DirectiveLocationAdded,
ChangeType.DirectiveLocationRemoved,
ChangeType.DirectiveArgumentAdded,
ChangeType.DirectiveArgumentRemoved,
ChangeType.DirectiveArgumentDescriptionChanged,
ChangeType.DirectiveArgumentDefaultValueChanged,
ChangeType.DirectiveArgumentTypeChanged,
ChangeType.EnumValueDescriptionChanged,
ChangeType.EnumValueDeprecationReasonChanged,
ChangeType.EnumValueDeprecationReasonAdded,
ChangeType.EnumValueDeprecationReasonRemoved,
ChangeType.FieldDescriptionChanged,
ChangeType.FieldDescriptionAdded,
ChangeType.FieldDescriptionRemoved,
ChangeType.FieldDeprecationAdded,
ChangeType.FieldDeprecationRemoved,
ChangeType.FieldDeprecationReasonChanged,
ChangeType.FieldDeprecationReasonAdded,
ChangeType.FieldDeprecationReasonRemoved,
ChangeType.InputFieldDescriptionAdded,
ChangeType.InputFieldDescriptionRemoved,
ChangeType.InputFieldDescriptionChanged,
ChangeType.TypeDescriptionChanged,
ChangeType.TypeDescriptionRemoved,
ChangeType.TypeDescriptionAdded,
]
module.exports = { createChangelogEntry }