gowalk-cicd 1.0.99 → 1.0.100

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
@@ -183,6 +183,11 @@ node /path/to/gowalk-cicd/bin/cli.mjs
183
183
  Save only on a cache miss with caching enabled, a real successful install and verified cache
184
184
  scope. Preserve resolved keys/paths and the official pinned restore/save programs. Failed or
185
185
  dry-run setup never publishes an SDK cache; pub dependency caching retains its job-end behavior.
186
+ - Exact `flutter-version` inputs may reuse previously resolved public release metadata before SDK
187
+ cache lookup. Metadata is scoped to platform, origin, channel, architecture and full version;
188
+ URL/pin/schema/release fields are checked before reuse. Blank, wildcard and version-file inputs
189
+ resolve online. Missing or invalid metadata falls back to the required proxied GET; missing
190
+ account transport still refuses even with a warm cache. SDK keys and checksum checks stay unchanged.
186
191
  - Gradle caches use explicit checksum-pinned restore/save actions, each optional and bounded
187
192
  to two minutes. Preserve setup-java's key inputs and absolute cache paths to reuse existing
188
193
  caches. Java setup, builds and store uploads remain mandatory; save runs after delivery.
@@ -1 +1 @@
1
- 1.0.99
1
+ 1.0.100
@@ -1 +1 @@
1
- 1.0.99
1
+ 1.0.100
@@ -97,12 +97,36 @@ runs:
97
97
  continue-on-error: true
98
98
  shell: bash
99
99
  run: node "$GITHUB_ACTION_PATH/../scripts/cache_scope.cjs" --snapshot
100
+ - name: Identify an exact Flutter release metadata cache
101
+ id: release-metadata
102
+ if: ${{ inputs.cache == 'true' && steps.scope.outcome == 'success' }}
103
+ continue-on-error: true
104
+ shell: bash
105
+ env:
106
+ GOWALK_FLUTTER_VERSION: ${{ inputs.flutter-version }}
107
+ GOWALK_FLUTTER_VERSION_FILE: ${{ inputs.flutter-version-file }}
108
+ GOWALK_FLUTTER_CHANNEL: ${{ inputs.channel }}
109
+ GOWALK_FLUTTER_ARCHITECTURE: ${{ inputs.architecture }}
110
+ run: python3 "$GITHUB_ACTION_PATH/../scripts/flutter_release_cache.py"
111
+ - name: Restore exact Flutter release metadata
112
+ id: cache-release
113
+ if: ${{ steps.release-metadata.outputs.enabled == 'true' }}
114
+ uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25
115
+ continue-on-error: true
116
+ env:
117
+ NODE_OPTIONS: --require "${{ steps.scope.outputs.preloader }}"
118
+ SEGMENT_DOWNLOAD_TIMEOUT_MINS: '1'
119
+ with:
120
+ path: ${{ steps.release-metadata.outputs.path }}
121
+ key: ${{ steps.release-metadata.outputs.key }}
100
122
  - name: Resolve Flutter release and cache identities
101
123
  id: flutter-action
102
124
  uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2
103
125
  env:
104
126
  BASH_ENV: ${{ steps.transport.outputs.manifest_env }}
105
127
  GOWALK_FLUTTER_MANIFEST_HELPER: ${{ steps.transport.outputs.manifest_helper }}
128
+ GOWALK_FLUTTER_RELEASE_CACHE: ${{ steps.release-metadata.outputs.path }}
129
+ GOWALK_FLUTTER_RELEASE_PIN: ${{ steps.release-metadata.outputs.pin }}
106
130
  with:
107
131
  channel: ${{ inputs.channel }}
108
132
  flutter-version: ${{ inputs.flutter-version }}
@@ -116,6 +140,17 @@ runs:
116
140
  pub-cache-path: ${{ inputs.pub-cache-path }}
117
141
  dry-run: 'true'
118
142
  git-source: ${{ inputs.git-source }}
143
+ - name: Save resolved Flutter release metadata
144
+ if: >-
145
+ ${{ steps.release-metadata.outputs.enabled == 'true' && steps.flutter-action.outcome == 'success'
146
+ && steps.cache-release.outputs.cache-hit != 'true' }}
147
+ uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25
148
+ continue-on-error: true
149
+ env:
150
+ NODE_OPTIONS: --require "${{ steps.scope.outputs.preloader }}"
151
+ with:
152
+ path: ${{ steps.release-metadata.outputs.path }}
153
+ key: ${{ steps.release-metadata.outputs.key }}
119
154
  - name: Cache Flutter
