gowalk-cicd 1.0.78 → 1.0.80

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.
Files changed (36) hide show
  1. package/CLAUDE.md +33 -3
  2. package/README.md +22 -18
  3. package/action/.daemux-version +1 -1
  4. package/action/action.yml +48 -38
  5. package/action/scripts/auto_detect.py +8 -8
  6. package/action/scripts/commit_bot_changes.sh +3 -29
  7. package/action/scripts/native_dependency_guard.py +59 -0
  8. package/action/scripts/native_pods.py +97 -0
  9. package/action/scripts/native_pods_diagnostics.py +53 -0
  10. package/action/scripts/native_pods_process.py +93 -0
  11. package/action/scripts/native_project_graph.py +148 -0
  12. package/action/scripts/source_maintenance.py +93 -0
  13. package/action/scripts/source_maintenance_fixture.py +58 -0
  14. package/action/scripts/test_auto_detect.py +11 -0
  15. package/action/scripts/test_native_dependency_guard.py +116 -0
  16. package/action/scripts/test_native_dependency_wiring.py +59 -0
  17. package/action/scripts/test_native_pods.py +106 -0
  18. package/action/scripts/test_native_pods_diagnostics.py +39 -0
  19. package/action/scripts/test_native_pods_process.py +41 -0
  20. package/action/scripts/test_source_maintenance.py +122 -0
  21. package/android-action/.daemux-version +1 -1
  22. package/android-action/action.yml +3 -1
  23. package/android-action/play-upload/action.yml +1 -1
  24. package/android-action/play-upload/owner.cjs +61 -0
  25. package/android-action/play-upload/owner_http.cjs +63 -0
  26. package/android-action/play-upload/owner_principal.cjs +15 -0
  27. package/android-action/play-upload/owner_record.cjs +63 -0
  28. package/android-action/play-upload/owner_run.cjs +62 -0
  29. package/android-action/play-upload/run.cjs +14 -3
  30. package/android-action/scripts/resolve_android.py +19 -1
  31. package/android-action/scripts/test_early_play_workflow.py +2 -2
  32. package/android-action/scripts/test_resolve_play_readiness.py +26 -0
  33. package/backend-action/.daemux-version +1 -1
  34. package/backend-action/remote_deploy.sh +5 -1
  35. package/package.json +1 -1
  36. package/templates/deploy.yml +8 -3
@@ -4,6 +4,9 @@
4
4
  from __future__ import annotations
5
5
 
6
6
  import os
7
+ import hashlib
8
+ import json
9
+ import re
7
10
  import shutil
8
11
  import sys
9
12
  from pathlib import Path
@@ -143,6 +146,21 @@ def main() -> None:
143
146
  print(f"Resolved {kind} Android package {package_name}, build {build_number}")
144
147
 
145
148
 
149
+ def owner_package() -> None:
150
+ """Resolve only local identity before the Node owner starts any provider operation."""
151
+ workspace = Path(os.environ.get("GITHUB_WORKSPACE", ".")).resolve()
152
+ kind = project_kind(workspace)
153
+ project = None if kind == "flutter" else find_gradle_project(workspace)
154
+ package = os.environ.get("INPUT_PACKAGE_NAME") or detect_package_name(
155
+ workspace, project.build_file if project else None)
156
+ account = find_play_service_account(workspace)
157
+ value = json.loads(account.read_text())
158
+ email = value.get("client_email")
159
+ if not isinstance(email, str) or not re.fullmatch(r"[^\s@]+@[^\s@]+", email):
160
+ raise ConfigError("Play principal identity is unavailable")
161
+ print(json.dumps({"package": package, "principal_sha256": hashlib.sha256(email.lower().encode()).hexdigest()}))
162
+
163
+
146
164
  def write_config(values: dict[str, str], signing) -> None:
147
165
  env_path = Path(os.environ["GITHUB_ENV"])
148
166
  output_path = Path(os.environ["GITHUB_OUTPUT"])
@@ -163,4 +181,4 @@ def write_config(values: dict[str, str], signing) -> None:
163
181
 
164
182
 
165
183
  if __name__ == "__main__":
