forked from github/docs
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent-changes-table-comment.js
More file actions
executable file
·186 lines (151 loc) · 6.38 KB
/
Copy pathcontent-changes-table-comment.js
File metadata and controls
executable file
·186 lines (151 loc) · 6.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/usr/bin/env node
import * as github from '@actions/github'
import core from '@actions/core'
import { getContents } from '../../script/helpers/git-utils.js'
import parse from '../../lib/read-frontmatter.js'
import getApplicableVersions from '../../lib/get-applicable-versions.js'
import nonEnterpriseDefaultVersion from '../../lib/non-enterprise-default-version.js'
import { allVersionShortnames } from '../../lib/all-versions.js'
import { waitUntilUrlIsHealthy } from './lib/wait-until-url-is-healthy.js'
const { GITHUB_TOKEN, APP_URL } = process.env
const context = github.context
if (!GITHUB_TOKEN) {
throw new Error(`GITHUB_TOKEN environment variable not set`)
}
if (!APP_URL) {
throw new Error(`APP_URL environment variable not set`)
}
// the max size of the comment (in bytes)
// the action we use to post the comment caps out at about 144kb
// see docs-engineering#1849 for more info
const MAX_COMMENT_SIZE = 125000
const PROD_URL = 'https://docs.github.com'
// When this file is invoked directly from action as opposed to being imported
if (import.meta.url.endsWith(process.argv[1])) {
const owner = context.repo.owner
const repo = context.payload.repository.name
const baseSHA = context.payload.pull_request.base.sha
const headSHA = context.payload.pull_request.head.sha
const isHealthy = await waitUntilUrlIsHealthy(new URL('/healthz', APP_URL).toString())
if (!isHealthy) {
core.setFailed(`Timeout waiting for preview environment: ${APP_URL}`)
} else {
const markdownTable = await main(owner, repo, baseSHA, headSHA)
core.setOutput('changesTable', markdownTable)
}
}
async function main(owner, repo, baseSHA, headSHA) {
const octokit = github.getOctokit(GITHUB_TOKEN)
// get the list of file changes from the PR
const response = await octokit.rest.repos.compareCommitsWithBasehead({
owner,
repo,
basehead: `${baseSHA}...${headSHA}`,
})
const { files } = response.data
const markdownTableHead = [
'| **Source** | **Preview** | **Production** | **What Changed** |',
'|:----------- |:----------- |:----------- |:----------- |',
]
let markdownTable = ''
const pathPrefix = 'content/'
const articleFiles = files.filter(
({ filename }) => filename.startsWith(pathPrefix) && filename.toLowerCase() !== 'readme.md'
)
const lines = await Promise.all(
articleFiles.map(async (file) => {
const sourceUrl = file.blob_url
const fileName = file.filename.slice(pathPrefix.length)
const fileUrl = fileName.replace('/index.md', '').replace(/\.md$/, '')
// get the file contents and decode them
// this script is called from the main branch, so we need the API call to get the contents from the branch, instead
const fileContents = await getContents(
owner,
repo,
// Can't get its content if it no longer exists.
// Meaning, you'd get a 404 on the `getContents()` utility function.
// So, to be able to get necessary meta data about what it *was*,
// if it was removed, fall back to the 'base'.
file.status === 'removed' ? baseSHA : headSHA,
file.filename
)
// parse the frontmatter
const { data } = parse(fileContents)
let contentCell = ''
let previewCell = ''
let prodCell = ''
if (file.status === 'added') contentCell = 'New file: '
else if (file.status === 'removed') contentCell = 'Removed: '
contentCell += `[\`${fileName}\`](${sourceUrl})`
try {
// the try/catch is needed because getApplicableVersions() returns either [] or throws an error when it can't parse the versions frontmatter
// try/catch can be removed if docs-engineering#1821 is resolved
// i.e. for feature based versioning, like ghae: 'issue-6337'
const fileVersions = getApplicableVersions(data.versions)
for (const plan in allVersionShortnames) {
// plan is the shortName (i.e., fpt)
// allVersionShortNames[plan] is the planName (i.e., free-pro-team)
// walk by the plan names since we generate links differently for most plans
const versions = fileVersions.filter((fileVersion) =>
fileVersion.includes(allVersionShortnames[plan])
)
if (versions.length === 1) {
// for fpt, ghec, and ghae
if (versions.toString() === nonEnterpriseDefaultVersion) {
// omit version from fpt url
previewCell += `[${plan}](${APP_URL}/${fileUrl})<br>`
prodCell += `[${plan}](${PROD_URL}/${fileUrl})<br>`
} else {
// for non-versioned releases (ghae, ghec) use full url
previewCell += `[${plan}](${APP_URL}/${versions}/${fileUrl})<br>`
prodCell += `[${plan}](${PROD_URL}/${versions}/${fileUrl})<br>`
}
} else if (versions.length) {
// for ghes releases, link each version
previewCell += `${plan}@ `
prodCell += `${plan}@ `
versions.forEach((version) => {
previewCell += `[${version.split('@')[1]}](${APP_URL}/${version}/${fileUrl}) `
prodCell += `[${version.split('@')[1]}](${PROD_URL}/${version}/${fileUrl}) `
})
previewCell += '<br>'
prodCell += '<br>'
}
}
} catch (e) {
console.error(
`Version information for ${file.filename} couldn't be determined from its frontmatter.`
)
}
let note = ''
if (file.status === 'removed') {
note = 'removed'
// If the file was removed, the `previewCell` no longer makes sense
// since it was based on looking at the base sha.
previewCell = 'n/a'
}
return `| ${contentCell} | ${previewCell} | ${prodCell} | ${note} |`
})
)
// this section limits the size of the comment
const cappedLines = []
let underMax = true
lines.reduce((previous, current, index, array) => {
if (underMax) {
if (previous + current.length > MAX_COMMENT_SIZE) {
underMax = false
cappedLines.push('**Note** There are more changes in this PR than we can show.')
return previous
}
cappedLines.push(array[index])
return previous + current.length
}
return previous
}, markdownTable.length)
if (cappedLines.length) {
cappedLines.unshift(...markdownTableHead)
}
markdownTable += cappedLines.join('\n')
return markdownTable
}
export default main