120
155
  id: cache-flutter
121
156
  if: ${{ inputs.cache == 'true' }}
@@ -159,6 +194,8 @@ runs:
159
194
  GOWALK_FLUTTER_MANIFEST_HELPER: ${{ steps.transport.outputs.manifest_helper }}
160
195
  GOWALK_FLUTTER_ARCHIVE_CACHE: >-
161
196
  ${{ inputs.cache == 'true' && format('{0}/gowalk-flutter-archives', runner.temp) || '' }}
197
+ GOWALK_FLUTTER_RELEASE_CACHE: ${{ steps.release-metadata.outputs.path }}
198
+ GOWALK_FLUTTER_RELEASE_PIN: ${{ steps.release-metadata.outputs.pin }}
162
199
  with:
163
200
  channel: ${{ steps.flutter-action.outputs.CHANNEL }}
164
201
  flutter-version: ${{ steps.flutter-action.outputs.VERSION }}
@@ -10,6 +10,7 @@ from urllib.parse import urlsplit
10
10
 
11
11
  import flutter_download
12
12
  import flutter_archive_cache
13
+ import flutter_release_cache
13
14
 
14
15
  RETRY_CODES = {5, 6, 7, 18, 28, 35, 52, 55, 56, 92, 95}
15
16
  RETRY_HTTP = {408, 429, 500, 502, 503, 504}
@@ -26,6 +27,8 @@ def download(arguments: list[str], directory: Path) -> int:
26
27
  destination = directory / "download"
27
28
  destination.touch(mode=0o600, exist_ok=True)
28
29
  destination.chmod(0o600)
30
+ if flutter_release_cache.reuse(arguments[-1], destination, kind):
31
+ return 0
29
32
  if flutter_archive_cache.reuse_complete(destination, kind):
30
33
  return 0
31
34
  attempted = 0
@@ -41,6 +44,7 @@ def download(arguments: list[str], directory: Path) -> int:
41
44
  curl_exit=code, http_status=http, proxy_status=proxy_http)
42
45
  if code == 0 and http in ({200, 206} if kind == "archive" else {200}):
43
46
  flutter_download.publish(destination, kind)
47
+ flutter_release_cache.remember(arguments[-1], destination, kind)
44
48
  if attempt > 1:
45
49
  print(f"flutter_{kind}_recovered attempts={attempt}", file=sys.stderr)
46
50
  return 0
