11'use strict'
22
3- /* eslint-disable no-console */
4-
53// TODO: Support major versions.
64
7- const { execSync } = require ( 'child_process' )
85const fs = require ( 'fs' )
6+ const os = require ( 'os' )
97const path = require ( 'path' )
8+ const { capture, checkpoint, exit, fatal, success, run } = require ( './helpers/terminal' )
9+ const { checkBranchDiff, checkGitHub, checkGit } = require ( './helpers/requirements' )
1010
11- // Helpers for colored output.
12- const log = msg => console . log ( msg )
13- const success = msg => console . log ( `\x1b[32m${ msg } \x1b[0m` )
14- const error = msg => console . log ( `\x1b[31m${ msg } \x1b[0m` )
15- const whisper = msg => console . log ( `\x1b[90m${ msg } \x1b[0m` )
11+ checkGit ( )
12+ checkBranchDiff ( )
1613
17- const currentBranch = capture ( 'git branch --show-current' )
1814const releaseLine = process . argv [ 2 ]
1915
2016// Validate release line argument.
2117if ( ! releaseLine || releaseLine === 'help' || releaseLine === '--help' ) {
22- log ( 'Usage: node scripts/release/proposal <release-line> [release-type]' )
23- process . exit ( 0 )
18+ exit ( 'Usage: node scripts/release/proposal <release-line> [release-type]' )
2419} else if ( ! releaseLine ?. match ( / ^ \d + $ / ) ) {
25- error ( 'Invalid release line. Must be a whole number.' )
26- process . exit ( 1 )
20+ fatal ( 'Invalid release line. Must be a whole number.' )
2721}
2822
2923// Make sure the release branch is up to date to prepare for new proposal.
@@ -36,20 +30,21 @@ const diffCmd = [
3630 'branch-diff' ,
3731 '--user DataDog' ,
3832 '--repo dd-trace-js' ,
39- isActivePatch ( )
40- ? `--exclude-label=semver-major,semver-minor,dont-land-on-v${ releaseLine } .x`
41- : `--exclude-label=semver-major,dont-land-on-v${ releaseLine } .x`
33+ `--exclude-label=semver-major,dont-land-on-v${ releaseLine } .x`
4234] . join ( ' ' )
4335
44- // Determine the new version.
45- const [ lastMajor , lastMinor , lastPatch ] = require ( '../../package.json' ) . version . split ( '.' ) . map ( Number )
46- const lineDiff = capture ( `${ diffCmd } v${ releaseLine } .x master` )
36+ // Determine the new version and release notes location .
37+ const [ , lastMinor , lastPatch ] = require ( '../../package.json' ) . version . split ( '.' ) . map ( Number )
38+ const lineDiff = capture ( `${ diffCmd } --markdown=true v${ releaseLine } .x master` )
4739const newVersion = lineDiff . includes ( 'SEMVER-MINOR' )
4840 ? `${ releaseLine } .${ lastMinor + 1 } .0`
4941 : `${ releaseLine } .${ lastMinor } .${ lastPatch + 1 } `
42+ const notesDir = path . join ( os . tmpdir ( ) , 'release_notes' )
43+ const notesFile = path . join ( notesDir , `${ newVersion } .md` )
5044
51- // Checkout new branch and output new changes .
45+ // Checkout new or existing branch .
5246run ( `git checkout v${ newVersion } -proposal || git checkout -b v${ newVersion } -proposal` )
47+ run ( `git remote show origin | grep v${ newVersion } && git pull || exit 0` )
5348
5449// Get the hashes of the last version and the commits to add.
5550const lastCommit = capture ( 'git log -1 --pretty=%B' ) . trim ( )
@@ -69,60 +64,38 @@ if (proposalDiff) {
6964 try {
7065 run ( `echo "${ proposalDiff } " | xargs git cherry-pick` )
7166 } catch ( err ) {
72- error ( 'Cherry-pick failed. Resolve the conflicts and run `git cherry-pick --continue` to continue.' )
73- error ( 'When all conflicts have been resolved, run this script again.' )
74- process . exit ( 1 )
67+ fatal (
68+ 'Cherry-pick failed. Resolve the conflicts and run `git cherry-pick --continue` to continue.' ,
69+ 'When all conflicts have been resolved, run this script again.'
70+ )
7571 }
7672}
7773
7874// Update package.json with new version.
79- run ( `npm version --git-tag-version=false ${ newVersion } ` )
75+ run ( `npm version --allow-same-version -- git-tag-version=false ${ newVersion } ` )
8076run ( `git commit -uno -m v${ newVersion } package.json || exit 0` )
8177
82- ready ( )
78+ // Write release notes to a file that can be copied to the GitHub release.
79+ fs . mkdirSync ( notesDir , { recursive : true } )
80+ fs . writeFileSync ( notesFile , lineDiff )
8381
84- // Check if current branch is already an active patch proposal branch to avoid
85- // creating a new minor proposal branch if new minor commits are added to the
86- // main branch during a existing patch release.
87- function isActivePatch ( ) {
88- const currentMatch = currentBranch . match ( / ^ ( \d + ) \. ( \d + ) \. ( \d + ) - p r o p o s a l $ / )
82+ success ( 'Release proposal is ready.' )
83+ success ( `Changelog at ${ os . tmpdir ( ) } /release_notes/${ newVersion } .md` )
8984
90- if ( currentMatch ) {
91- const [ major , minor , patch ] = currentMatch . slice ( 1 ) . map ( Number )
85+ // Stop and ask the user if they want to proceed with pushing everything upstream.
86+ checkpoint ( 'Push the release upstream and create/update PR?' )
9287
93- if ( major === lastMajor && minor === lastMinor && patch > lastPatch ) {
94- return true
95- }
96- }
88+ checkGitHub ( )
9789
98- return false
99- }
90+ run ( 'git push -f -u origin HEAD' )
10091
101- // Output a command to the terminal and execute it .
102- function run ( cmd ) {
103- whisper ( `> ${ cmd } `)
104-
105- const output = execSync ( cmd , { } ) . toString ( )
106-
107- log ( output )
92+ // Create or edit the PR. This will also automatically output a link to the PR .
93+ try {
94+ run ( `gh pr create -d -B v ${ releaseLine } .x -t "v ${ newVersion } proposal" -F ${ notesFile } `)
95+ } catch ( e ) {
96+ // PR already exists so update instead.
97+ // TODO: Keep existing non-release-notes PR description if there is one.
98+ run ( `gh pr edit -F " ${ notesFile } "` )
10899}
109100
110- // Run a command and capture its output to return it to the caller.
111- function capture ( cmd ) {
112- return execSync ( cmd , { } ) . toString ( )
113- }
114-
115- // Write release notes to a file that can be copied to the GitHub release.
116- function ready ( ) {
117- const notesDir = path . join ( __dirname , '..' , '..' , '.github' , 'release_notes' )
118- const notesFile = path . join ( notesDir , `${ newVersion } .md` )
119- const lineDiff = capture ( `${ diffCmd } --markdown=true v${ releaseLine } .x master` )
120-
121- fs . mkdirSync ( notesDir , { recursive : true } )
122- fs . writeFileSync ( notesFile , lineDiff )
123-
124- success ( 'Release proposal is ready.' )
125- success ( `Changelog at .github/release_notes/${ newVersion } .md` )
126-
127- process . exit ( 0 )
128- }
101+ success ( 'Release PR is ready.' )
0 commit comments