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.
- package/CLAUDE.md +33 -3
- package/README.md +22 -18
- package/action/.daemux-version +1 -1
- package/action/action.yml +48 -38
- package/action/scripts/auto_detect.py +8 -8
- package/action/scripts/commit_bot_changes.sh +3 -29
- package/action/scripts/native_dependency_guard.py +59 -0
- package/action/scripts/native_pods.py +97 -0
- package/action/scripts/native_pods_diagnostics.py +53 -0
- package/action/scripts/native_pods_process.py +93 -0
- package/action/scripts/native_project_graph.py +148 -0
- package/action/scripts/source_maintenance.py +93 -0
- package/action/scripts/source_maintenance_fixture.py +58 -0
- package/action/scripts/test_auto_detect.py +11 -0
- package/action/scripts/test_native_dependency_guard.py +116 -0
- package/action/scripts/test_native_dependency_wiring.py +59 -0
- package/action/scripts/test_native_pods.py +106 -0
- package/action/scripts/test_native_pods_diagnostics.py +39 -0
- package/action/scripts/test_native_pods_process.py +41 -0
- package/action/scripts/test_source_maintenance.py +122 -0
- package/android-action/.daemux-version +1 -1
- package/android-action/action.yml +3 -1
- package/android-action/play-upload/action.yml +1 -1
- package/android-action/play-upload/owner.cjs +61 -0
- package/android-action/play-upload/owner_http.cjs +63 -0
- package/android-action/play-upload/owner_principal.cjs +15 -0
- package/android-action/play-upload/owner_record.cjs +63 -0
- package/android-action/play-upload/owner_run.cjs +62 -0
- package/android-action/play-upload/run.cjs +14 -3
- package/android-action/scripts/resolve_android.py +19 -1
- package/android-action/scripts/test_early_play_workflow.py +2 -2
- package/android-action/scripts/test_resolve_play_readiness.py +26 -0
- package/backend-action/.daemux-version +1 -1
- package/backend-action/remote_deploy.sh +5 -1
- package/package.json +1 -1
- 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 / "
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
package/templates/deploy.yml
CHANGED
|
@@ -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
|
-
|
|
479
|
-
|
|
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 }}
|