@@ -0,0 +1,120 @@
1
+ """Optional public release metadata for exact Flutter pins; SDK identities remain the official action's."""
2
+ import hashlib
3
+ import json
4
+ import os
5
+ from pathlib import Path
6
+ import re
7
+ import sys
8
+ import tempfile
9
+
10
+ import flutter_download
11
+
12
+ MAX_BYTES = 16 * 1024 * 1024
13
+ EXACT_VERSION = re.compile(r"[0-9]+\.[0-9]+\.[0-9]+(?:[-+][0-9A-Za-z][0-9A-Za-z.-]*)?")
14
+
15
+
16
+ def valid_pin(value) -> bool:
17
+ return (isinstance(value, dict) and set(value) == {"version", "channel", "architecture"}
18
+ and isinstance(value["version"], str)
19
+ and bool(EXACT_VERSION.fullmatch(value["version"]))
20
+ and value["channel"] in ("stable", "beta", "dev")
21
+ and value["architecture"] in ("x64", "arm64"))
22
+
23
+
24
+ def context():
25
+ try:
26
+ pin = json.loads(os.environ.get("GOWALK_FLUTTER_RELEASE_PIN", "null"))
27
+ raw = os.environ.get("GOWALK_FLUTTER_RELEASE_CACHE")
28
+ if not raw or not valid_pin(pin):
29
+ return None
30
+ path = Path(raw)
31
+ parent = Path(os.environ["RUNNER_TEMP"]).resolve(strict=True) / "gowalk-flutter-manifests"
32
+ if (path.is_symlink() or path.parent.is_symlink() or path.parent.resolve() != parent
33
+ or not re.fullmatch(r"[a-f0-9]{64}\.json", path.name)):
34
+ return None
35
+ return path, pin
36
+ except (KeyError, OSError, ValueError, TypeError):
37
+ return None
38
+
39
+
40
+ def matches(manifest, pin) -> bool:
41
+ if not isinstance(manifest, dict) or not isinstance(manifest.get("releases"), list):
42
+ return False
43
+ rows = [row for row in manifest["releases"] if isinstance(row, dict)
44
+ and row.get("version") == pin["version"] and row.get("channel") == pin["channel"]
45
+ and (row.get("dart_sdk_arch") or "x64") == pin["architecture"]]
46
+ if len(rows) != 1:
47
+ return False
48
+ row = rows[0]
49
+ return (isinstance(row.get("archive"), str) and not row["archive"].startswith("/")
50
+ and ".." not in row["archive"] and "\\" not in row["archive"]
51
+ and bool(re.fullmatch(r"[a-fA-F0-9]{40}", str(row.get("hash", ""))))
52
+ and bool(re.fullmatch(r"[a-fA-F0-9]{64}", str(row.get("sha256", "")))))
53
+
54
+
55
+ def reuse(url: str, destination: Path, kind: str) -> bool:
56
+ selected = context() if kind == "manifest" else None
57
+ if not selected:
58
+ return False
59
+ path, pin = selected
60
+ try:
61
+ if not path.is_file() or path.stat().st_size > MAX_BYTES:
62
+ return False
63
+ record = json.loads(path.read_bytes())
64
+ if (not isinstance(record, dict) or record.get("schema") != 1 or record.get("url") != url
65
+ or record.get("pin") != pin or not matches(record.get("manifest"), pin)):
66
+ return False
67
+ destination.write_text(json.dumps(record["manifest"]))
68
+ flutter_download.publish(destination, kind)
69
+ print('::notice title=flutter_manifest_cache::{"schema":"gowalk-cicd/flutter-manifest-cache.v1",'
70
+ '"event":"hit"}', file=sys.stderr)
71
+ return True
72
+ except (OSError, ValueError, TypeError):
73
+ return False
74
+
75
+
76
+ def remember(url: str, destination: Path, kind: str) -> None:
77
+ selected = context() if kind == "manifest" else None
78
+ if not selected or destination.stat().st_size > MAX_BYTES:
79
+ return
80
+ path, pin = selected
81
+ temporary = None
82
+ try:
83
+ manifest = json.loads(destination.read_bytes())
84
+ if not matches(manifest, pin):
85
+ return
86
+ content = json.dumps({"schema": 1, "url": url, "pin": pin, "manifest": manifest}).encode()
87
+ if len(content) > MAX_BYTES:
88
+ return
89
+ path.parent.mkdir(mode=0o700, exist_ok=True)
90
+ with tempfile.NamedTemporaryFile(dir=path.parent, prefix="incoming-", delete=False) as output:
91
+ temporary = Path(output.name)
92
+ output.write(content)
93
+ os.replace(temporary, path)
94
+ except (OSError, ValueError, TypeError):
95
+ pass # This optional cache cannot turn a completed provider read into a failed setup.
96
+ finally:
97
+ if temporary:
98
+ try:
99
+ temporary.unlink(missing_ok=True)
100
+ except OSError:
101
+ pass
102
+
103
+
104
+ def prepare() -> None:
105
+ pin = {"version": os.environ.get("GOWALK_FLUTTER_VERSION", ""),
106
+ "channel": os.environ.get("GOWALK_FLUTTER_CHANNEL", ""),
107
+ "architecture": os.environ.get("GOWALK_FLUTTER_ARCHITECTURE", "").lower()}
108
+ with Path(os.environ["GITHUB_OUTPUT"]).open("a") as output:
109
+ if os.environ.get("GOWALK_FLUTTER_VERSION_FILE") or not valid_pin(pin):
110
+ output.write("enabled=false\n")
111
+ return
112
+ namespace = {"pin": pin, "os": os.environ["RUNNER_OS"],
113
+ "origin": os.environ.get("FLUTTER_STORAGE_BASE_URL", "https://storage.googleapis.com")}
114
+ digest = hashlib.sha256(json.dumps(namespace, sort_keys=True).encode()).hexdigest()
115
+ path = Path(os.environ["RUNNER_TEMP"]).resolve(strict=True) / "gowalk-flutter-manifests" / f"{digest}.json"
116
+ output.write(f"enabled=true\nkey=flutter-manifest-v1-{digest}\npath={path}\npin={json.dumps(pin)}\n")
117
+
118
+
119
+ if __name__ == "__main__":
120
+ prepare()
@@ -0,0 +1,103 @@
1
+ """Exact pinned setup survives a manifest outage after a verified resolution; misses stay proxied."""
2
+ import json
3
+ import os
4
+ from pathlib import Path
5
+ import subprocess
6
+ import tempfile
7
+ import unittest
8
+
9
+ from flutter_manifest_fixture import HOST, official_setup, tls_proxy
10
+
11
+ ROOT = Path(__file__).parent
12
+
13
+
14
+ class ReleaseCacheTests(unittest.TestCase):
15
+ @classmethod
16
+ def setUpClass(cls):
17
+ cls.source = official_setup()
18
+
19
+ def setUp(self):
20
+ scratch = tempfile.TemporaryDirectory()
21
+ self.addCleanup(scratch.cleanup)
22
+ self.root = Path(scratch.name)
23
+ self.setup = self.root / "setup.sh"
24
+ self.setup.write_bytes(self.source)
25
+ self.cache = self.root / "gowalk-flutter-manifests" / ("a" * 64 + ".json")
26
+
27
+ def invoke(self, server, cert, *, version="3.44.0", bound=True):
28
+ pin = {"version": version, "channel": "stable", "architecture": "x64"}
29
+ env = {**os.environ, "RUNNER_TEMP": str(self.root), "RUNNER_OS": "Linux", "RUNNER_ARCH": "X64",
30
+ "RUNNER_TOOL_CACHE": str(self.root / "tools"), "GITHUB_OUTPUT": str(self.root / "output"),
31
+ "PUB_CACHE": str(self.root / "pub"), "FLUTTER_STORAGE_BASE_URL": "https://" + HOST,
32
+ "BASH_ENV": str(ROOT / "flutter_manifest_env.sh"), "CURL_CA_BUNDLE": str(cert),
33
+ "GOWALK_FLUTTER_MANIFEST_HELPER": str(ROOT / "flutter_manifest.py"),
34
+ "GOWALK_FLUTTER_RELEASE_CACHE": str(self.cache), "GOWALK_FLUTTER_RELEASE_PIN": json.dumps(pin),
35
+ "HTTPS_PROXY": f"http://127.0.0.1:{server.server_address[1]}", "NO_PROXY": "", "no_proxy": ""}
36
+ env.pop("https_proxy", None)
37
+ if not bound:
38
+ env.pop("HTTPS_PROXY")
39
+ (self.root / "output").write_text("")
40
+ return subprocess.run(["bash", str(self.setup), "-p", "-n", version, "-a", "x64",
41
+ "-k", "same-:version:-:hash:", "stable"], env=env,
42
+ capture_output=True, text=True, timeout=15)
43
+
44
+ def test_warm_resolution_preserves_sdk_identity_without_another_manifest_connection(self):
45
+ with tls_proxy(self.root, ["ok", "origin_auth"]) as (server, cert):
46
+ cold = self.invoke(server, cert)
47
+ self.assertEqual(cold.returncode, 0, cold.stderr)
48
+ identity = (self.root / "output").read_text()
49
+ warm = self.invoke(server, cert)
50
+ self.assertEqual(warm.returncode, 0, warm.stderr)
51
+ self.assertEqual((server.connects, len(server.requests)), (1, 1))
52
+ self.assertEqual((self.root / "output").read_text(), identity)
53
+ self.assertIn("flutter_manifest_cache", warm.stderr)
54
+
55
+ def test_changed_pin_does_not_reuse_another_versions_manifest(self):
56
+ with tls_proxy(self.root, ["ok", "origin_auth"]) as (server, cert):
57
+ self.assertEqual(self.invoke(server, cert).returncode, 0)
58
+ changed = self.invoke(server, cert, version="3.44.1")
59
+ self.assertNotEqual(changed.returncode, 0)
60
+ self.assertEqual(server.connects, 2)
61
+ self.assertIn("http_status=401", changed.stderr)
62
+
63
+ def test_invalid_cache_is_recovered_through_the_assigned_proxy(self):
64
+ self.cache.parent.mkdir()
65
+ self.cache.write_text("truncated JSON")
66
+ with tls_proxy(self.root, ["ok"]) as (server, cert):
67
+ result = self.invoke(server, cert)
68
+ self.assertEqual(result.returncode, 0, result.stderr)
69
+ self.assertEqual(server.connects, 1)
70
+ self.assertEqual(json.loads(self.cache.read_text())["pin"]["version"], "3.44.0")
71
+
72
+ def test_warm_cache_never_bypasses_the_required_account_pin(self):
73
+ with tls_proxy(self.root, ["ok"]) as (server, cert):
74
+ self.assertEqual(self.invoke(server, cert).returncode, 0)
75
+ result = self.invoke(server, cert, bound=False)
76
+ self.assertNotEqual(result.returncode, 0)
77
+ self.assertEqual(server.connects, 1)
78
+
79
+ def test_wrong_origin_or_invalid_release_metadata_requires_a_fresh_provider_read(self):
80
+ for change in ["url", "sha256"]:
81
+ with self.subTest(change=change), tls_proxy(self.root, ["ok", "ok"]) as (server, cert):
82
+ self.cache.unlink(missing_ok=True)
83
+ self.assertEqual(self.invoke(server, cert).returncode, 0)
84
+ record = json.loads(self.cache.read_text())
85
+ if change == "url":
86
+ record["url"] = "https://another.fixture.invalid/releases_linux.json"
87
+ else:
88
+ record["manifest"]["releases"][0]["sha256"] = "invalid"
89
+ self.cache.write_text(json.dumps(record))
90
+ self.assertEqual(self.invoke(server, cert).returncode, 0)
91
+ self.assertEqual(server.connects, 2)
92
+
93
+ def test_wildcard_selection_still_resolves_a_fresh_manifest(self):
94
+ with tls_proxy(self.root, ["ok", "origin_auth"]) as (server, cert):
95
+ self.assertEqual(self.invoke(server, cert, version="3.44.x").returncode, 0)
96
+ result = self.invoke(server, cert, version="3.44.x")
97
+ self.assertNotEqual(result.returncode, 0)
98
+ self.assertEqual(server.connects, 2)
99
+ self.assertFalse(self.cache.exists())
100
+
101
+
102
+ if __name__ == "__main__":
103
+ unittest.main()
@@ -0,0 +1,49 @@
1
+ """Metadata cache selectors cannot freeze moving requests or cross platforms/origins."""
2
+ import json
3
+ import os
4
+ from pathlib import Path
5
+ import subprocess
6
+ import sys
7
+ import tempfile
8
+ import unittest
9
+
10
+ SCRIPT = Path(__file__).with_name("flutter_release_cache.py")
11
+
12
+
13
+ class CacheSelectionTests(unittest.TestCase):
14
+ def prepare(self, root, **changes):
15
+ output = root / "outputs"
16
+ output.write_text("")
17
+ env = {**os.environ, "GITHUB_OUTPUT": str(output), "RUNNER_TEMP": str(root), "RUNNER_OS": "Linux",
18
+ "GOWALK_FLUTTER_VERSION": "3.44.0", "GOWALK_FLUTTER_VERSION_FILE": "",
19
+ "GOWALK_FLUTTER_CHANNEL": "stable", "GOWALK_FLUTTER_ARCHITECTURE": "X64",
20
+ "FLUTTER_STORAGE_BASE_URL": "https://storage.googleapis.com", **changes}
21
+ result = subprocess.run([sys.executable, str(SCRIPT)], env=env, capture_output=True, timeout=10)
22
+ self.assertEqual((result.returncode, result.stdout, result.stderr), (0, b"", b""))
23
+ return dict(line.split("=", 1) for line in output.read_text().splitlines())
24
+
25
+ def test_exact_identity_is_stable_and_other_selections_have_separate_keys(self):
26
+ with tempfile.TemporaryDirectory() as directory:
27
+ root = Path(directory)
28
+ first = self.prepare(root)
29
+ self.assertEqual(first, self.prepare(root))
30
+ self.assertEqual(first["enabled"], "true")
31
+ self.assertEqual(json.loads(first["pin"])["architecture"], "x64")
32
+ for change in [{"RUNNER_OS": "macOS"}, {"GOWALK_FLUTTER_ARCHITECTURE": "ARM64"},
33
+ {"GOWALK_FLUTTER_CHANNEL": "beta"}, {"GOWALK_FLUTTER_VERSION": "3.44.1"},
34
+ {"FLUTTER_STORAGE_BASE_URL": "https://another.fixture.invalid"}]:
35
+ with self.subTest(change=change):
36
+ self.assertNotEqual(first["key"], self.prepare(root, **change)["key"])
37
+
38
+ def test_moving_or_file_resolved_versions_do_not_enable_metadata_reuse(self):
39
+ with tempfile.TemporaryDirectory() as directory:
40
+ root = Path(directory)
41
+ for change in [{"GOWALK_FLUTTER_VERSION": ""}, {"GOWALK_FLUTTER_VERSION": "3.x"},
42
+ {"GOWALK_FLUTTER_CHANNEL": "master"},
43
+ {"GOWALK_FLUTTER_VERSION_FILE": "pubspec.yaml"}]:
44
+ with self.subTest(change=change):
45
+ self.assertEqual(self.prepare(root, **change), {"enabled": "false"})
46
+
47
+
48
+ if __name__ == "__main__":
49
+ unittest.main()
@@ -9,6 +9,22 @@ ROOT = Path(__file__).resolve().parents[2]
9
9
 
