@riddledc/riddle-proof 0.8.30 → 0.8.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced/engine-harness.cjs +132 -10
- package/dist/advanced/engine-harness.js +2 -2
- package/dist/advanced/index.cjs +132 -10
- package/dist/advanced/index.d.cts +2 -2
- package/dist/advanced/index.d.ts +2 -2
- package/dist/advanced/index.js +4 -4
- package/dist/advanced/proof-run-core.cjs +3 -1
- package/dist/advanced/proof-run-core.d.cts +1 -1
- package/dist/advanced/proof-run-core.d.ts +1 -1
- package/dist/advanced/proof-run-core.js +1 -1
- package/dist/advanced/proof-run-engine.cjs +80 -1
- package/dist/advanced/proof-run-engine.d.cts +2 -2
- package/dist/advanced/proof-run-engine.d.ts +2 -2
- package/dist/advanced/proof-run-engine.js +2 -2
- package/dist/advanced/runner.js +2 -2
- package/dist/{chunk-3OTO7IDH.js → chunk-C2NHHBFV.js} +1 -1
- package/dist/{chunk-32RE64IO.js → chunk-IOI6QR3B.js} +78 -1
- package/dist/{chunk-XJA2GDVN.js → chunk-U73JPBZW.js} +1 -1
- package/dist/{chunk-K6HZUSHH.js → chunk-X7SQTCIQ.js} +3 -1
- package/dist/{chunk-UWO4YR7I.js → chunk-ZREWMTFA.js} +53 -10
- package/dist/cli/index.js +3 -3
- package/dist/cli.cjs +132 -10
- package/dist/cli.js +3 -3
- package/dist/engine-harness.cjs +132 -10
- package/dist/engine-harness.js +2 -2
- package/dist/index.cjs +132 -10
- package/dist/index.js +3 -3
- package/dist/{proof-run-core-C8FDUhle.d.cts → proof-run-core-B1GeqkR8.d.cts} +2 -0
- package/dist/{proof-run-core-C8FDUhle.d.ts → proof-run-core-B1GeqkR8.d.ts} +2 -0
- package/dist/proof-run-core.cjs +3 -1
- package/dist/proof-run-core.d.cts +1 -1
- package/dist/proof-run-core.d.ts +1 -1
- package/dist/proof-run-core.js +1 -1
- package/dist/{proof-run-engine-By7oLsF-.d.ts → proof-run-engine-DYfmd8d7.d.ts} +4 -4
- package/dist/{proof-run-engine-D80hVFMf.d.cts → proof-run-engine-DeHxtGnW.d.cts} +4 -4
- package/dist/proof-run-engine.cjs +80 -1
- package/dist/proof-run-engine.d.cts +2 -2
- package/dist/proof-run-engine.d.ts +2 -2
- package/dist/proof-run-engine.js +2 -2
- package/dist/runner.js +2 -2
- package/lib/workspace-core.mjs +62 -7
- package/package.json +2 -2
- package/runtime/lib/riddle_core_call.mjs +662 -40
- package/runtime/lib/ship.py +363 -16
- package/runtime/lib/util.py +117 -40
- package/runtime/lib/verify.py +4 -3
- package/runtime/pipelines/riddle-proof-ship.lobster +11 -1
- package/runtime/tests/recon_verify_smoke.py +132 -0
- package/runtime/tests/ship_artifact_publication.py +185 -0
package/runtime/lib/verify.py
CHANGED
|
@@ -310,12 +310,13 @@ def payload_has_capture_artifacts(payload):
|
|
|
310
310
|
def capture_payload_error(payload):
|
|
311
311
|
if not isinstance(payload, dict):
|
|
312
312
|
return ''
|
|
313
|
-
if payload.get('ok') is False
|
|
314
|
-
for key in ('error', 'stderr', 'stdout'):
|
|
313
|
+
if payload.get('ok') is False:
|
|
314
|
+
for key in ('error', 'script_error', 'stderr', 'stdout'):
|
|
315
315
|
value = payload.get(key)
|
|
316
316
|
if value:
|
|
317
317
|
return str(value).strip()
|
|
318
|
-
|
|
318
|
+
if not payload_has_capture_artifacts(payload):
|
|
319
|
+
return 'capture tool returned ok=false without artifacts'
|
|
319
320
|
return ''
|
|
320
321
|
|
|
321
322
|
|
|
@@ -12,7 +12,17 @@ steps:
|
|
|
12
12
|
import json, os
|
|
13
13
|
state_file = os.environ.get('RIDDLE_PROOF_STATE_FILE', '/tmp/riddle-proof-state.json')
|
|
14
14
|
s = json.load(open(state_file))
|
|
15
|
-
|
|
15
|
+
after = ((s.get('evidence_bundle') or {}).get('after') or {})
|
|
16
|
+
observation = after.get('observation') or {}
|
|
17
|
+
supporting = after.get('supporting_artifacts') or {}
|
|
18
|
+
has_structured_after = bool(
|
|
19
|
+
observation.get('valid') and (
|
|
20
|
+
supporting.get('has_structured_payload') or
|
|
21
|
+
supporting.get('proof_evidence_present') or
|
|
22
|
+
observation.get('telemetry_ready')
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
if not s.get('after_cdn') and not has_structured_after:
|
|
16
26
|
raise SystemExit('No after evidence. Run verify first.')
|
|
17
27
|
print('SHIP')
|
|
18
28
|
print(' Proof goal: ' + s.get('change_request', ''))
|
|
@@ -89,6 +89,138 @@ class FakeRiddle:
|
|
|
89
89
|
'id': f'pv-{label}',
|
|
90
90
|
'preview_url': f'https://preview.example.com/{label}/',
|
|
91
91
|
}
|
|
92
|
+
if tool == 'riddle_server_preview':
|
|
93
|
+
script = args.get('script', '')
|
|
94
|
+
target_path = args.get('path') or '/'
|
|
95
|
+
path_only, _, query = str(target_path).partition('?')
|
|
96
|
+
search = '?' + query if query else ''
|
|
97
|
+
delegated_markers = [
|
|
98
|
+
'after-proof',
|
|
99
|
+
'audioNoProof',
|
|
100
|
+
'audioFailedProof',
|
|
101
|
+
'throwAfterProofEvidence',
|
|
102
|
+
'attack_ms_after',
|
|
103
|
+
'window.__riddleProofEvidence',
|
|
104
|
+
'globalThis.__riddleProofEvidence',
|
|
105
|
+
'clickedSkipHashNavigation',
|
|
106
|
+
'pricingQueryHashDropsTerminal',
|
|
107
|
+
'pricingQueryHashStructuredNegativeControl',
|
|
108
|
+
'pricingQueryHashPassesWithPageStateHashGap',
|
|
109
|
+
'clickedProofNavigation',
|
|
110
|
+
'clickedHomeNavigation',
|
|
111
|
+
'skipLinkTimeout',
|
|
112
|
+
'interactionThrownAfterFailedEvidence',
|
|
113
|
+
'interactionThrownError',
|
|
114
|
+
]
|
|
115
|
+
if any(marker in script for marker in delegated_markers):
|
|
116
|
+
return self.invoke_retry('riddle_script', {'script': script}, retries=retries, timeout=timeout)
|
|
117
|
+
if path_only == '/wrong' or '/wrong' in script:
|
|
118
|
+
return {
|
|
119
|
+
'ok': True,
|
|
120
|
+
'runner': 'local-server-preview',
|
|
121
|
+
'target_url': 'http://127.0.0.1:3000/wrong',
|
|
122
|
+
'screenshots': [{'url': 'https://cdn.example.com/wrong.png'}],
|
|
123
|
+
'outputs': [{'name': 'wrong.png', 'url': 'https://cdn.example.com/wrong.png'}],
|
|
124
|
+
'console': ['RIDDLE_PROOF_STATE:{"bodyTextLength":5,"interactiveElements":0,"pathname":"/wrong","title":"Wrong"}'],
|
|
125
|
+
}
|
|
126
|
+
if '/games/drum-sequencer' in path_only:
|
|
127
|
+
page_state = {
|
|
128
|
+
'bodyTextLength': 240,
|
|
129
|
+
'visibleTextSample': 'Neon Step Sequencer Monkberry Moon Delight Mix Board Play All',
|
|
130
|
+
'interactiveElements': 8,
|
|
131
|
+
'visibleInteractiveElements': 8,
|
|
132
|
+
'pathname': '/games/drum-sequencer',
|
|
133
|
+
'search': search,
|
|
134
|
+
'title': 'Neon Step Sequencer',
|
|
135
|
+
'buttons': ['Play All', 'Shuffle'],
|
|
136
|
+
'headings': ['Neon Step Sequencer'],
|
|
137
|
+
'links': [],
|
|
138
|
+
'canvasCount': 1,
|
|
139
|
+
'largeVisibleElements': [{'tag': 'canvas', 'text': ''}],
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
'ok': True,
|
|
143
|
+
'runner': 'local-server-preview',
|
|
144
|
+
'target_url': 'http://127.0.0.1:3000' + str(target_path),
|
|
145
|
+
'screenshots': [{'url': 'https://cdn.example.com/sequencer-before.png'}],
|
|
146
|
+
'outputs': [{'name': 'before.png', 'url': 'https://cdn.example.com/sequencer-before.png'}],
|
|
147
|
+
'console': state_console(page_state),
|
|
148
|
+
}
|
|
149
|
+
if '/games/tic-tac-toe' in path_only:
|
|
150
|
+
return {
|
|
151
|
+
'ok': True,
|
|
152
|
+
'runner': 'local-server-preview',
|
|
153
|
+
'target_url': 'http://127.0.0.1:3000' + str(target_path),
|
|
154
|
+
'screenshots': [{'url': 'https://cdn.example.com/tictactoe-before.png'}],
|
|
155
|
+
'outputs': [{'name': 'before.png', 'url': 'https://cdn.example.com/tictactoe-before.png'}],
|
|
156
|
+
'console': state_console({
|
|
157
|
+
'bodyTextLength': 220,
|
|
158
|
+
'visibleTextSample': 'LilArcade Tic Tac Toe Player X Reset Game',
|
|
159
|
+
'interactiveElements': 5,
|
|
160
|
+
'visibleInteractiveElements': 5,
|
|
161
|
+
'pathname': '/games/tic-tac-toe',
|
|
162
|
+
'title': 'TicTacToe',
|
|
163
|
+
'buttons': ['Reset Game'],
|
|
164
|
+
'headings': ['Tic Tac Toe'],
|
|
165
|
+
'links': [],
|
|
166
|
+
'canvasCount': 0,
|
|
167
|
+
'largeVisibleElements': [{'tag': 'button', 'text': 'Reset Game'}],
|
|
168
|
+
}),
|
|
169
|
+
}
|
|
170
|
+
if 'after-proof' in script:
|
|
171
|
+
after_url = 'https://cdn.example.com/after-artifact' if 'noVisualDelta' in script else 'https://cdn.example.com/after.png'
|
|
172
|
+
outputs = [{'name': 'after.png', 'url': after_url}]
|
|
173
|
+
if 'proof-session' in script:
|
|
174
|
+
outputs.append({'name': 'proof-session.json', 'url': 'https://cdn.example.com/proof-session.json'})
|
|
175
|
+
payload = {
|
|
176
|
+
'ok': True,
|
|
177
|
+
'runner': 'local-server-preview',
|
|
178
|
+
'target_url': 'http://127.0.0.1:3000' + str(target_path),
|
|
179
|
+
'screenshots': [{'url': after_url}],
|
|
180
|
+
'outputs': outputs,
|
|
181
|
+
'console': state_console({
|
|
182
|
+
'bodyTextLength': 180,
|
|
183
|
+
'visibleTextSample': 'Pricing CTA Buy Now',
|
|
184
|
+
'interactiveElements': 4,
|
|
185
|
+
'visibleInteractiveElements': 4,
|
|
186
|
+
'pathname': path_only or '/pricing',
|
|
187
|
+
'search': search,
|
|
188
|
+
'title': 'After',
|
|
189
|
+
'buttons': ['Buy Now'],
|
|
190
|
+
'headings': ['Pricing'],
|
|
191
|
+
'links': [],
|
|
192
|
+
'canvasCount': 0,
|
|
193
|
+
'largeVisibleElements': [{'tag': 'button', 'text': 'Buy Now'}],
|
|
194
|
+
}),
|
|
195
|
+
}
|
|
196
|
+
if 'noVisualDelta' not in script:
|
|
197
|
+
payload['visual_diff'] = {
|
|
198
|
+
'diffPercentage': 1.2,
|
|
199
|
+
'differentPixels': 12000,
|
|
200
|
+
'totalPixels': 972000,
|
|
201
|
+
}
|
|
202
|
+
return payload
|
|
203
|
+
return {
|
|
204
|
+
'ok': True,
|
|
205
|
+
'runner': 'local-server-preview',
|
|
206
|
+
'target_url': 'http://127.0.0.1:3000' + str(target_path),
|
|
207
|
+
'screenshots': [{'url': 'https://cdn.example.com/home-before.png'}],
|
|
208
|
+
'outputs': [{'name': 'before.png', 'url': 'https://cdn.example.com/home-before.png'}],
|
|
209
|
+
'console': state_console({
|
|
210
|
+
'bodyTextLength': 180,
|
|
211
|
+
'visibleTextSample': 'Riddle Proof homepage hero Start Free',
|
|
212
|
+
'interactiveElements': 4,
|
|
213
|
+
'visibleInteractiveElements': 4,
|
|
214
|
+
'pathname': path_only or '/',
|
|
215
|
+
'search': search,
|
|
216
|
+
'title': 'Riddle',
|
|
217
|
+
'buttons': ['Start Free'],
|
|
218
|
+
'headings': ['Riddle Proof'],
|
|
219
|
+
'links': [],
|
|
220
|
+
'canvasCount': 0,
|
|
221
|
+
'largeVisibleElements': [{'tag': 'button', 'text': 'Start Free'}],
|
|
222
|
+
}),
|
|
223
|
+
}
|
|
92
224
|
if tool == 'riddle_script':
|
|
93
225
|
script = args.get('script', '')
|
|
94
226
|
if 'preview.example.com' in script and '/wrong' in script:
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import subprocess
|
|
4
|
+
import tempfile
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
ROOT = Path(__file__).resolve().parents[2]
|
|
9
|
+
SHIP = ROOT / "runtime" / "lib" / "ship.py"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def run(args, cwd, env=None):
|
|
13
|
+
result = subprocess.run(args, cwd=cwd, env=env, capture_output=True, text=True, timeout=120)
|
|
14
|
+
if result.returncode != 0:
|
|
15
|
+
raise AssertionError(
|
|
16
|
+
f"{' '.join(args)} failed\nstdout:\n{result.stdout}\nstderr:\n{result.stderr}"
|
|
17
|
+
)
|
|
18
|
+
return result
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def write_fake_gh(path):
|
|
22
|
+
path.write_text(
|
|
23
|
+
"""#!/usr/bin/env python3
|
|
24
|
+
import json
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
|
|
28
|
+
args = sys.argv[1:]
|
|
29
|
+
if args[:3] == ["repo", "view", "--json"]:
|
|
30
|
+
print("example/test-repo")
|
|
31
|
+
raise SystemExit(0)
|
|
32
|
+
if args[:2] == ["pr", "list"]:
|
|
33
|
+
print("")
|
|
34
|
+
raise SystemExit(0)
|
|
35
|
+
if args[:2] == ["pr", "create"]:
|
|
36
|
+
print("https://github.com/example/test-repo/pull/321")
|
|
37
|
+
raise SystemExit(0)
|
|
38
|
+
if args[:2] == ["pr", "comment"]:
|
|
39
|
+
body = ""
|
|
40
|
+
if "--body" in args:
|
|
41
|
+
body = args[args.index("--body") + 1]
|
|
42
|
+
with open(os.environ["FAKE_GH_COMMENT_BODY"], "w") as f:
|
|
43
|
+
f.write(body)
|
|
44
|
+
print("https://github.com/example/test-repo/pull/321#issuecomment-999")
|
|
45
|
+
raise SystemExit(0)
|
|
46
|
+
if args[:2] == ["pr", "checks"]:
|
|
47
|
+
print("[]")
|
|
48
|
+
raise SystemExit(0)
|
|
49
|
+
if args[:2] == ["pr", "ready"]:
|
|
50
|
+
raise SystemExit(0)
|
|
51
|
+
if args[:2] == ["pr", "edit"]:
|
|
52
|
+
raise SystemExit(0)
|
|
53
|
+
print("unknown gh command: " + " ".join(args), file=sys.stderr)
|
|
54
|
+
raise SystemExit(1)
|
|
55
|
+
""",
|
|
56
|
+
encoding="utf-8",
|
|
57
|
+
)
|
|
58
|
+
path.chmod(0o755)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def main():
|
|
62
|
+
with tempfile.TemporaryDirectory(prefix="riddle-proof-ship-artifacts-") as tmp:
|
|
63
|
+
root = Path(tmp)
|
|
64
|
+
origin = root / "origin.git"
|
|
65
|
+
repo = root / "repo"
|
|
66
|
+
artifacts = root / "artifacts"
|
|
67
|
+
bin_dir = root / "bin"
|
|
68
|
+
state_path = root / "state.json"
|
|
69
|
+
comment_body_path = root / "comment.md"
|
|
70
|
+
artifacts.mkdir()
|
|
71
|
+
bin_dir.mkdir()
|
|
72
|
+
|
|
73
|
+
run(["git", "init", "--bare", str(origin)], cwd=root)
|
|
74
|
+
run(["git", "init", str(repo)], cwd=root)
|
|
75
|
+
run(["git", "config", "user.name", "Test User"], cwd=repo)
|
|
76
|
+
run(["git", "config", "user.email", "test@example.com"], cwd=repo)
|
|
77
|
+
run(["git", "remote", "add", "origin", str(origin)], cwd=repo)
|
|
78
|
+
run(["git", "checkout", "-b", "agent/proof-artifact-test"], cwd=repo)
|
|
79
|
+
(repo / "README.md").write_text("initial\n", encoding="utf-8")
|
|
80
|
+
run(["git", "add", "README.md"], cwd=repo)
|
|
81
|
+
run(["git", "commit", "-m", "Initial"], cwd=repo)
|
|
82
|
+
(repo / "README.md").write_text("changed\n", encoding="utf-8")
|
|
83
|
+
|
|
84
|
+
# Tiny valid PNG header/body is enough for GitHub Markdown image embedding.
|
|
85
|
+
screenshot = artifacts / "after-proof.png"
|
|
86
|
+
screenshot.write_bytes(
|
|
87
|
+
bytes.fromhex(
|
|
88
|
+
"89504e470d0a1a0a0000000d4948445200000001000000010802000000907753de"
|
|
89
|
+
"0000000c49444154789c63606060000000040001f61738550000000049454e44ae426082"
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
proof_json = artifacts / "proof.json"
|
|
93
|
+
proof_json.write_text(
|
|
94
|
+
json.dumps(
|
|
95
|
+
{
|
|
96
|
+
"version": "riddle-proof.test.v1",
|
|
97
|
+
"assertions": [{"name": "proof image published", "passed": True}],
|
|
98
|
+
},
|
|
99
|
+
indent=2,
|
|
100
|
+
),
|
|
101
|
+
encoding="utf-8",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
write_fake_gh(bin_dir / "gh")
|
|
105
|
+
state = {
|
|
106
|
+
"repo_dir": str(repo),
|
|
107
|
+
"branch": "agent/proof-artifact-test",
|
|
108
|
+
"target_branch": "agent/proof-artifact-test",
|
|
109
|
+
"run_id": "rp_ship_artifact_test",
|
|
110
|
+
"change_request": "Prove PR artifact publication.",
|
|
111
|
+
"commit_message": "Test proof artifact publication",
|
|
112
|
+
"success_criteria": "The PR proof comment embeds a GitHub-hosted image.",
|
|
113
|
+
"verification_mode": "proof",
|
|
114
|
+
"requested_reference": "none",
|
|
115
|
+
"reference": "none",
|
|
116
|
+
"verify_status": "evidence_captured",
|
|
117
|
+
"after_cdn": screenshot.as_uri(),
|
|
118
|
+
"assertion_status": "passed",
|
|
119
|
+
"proof_summary": "All assertions passed.",
|
|
120
|
+
"proof_assessment_source": "supervising_agent",
|
|
121
|
+
"proof_assessment": {
|
|
122
|
+
"source": "supervising_agent",
|
|
123
|
+
"decision": "ready_to_ship",
|
|
124
|
+
"summary": "Evidence is strong enough to ship.",
|
|
125
|
+
},
|
|
126
|
+
"evidence_bundle": {
|
|
127
|
+
"verification_mode": "proof",
|
|
128
|
+
"after": {
|
|
129
|
+
"observation": {"valid": True, "reason": "ok", "telemetry_ready": True},
|
|
130
|
+
"supporting_artifacts": {
|
|
131
|
+
"has_structured_payload": True,
|
|
132
|
+
"proof_evidence_present": True,
|
|
133
|
+
"image_outputs": [{"name": "after-proof.png", "url": screenshot.as_uri()}],
|
|
134
|
+
"data_outputs": [{"name": "proof.json", "url": proof_json.as_uri()}],
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
"verify_results": {
|
|
139
|
+
"after": {
|
|
140
|
+
"raw": {
|
|
141
|
+
"outputs": [
|
|
142
|
+
{"name": "after-proof.png", "url": screenshot.as_uri(), "path": str(screenshot)},
|
|
143
|
+
{"name": "proof.json", "url": proof_json.as_uri(), "path": str(proof_json)},
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
state_path.write_text(json.dumps(state, indent=2), encoding="utf-8")
|
|
150
|
+
env = {
|
|
151
|
+
**os.environ,
|
|
152
|
+
"PATH": str(bin_dir) + os.pathsep + os.environ.get("PATH", ""),
|
|
153
|
+
"RIDDLE_PROOF_STATE_FILE": str(state_path),
|
|
154
|
+
"FAKE_GH_COMMENT_BODY": str(comment_body_path),
|
|
155
|
+
"DISCORD_BOT_TOKEN": "",
|
|
156
|
+
"OPENCLAW_HOME": str(root / "openclaw-home"),
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
run(["python3", str(SHIP)], cwd=repo, env=env)
|
|
160
|
+
|
|
161
|
+
updated = json.loads(state_path.read_text(encoding="utf-8"))
|
|
162
|
+
publication = updated.get("proof_artifact_publication") or {}
|
|
163
|
+
assert publication.get("ok") is True, "proof artifact publication should be recorded"
|
|
164
|
+
assert publication.get("artifacts"), "published artifact list should be recorded"
|
|
165
|
+
assert updated.get("ship_report", {}).get("after_artifact_url", "").startswith(
|
|
166
|
+
"https://raw.githubusercontent.com/example/test-repo/"
|
|
167
|
+
), "ship report should expose a public after artifact URL"
|
|
168
|
+
|
|
169
|
+
comment = comment_body_path.read_text(encoding="utf-8")
|
|
170
|
+
assert "file://" not in comment, "PR proof comment must not expose local file URLs"
|
|
171
|
+
assert "
|
|
174
|
+
assert "[proof.json](https://github.com/example/test-repo/blob/" in comment, (
|
|
175
|
+
"PR proof comment should link the structured proof JSON"
|
|
176
|
+
)
|
|
177
|
+
assert "Proof artifacts:" in comment, "PR proof comment should link the artifact bundle"
|
|
178
|
+
|
|
179
|
+
artifact_branch = publication.get("branch")
|
|
180
|
+
refs = run(["git", f"--git-dir={origin}", "show-ref", f"refs/heads/{artifact_branch}"], cwd=root)
|
|
181
|
+
assert artifact_branch in refs.stdout, "artifact branch should be pushed to origin"
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
if __name__ == "__main__":
|
|
185
|
+
main()
|