gowalk-cicd 1.0.118 → 1.0.120

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.
@@ -1 +1 @@
1
- 1.0.118
1
+ 1.0.120
@@ -66,8 +66,19 @@ async function phase(action, platform) {
66
66
  if (!id) return; // Existing consumers without release-intent wiring keep their original behavior.
67
67
  const record = await api.request('GET', `/deployments/${id}`);
68
68
  const expected = owner(platform, record.payload?.fingerprint);
69
+ // An EARLIER attempt of this same run owns the record legitimately. `admit`
70
+ // only re-runs when it failed, so "re-run failed jobs" — the ordinary way to
71
+ // recover a release that died in transport — leaves the record stamped with
72
+ // attempt 1 while the platform job runs as attempt 2. Demanding an exact
73
+ // match made that recovery impossible: obd-scanner's Android leg built for
74
+ // twenty-six minutes, then refused at the upload intent (2026-09-09).
75
+ // Upload-once does not rest on this number. It rests on the status check
76
+ // below, which admits `begin-upload` only from `building`; a record whose
77
+ // upload already began or finished is refused there, on any attempt.
78
+ const attempt = record.payload?.run_attempt;
69
79
  if (record.payload?.schema !== SCHEMA || record.payload.run_id !== expected.run_id ||
70
- record.payload.run_attempt !== expected.run_attempt || record.sha !== expected.source_sha ||
80
+ !Number.isSafeInteger(attempt) || attempt > expected.run_attempt ||
81
+ record.sha !== expected.source_sha ||
71
82
  record.payload.platform !== platform) throw Error('mobile_release_phase_owner_mismatch');
72
83
  const statuses = await api.request('GET', `/deployments/${id}/statuses?per_page=1`);
73
84
  const required = action === 'begin-upload' ? 'building' : 'uploading';
@@ -37,6 +37,23 @@ def excerpt(text: str) -> str:
37
37
  return primary + "\n...\n" + tail
38
38
 
39
39
 
40
+ #: A long run that is a lowercase snake_case NAME rather than an opaque value.
41
+ #: Thirteen of this helper's own error codes are 32 characters or more —
42
+ #: `native_podfile_lock_commit_required` is 35, `native_google_transport_helper_required`
43
+ #: is 39 — so the catch-all below erased the one token the classifier reads back
44
+ #: out of this text: task 1185's iOS failure published `"code": "[long value
45
+ #: redacted]"` with an empty `signals` list, and every pods failure in the fleet
46
+ #: was diagnosed blind (2026-09-09). Requiring an underscore keeps hex digests and
47
+ #: base32/base64 material opaque, and every environment value has already been
48
+ #: replaced verbatim above.
49
+ _NAME_RUN = re.compile(r"[a-z]+(?:_[a-z0-9]+)+")
50
+
51
+
52
+ def _opaque_run(match: "re.Match[str]") -> str:
53
+ run = match.group(0)
54
+ return run if _NAME_RUN.fullmatch(run) else "[long value redacted]"
55
+
56
+
40
57
  def diagnostic(log, environment: dict[str, str] | None = None) -> str:
41
58
  log.flush()
42
59
  log.seek(0, os.SEEK_END)
@@ -57,7 +74,7 @@ def diagnostic(log, environment: dict[str, str] | None = None) -> str:
57
74
  r'\1"[redacted]"', text)
58
75
  text = re.sub(r"\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+", "[token redacted]", text)
59
76
  text = re.sub(r"https?://[^\s\"'<>]+", "[URL redacted]", text)
60
- text = re.sub(r"[A-Za-z0-9_+/=-]{32,}", "[long value redacted]", text)
77
+ text = re.sub(r"[A-Za-z0-9_+/=-]{32,}", _opaque_run, text)
61
78
  return excerpt(text)
62
79
 
63
80
 
@@ -23,6 +23,26 @@ class DiagnosticsTests(unittest.TestCase):
23
23
  'cHJpdmF0ZTpwYXNzd29yZA==', 'PRIVATE_JSON_MARKER', 'PRIVATE_ACCESS_MARKER', 'SECRET'):
24
24
  self.assertNotIn(private, result)
25
25
 
26
+ def test_error_codes_survive_while_opaque_runs_of_the_same_length_do_not(self):
27
+ """The classifier reads the plugin's own code back out of this text, and
28
+ thirteen of the twenty-seven are 32 characters or more. Task 1185's iOS
29
+ failure published `"code": "[long value redacted]"` with an empty
30
+ `signals` list because the catch-all erased it (2026-09-09)."""
31
+ body = ('{"code": "native_podfile_lock_commit_required", "ok": false}\n'
32
+ 'native_google_transport_helper_required native_pods_operation_unverified\n'
33
+ 'digest 9f2b7c1e4a8d3b6f0c5e2a9d7b4f1c8e3a6d0b5f were removed\n'
34
+ 'MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQ==\n'
35
+ 'a_secret_looking_run_of_lowercase_words_kept\n')
36
+ result = diagnostic(io.BytesIO(body.encode()), {})
37
+ for code in ('native_podfile_lock_commit_required', 'native_google_transport_helper_required',
38
+ 'native_pods_operation_unverified'):
39
+ self.assertIn(code, result)
40
+ # A hex digest and base64 material carry no underscore and stay opaque.
41
+ for opaque in ('9f2b7c1e4a8d3b6f0c5e2a9d7b4f1c8e3a6d0b5f',
42
+ 'MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQ=='):
43
+ self.assertNotIn(opaque, result)
44
+ self.assertIn('[long value redacted]', result)
45
+
26
46
  def test_truncated_first_line_and_small_proxy_credentials_cannot_escape(self):
27
47
  body = b"x" * 17000 + b"TAIL_SECRET\n[!] Unable to satisfy dependency version 12.3.\n"
28
48
  self.assertNotIn("TAIL_SECRET", diagnostic(io.BytesIO(body), {}))
@@ -1 +1 @@
1
- 1.0.118
1
+ 1.0.120
@@ -1 +1 @@
1
- 1.0.118
1
+ 1.0.120
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gowalk-cicd",
3
- "version": "1.0.118",
3
+ "version": "1.0.120",
4
4
  "description": "Zero-config GitHub Actions delivery for iOS TestFlight and Android Google Play.",
5
5
  "type": "module",
6
6
  "bin": {