10
10
 
11
11
  class BootstrapWiringTests(unittest.TestCase):
12
+ def assert_protected_setup(self, resolve, install):
13
+ for step in (resolve, install):
14
+ self.assertEqual(step["uses"], "subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2")
15
+ expected_env = {
16
+ "BASH_ENV": "${{ steps.transport.outputs.manifest_env }}",
17
+ "GOWALK_FLUTTER_MANIFEST_HELPER": "${{ steps.transport.outputs.manifest_helper }}",
18
+ "GOWALK_FLUTTER_RELEASE_CACHE": "${{ steps.release-metadata.outputs.path }}",
19
+ "GOWALK_FLUTTER_RELEASE_PIN": "${{ steps.release-metadata.outputs.pin }}"}
20
+ if step is install:
21
+ expected_env["GOWALK_FLUTTER_ARCHIVE_CACHE"] = (
22
+ "${{ inputs.cache == 'true' && format('{0}/gowalk-flutter-archives', runner.temp) || '' }}")
23
+ self.assertEqual(step["env"], expected_env)
24
+ self.assertEqual(step["with"]["cache"], "false")
25
+ self.assertEqual(step["with"]["pub-cache"], "false")
26
+ self.assertNotIn("continue-on-error", step)
27
+
12
28
  def test_each_flutter_sdk_setup_has_a_prior_mandatory_account_guard(self):
