gowalk-cicd 1.0.59 → 1.0.60
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 +5 -0
- package/action/.daemux-version +1 -1
- package/android-action/.daemux-version +1 -1
- package/android-action/action.yml +9 -10
- package/android-action/scripts/play_store_proxy.py +50 -2
- package/android-action/scripts/test_play_store_proxy.py +41 -0
- package/backend-action/.daemux-version +1 -1
- package/package.json +1 -1
- package/templates/deploy.yml +10 -4
package/CLAUDE.md
CHANGED
|
@@ -96,6 +96,11 @@ node /path/to/gowalk-cicd/bin/cli.mjs
|
|
|
96
96
|
- Keep the CLI surface minimal: `--dry-run`, `-v`, `-h`. Resist adding flags.
|
|
97
97
|
- `.gitignore` in the consumer repo is git-tracked. Never auto-edit it.
|
|
98
98
|
Detect and warn, tell the user to remove lines manually.
|
|
99
|
+
- Never repeat an `env:` key in another case (`NO_PROXY` beside `no_proxy`): GitHub
|
|
100
|
+
compares env keys case-insensitively and rejects the whole workflow or action file.
|
|
101
|
+
`test/env-keys.test.mjs` scans the templates and actions. Set lower-case spellings
|
|
102
|
+
or clear a lower-case bypass through `play_store_proxy.py -- <command>` (child
|
|
103
|
+
process) or `play_store_proxy.py --github-env` (later steps), never through YAML.
|
|
99
104
|
- Store API calls belong in GitHub Actions. Local tests may build/sign but must
|
|
100
105
|
never connect to App Store Connect or Google Play.
|
|
101
106
|
- Every Apple/Google store API call, OAuth exchange and binary upload requires its
|
package/action/.daemux-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.60
|
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.60
|
|
@@ -128,17 +128,16 @@ runs:
|
|
|
128
128
|
if: ${{ steps.config.outputs.project_kind == 'flutter' }}
|
|
129
129
|
shell: bash
|
|
130
130
|
env:
|
|
131
|
+
# Only one spelling per key: GitHub compares env keys case-insensitively and
|
|
132
|
+
# rejects the file otherwise. play_store_proxy.py -- runs the build with both
|
|
133
|
+
# spellings set and both NO_PROXY spellings cleared for the child process.
|
|
131
134
|
HTTPS_PROXY: ${{ env.GOOGLE_STORE_PROXY_URL }}
|
|
132
135
|
HTTP_PROXY: ${{ env.GOOGLE_STORE_PROXY_URL }}
|
|
133
|
-
https_proxy: ${{ env.GOOGLE_STORE_PROXY_URL }}
|
|
134
|
-
http_proxy: ${{ env.GOOGLE_STORE_PROXY_URL }}
|
|
135
136
|
NO_PROXY: ''
|
|
136
|
-
no_proxy: ''
|
|
137
137
|
BUILD_NAME: ${{ inputs.build-name }}
|
|
138
138
|
DART_DEFINES: ${{ inputs.dart-defines }}
|
|
139
139
|
DART_SYMBOLS_DIR: ${{ runner.temp }}/dart-symbols/android
|
|
140
140
|
run: |
|
|
141
|
-
python3 "${{ github.action_path }}/scripts/play_store_proxy.py"
|
|
142
141
|
args=(
|
|
143
142
|
build appbundle
|
|
144
143
|
--release
|
|
@@ -158,7 +157,7 @@ runs:
|
|
|
158
157
|
*) echo "::error::dart-defines entry '$define' is not KEY=VALUE"; exit 1 ;;
|
|
159
158
|
esac
|
|
160
159
|
done
|
|
161
|
-
flutter "${args[@]}"
|
|
160
|
+
python3 "${{ github.action_path }}/scripts/play_store_proxy.py" -- flutter "${args[@]}"
|
|
162
161
|
if ! ls "$DART_SYMBOLS_DIR"/*.symbols >/dev/null 2>&1; then
|
|
163
162
|
echo "::error::flutter build wrote no Dart symbol files to $DART_SYMBOLS_DIR;" \
|
|
164
163
|
"a crash from this obfuscated build could never be read. Refusing to ship it."
|
|
@@ -234,17 +233,17 @@ runs:
|
|
|
234
233
|
shell: bash
|
|
235
234
|
working-directory: ${{ steps.config.outputs.gradle_root || '.' }}
|
|
236
235
|
env:
|
|
236
|
+
# Only one spelling per key: GitHub compares env keys case-insensitively and
|
|
237
|
+
# rejects the file otherwise. play_store_proxy.py -- runs the build with both
|
|
238
|
+
# spellings set and both NO_PROXY spellings cleared for the child process.
|
|
237
239
|
HTTPS_PROXY: ${{ env.GOOGLE_STORE_PROXY_URL }}
|
|
238
240
|
HTTP_PROXY: ${{ env.GOOGLE_STORE_PROXY_URL }}
|
|
239
|
-
https_proxy: ${{ env.GOOGLE_STORE_PROXY_URL }}
|
|
240
|
-
http_proxy: ${{ env.GOOGLE_STORE_PROXY_URL }}
|
|
241
241
|
NO_PROXY: ''
|
|
242
|
-
no_proxy: ''
|
|
243
242
|
ANDROID_BUILD_NAME: ${{ inputs.build-name }}
|
|
244
243
|
run: |
|
|
245
244
|
chmod +x ./gradlew
|
|
246
|
-
python3 "${{ github.action_path }}/scripts/play_store_proxy.py"
|
|
247
|
-
|
|
245
|
+
python3 "${{ github.action_path }}/scripts/play_store_proxy.py" -- \
|
|
246
|
+
./gradlew "${ANDROID_GRADLE_MODULE}:bundleRelease" \
|
|
248
247
|
--init-script "${{ github.action_path }}/scripts/version_override.init.gradle" \
|
|
249
248
|
--console=plain --stacktrace
|
|
250
249
|
|
|
@@ -1,11 +1,27 @@
|
|
|
1
|
-
"""Google CI transport uses only the account-pinned proxy, including OAuth exchanges.
|
|
1
|
+
"""Google CI transport uses only the account-pinned proxy, including OAuth exchanges.
|
|
2
|
+
|
|
3
|
+
Three entry points, all from the one validated ``GOOGLE_STORE_PROXY_URL``:
|
|
4
|
+
|
|
5
|
+
* ``python3 play_store_proxy.py`` refuses a missing or malformed proxy.
|
|
6
|
+
* ``python3 play_store_proxy.py -- <command …>`` runs a build tool with the explicit
|
|
7
|
+
proxy in both spellings and both ``NO_PROXY`` spellings cleared. Setting the pairs
|
|
8
|
+
as YAML ``env`` keys is not an option: GitHub compares env keys case-insensitively
|
|
9
|
+
and rejects the workflow or action file as invalid.
|
|
10
|
+
* ``python3 play_store_proxy.py --github-env`` clears the lower-case ambient bypass
|
|
11
|
+
(``no_proxy``) for the rest of the job, so a JavaScript upload step that can only
|
|
12
|
+
carry upper-case ``env`` keys cannot be bypassed by a runner's ambient value.
|
|
13
|
+
"""
|
|
2
14
|
from __future__ import annotations
|
|
3
15
|
|
|
4
16
|
import os
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
5
19
|
from urllib.parse import urlsplit
|
|
6
20
|
|
|
7
21
|
import requests
|
|
8
22
|
|
|
23
|
+
LOWERCASE_BYPASS = ("no_proxy",)
|
|
24
|
+
|
|
9
25
|
|
|
10
26
|
def proxy_url() -> str:
|
|
11
27
|
value = os.environ.get("GOOGLE_STORE_PROXY_URL", "").strip()
|
|
@@ -33,5 +49,37 @@ def session() -> requests.Session:
|
|
|
33
49
|
return client
|
|
34
50
|
|
|
35
51
|
|
|
36
|
-
|
|
52
|
+
def run(argv: list[str]) -> int:
|
|
53
|
+
"""Run ``argv`` with the explicit proxy environment; the parent process is untouched."""
|
|
54
|
+
if not argv:
|
|
55
|
+
raise SystemExit("usage: play_store_proxy.py -- <command …>")
|
|
56
|
+
env = environment()
|
|
57
|
+
return subprocess.run(argv, env=env, check=False).returncode
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def clear_github_env_bypass(path: str | None = None) -> list[str]:
|
|
61
|
+
"""Append empty lower-case bypass assignments to ``$GITHUB_ENV`` for later steps."""
|
|
37
62
|
proxy_url()
|
|
63
|
+
target = path or os.environ.get("GITHUB_ENV", "")
|
|
64
|
+
if not target:
|
|
65
|
+
raise SystemExit("GITHUB_ENV is not set; run this inside a GitHub Actions step")
|
|
66
|
+
lines = [f"{name}=" for name in LOWERCASE_BYPASS]
|
|
67
|
+
with open(target, "a", encoding="utf-8") as handle:
|
|
68
|
+
handle.write("".join(line + "\n" for line in lines))
|
|
69
|
+
return lines
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def main(args: list[str]) -> int:
|
|
73
|
+
if args[:1] == ["--github-env"]:
|
|
74
|
+
clear_github_env_bypass()
|
|
75
|
+
return 0
|
|
76
|
+
if args[:1] == ["--"]:
|
|
77
|
+
return run(args[1:])
|
|
78
|
+
if args:
|
|
79
|
+
raise SystemExit("usage: play_store_proxy.py [--github-env | -- <command …>]")
|
|
80
|
+
proxy_url()
|
|
81
|
+
return 0
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -35,6 +35,47 @@ class PlayProxyTests(unittest.TestCase):
|
|
|
35
35
|
self.assertFalse(client.trust_env)
|
|
36
36
|
self.assertEqual(client.proxies, {"http": value, "https": value})
|
|
37
37
|
|
|
38
|
+
def test_exec_form_gives_the_child_both_spellings_and_no_bypass(self):
|
|
39
|
+
value = "http://assigned.proxy.test:1234"
|
|
40
|
+
probe = ("import os, json; print(json.dumps({k: os.environ.get(k) for k in "
|
|
41
|
+
"['HTTPS_PROXY', 'https_proxy', 'HTTP_PROXY', 'http_proxy', 'NO_PROXY', 'no_proxy']}))")
|
|
42
|
+
with mock.patch.dict(os.environ, {"GOOGLE_STORE_PROXY_URL": value, "no_proxy": "*", "NO_PROXY": "*"}), \
|
|
43
|
+
mock.patch.object(play_store_proxy.subprocess, "run") as run:
|
|
44
|
+
run.return_value.returncode = 0
|
|
45
|
+
self.assertEqual(play_store_proxy.run([sys.executable, "-c", probe]), 0)
|
|
46
|
+
child = run.call_args.kwargs["env"]
|
|
47
|
+
self.assertEqual(child["https_proxy"], value)
|
|
48
|
+
self.assertEqual(child["HTTP_PROXY"], value)
|
|
49
|
+
self.assertEqual(child["no_proxy"], "")
|
|
50
|
+
self.assertEqual(child["NO_PROXY"], "")
|
|
51
|
+
self.assertEqual(os.environ["no_proxy"], "*")
|
|
52
|
+
|
|
53
|
+
def test_exec_form_refuses_without_a_proxy_before_running_anything(self):
|
|
54
|
+
with mock.patch.dict(os.environ, {"GOOGLE_STORE_PROXY_URL": ""}), \
|
|
55
|
+
mock.patch.object(play_store_proxy.subprocess, "run") as run:
|
|
56
|
+
with self.assertRaises(SystemExit):
|
|
57
|
+
play_store_proxy.run(["true"])
|
|
58
|
+
run.assert_not_called()
|
|
59
|
+
|
|
60
|
+
def test_github_env_mode_clears_only_the_lowercase_bypass_for_later_steps(self):
|
|
61
|
+
import tempfile
|
|
62
|
+
value = "http://assigned.proxy.test:1234"
|
|
63
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
64
|
+
target = os.path.join(tmp, "github.env")
|
|
65
|
+
with mock.patch.dict(os.environ, {"GOOGLE_STORE_PROXY_URL": value, "GITHUB_ENV": target, "no_proxy": "*"}):
|
|
66
|
+
self.assertEqual(play_store_proxy.clear_github_env_bypass(), ["no_proxy="])
|
|
67
|
+
self.assertEqual(os.environ["no_proxy"], "*")
|
|
68
|
+
with open(target, encoding="utf-8") as handle:
|
|
69
|
+
self.assertEqual(handle.read(), "no_proxy=\n")
|
|
70
|
+
|
|
71
|
+
def test_github_env_mode_refuses_without_a_proxy_or_target(self):
|
|
72
|
+
with mock.patch.dict(os.environ, {"GOOGLE_STORE_PROXY_URL": "", "GITHUB_ENV": "/nonexistent"}):
|
|
73
|
+
with self.assertRaises(SystemExit):
|
|
74
|
+
play_store_proxy.clear_github_env_bypass()
|
|
75
|
+
with mock.patch.dict(os.environ, {"GOOGLE_STORE_PROXY_URL": "http://assigned.proxy.test:1234", "GITHUB_ENV": ""}):
|
|
76
|
+
with self.assertRaises(SystemExit):
|
|
77
|
+
play_store_proxy.clear_github_env_bypass()
|
|
78
|
+
|
|
38
79
|
|
|
39
80
|
if __name__ == "__main__":
|
|
40
81
|
unittest.main()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.60
|
package/package.json
CHANGED
package/templates/deploy.yml
CHANGED
|
@@ -453,6 +453,14 @@ jobs:
|
|
|
453
453
|
# Note this also means nothing else should be creating edits against these
|
|
454
454
|
# packages while a deploy runs — an external poller that opens and deletes an
|
|
455
455
|
# edit will invalidate the one this step is holding.
|
|
456
|
+
# The upload action is JavaScript, so its proxy can only be the step's own
|
|
457
|
+
# upper-case env keys below. Node clients fall back to a lower-case no_proxy
|
|
458
|
+
# when NO_PROXY is empty, and GitHub refuses both spellings in one env map, so
|
|
459
|
+
# the ambient lower-case bypass is cleared through GITHUB_ENV instead.
|
|
460
|
+
- name: Clear ambient lower-case proxy bypass
|
|
461
|
+
if: ${{ steps.mode.outputs.mode == 'local' && steps.play.outputs.ready == 'true' }}
|
|
462
|
+
shell: bash
|
|
463
|
+
run: python3 .github/actions/android-app/scripts/play_store_proxy.py --github-env
|
|
456
464
|
- name: Upload Android release to Google Play
|
|
457
465
|
id: play-upload
|
|
458
466
|
continue-on-error: true
|
|
@@ -461,8 +469,7 @@ jobs:
|
|
|
461
469
|
env:
|
|
462
470
|
HTTPS_PROXY: ${{ secrets.GOOGLE_STORE_PROXY_URL }}
|
|
463
471
|
HTTP_PROXY: ${{ secrets.GOOGLE_STORE_PROXY_URL }}
|
|
464
|
-
NO_PROXY: ''
|
|
465
|
-
no_proxy: ''
|
|
472
|
+
NO_PROXY: '' # one spelling only: GitHub rejects env keys that differ by case
|
|
466
473
|
with:
|
|
467
474
|
serviceAccountJson: ${{ steps.android.outputs.play-service-account }}
|
|
468
475
|
packageName: ${{ steps.android.outputs.package-name }}
|
|
@@ -490,8 +497,7 @@ jobs:
|
|
|
490
497
|
env:
|
|
491
498
|
HTTPS_PROXY: ${{ secrets.GOOGLE_STORE_PROXY_URL }}
|
|
492
499
|
HTTP_PROXY: ${{ secrets.GOOGLE_STORE_PROXY_URL }}
|
|
493
|
-
NO_PROXY: ''
|
|
494
|
-
no_proxy: ''
|
|
500
|
+
NO_PROXY: '' # one spelling only: GitHub rejects env keys that differ by case
|
|
495
501
|
with:
|
|
496
502
|
serviceAccountJson: ${{ steps.android.outputs.play-service-account }}
|
|
497
503
|
packageName: ${{ steps.android.outputs.package-name }}
|