Spaces:
Running
Running
Print a hosted-proof watch command (#41)
Browse filesPrint 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 +4 -2
- src/lumi_eggcracker/hosted_proof.py +18 -1
- tests/test_hosted_proof.py +55 -0
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.
|
| 37 |
-
|
|
|
|
|
|
|
| 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(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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}",
|