gowalk-cicd 1.0.103 → 1.0.105

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 CHANGED
@@ -204,6 +204,8 @@ node /path/to/gowalk-cicd/bin/cli.mjs
204
204
  secret. The action writes it to host-side `.runtime.env` with mode 0600 and
205
205
  supplies it to Compose after the generated `.env`; never print its content.
206
206
  Backend failures emit `backend_deploy_failed` with the fixed runner/host phase and exit code.
207
+ Its message uses the exact schema/phase/exit envelope documented in README, without JSON braces
208
+ that multiline secret masking can erase. Never reconstruct masked fields or include arbitrary text.
207
209
  Native bundle/Pods failures emit `native_prepare_failed` with fixed refusal codes and observed
208
210
  signals. Unknown signals remain unclassified; never copy child output or commands into annotations.
209
211
  - The mobile workflow's feature-branch-only `backend-preview` job bootstraps a
@@ -262,6 +264,10 @@ node /path/to/gowalk-cicd/bin/cli.mjs
262
264
  Both Flutter configuration and the Pods helper use that bundle in their child process,
263
265
  preserving Podfile.lock and the parent job environment. Deliver the workflow wrapper and
264
266
  action helper together; action-only updates cannot repair earlier custom configuration steps.
267
+ Flutter `--config-only` can query Xcode and install Pods before returning. In the workflow,
268
+ generate Flutter inputs with `pub get`, run the locked `native_pods.py` preparation and
269
+ completed graph guard, then configure the release with its existing obfuscation/define flags.
270
+ The graph guard still applies when there is no Podfile. Preserve this order in custom CI.
265
271
  - Locked Pod installation retries once only when its own attempt reports curl 18 and a partial-transfer
266
272
  symptom. Both attempts share the existing 900-second budget and recheck Podfile, Podfile.lock and
267
273
  selected Ruby bundle inputs. TLS/auth/permission, checksum, version and deployment errors never retry.
package/README.md CHANGED
@@ -58,6 +58,12 @@ When adopting this update, retain the `native_bundle.py` configuration wrapper i
58
58
  custom workflows as well as updating the action files. Apps without a Gemfile keep
59
59
  their existing CocoaPods runtime.
60
60
 
61
+ Flutter `--config-only` can query Xcode and invoke `pod install`. The workflow first
62
+ runs `flutter pub get` to generate the Podfile's Flutter inputs, then installs locked
63
+ Pods and verifies the selected workspace before release configuration. Preserve this
64
+ order in customized validation and deployment workflows; updating action directories
65
+ alone cannot repair a configuration step that runs before the action.
66
+
61
67
  ## Flutter web release
62
68
 
63
69
  The conditional `deploy-web.yml` workflow runs on changes to Flutter source,
@@ -336,6 +342,10 @@ Native bundle and Pods failures emit `native_prepare_failed` with schema
336
342
  Backend failures emit `backend_deploy_failed` with schema `gowalk-cicd/backend-deploy-failed.v1`,
337
343
  the runner/host `phase` and `exit_code`. These receipts preserve the failed result and exclude
338
344
  private command output, commands, hosts and runtime credentials. Empty signals are unclassified.
345
+ The backend message is `gowalk-cicd/backend-deploy-failed.v1 phase=<phase> exit_code=<code>`:
346
+ fixed fields without JSON braces, which GitHub can mask when a multiline secret has brace-only lines.
347
+ Consumers validate the complete envelope, the known phase and exit code 1–255; older JSON messages
348
+ remain readable by the panel. Masked fields are never reconstructed and credential masking stays enabled.
339
349
  An interrupted locked Pod fetch can retry once within the existing 900-second install budget.
340
350
  `native_pods_retry` (`gowalk-cicd/native-pods-retry.v1`) reports `attempt:2` and `reason:partial_transfer`;
341
351
  successful preparation reports `pod_attempts`. Dependency locks, relay and TLS checks remain enforced.