166
- main()
184
+ owner_package() if sys.argv[1:] == ["--owner-package"] else main()
@@ -27,7 +27,7 @@ class EarlyPlayWorkflowTests(unittest.TestCase):
27
27
  script = "\n".join(line[10:] for line in step.splitlines())
28
28
  with tempfile.TemporaryDirectory() as tmp:
29
29
  root = Path(tmp)
30
- fake = root / "python3"
30
+ fake = root / "node"
31
31
  fake.write_text('#!/bin/sh\necho called >> "$TRACE"\necho ready=true >> "$GITHUB_OUTPUT"\n')
32
32
  fake.chmod(0o700)
33
33
  env = {**os.environ, "PATH": f"{root}:{os.environ['PATH']}", "EARLY_READINESS": readiness,
@@ -69,7 +69,7 @@ class EarlyPlayWorkflowTests(unittest.TestCase):
69
69
  self.assertEqual(result.returncode, 0, result.stderr)
70
70
  self.assertEqual(output, "ready=true\n")
71
71
  self.assertTrue(called)
72
- self.assertRegex(readiness_step(), r'--package "\$PACKAGE_NAME"\s*\\\s*--service-account "\$SERVICE_ACCOUNT"')
72
+ self.assertIn('preflight "$PACKAGE_NAME" "$SERVICE_ACCOUNT"', readiness_step())
73
73
 
74
74
  def test_invalid_output_cannot_silently_skip_preflight(self):
75
75
  result, output, called = self.run_step("unknown")
@@ -1,9 +1,11 @@
1
1
  """Store evidence must exist before configuration enables compilation or version writes."""
2
2
  import io
3
+ import hashlib
3
4
  import json
4
5
  import os
5
6
  from pathlib import Path
6
7
  import sys
8
+ import subprocess
7
9
  import tempfile
8
10
  import unittest
9
11
  from unittest import mock
@@ -43,6 +45,30 @@ class ResolvePlayReadinessTests(unittest.TestCase):
43
45
  self.assertIn("ANDROID_PLAY_API_READY=true\n", self.env_file.read_text())
44
46
  self.prepare.assert_called_once_with(self.root)
45
47
 
48
+ def test_owner_package_is_local_and_preserves_an_explicit_identity(self):
49
+ for path in (self.root / "creds").iterdir():
50
+ if path.suffix != ".json":
51
+ path.unlink()
52
+ resolve.owner_package()
53
+ expected = hashlib.sha256(b"fixture@example.invalid").hexdigest()
54
+ self.assertEqual(json.loads(sys.stdout.getvalue()), {"package": "com.example.app", "principal_sha256": expected})
55
+ self.read.assert_not_called()
56
+ self.prepare.assert_not_called()
57
+ self.assertFalse(self.env_file.exists())
58
+ self.assertFalse(self.outputs.exists())
59
+ with mock.patch.dict(os.environ, {"INPUT_PACKAGE_NAME": "org.owned.override"}):
60
+ resolve.owner_package()
61
+ self.assertEqual(json.loads(sys.stdout.getvalue().splitlines()[-1])["package"], "org.owned.override")
62
+ self.assertNotIn("fixture@example.invalid", sys.stdout.getvalue())
63
+
64
+ def test_actual_local_identity_child_emits_only_package_and_principal_hash(self):
65
+ result = subprocess.run([sys.executable, str(Path(resolve.__file__)), "--owner-package"],
66
+ capture_output=True, text=True, timeout=10, check=False)
67
+ self.assertEqual(result.returncode, 0, result.stderr)
68
+ self.assertEqual(json.loads(result.stdout), {"package": "com.example.app",
69
+ "principal_sha256": hashlib.sha256(b"fixture@example.invalid").hexdigest()})
70
+ self.assertNotIn("fixture@example.invalid", result.stdout + result.stderr)
71
+
46
72
  def test_absent_package_keeps_configuration_for_a_first_release_artifact(self):
47
73
  self.read.return_value = PlayInventory(False, None)
48
74
  resolve.main()
@@ -1 +1 @@
1
- 1.0.78
1
+ 1.0.80
@@ -107,11 +107,15 @@ fi
107
107
  # and the application together; a green deploy must mean the public API works.
108
108
  PUBLIC_HEALTHY=""
109
109
  for i in $(seq 1 15); do
110
- if curl -fsS "https://$DOMAIN$HEALTH" >/dev/null 2>&1; then
110
+ CURL_EXIT=0
111
+ HTTP_CODE="$(curl --disable --silent --output /dev/null --write-out '%{http_code}' \
112
+ --proto '=https' --connect-timeout 5 --max-time 10 "https://$DOMAIN$HEALTH")" || CURL_EXIT=$?
113
+ if [ "$CURL_EXIT" -eq 0 ] && [[ "$HTTP_CODE" =~ ^2[0-9]{2}$ ]]; then
111
114
  log "public https://$DOMAIN$HEALTH healthy (after ${i}x2s)"
