gowalk-cicd 1.0.69 → 1.0.70
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/action/.daemux-version +1 -1
- package/android-action/.daemux-version +1 -1
- package/android-action/action.yml +2 -1
- package/android-action/scripts/fixtures/flutter_test_loopback.dart +58 -0
- package/android-action/scripts/test_flutter_test_loopback.py +73 -0
- package/backend-action/.daemux-version +1 -1
- package/package.json +1 -1
package/action/.daemux-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.70
|
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.70
|
|
@@ -121,7 +121,8 @@ runs:
|
|
|
121
121
|
-- bash --noprofile --norc -e -o pipefail {0}
|
|
122
122
|
run: |
|
|
123
123
|
if [ -d test ]; then
|
|
124
|
-
|
|
124
|
+
# Apply only loopback exceptions after the shell's proxy wrapper; retain the exit for SDK traffic.
|
|
125
|
+
env no_proxy=localhost,127.0.0.1,::1 NO_PROXY=localhost,127.0.0.1,::1 flutter test --no-pub
|
|
125
126
|
else
|
|
126
127
|
echo "No test/ directory; skipping flutter test."
|
|
127
128
|
fi
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Owned sockets only: exercise Dart's real HTTP/WebSocket proxy behavior under the CI wrapper.
|
|
2
|
+
import 'dart:convert';
|
|
3
|
+
import 'dart:io';
|
|
4
|
+
|
|
5
|
+
Future<void> serve(HttpRequest request) async {
|
|
6
|
+
if (WebSocketTransformer.isUpgradeRequest(request)) {
|
|
7
|
+
final socket = await WebSocketTransformer.upgrade(request);
|
|
8
|
+
socket.listen(socket.add);
|
|
9
|
+
} else {
|
|
10
|
+
request.response.write('owned-loopback');
|
|
11
|
+
await request.response.close();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
void checkProviderRoutes() {
|
|
16
|
+
final expected = 'PROXY ${Uri.parse(Platform.environment['HTTPS_PROXY']!).authority}';
|
|
17
|
+
for (final host in ['storage.googleapis.com', 'pub.dev', 'oauth2.googleapis.com',
|
|
18
|
+
'firebaseappcheck.googleapis.com', 'api.appstoreconnect.apple.com']) {
|
|
19
|
+
if (HttpClient.findProxyFromEnvironment(Uri.parse('https://$host/a')) != expected) {
|
|
20
|
+
throw StateError('provider route bypassed');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
Future<void> main(List<String> args) async {
|
|
26
|
+
if (args.isEmpty || args.first != 'test' || !args.contains('--no-pub')) {
|
|
27
|
+
throw StateError('the rendered command must skip repeated dependency resolution');
|
|
28
|
+
}
|
|
29
|
+
final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
|
|
30
|
+
server.listen(serve);
|
|
31
|
+
final client = HttpClient()..connectionTimeout = const Duration(seconds: 3);
|
|
32
|
+
WebSocket? socket;
|
|
33
|
+
try {
|
|
34
|
+
checkProviderRoutes();
|
|
35
|
+
socket = await WebSocket.connect('ws://127.0.0.1:${server.port}/socket')
|
|
36
|
+
.timeout(const Duration(seconds: 3));
|
|
37
|
+
socket.add('owned-frame');
|
|
38
|
+
if (await socket.first.timeout(const Duration(seconds: 3)) != 'owned-frame') {
|
|
39
|
+
throw StateError('local frame was not echoed');
|
|
40
|
+
}
|
|
41
|
+
final local = await (await client.getUrl(Uri.parse('http://127.0.0.1:${server.port}/http'))).close();
|
|
42
|
+
if (await local.transform(utf8.decoder).join() != 'owned-loopback') {
|
|
43
|
+
throw StateError('local HTTP reached the exit');
|
|
44
|
+
}
|
|
45
|
+
final remote = await (await client.getUrl(Uri.parse('http://unresolvable.invalid/pub'))).close();
|
|
46
|
+
if (await remote.transform(utf8.decoder).join() != 'through pinned proxy') {
|
|
47
|
+
throw StateError('external HTTP did not reach the owned exit');
|
|
48
|
+
}
|
|
49
|
+
print(jsonEncode({'ok': true, 'loopback': ['http', 'websocket'], 'external': 'assigned_proxy'}));
|
|
50
|
+
} catch (error) {
|
|
51
|
+
print(jsonEncode({'ok': false, 'error': error.runtimeType.toString()}));
|
|
52
|
+
exitCode = 7;
|
|
53
|
+
} finally {
|
|
54
|
+
await socket?.close();
|
|
55
|
+
client.close(force: true);
|
|
56
|
+
await server.close(force: true);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""The rendered Flutter test step keeps external proxying while its owned sockets stay local."""
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import shlex
|
|
6
|
+
import subprocess
|
|
7
|
+
import tempfile
|
|
8
|
+
import unittest
|
|
9
|
+
|
|
10
|
+
import yaml
|
|
11
|
+
|
|
12
|
+
from test_jvm_proxy_relay import upstream
|
|
13
|
+
|
|
14
|
+
ROOT = Path(__file__).resolve().parents[2]
|
|
15
|
+
DART = os.environ.get("DART_SDK_BIN", "")
|
|
16
|
+
STEP = next(step for step in yaml.safe_load((ROOT / "android-action/action.yml").read_text())["runs"]["steps"]
|
|
17
|
+
if step["name"] == "Test Flutter app")
|
|
18
|
+
LOOPBACK = "env no_proxy=localhost,127.0.0.1,::1 NO_PROXY=localhost,127.0.0.1,::1 "
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def run_step(step, *, without_exception=False):
|
|
22
|
+
"""Run the real wrapper and rendered shell, substituting only a socket-owning Dart probe for Flutter."""
|
|
23
|
+
with tempfile.TemporaryDirectory() as directory, upstream() as (proxy, seen):
|
|
24
|
+
cwd = Path(directory)
|
|
25
|
+
(cwd / "test").mkdir()
|
|
26
|
+
(cwd / "test/probe_test.dart").write_text("// owned fixture\n")
|
|
27
|
+
subprocess.run(["git", "init", "-q", directory], check=True)
|
|
28
|
+
subprocess.run(["git", "add", "test"], cwd=cwd, check=True)
|
|
29
|
+
probe = ROOT / "android-action/scripts/fixtures/flutter_test_loopback.dart"
|
|
30
|
+
flutter = cwd / "flutter"
|
|
31
|
+
flutter.write_text(f'#!/bin/sh\nexec {shlex.quote(DART)} --disable-dart-dev {shlex.quote(str(probe))} "$@"\n')
|
|
32
|
+
flutter.chmod(0o700)
|
|
33
|
+
script = cwd / "step.sh"
|
|
34
|
+
run = step["run"].replace(LOOPBACK, "") if without_exception else step["run"]
|
|
35
|
+
script.write_text(run)
|
|
36
|
+
command = step["shell"].replace("${{ github.action_path }}", str(ROOT / "android-action"))
|
|
37
|
+
command = shlex.split(command.replace("{0}", str(script)))
|
|
38
|
+
env = {**os.environ, "GOOGLE_STORE_PROXY_URL": proxy, "PATH": str(cwd) + os.pathsep + os.environ["PATH"],
|
|
39
|
+
"NO_PROXY": "*", "no_proxy": "*", "HTTPS_PROXY": "http://wrong.invalid:1"}
|
|
40
|
+
result = subprocess.run(command, cwd=cwd, env=env, capture_output=True, text=True, timeout=15)
|
|
41
|
+
return result, seen
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class FlutterLoopbackTests(unittest.TestCase):
|
|
45
|
+
def test_exception_is_command_scoped_inside_the_validated_wrapper(self):
|
|
46
|
+
self.assertIn("play_store_proxy.py -- bash", STEP["shell"])
|
|
47
|
+
command = next(line.strip() for line in STEP["run"].splitlines() if line.strip().startswith("env "))
|
|
48
|
+
self.assertEqual(shlex.split(command), ["env", "no_proxy=localhost,127.0.0.1,::1",
|
|
49
|
+
"NO_PROXY=localhost,127.0.0.1,::1", "flutter", "test", "--no-pub"])
|
|
50
|
+
self.assertNotIn("env", STEP)
|
|
51
|
+
self.assertNotRegex(STEP["run"], r"(?:unset|env\s+-u)")
|
|
52
|
+
|
|
53
|
+
@unittest.skipUnless(DART and Path(DART).is_file(), "set DART_SDK_BIN to an installed native Dart executable")
|
|
54
|
+
def test_real_dart_http_and_websocket_survive_while_external_http_uses_the_exit(self):
|
|
55
|
+
result, seen = run_step(STEP)
|
|
56
|
+
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
|
57
|
+
self.assertEqual(json.loads(result.stdout),
|
|
58
|
+
{"ok": True, "loopback": ["http", "websocket"], "external": "assigned_proxy"})
|
|
59
|
+
self.assertEqual([(method, url) for method, url, _auth in seen],
|
|
60
|
+
[("GET", "http://unresolvable.invalid/pub")])
|
|
61
|
+
self.assertNotIn("fixture-pass", result.stdout + result.stderr)
|
|
62
|
+
|
|
63
|
+
@unittest.skipUnless(DART and Path(DART).is_file(), "set DART_SDK_BIN to an installed native Dart executable")
|
|
64
|
+
def test_original_empty_bypass_routes_the_websocket_to_the_exit_and_fails(self):
|
|
65
|
+
result, seen = run_step(STEP, without_exception=True)
|
|
66
|
+
self.assertEqual(result.returncode, 7, result.stdout + result.stderr)
|
|
67
|
+
self.assertEqual(json.loads(result.stdout), {"ok": False, "error": "WebSocketException"})
|
|
68
|
+
self.assertEqual(len(seen), 1)
|
|
69
|
+
self.assertRegex(seen[0][1], r"^http://127\.0\.0\.1:\d+/socket$")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
if __name__ == "__main__":
|
|
73
|
+
unittest.main()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.70
|