@@ -1 +1 @@
1
- 1.0.103
1
+ 1.0.105
@@ -0,0 +1,107 @@
1
+ """Execute the shipped Flutter preparation shell with real guards and offline tool fixtures."""
2
+ import json
3
+ import os
4
+ from pathlib import Path
5
+ import subprocess
6
+ import sys
7
+ import tempfile
8
+ import textwrap
9
+ import unittest
10
+
11
+ import native_bundle
12
+ from test_native_dependency_guard import project
13
+
14
+ REPO = Path(__file__).resolve().parents[2]
15
+
16
+
17
+ class FlutterPreparationTests(unittest.TestCase):
18
+ def setUp(self):
19
+ temporary = tempfile.TemporaryDirectory(prefix="flutter-native-preparation-")
20
+ self.addCleanup(temporary.cleanup)
21
+ self.root = Path(temporary.name).resolve()
22
+ project(self.root, "ios/Runner.xcodeproj")
23
+ workspace = self.root / "ios/Runner.xcworkspace"
24
+ workspace.mkdir()
25
+ (workspace / "contents.xcworkspacedata").write_text(
26
+ '<Workspace><FileRef location="group:Runner.xcodeproj"/></Workspace>')
27
+ for name in ("Podfile", "Podfile.lock"):
28
+ (self.root / "ios" / name).write_text("fixture lock\n")
29
+ (self.root / "pubspec.yaml").write_text("name: fixture\n")
30
+ for argv in (["git", "init", "-q"], ["git", "add", "."],
31
+ ["git", "-c", "user.name=Fixture", "-c", "user.email=fixture@example.invalid",
32
+ "commit", "-qm", "Freeze native fixture"]):
33
+ subprocess.run(argv, cwd=self.root, capture_output=True, check=True)
34
+ actions = self.root / ".github/actions"
35
+ actions.mkdir(parents=True)
36
+ (actions / "swift-app").symlink_to(REPO / "action", target_is_directory=True)
37
+ (actions / "android-app").symlink_to(REPO / "android-action", target_is_directory=True)
38
+ self.env = {**os.environ, "PATH": str(self.root) + os.pathsep + os.environ["PATH"],
39
+ "GITHUB_WORKSPACE": str(self.root), "RUNNER_TEMP": str(self.root / "scratch"),
40
+ "ONBOARDING_PUBLIC_ORIGIN": "https://flow.example.invalid",
41
+ "GOOGLE_STORE_PROXY_URL": "http://fixture:PRIVATE_SENTINEL@127.0.0.1:9"}
42
+ self.install_tools()
43
+
44
+ def executable(self, name, source):
45
+ path = self.root / name
46
+ path.write_text("#!" + sys.executable + "\n" + textwrap.dedent(source))
47
+ path.chmod(0o700)
48
+
49
+ def install_tools(self):
50
+ self.executable("flutter", '''
51
+ import json,sys
52
+ from pathlib import Path
53
+ if sys.argv[1:3] == ["pub", "get"]:
54
+ folder=Path("ios/Flutter");folder.mkdir()
55
+ (folder/"Generated.xcconfig").write_text("generated")
56
+ elif sys.argv[1:3] == ["build", "ios"]:
57
+ Path("configuration.json").write_text(json.dumps(sys.argv[1:]))
58
+ assert Path("ios/pods-ready").is_file(), "Configuration reached before locked Pods"
59
+ else:
60
+ raise SystemExit(9)
61
+ ''')
62
+ self.executable("pod", '''
63
+ import json,os,sys
64
+ from pathlib import Path
65
+ assert Path("Flutter/Generated.xcconfig").is_file()
66
+ assert sys.argv[1:] == ["install", "--deployment", "--no-repo-update"]
67
+ assert os.environ["HTTPS_PROXY"].startswith("http://127.0.0.1:")
68
+ Path("pod-called").touch()
69
+ if os.environ.get("FAIL_PODS"):
70
+ raise SystemExit(7)
71
+ Path("pods-ready").touch()
72
+ ''')
73
+
74
+ def run_preparation(self):
75
+ body = (REPO / "templates/deploy.yml").read_text().split(
76
+ "- name: Prepare Flutter iOS project\n", 1)[1].split("\n - ", 1)[0]
77
+ script = textwrap.dedent(body.split(" run: |\n", 1)[1])
78
+ result = subprocess.run([sys.executable, native_bundle.__file__, "--folder", "ios",
79
+ "--proxy-helper", str(REPO / "android-action/scripts/play_store_proxy.py"),
80
+ "--", "bash", "--noprofile", "--norc", "-e", "-o", "pipefail", "-c", script],
81
+ cwd=self.root, env=self.env, capture_output=True, text=True, timeout=20)
82
+ self.assertNotIn("PRIVATE_SENTINEL", result.stdout + result.stderr)
83
+ return result
84
+
85
+ def test_locked_pods_precede_configuration_without_losing_release_arguments(self):
86
+ result = self.run_preparation()
87
+ self.assertEqual(result.returncode, 0, result.stdout)
88
+ self.assertTrue((self.root / "ios/pod-called").exists())
89
+ args = json.loads((self.root / "configuration.json").read_text())
90
+ for arg in ("--release", "--no-codesign", "--config-only", "--obfuscate",
91
+ "--split-debug-info=" + str(self.root / "scratch/dart-symbols/ios"),
92
+ "ONBOARDING_PUBLIC_ORIGIN=https://flow.example.invalid"):
93
+ self.assertIn(arg, args)
94
+ self.assertEqual((self.root / "ios/Podfile.lock").read_text(), "fixture lock\n")
95
+
96
+ def test_failed_locked_install_stops_before_flutter_configuration(self):
97
+ self.env["FAIL_PODS"] = "1"
98
+ self.assertNotEqual(self.run_preparation().returncode, 0)
99
+ self.assertTrue((self.root / "ios/pod-called").exists())
100
+ self.assertFalse((self.root / "configuration.json").exists())
101
+
102
+ def test_remote_package_graph_refuses_before_configuration_without_podfile(self):
103
+ (self.root / "ios/Podfile").unlink()
104
+ project(self.root, "ios/Runner.xcodeproj", {"remote": {"isa": "XCRemoteSwiftPackageReference"}})
105
+ self.assertNotEqual(self.run_preparation().returncode, 0)
106
+ self.assertFalse((self.root / "ios/pod-called").exists())
107
+ self.assertFalse((self.root / "configuration.json").exists())
@@ -1 +1 @@
1
- 1.0.103
1
+ 1.0.105
@@ -1 +1 @@
1
- 1.0.103
1
+ 1.0.105
@@ -8,8 +8,9 @@ backend_report_failure() {
8
8
  remote_setup|initialize_environment|compose_up|discover_port|local_health|configure_vhost|certificate|public_health) ;;