112
115
  PUBLIC_HEALTHY=1
113
116
  break
114
117
  fi
118
+ log "public health not ready (curl=$CURL_EXIT, http=$HTTP_CODE, attempt=$i/15)"
115
119
  sleep 2
116
120
  done
117
121
  if [ -z "$PUBLIC_HEALTHY" ]; then
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gowalk-cicd",
3
- "version": "1.0.78",
3
+ "version": "1.0.80",
4
4
  "description": "Zero-config GitHub Actions delivery for iOS TestFlight and Android Google Play.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,6 +19,7 @@ on:
19
19
 
20
20
  permissions:
21
21
  contents: write
22
+ actions: read
22
23
  models: read
23
24
 
24
25
  concurrency:
@@ -233,6 +234,7 @@ jobs:
233
234
  name: Android Google Play
234
235
  env:
235
236
  GOOGLE_STORE_PROXY_URL: ${{ secrets.GOOGLE_STORE_PROXY_URL }}
237
+ PLAY_EDIT_BINDING: ${{ vars.APP_ROBOT_PLAY_EDIT_BINDING }}
236
238
  if: ${{ vars.DEPLOY_PLATFORMS != 'ios' }}
237
239
  runs-on: ubuntu-24.04
238
240
  timeout-minutes: 45
@@ -426,6 +428,8 @@ jobs:
426
428
  id: android
427
429
  if: ${{ steps.mode.outputs.mode == 'local' }}
428
430
  uses: ./.github/actions/android-app
431
+ env:
432
+ GITHUB_TOKEN: ${{ github.token }}
429
433
  with:
430
434
  # Tests belong to the CI gate, not the deploy path — same policy as
431
435
  # the iOS job above. Running the analyzer here breaks every deploy
@@ -467,6 +471,7 @@ jobs:
467
471
  PACKAGE_NAME: ${{ steps.android.outputs.package-name }}
468
472
  SERVICE_ACCOUNT: ${{ steps.android.outputs.play-service-account }}
469
473
  EARLY_READINESS: ${{ steps.android.outputs.play-api-ready }}
474
+ GITHUB_TOKEN: ${{ github.token }}
470
475
  run: |
471
476
  # Current actions verify readiness before compiling. Older action copies
472
477
  # have no output, so retain their standalone preflight compatibility.
@@ -475,9 +480,8 @@ jobs:
475
480
  '') ;;
476
481
  *) echo "::error::Invalid Android readiness output"; exit 1 ;;
477
482
  esac
478
- python3 .github/actions/android-app/scripts/play_preflight.py \
479
- --package "$PACKAGE_NAME" \
480
- --service-account "$SERVICE_ACCOUNT"
483
+ node .github/actions/android-app/play-upload/owner_run.cjs \
484
+ preflight "$PACKAGE_NAME" "$SERVICE_ACCOUNT"
481
485
  # The wrapper verifies the unchanged pinned official uploader and classifies
482
486
  # its immediate result. Only explicit expired/deleted edits retry normally;
483
487
  # only Play's explicit review-hold refusal changes the review flag.
@@ -487,6 +491,7 @@ jobs:
487
491
  uses: ./.github/actions/android-app/play-upload
488
492
  env:
489
493
  GOOGLE_STORE_PROXY_URL: ${{ secrets.GOOGLE_STORE_PROXY_URL }}
494
+ GITHUB_TOKEN: ${{ github.token }}
490
495
  with:
491
496
  serviceAccountJson: ${{ steps.android.outputs.play-service-account }}
492
497
  packageName: ${{ steps.android.outputs.package-name }}