noqt commited on
Commit
519df41
·
unverified ·
1 Parent(s): d45c58f

Print a hosted-proof watch command (#41)

Browse files

Print a strict, host-bound gh command for following the exact dispatched run's progress and final status. The run URL remains the route to the bounded result; dispatch and containment are unchanged.

TRY_IT.md CHANGED
@@ -33,8 +33,10 @@ python (Join-Path $proofDir "scripts\start_hosted_proof.py") `
33
  ```
34
 
35
  The helper prints the exact workflow-run URL when GitHub returns it directly or
36
- the helper can uniquely correlate it to this dispatch. Open that URL to watch
37
- the proof and read its bounded result.
 
 
38
 
39
  ### Or use the GitHub interface
40
 
 
33
  ```
34
 
35
  The helper prints the exact workflow-run URL when GitHub returns it directly or
36
+ the helper can uniquely correlate it to this dispatch. For an exact run URL it
37
+ also prints a copyable `gh run watch` command, so you can follow the workflow's
38
+ progress and final status without leaving the terminal. Open the run URL to read
39
+ the bounded result.
40
 
41
  ### Or use the GitHub interface
42
 
src/lumi_eggcracker/hosted_proof.py CHANGED
@@ -22,7 +22,11 @@ RESULT_FORM_URL = (
22
  )
23
  REVIEWED_WORKFLOW_BLOB = "3761d8c3b29f1265e402c788e5340f6eab2c70df"
24
  ACKNOWLEDGEMENT = "i_understand_this_kills_a_test_tree=true"
25
- RUN_URL = re.compile(r"^https://github\.com/[^/]+/[^/]+/actions/runs/[1-9][0-9]*$")
 
 
 
 
26
  CORRELATION_ID = re.compile(r"^[0-9a-f]{32}$")
27
  RUN_NAME_PREFIX = "Containment probe"
28
  RUN_LOOKUP_ATTEMPTS = 3
@@ -44,6 +48,16 @@ class HostedProofError(RuntimeError):
44
  """A bounded, user-actionable hosted-proof startup failure."""
45
 
46
 
 
 
 
 
 
 
 
 
 
 
47
  def _default_runner(
48
  command: Sequence[str],
49
  *,
@@ -405,5 +419,8 @@ def main(argv: Sequence[str] | None = None) -> int:
405
  except HostedProofError as error:
406
  parser.error(str(error))
407
  print(f"Hosted proof dispatched: {url}")
 
 
 
408
  print(f"After it finishes, share the public run or friction: {RESULT_FORM_URL}")
409
  return 0
 
22
  )
23
  REVIEWED_WORKFLOW_BLOB = "3761d8c3b29f1265e402c788e5340f6eab2c70df"
24
  ACKNOWLEDGEMENT = "i_understand_this_kills_a_test_tree=true"
25
+ RUN_URL = re.compile(
26
+ r"^https://github\.com/"
27
+ r"(?P<owner>[A-Za-z0-9](?:[A-Za-z0-9-]{0,38}))/"
28
+ r"Lumi-Eggcracker/actions/runs/(?P<run_id>[1-9][0-9]*)$"
29
+ )
30
  CORRELATION_ID = re.compile(r"^[0-9a-f]{32}$")
31
  RUN_NAME_PREFIX = "Containment probe"
32
  RUN_LOOKUP_ATTEMPTS = 3
 
48
  """A bounded, user-actionable hosted-proof startup failure."""
49
 
50
 
51
+ def _watch_command(url: str) -> str | None:
52
+ """Return a copyable command only for an exact validated hosted-proof run URL."""
53
+
54
+ match = RUN_URL.fullmatch(url)
55
+ if match is None:
56
+ return None
57
+ repository = f"{HOST}/{match.group('owner')}/{REPOSITORY_NAME}"
58
+ return f"gh run watch {match.group('run_id')} --repo {repository} --exit-status"
59
+
60
+
61
  def _default_runner(
62
  command: Sequence[str],
63
  *,
 
419
  except HostedProofError as error:
420
  parser.error(str(error))
421
  print(f"Hosted proof dispatched: {url}")
422
+ watch_command = _watch_command(url)
423
+ if watch_command is not None:
424
+ print(f"Watch from this terminal: {watch_command}")
425
  print(f"After it finishes, share the public run or friction: {RESULT_FORM_URL}")
426
  return 0
tests/test_hosted_proof.py CHANGED
@@ -143,6 +143,61 @@ class HostedProofTests(unittest.TestCase):
143
 
144
  self.assertEqual(0, status)
145
  starter.assert_called_once_with(acknowledged=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  self.assertEqual(
147
  [
148
  f"Hosted proof dispatched: {url}",
 
143
 
144
  self.assertEqual(0, status)
145
  starter.assert_called_once_with(acknowledged=True)
146
+ self.assertEqual(
147
+ [
148
+ f"Hosted proof dispatched: {url}",
149
+ (
150
+ "Watch from this terminal: "
151
+ "gh run watch 123 --repo github.com/operator/Lumi-Eggcracker "
152
+ "--exit-status"
153
+ ),
154
+ (
155
+ "After it finishes, share the public run or friction: "
156
+ f"{hosted_proof_module.RESULT_FORM_URL}"
157
+ ),
158
+ ],
159
+ output.getvalue().splitlines(),
160
+ )
161
+
162
+ def test_watch_command_requires_exact_safe_github_run_url(self) -> None:
163
+ self.assertEqual(
164
+ (
165
+ "gh run watch 123 --repo github.com/operator/Lumi-Eggcracker "
166
+ "--exit-status"
167
+ ),
168
+ hosted_proof_module._watch_command(
169
+ "https://github.com/operator/Lumi-Eggcracker/actions/runs/123"
170
+ ),
171
+ )
172
+ rejected = (
173
+ "http://github.com/operator/Lumi-Eggcracker/actions/runs/123",
174
+ "https://gitlab.com/operator/Lumi-Eggcracker/actions/runs/123",
175
+ "https://github.com/operator/other/actions/runs/123",
176
+ "https://github.com/operator/Lumi-Eggcracker/actions/runs/0",
177
+ "https://github.com/operator/Lumi-Eggcracker/actions/runs/123?x=1",
178
+ "https://github.com/operator/Lumi-Eggcracker/actions/runs/123#fragment",
179
+ "https://github.com/bad;name/Lumi-Eggcracker/actions/runs/123",
180
+ )
181
+ for url in rejected:
182
+ with self.subTest(url=url):
183
+ self.assertIsNone(hosted_proof_module._watch_command(url))
184
+
185
+ def test_cli_omits_watch_command_for_fallback_workflow_page(self) -> None:
186
+ url = (
187
+ "https://github.com/operator/Lumi-Eggcracker/actions/workflows/"
188
+ "containment-probe.yml"
189
+ )
190
+ output = StringIO()
191
+ with (
192
+ patch.object(hosted_proof_module.shutil, "which", return_value="gh"),
193
+ patch.object(hosted_proof_module, "start_hosted_proof", return_value=url),
194
+ redirect_stdout(output),
195
+ ):
196
+ status = hosted_proof_module.main(
197
+ ["--i-understand-this-kills-a-test-tree"]
198
+ )
199
+
200
+ self.assertEqual(0, status)
201
  self.assertEqual(
202
  [
203
  f"Hosted proof dispatched: {url}",