9
9
  *) phase=unclassified ;;
10
10
  esac
11
- printf '::error title=backend_deploy_failed::{"schema":"gowalk-cicd/backend-deploy-failed.v1",'\
12
- '"phase":"%s","exit_code":%s}\n' "$phase" "$status"
11
+ # Multiline JSON secrets can make GitHub mask standalone braces. Keep fixed evidence readable.
12
+ printf '::error title=backend_deploy_failed::gowalk-cicd/backend-deploy-failed.v1 phase=%s exit_code=%s\n' \
13
+ "$phase" "$status"
13
14
  return "$status"
14
15
  }
15
16
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gowalk-cicd",
3
- "version": "1.0.103",
3
+ "version": "1.0.105",
4
4
  "description": "Zero-config GitHub Actions delivery for iOS TestFlight and Android Google Play.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -149,6 +149,13 @@ jobs:
149
149
  ONBOARDING_PUBLIC_ORIGIN: ${{ vars.ONBOARDING_PUBLIC_ORIGIN }}
150
150
  run: |
151
151
  flutter pub get
152
+ # Even --config-only can query Xcode and run pod install. Pub generates
153
+ # the Flutter configuration needed by the Podfile without resolving Pods.
154
+ python3 .github/actions/swift-app/scripts/native_pods.py "$GITHUB_WORKSPACE" \
155
+ --container ios/Runner.xcworkspace \
156
+ --proxy-helper .github/actions/android-app/scripts/play_store_proxy.py
157
+ python3 .github/actions/swift-app/scripts/native_dependency_guard.py "$GITHUB_WORKSPACE" \
158
+ --container ios/Runner.xcworkspace
152
159
  # Flutter removed the synthetic `package:flutter_gen`, so apps that use
153
160
  # AppLocalizations now generate real sources. Nothing else generates them,
154
161
  # and analyze/compile fail on the missing import if we skip this.