mirror of
https://github.com/desktop/desktop
synced 2024-09-13 05:11:21 +00:00
Add draft-release:pr
script to create release PRs
This commit is contained in:
parent
0875026c1b
commit
fe093ae481
|
@ -36,6 +36,7 @@
|
|||
"rebuild-hard:prod": "yarn clean-slate && yarn build:prod",
|
||||
"draft-release": "ts-node -P script/tsconfig.json script/draft-release/index.ts",
|
||||
"draft-release:format": "prettier --check --write changelog.json app/package.json && yarn validate-changelog",
|
||||
"draft-release:pr": "ts-node -P script/tsconfig.json script/draft-release/draft-pull-request.ts",
|
||||
"validate-changelog": "ts-node -P script/tsconfig.json script/validate-changelog.ts"
|
||||
},
|
||||
"author": {
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
import * as HTTPS from 'https'
|
||||
|
||||
export interface IAPIPR {
|
||||
readonly title: string
|
||||
readonly body: string
|
||||
readonly headRefName: string
|
||||
}
|
||||
|
||||
type GraphQLResponse = {
|
||||
readonly data: {
|
||||
readonly repository: {
|
||||
readonly pullRequest: IAPIPR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function fetchPR(id: number): Promise<IAPIPR | null> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options: HTTPS.RequestOptions = {
|
||||
host: 'api.github.com',
|
||||
protocol: 'https:',
|
||||
path: '/graphql',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `bearer ${process.env.GITHUB_ACCESS_TOKEN}`,
|
||||
'User-Agent': 'what-the-changelog',
|
||||
},
|
||||
}
|
||||
|
||||
const request = HTTPS.request(options, response => {
|
||||
let received = ''
|
||||
response.on('data', chunk => {
|
||||
received += chunk
|
||||
})
|
||||
|
||||
response.on('end', () => {
|
||||
try {
|
||||
const json: GraphQLResponse = JSON.parse(received)
|
||||
const pr = json.data.repository.pullRequest
|
||||
resolve(pr)
|
||||
} catch (e) {
|
||||
resolve(null)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const graphql = `
|
||||
{
|
||||
repository(owner: "desktop", name: "desktop") {
|
||||
pullRequest(number: ${id}) {
|
||||
title
|
||||
body
|
||||
headRefName
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
request.write(JSON.stringify({ query: graphql }))
|
||||
|
||||
request.end()
|
||||
})
|
||||
}
|
|
@ -2,7 +2,7 @@ import * as Path from 'path'
|
|||
import * as Fs from 'fs'
|
||||
import { gt as greaterThan } from 'semver'
|
||||
|
||||
import { fetchPR, IAPIPR } from './api'
|
||||
import { fetchPR, IAPIPR } from '../pr-api'
|
||||
|
||||
const PlaceholderChangeType = '???'
|
||||
const OfficialOwner = 'desktop'
|
||||
|
|
61
script/draft-release/draft-pull-request.ts
Normal file
61
script/draft-release/draft-pull-request.ts
Normal file
|
@ -0,0 +1,61 @@
|
|||
/// <reference path="../globals.d.ts" />
|
||||
|
||||
import appPackage from '../../app/package.json'
|
||||
import { createPR } from '../pr-api'
|
||||
|
||||
const numberToOrdinal = (n: number) => {
|
||||
const s = ['th', 'st', 'nd', 'rd']
|
||||
const v = n % 100
|
||||
return n + (s[(v - 20) % 10] || s[v] || s[0])
|
||||
}
|
||||
|
||||
function getPullRequestBody(fullVersion: string): string {
|
||||
const versionComponents = fullVersion.split('-')
|
||||
const version = versionComponents[0]
|
||||
|
||||
let releaseDescription = `v${version} production release`
|
||||
if (versionComponents.length > 1) {
|
||||
const channelVersion = versionComponents[1]
|
||||
if (!channelVersion.startsWith('beta')) {
|
||||
throw new Error('We should not create release PRs for test builds')
|
||||
}
|
||||
|
||||
const buildNumber = parseInt(channelVersion.substring('beta'.length))
|
||||
releaseDescription = `${numberToOrdinal(
|
||||
buildNumber
|
||||
)} beta of the v${version} series`
|
||||
}
|
||||
|
||||
return `## Description
|
||||
Looking for the PR for the upcoming ${releaseDescription}? Well, you've just found it, congratulations!
|
||||
|
||||
## Release checklist
|
||||
|
||||
- [ ] Check to see if there are any errors in Sentry that have only occurred since the last production release
|
||||
- [ ] Verify that all feature flags are flipped appropriately
|
||||
- [ ] If there are any new metrics, ensure that central and desktop.github.com have been updated
|
||||
`
|
||||
}
|
||||
|
||||
function getPullRequestTitle(fullVersion: string): string {
|
||||
return `Release ${fullVersion}`
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', error => {
|
||||
console.error(error.message)
|
||||
})
|
||||
|
||||
async function run() {
|
||||
const version = appPackage.version
|
||||
console.log(`Creating release Pull Request for ${version}...`)
|
||||
const title = getPullRequestTitle(version)
|
||||
const body = getPullRequestBody(version)
|
||||
const response = await createPR(title, body, `releases/${version}`)
|
||||
if (response === null) {
|
||||
console.error('Failed to create release Pull Request')
|
||||
process.exit(1)
|
||||
}
|
||||
console.log(`Done: ${response.permalink}`)
|
||||
}
|
||||
|
||||
run()
|
117
script/pr-api.ts
Normal file
117
script/pr-api.ts
Normal file
|
@ -0,0 +1,117 @@
|
|||
import * as HTTPS from 'https'
|
||||
|
||||
export interface IAPIPR {
|
||||
readonly title: string
|
||||
readonly body: string
|
||||
readonly headRefName: string
|
||||
readonly permalink: string
|
||||
}
|
||||
|
||||
type GraphQLResponse = {
|
||||
readonly data: {
|
||||
readonly repository: {
|
||||
readonly pullRequest: IAPIPR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function gitHubRequest(
|
||||
options: HTTPS.RequestOptions,
|
||||
body: Record<string, any>
|
||||
): Promise<Record<string, any> | null> {
|
||||
const opts: HTTPS.RequestOptions = {
|
||||
host: 'api.github.com',
|
||||
protocol: 'https:',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `bearer ${process.env.GITHUB_ACCESS_TOKEN}`,
|
||||
'User-Agent': 'what-the-changelog',
|
||||
},
|
||||
...options,
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = HTTPS.request(opts, response => {
|
||||
let received = ''
|
||||
response.on('data', chunk => {
|
||||
received += chunk
|
||||
})
|
||||
|
||||
response.on('end', () => {
|
||||
try {
|
||||
resolve(JSON.parse(received))
|
||||
} catch (e) {
|
||||
resolve(null)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
request.write(JSON.stringify(body))
|
||||
|
||||
request.end()
|
||||
})
|
||||
}
|
||||
|
||||
export async function fetchPR(id: number): Promise<IAPIPR | null> {
|
||||
const options: HTTPS.RequestOptions = {
|
||||
path: '/graphql',
|
||||
}
|
||||
|
||||
const graphql = `
|
||||
{
|
||||
repository(owner: "desktop", name: "desktop") {
|
||||
pullRequest(number: ${id}) {
|
||||
title
|
||||
body
|
||||
headRefName
|
||||
permalink
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const body = { query: graphql }
|
||||
|
||||
const response = await gitHubRequest(options, body)
|
||||
if (response === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const json: GraphQLResponse = response as GraphQLResponse
|
||||
return json.data.repository.pullRequest
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export async function createPR(
|
||||
title: string,
|
||||
body: string,
|
||||
branch: string
|
||||
): Promise<IAPIPR | null> {
|
||||
const options: HTTPS.RequestOptions = {
|
||||
path: '/repos/sergiou87/desktop/pulls',
|
||||
}
|
||||
|
||||
const response = await gitHubRequest(options, {
|
||||
title,
|
||||
body,
|
||||
base: 'development',
|
||||
head: branch,
|
||||
})
|
||||
|
||||
if (response === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
return {
|
||||
title: response.title,
|
||||
body: response.body,
|
||||
headRefName: response.head.ref,
|
||||
permalink: response.html_url,
|
||||
}
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue