@@ -9,17 +9,24 @@ Launch the BrowserBox GitHub Actions runner workflow and print the live link.
99
1010Options:
1111 --license-key KEY Set/update the repo secret BBX_LICENSE_KEY before running.
12- --repo OWNER/NAME Repository to run the workflow in.
12+ --repo OWNER/NAME Repository to run the workflow in, creating it if needed.
13+ --repo-name NAME Repo name for auto-create. Defaults to <directory>-runner.
1314 --ref REF Branch/ref to run. Defaults to the current branch or main.
1415 --timeout MINUTES Session timeout. Defaults to 10.
1516 --workflow NAME Workflow name or file. Defaults to "Live Smoke Run".
17+ --public Create the bootstrap repo as public.
18+ --private Create the bootstrap repo as private. This is the default.
19+ --no-create-repo Fail instead of creating/bootstrapping a repo.
20+ --remote NAME Local git remote name for bootstrap pushes. Defaults to browserbox-runner.
1621 --no-open Do not open the tracking issue in a browser.
1722 -h, --help Show this help.
1823
1924Environment:
2025 BBX_LICENSE_KEY, BROWSERBOX_LICENSE_KEY, or BROWSERBOX_ACTION_LICENSE_KEY
2126 Used when --license-key is not supplied.
2227 BROWSERBOX_RUN_REPO Default repository, in owner/name form.
28+ BROWSERBOX_RUN_REPO_NAME
29+ Repo name for auto-create.
2330EOF
2431}
2532
@@ -29,7 +36,7 @@ die() {
2936}
3037
3138log () {
32- printf ' [browserbox] %s\n' " $* "
39+ printf ' [browserbox] %s\n' " $* " >&2
3340}
3441
3542remote_repo () {
@@ -69,14 +76,133 @@ workflow_exists() {
6976 gh workflow view " $workflow " --repo " $repo " > /dev/null 2>&1
7077}
7178
79+ repo_exists () {
80+ local repo=" $1 "
81+ gh repo view " $repo " > /dev/null 2>&1
82+ }
83+
84+ authenticated_owner () {
85+ gh api user --jq .login
86+ }
87+
88+ repo_clone_url () {
89+ local repo=" $1 "
90+ local protocol
91+
92+ protocol=" $( gh config get git_protocol -h github.com 2> /dev/null || true) "
93+ if [[ " $protocol " == " ssh" ]]; then
94+ gh repo view " $repo " --json sshUrl --jq .sshUrl
95+ else
96+ gh repo view " $repo " --json url --jq .url
97+ fi
98+ }
99+
100+ sanitize_repo_name () {
101+ local raw=" $1 "
102+ local clean
103+
104+ clean=" $( printf ' %s' " $raw " | tr ' ' ' -' | tr -cd ' A-Za-z0-9._-' ) "
105+ clean=" ${clean# .} "
106+ clean=" ${clean% -} "
107+ printf ' %s\n' " ${clean:- browserbox-runner} "
108+ }
109+
110+ default_auto_repo () {
111+ local owner=" $1 "
112+ local name
113+ local root
114+
115+ root=" $( git rev-parse --show-toplevel 2> /dev/null || pwd) "
116+ name=" ${BROWSERBOX_RUN_REPO_NAME:- ${repo_name_arg:- } } "
117+ if [[ -z " $name " ]]; then
118+ name=" $( basename " $root " ) -runner"
119+ fi
120+
121+ printf ' %s/%s\n' " $owner " " $( sanitize_repo_name " $name " ) "
122+ }
123+
124+ ensure_git_worktree () {
125+ git rev-parse --is-inside-work-tree > /dev/null 2>&1 || die " repo bootstrap requires running from this git checkout"
126+ }
127+
128+ warn_uncommitted_bootstrap_changes () {
129+ local dirty
130+
131+ dirty=" $( git status --porcelain -- .github/workflows/smoke-run.yml action.yml scripts/run-browserbox.sh run-browserbox.sh 2> /dev/null || true) "
132+ if [[ -n " $dirty " ]]; then
133+ log " warning: workflow/action helper files have uncommitted changes; only committed HEAD will be pushed"
134+ fi
135+ }
136+
137+ ensure_remote () {
138+ local remote=" $1 "
139+ local repo=" $2 "
140+ local url
141+
142+ url=" $( repo_clone_url " $repo " ) "
143+ if git remote get-url " $remote " > /dev/null 2>&1 ; then
144+ if [[ " $( git remote get-url " $remote " ) " != " $url " ]]; then
145+ git remote set-url " $remote " " $url "
146+ fi
147+ else
148+ git remote add " $remote " " $url "
149+ fi
150+ }
151+
152+ wait_for_workflow () {
153+ local repo=" $1 "
154+ local workflow=" $2 "
155+
156+ for _ in $( seq 1 60) ; do
157+ workflow_exists " $repo " " $workflow " && return 0
158+ sleep 2
159+ done
160+
161+ return 1
162+ }
163+
164+ bootstrap_repo () {
165+ local repo=" $1 "
166+ local workflow=" $2 "
167+ local ref=" $3 "
168+
169+ [[ " $create_repo " == " true" ]] || die " no repo with workflow \" ${workflow} \" found; pass --repo OWNER/NAME or remove --no-create-repo"
170+ ensure_git_worktree
171+ warn_uncommitted_bootstrap_changes
172+
173+ if repo_exists " $repo " ; then
174+ log " bootstrap repo exists: ${repo} "
175+ else
176+ log " creating ${repo_visibility} repo: ${repo} "
177+ gh repo create " $repo " " --${repo_visibility} " --disable-wiki \
178+ --description " Private BrowserBox GitHub Actions runner"
179+ fi
180+
181+ ensure_remote " $bootstrap_remote " " $repo "
182+
183+ log " pushing current HEAD to ${repo} :${ref} "
184+ if ! git push " $bootstrap_remote " " HEAD:refs/heads/${ref} " ; then
185+ die " failed to push current HEAD to ${repo} :${ref} ; resolve the git push error or pass a different --repo"
186+ fi
187+
188+ log " waiting for workflow \" ${workflow} \" to become available"
189+ wait_for_workflow " $repo " " $workflow " || die " workflow \" ${workflow} \" was not found in ${repo} after pushing ${ref} "
190+ }
191+
72192resolve_repo () {
73193 local workflow=" $1 "
74- local repo=" ${BROWSERBOX_RUN_REPO:- } "
194+ local ref=" $2 "
195+ local explicit_repo=" ${repo_arg:- ${BROWSERBOX_RUN_REPO:- } } "
75196 local candidate
76197 local candidates=()
77198
78- if [[ -n " $repo " ]]; then
79- printf ' %s\n' " $repo "
199+ if [[ -n " $explicit_repo " ]]; then
200+ if workflow_exists " $explicit_repo " " $workflow " ; then
201+ printf ' %s\n' " $explicit_repo "
202+ return 0
203+ fi
204+ bootstrap_repo " $explicit_repo " " $workflow " " $ref "
205+ printf ' %s\n' " $explicit_repo "
80206 return 0
81207 fi
82208
@@ -95,8 +221,10 @@ resolve_repo() {
95221 fi
96222 done
97223
98- [[ " ${# candidates[@]} " -gt 0 ]] || die " could not infer a GitHub repo; pass --repo OWNER/NAME"
99- printf ' %s\n' " ${candidates[0]} "
224+ candidate=" $( default_auto_repo " $( authenticated_owner) " ) "
225+ log " no existing candidate repo has workflow \" ${workflow} \" "
226+ bootstrap_repo " $candidate " " $workflow " " $ref "
227+ printf ' %s\n' " $candidate "
100228}
101229
102230open_url () {
@@ -216,11 +344,15 @@ wait_for_login_link() {
216344
217345license_key=" ${BBX_LICENSE_KEY:- ${BROWSERBOX_LICENSE_KEY:- ${BROWSERBOX_ACTION_LICENSE_KEY:- } } } "
218346repo_arg=" "
347+ repo_name_arg=" "
219348ref=" $( git branch --show-current 2> /dev/null || true) "
220349ref=" ${ref:- main} "
221350timeout_mins=" 10"
222351workflow=" Live Smoke Run"
223352open_issue=" true"
353+ create_repo=" true"
354+ repo_visibility=" private"
355+ bootstrap_remote=" browserbox-runner"
224356
225357while [[ $# -gt 0 ]]; do
226358 case " $1 " in
@@ -242,6 +374,15 @@ while [[ $# -gt 0 ]]; do
242374 repo_arg=" ${1#* =} "
243375 shift
244376 ;;
377+ --repo-name)
378+ [[ $# -ge 2 ]] || die " --repo-name requires a value"
379+ repo_name_arg=" $2 "
380+ shift 2
381+ ;;
382+ --repo-name=* )
383+ repo_name_arg=" ${1#* =} "
384+ shift
385+ ;;
245386 --ref)
246387 [[ $# -ge 2 ]] || die " --ref requires a value"
247388 ref=" $2 "
@@ -269,6 +410,27 @@ while [[ $# -gt 0 ]]; do
269410 workflow=" ${1#* =} "
270411 shift
271412 ;;
413+ --public)
414+ repo_visibility=" public"
415+ shift
416+ ;;
417+ --private)
418+ repo_visibility=" private"
419+ shift
420+ ;;
421+ --no-create-repo)
422+ create_repo=" false"
423+ shift
424+ ;;
425+ --remote)
426+ [[ $# -ge 2 ]] || die " --remote requires a value"
427+ bootstrap_remote=" $2 "
428+ shift 2
429+ ;;
430+ --remote=* )
431+ bootstrap_remote=" ${1#* =} "
432+ shift
433+ ;;
272434 --no-open)
273435 open_issue=" false"
274436 shift
@@ -287,14 +449,16 @@ while [[ $# -gt 0 ]]; do
287449 esac
288450done
289451
290- command -v gh > /dev/null 2>&1 || die " gh is required"
291- gh auth status > /dev/null 2>&1 || die " gh is not authenticated"
452+ command -v gh > /dev/null 2>&1 || die " gh is required. Install GitHub CLI: https://cli.github.com/ "
453+ gh auth status > /dev/null 2>&1 || die " gh is not authenticated. Run: gh auth login --scopes repo "
292454[[ " $timeout_mins " =~ ^[0-9]+$ ]] || die " --timeout must be an integer number of minutes"
455+ [[ " $repo_visibility " == " private" || " $repo_visibility " == " public" ]] || die " repo visibility must be private or public"
456+ [[ -n " $bootstrap_remote " ]] || die " --remote cannot be empty"
293457
294458if [[ -n " $repo_arg " ]]; then
295- repo=" $repo_arg "
459+ repo=" $( resolve_repo " $workflow " " $ref " ) "
296460else
297- repo=" $( resolve_repo " $workflow " ) "
461+ repo=" $( resolve_repo " $workflow " " $ref " ) "
298462fi
299463
300464log " repo: ${repo} "
0 commit comments