13
29
  checked = 0
14
30
  for name in ("deploy.yml", "deploy-web.yml"):
@@ -34,18 +50,7 @@ class BootstrapWiringTests(unittest.TestCase):
34
50
  self.assertTrue(scope["continue-on-error"])
35
51
  self.assertIn("--snapshot", scope["run"])
36
52
  self.assertEqual(resolve["with"]["dry-run"], "true")
37
- for step in (resolve, install):
38
- self.assertEqual(step["uses"], "subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2")
39
- expected_env = {
40
- "BASH_ENV": "${{ steps.transport.outputs.manifest_env }}",
41
- "GOWALK_FLUTTER_MANIFEST_HELPER": "${{ steps.transport.outputs.manifest_helper }}"}
42
- if step is install:
43
- expected_env["GOWALK_FLUTTER_ARCHIVE_CACHE"] = (
44
- "${{ inputs.cache == 'true' && format('{0}/gowalk-flutter-archives', runner.temp) || '' }}")
45
- self.assertEqual(step["env"], expected_env)
46
- self.assertEqual(step["with"]["cache"], "false")
47
- self.assertEqual(step["with"]["pub-cache"], "false")
48
- self.assertNotIn("continue-on-error", step)
53
+ self.assert_protected_setup(resolve, install)
49
54
  self.assertNotIn("BASH_ENV", setup.get("env", {}))
50
55
  for step in (guard, scope, sdk_cache, pub_cache, save):
51
56
  self.assertNotIn("BASH_ENV", step.get("env", {}))
@@ -1 +1 @@
1
- 1.0.99
1
+ 1.0.100
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gowalk-cicd",
3
- "version": "1.0.99",
3
+ "version": "1.0.100",
4
4
  "description": "Zero-config GitHub Actions delivery for iOS TestFlight and Android Google Play.",
5
5
  "type": "module",
6
6
  "bin": {