1
- #! /usr/bin/env -S deno run --allow-run=gh,git --allow-net --allow-read --allow-write --allow-env
1
+ #! /usr/bin/env -S deno run --allow-run=gh,git,mktemp --allow-net --allow-read --allow-write --allow-env
2
2
3
3
/*
4
4
* This Source Code Form is subject to the terms of the Mozilla Public
10
10
import * as flags from 'https://deno.land/[email protected] /flags/mod.ts'
11
11
import * as path from 'https://deno.land/[email protected] /path/mod.ts'
12
12
import $ from 'https://deno.land/x/[email protected] /mod.ts'
13
+ import { existsSync } from 'jsr:@std/[email protected] '
13
14
14
15
const HELP = `
15
- Update tools/console_version in ../omicron with current console commit
16
- hash and tarball hash and create PR in Omicron with that change.
16
+ Update tools/console_version in ../omicron to the specified console
17
+ commit and create PR in Omicron with that change. We use a git worktree
18
+ to avoid touching your Omicron clone.
17
19
18
20
Requirements:
19
21
- GitHub CLI installed
20
22
- Omicron is a sibling dir to console
21
23
22
24
Usage:
23
- ./tools/deno/bump-omicron.ts [options]
25
+ ./tools/deno/bump-omicron.ts [commit-ish=main] [ options]
24
26
25
27
Options:
26
28
-d, --dry-run Dry run, showing changes without creating PR
27
29
-h, --help Show this help message
28
30
-m, --message <msg> Add message to PR title: 'Bump web console (<msg>)'
29
31
`
30
32
31
- const OMICRON_DIR = '../omicron'
32
- const VERSION_FILE = path . join ( OMICRON_DIR , 'tools/console_version' )
33
-
33
+ const OMICRON_DIR = path . resolve ( '../omicron' )
34
34
const GH_MISSING = 'GitHub CLI not found. Please install it and try again.'
35
- const VERSION_FILE_MISSING = `Omicron console version file at '${ VERSION_FILE } ' not found. This script assumes Omicron is cloned in a sibling directory next to Console.`
36
35
37
36
/**
38
37
* These lines get printed in an Omicron PR, so any references to commits or
@@ -56,29 +55,28 @@ function linkifyGitLog(line: string): string {
56
55
return `* ${ shaLink } ${ rest } `
57
56
}
58
57
59
- // script starts here
58
+ async function makeOmicronWorktree ( ) {
59
+ const tmpDir = await $ `mktemp -d` . text ( )
60
+ await $ `git worktree add ${ tmpDir } origin/main` . cwd ( OMICRON_DIR ) . quiet ( 'stdout' )
60
61
61
- const args = flags . parse ( Deno . args , {
62
- alias : { dryRun : [ 'd' , 'dry-run' ] , h : 'help' , m : 'message' } ,
63
- boolean : [ 'dryRun' , 'help' ] ,
64
- string : [ 'message' ] ,
65
- } )
66
-
67
- if ( args . help ) {
68
- console . info ( HELP )
69
- Deno . exit ( )
62
+ return {
63
+ dir : tmpDir ,
64
+ [ Symbol . asyncDispose ] : async function ( ) {
65
+ console . log ( 'Cleaning up worktree' )
66
+ await $ `git worktree remove ${ tmpDir } ` . cwd ( OMICRON_DIR ) . quiet ( 'stdout' )
67
+ } ,
68
+ }
70
69
}
71
70
72
- const newCommit = await $ `git rev-parse HEAD` . text ( )
73
-
74
- const shaUrl = `https://dl.oxide.computer/releases/console/${ newCommit } .sha256.txt`
75
- const shaResp = await fetch ( shaUrl )
71
+ async function fetchTarballSha ( commit : string ) {
72
+ const shaUrl = `https://dl.oxide.computer/releases/console/${ commit } .sha256.txt`
73
+ const shaResp = await fetch ( shaUrl )
76
74
77
- if ( ! shaResp . ok ) {
78
- const workflowId =
79
- await $ `gh run list -L 1 -w 'Upload assets to dl.oxide.computer' --json databaseId --jq '.[0].databaseId'` . text ( )
80
- console . error (
81
- `
75
+ if ( ! shaResp . ok ) {
76
+ const workflowId =
77
+ await $ `gh run list -L 1 -w 'Upload assets to dl.oxide.computer' --json databaseId --jq '.[0].databaseId'` . text ( )
78
+ console . error (
79
+ `
82
80
Failed to fetch console tarball SHA. Either the current commit is not on origin/main or the asset upload job is still running.
83
81
84
82
Status: ${ shaResp . status }
@@ -87,90 +85,133 @@ Body: ${await shaResp.text()}
87
85
88
86
Running 'gh run watch ${ workflowId } ' to watch the current upload action.
89
87
`
90
- )
91
- await $ `gh run watch ${ workflowId } `
92
- Deno . exit ( 1 )
93
- }
94
-
95
- const newSha2 = ( await shaResp . text ( ) ) . trim ( )
96
- const newVersionFile = `COMMIT="${ newCommit } "\nSHA2="${ newSha2 } "\n`
88
+ )
89
+ await $ `gh run watch ${ workflowId } `
90
+ return Deno . exit ( 1 )
91
+ }
97
92
98
- const oldVersionFile = await Deno . readTextFile ( VERSION_FILE ) . catch ( ( ) => {
99
- throw Error ( VERSION_FILE_MISSING )
100
- } )
93
+ return ( await shaResp . text ( ) ) . trim ( )
94
+ }
101
95
102
- const oldCommit = / C O M M I T = " ? ( [ a - f 0 - 9 ] + ) " ? / . exec ( oldVersionFile ) ?. [ 1 ]
103
- if ( ! oldCommit ) throw Error ( 'Could not parse existing version file' )
96
+ async function getOldCommit ( ) {
97
+ const oldVersionFile = await $ `git show origin/main:tools/console_version`
98
+ . cwd ( OMICRON_DIR )
99
+ . text ( )
104
100
105
- if ( oldCommit === newCommit ) {
106
- console . info ( 'Nothing to update: Omicron already has the current commit pinned ')
107
- Deno . exit ( )
101
+ const oldCommit = / C O M M I T = " ? ( [ a - f 0 - 9 ] + ) " ? / . exec ( oldVersionFile ) ?. [ 1 ]
102
+ if ( ! oldCommit ) throw new Error ( 'Could not parse existing version file ')
103
+ return oldCommit
108
104
}
109
105
110
- const commitRange = `${ oldCommit . slice ( 0 , 8 ) } ...${ newCommit . slice ( 0 , 8 ) } `
106
+ async function makeOmicronPR (
107
+ consoleCommit : string ,
108
+ prTitle : string ,
109
+ changesLink : string ,
110
+ commits : string
111
+ ) {
112
+ const branchName = 'bump-console-' + consoleCommit . slice ( 0 , 8 )
113
+
114
+ {
115
+ // create git worktree for latest main in temp dir. `using` ensures this gets
116
+ // cleaned up at the end of the block
117
+ await using worktree = await makeOmicronWorktree ( )
118
+
119
+ const newSha2 = await fetchTarballSha ( consoleCommit )
120
+ const newVersionFile = `COMMIT="${ consoleCommit } "\nSHA2="${ newSha2 } "\n`
121
+
122
+ const versionFileAbsPath = path . resolve ( worktree . dir , 'tools/console_version' )
123
+ await Deno . writeTextFile ( versionFileAbsPath , newVersionFile )
124
+ console . info ( 'Updated ' , versionFileAbsPath )
125
+
126
+ // cd to omicron, pull main, create new branch, commit changes, push, PR it, go back to
127
+ // main, delete branch
128
+ Deno . chdir ( worktree . dir )
129
+ await $ `git checkout -b ${ branchName } `
130
+ console . info ( 'Created branch' , branchName )
131
+
132
+ await $ `git add tools/console_version`
133
+
134
+ // commits are console commits, so they won't auto-link in omicron
135
+ const commitsMarkdown = commits . split ( '\n' ) . map ( linkifyGitLog ) . join ( '\n' )
136
+ const prBody = `${ changesLink } \n\n${ commitsMarkdown } `
137
+ await $ `git commit -m ${ prTitle } -m ${ prBody } `
138
+
139
+ await $ `git push --set-upstream origin ${ branchName } `
140
+ console . info ( 'Committed changes and pushed' )
141
+
142
+ // create PR
143
+ const prUrl = await $ `gh pr create --title ${ prTitle } --body ${ prBody } ` . text ( )
144
+ console . info ( 'PR created:' , prUrl )
145
+
146
+ // set it to auto merge
147
+ const prNum = prUrl . match ( / \d + $ / ) ! [ 0 ]
148
+ await $ `gh pr merge ${ prNum } --auto --squash`
149
+ console . info ( 'PR set to auto-merge when CI passes' )
150
+ }
111
151
112
- const commits = await $ `git log --graph --oneline ${ commitRange } ` . text ( )
113
- // commits are console commits, so they won't auto-link in omicron
114
- const commitsMarkdown = commits . split ( '\n' ) . map ( linkifyGitLog ) . join ( '\n' )
152
+ // worktree has been cleaned up, so branch delete is allowed
153
+ await $ `git branch -D ${ branchName } ` . cwd ( OMICRON_DIR )
154
+ }
115
155
116
- const changesLine = `https://github.com/oxidecomputer/console/compare/${ commitRange } `
156
+ // wrapped in a function so we can do early returns rather than early
157
+ // Deno.exits, which mess up the worktree cleanup
158
+ async function run ( commitIsh : string , dryRun : boolean , messageArg : string | undefined ) {
159
+ await $ `git fetch` . cwd ( OMICRON_DIR )
117
160
118
- const branchName = 'bump-console-' + newCommit . slice ( 0 , 8 )
119
- const prBody = ` ${ changesLine } \n\n ${ commitsMarkdown } `
161
+ const oldConsoleCommit = await getOldCommit ( )
162
+ const newConsoleCommit = await $ `git rev-parse ${ commitIsh } ` . text ( )
120
163
121
- console . info ( `\n${ changesLine } \n\n${ commits } \n` )
164
+ if ( oldConsoleCommit === newConsoleCommit ) {
165
+ console . info ( `Nothing to update: Omicron already has ${ newConsoleCommit } pinned` )
166
+ return
167
+ }
122
168
123
- if ( args . dryRun ) Deno . exit ( )
169
+ const commitRange = `${ oldConsoleCommit . slice ( 0 , 8 ) } ...${ newConsoleCommit . slice ( 0 , 8 ) } `
170
+ const commits = await $ `git log --graph --oneline ${ commitRange } ` . text ( )
171
+ const changesLink = `https://github.com/oxidecomputer/console/compare/${ commitRange } `
124
172
125
- const message =
126
- args . message ||
127
- ( await $ . prompt ( {
128
- message : 'Description? (enter to skip)' ,
129
- noClear : true ,
130
- } ) )
173
+ console . info ( `\n${ changesLink } \n\n${ commits } \n` )
131
174
132
- const prTitle = 'Bump web console' + ( message ? ` ( ${ message } )` : '' )
175
+ if ( dryRun ) return
133
176
134
- console . info ( `\nPR title: ${ prTitle } \n` )
177
+ const message =
178
+ messageArg ||
179
+ ( await $ . prompt ( { message : 'Description? (enter to skip)' , noClear : true } ) )
180
+ const prTitle = 'Bump web console' + ( message ? ` (${ message } )` : '' )
181
+ console . info ( `\nPR title: ${ prTitle } \n` )
135
182
136
- const go = await $ . confirm ( { message : 'Make Omicron PR?' , noClear : true } )
137
- if ( ! go ) Deno . exit ( )
183
+ const go = await $ . confirm ( { message : 'Make Omicron PR?' , noClear : true } )
184
+ if ( ! go ) return
138
185
139
- if ( ! $ . commandExistsSync ( 'gh' ) ) throw Error ( GH_MISSING )
186
+ if ( ! $ . commandExistsSync ( 'gh' ) ) throw new Error ( GH_MISSING )
140
187
141
- await Deno . writeTextFile ( VERSION_FILE , newVersionFile )
142
- console . info ( 'Updated ' , VERSION_FILE )
188
+ const consoleDir = Deno . cwd ( ) // save it so we can change back
143
189
144
- const consoleDir = Deno . cwd ( )
190
+ await makeOmicronPR ( newConsoleCommit , prTitle , changesLink , commits )
145
191
146
- // cd to omicron, pull main, create new branch, commit changes, push, PR it, go back to
147
- // main, delete branch
148
- Deno . chdir ( OMICRON_DIR )
149
- await $ `git checkout main`
150
- await $ `git pull`
151
- await $ `git checkout -b ${ branchName } `
152
- console . info ( 'Created branch' , branchName )
192
+ // bump omicron tag in console to current commit
193
+ Deno . chdir ( consoleDir )
194
+ console . info ( 'Bumping omicron tag in console' )
195
+ await $ `git tag -f -a omicron -m 'pinned commit on omicron main' ${ commitIsh } `
196
+ await $ `git push -f origin tag omicron`
197
+ }
153
198
154
- await $ `git add tools/console_version`
155
- await $ `git commit -m ${ prTitle } -m ${ prBody } `
156
- await $ `git push --set-upstream origin ${ branchName } `
157
- console . info ( 'Committed changes and pushed' )
199
+ // script starts here
158
200
159
- // create PR
160
- const prUrl = await $ `gh pr create --title ${ prTitle } --body ${ prBody } ` . text ( )
161
- console . info ( 'PR created:' , prUrl )
201
+ const args = flags . parse ( Deno . args , {
202
+ alias : { dryRun : [ 'd' , 'dry-run' ] , h : 'help' , m : 'message' } ,
203
+ boolean : [ 'dryRun' , 'help' ] ,
204
+ string : [ 'message' ] ,
205
+ } )
162
206
163
- // set it to auto merge
164
- const prNum = prUrl . match ( / \d + $ / ) ! [ 0 ]
165
- await $ `gh pr merge ${ prNum } --auto --squash`
166
- console . info ( 'PR set to auto-merge when CI passes' )
207
+ if ( args . help ) {
208
+ console . info ( HELP )
209
+ Deno . exit ( )
210
+ }
167
211
168
- await $ `git checkout main`
169
- await $ `git branch -D ${ branchName } `
170
- console . info ( 'Checked out omicron main, deleted branch' , branchName )
212
+ if ( ! existsSync ( OMICRON_DIR ) ) {
213
+ throw new Error ( `Omicron repo not found at ${ OMICRON_DIR } ` )
214
+ }
171
215
172
- // bump omicron tag in console to current commit
173
- Deno . chdir ( consoleDir )
174
- console . info ( 'Bumping omicron tag in console' )
175
- await $ `git tag -f -a omicron -m 'pinned commit on omicron main'`
176
- await $ `git push -f origin tag omicron`
216
+ const commitIsh = args . _ [ 0 ] ?. toString ( ) || 'main'
217
+ await run ( commitIsh , args . dryRun , args . message )
0 commit comments