gowalk-cicd 1.0.95 → 1.0.96
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 -2
- package/README.md +5 -3
- package/action/.daemux-version +1 -1
- package/action/action.yml +9 -13
- package/action/scripts/autoupdate_check.sh +8 -65
- package/action/scripts/autoupdate_stage.py +62 -0
- package/action/scripts/test_autoupdate_stage.py +107 -0
- package/android-action/.daemux-version +1 -1
- package/backend-action/.daemux-version +1 -1
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -155,7 +155,7 @@ node /path/to/gowalk-cicd/bin/cli.mjs
|
|
|
155
155
|
Unknown code or origins refuse; SDK setup, non-cache children and later steps retain their
|
|
156
156
|
proxy requirement. Never export this scope through `GITHUB_ENV` or add cache domains to
|
|
157
157
|
`NO_PROXY`. The preloader snapshot under `RUNNER_TEMP` also covers official post/save after
|
|
158
|
-
consumer autoupdate changes the action directory. The installer delivers this development
|
|
158
|
+
older consumer autoupdate changes the action directory. The installer delivers this development
|
|
159
159
|
tooling for native iOS repositories too; it does not require an Android app.
|
|
160
160
|
- Flutter manifest/archive GET recovery is scoped to the two pinned setup invocations through `BASH_ENV`.
|
|
161
161
|
It retains the account proxy and allows four attempts within 60 seconds for manifests or 20 minutes
|
|
@@ -309,7 +309,10 @@ node /path/to/gowalk-cicd/bin/cli.mjs
|
|
|
309
309
|
from `${{ github.action_path }}` after that step. The plugin self-update
|
|
310
310
|
(`autoupdate_check.sh`) runs right after the snapshot and preserves its own source candidate,
|
|
311
311
|
before the version/signing/archive steps, so a red deploy still receives the
|
|
312
|
-
release that fixes it
|
|
312
|
+
release that fixes it. `autoupdate_stage.py` installs and preserves that update in one
|
|
313
|
+
task-owned detached worktree, then removes it. The active build's HEAD, index, dirty files
|
|
314
|
+
and action manifests stay unchanged: GitHub reparses local composites for post hooks using
|
|
315
|
+
cached step IDs, so a script snapshot alone cannot protect a changed manifest's step count.
|
|
313
316
|
`test_action_step_order.py` pins both. A new script reference in
|
|
314
317
|
`action/action.yml` therefore uses `"$SWIFT_APP_ACTION/scripts/..."`.
|
|
315
318
|
- Delivery dependencies may remain after a green job; each is a typed
|
package/README.md
CHANGED
|
@@ -335,8 +335,10 @@ into its current task branch and land it through the normal checked PR, includin
|
|
|
335
335
|
when later deploy steps fail. CI never pushes to the default branch or rebases its
|
|
336
336
|
dirty build checkout; a failed preservation is a job failure. The run that
|
|
337
337
|
performed the update keeps executing the version it started with: the action
|
|
338
|
-
snapshots
|
|
339
|
-
|
|
338
|
+
snapshots its scripts to `$RUNNER_TEMP`. Updates install and publish from a separate,
|
|
339
|
+
owned detached worktree that is removed afterward. The active checkout's action
|
|
340
|
+
manifests remain unchanged too: GitHub reloads local composites for post hooks using
|
|
341
|
+
previously cached step IDs, so changing their step count can crash cleanup.
|
|
340
342
|
|
|
341
343
|
### Google Play upload retry
|
|
342
344
|
|
|
@@ -1011,7 +1013,7 @@ The vendored action ships with a per-run autoupdate check. On every
|
|
|
1011
1013
|
push to your default branch, the action queries npm for the latest
|
|
1012
1014
|
`gowalk-cicd`, compares against the local marker at
|
|
1013
1015
|
`.github/actions/swift-app/.daemux-version`, and if newer, re-vendors
|
|
1014
|
-
the package via `npx --yes` and preserves
|
|
1016
|
+
the package via `npx --yes` in a temporary detached worktree and preserves its action files
|
|
1015
1017
|
on a verified task branch (under both `.github/actions/swift-app/` and
|
|
1016
1018
|
`.github/actions/android-app/`).
|
|
1017
1019
|
`.github/workflows/deploy.yml` is NEVER auto-committed — see
|
package/action/.daemux-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.96
|
package/action/action.yml
CHANGED
|
@@ -179,7 +179,7 @@ inputs:
|
|
|
179
179
|
description: >
|
|
180
180
|
Per-run check for a newer gowalk-cicd on npm. When 'true'
|
|
181
181
|
(default), the action queries npm on default-branch push runs and
|
|
182
|
-
|
|
182
|
+
installs the package in a detached worktree via `npx --yes` if its version is
|
|
183
183
|
newer than the marker at .github/actions/swift-app/.daemux-version.
|
|
184
184
|
The refreshed action files (NOT deploy.yml, which GITHUB_TOKEN
|
|
185
185
|
cannot push) are retained on a verified task branch for the app session's
|
|
@@ -190,14 +190,9 @@ inputs:
|
|
|
190
190
|
runs:
|
|
191
191
|
using: composite
|
|
192
192
|
steps:
|
|
193
|
-
#
|
|
194
|
-
#
|
|
195
|
-
#
|
|
196
|
-
# local-action consumers — while this run is still executing the
|
|
197
|
-
# action.yml the runner loaded at job start. Reading scripts from the
|
|
198
|
-
# refreshed tree under the old step definitions is a mid-run version
|
|
199
|
-
# mix; one copy taken before anything mutates keeps the whole run on
|
|
200
|
-
# one version of the plugin.
|
|
193
|
+
# Keep every script and prompt on the version this invocation started with.
|
|
194
|
+
# Auto-update installs and publishes from a detached worktree; it never changes
|
|
195
|
+
# action manifests that the runner may reparse using already-cached step IDs.
|
|
201
196
|
- name: Snapshot this action for the whole run
|
|
202
197
|
shell: bash
|
|
203
198
|
run: |
|
|
@@ -233,6 +228,8 @@ runs:
|
|
|
233
228
|
mkdir -p "$RUNNER_TEMP/swift-app-bot-scripts"
|
|
234
229
|
cp "$SWIFT_APP_ACTION/scripts/autoupdate_check.sh" \
|
|
235
230
|
"$RUNNER_TEMP/swift-app-bot-scripts/"
|
|
231
|
+
cp "$SWIFT_APP_ACTION/scripts/autoupdate_stage.py" \
|
|
232
|
+
"$RUNNER_TEMP/swift-app-bot-scripts/"
|
|
236
233
|
cp "$SWIFT_APP_ACTION/scripts/commit_bot_changes.sh" \
|
|
237
234
|
"$RUNNER_TEMP/swift-app-bot-scripts/"
|
|
238
235
|
cp "$SWIFT_APP_ACTION/scripts/source_maintenance.py" \
|
|
@@ -243,8 +240,8 @@ runs:
|
|
|
243
240
|
- name: Check for app-ci update
|
|
244
241
|
# Default branch + push event only. `auto-update` is on by default;
|
|
245
242
|
# consumers can pin via `false`. Runs from $BOT_SCRIPTS_DIR
|
|
246
|
-
# (RUNNER_TEMP)
|
|
247
|
-
#
|
|
243
|
+
# (RUNNER_TEMP); installation and candidate preservation run in an isolated
|
|
244
|
+
# checkout so current action manifests and scripts remain unchanged.
|
|
248
245
|
if: ${{ inputs.auto-update == 'true'
|
|
249
246
|
&& github.event_name == 'push'
|
|
250
247
|
&& github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
|
@@ -521,8 +518,7 @@ runs:
|
|
|
521
518
|
# this action; this script is a no-op when nothing is staged, so a
|
|
522
519
|
# PR / feature-branch run that somehow reaches this step does no
|
|
523
520
|
# harm — but we still gate on default-branch push for clarity.
|
|
524
|
-
# Runs from $BOT_SCRIPTS_DIR
|
|
525
|
-
# ${action_path} earlier in this run and must not swap scripts under us.
|
|
521
|
+
# Runs from the same immutable $BOT_SCRIPTS_DIR as the update candidate.
|
|
526
522
|
if: ${{ (inputs.archive == 'true' || inputs.auto-update == 'true')
|
|
527
523
|
&& github.event_name == 'push'
|
|
528
524
|
&& github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Checks npm for newer gowalk-cicd
|
|
3
|
-
#
|
|
2
|
+
# Checks npm for newer gowalk-cicd and preserves an isolated update candidate.
|
|
3
|
+
# Exits 0 on no-op or package-install failure; unconfirmed source preservation fails.
|
|
4
4
|
#
|
|
5
5
|
# DESIGN DECISION (intentional, not a bug):
|
|
6
6
|
# This script runs `npx --yes gowalk-cicd@<latest>` on every default-
|
|
@@ -53,66 +53,9 @@ if [[ "$current" == "$latest" ]]; then
|
|
|
53
53
|
exit 0
|
|
54
54
|
fi
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
git config user.name >/dev/null 2>&1 || git config user.name "github-actions[bot]"
|
|
64
|
-
git config user.email >/dev/null 2>&1 || \
|
|
65
|
-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
66
|
-
|
|
67
|
-
# NOTE: We deliberately do NOT stage .github/workflows/deploy.yml here.
|
|
68
|
-
# GITHUB_TOKEN cannot push workflow-file changes regardless of
|
|
69
|
-
# `permissions: contents: write` (GH security policy: prevents CI from
|
|
70
|
-
# modifying its own triggers). Consumer-side deploy.yml updates require
|
|
71
|
-
# manual `npx --yes gowalk-cicd` by the user. Document this in
|
|
72
|
-
# the action README and changelog when shipping a deploy.yml schema change.
|
|
73
|
-
|
|
74
|
-
# Stage everything under the vendored action dir EXCEPT Python bytecode
|
|
75
|
-
# caches. The action's own scripts run during this CI job and Python
|
|
76
|
-
# generates `__pycache__/*.pyc` files inside scripts/ as a side effect of
|
|
77
|
-
# importing them. Those caches are build artifacts of *this* run, not part
|
|
78
|
-
# of the action distribution -- sweeping them into the autoupdate bot
|
|
79
|
-
# commit is benign but noisy (every clean run emits a "refresh
|
|
80
|
-
# __pycache__" diff).
|
|
81
|
-
#
|
|
82
|
-
# DESIGN DECISION: use `git add -A` (stages additions, modifications, AND
|
|
83
|
-
# deletions) instead of the previous `git ls-files -mo` flow. `ls-files
|
|
84
|
-
# -mo` enumerates modified + other (untracked) paths but NOT deletions:
|
|
85
|
-
# when an autoupdate cycle removes a script (e.g. `prepare_signing.py`
|
|
86
|
-
# gets renamed to `prepare_signing_v2.py` in a new release), the old
|
|
87
|
-
# file would stay tracked in the consumer repo forever -- the bot
|
|
88
|
-
# commit would add the new file but never remove the orphan. `git add
|
|
89
|
-
# -A` handles all three change kinds in one call. We then unstage any
|
|
90
|
-
# pycache paths that the broad `-A` swept in.
|
|
91
|
-
#
|
|
92
|
-
# Force flag (`-f`) is implicit in `git add -A` for explicitly listed
|
|
93
|
-
# pathspecs that match .gitignore patterns? No -- `-A` still honors
|
|
94
|
-
# .gitignore for untracked files. To preserve the prior semantics
|
|
95
|
-
# (consumer's .gitignore must not silently drop autoupdate-managed
|
|
96
|
-
# files like `.daemux-version`), we pass `--force` explicitly.
|
|
97
|
-
#
|
|
98
|
-
# BSD xargs (default on macos-15 runners) does NOT support
|
|
99
|
-
# `-r`/`--no-run-if-empty`, so we capture pycache paths into a variable
|
|
100
|
-
# and skip the xargs call when empty -- portable across BSD and GNU.
|
|
101
|
-
git add -A --force -- \
|
|
102
|
-
.github/actions/swift-app/ \
|
|
103
|
-
.github/actions/android-app/ 2>/dev/null || true
|
|
104
|
-
|
|
105
|
-
# Unstage Python bytecode that the broad `-A` swept in. `git diff
|
|
106
|
-
# --cached --name-only` lists every staged path; we filter to pycache
|
|
107
|
-
# entries and `git reset HEAD --` to drop them from the index. Working
|
|
108
|
-
# tree is left untouched (the .pyc files remain on disk; only their
|
|
109
|
-
# index entries are removed).
|
|
110
|
-
pycache="$(git diff --cached --name-only -- \
|
|
111
|
-
.github/actions/swift-app/ .github/actions/android-app/ \
|
|
112
|
-
| grep -E '(/__pycache__/|\.pyc$)' || true)"
|
|
113
|
-
if [[ -n "$pycache" ]]; then
|
|
114
|
-
printf '%s\n' "$pycache" \
|
|
115
|
-
| tr '\n' '\0' \
|
|
116
|
-
| xargs -0 git reset HEAD -- 2>/dev/null || true
|
|
117
|
-
fi
|
|
118
|
-
echo "autoupdate: staged refreshed iOS + Android actions (deploy.yml not staged; __pycache__ excluded)"
|
|
56
|
+
# The runner reparses local action.yml files for post hooks and reuses the step IDs
|
|
57
|
+
# it cached before execution. Updating these files in place can change their step
|
|
58
|
+
# count and crash cleanup. Publish the update candidate from a detached worktree;
|
|
59
|
+
# the current job keeps its exact manifests, scripts, index and uncommitted source.
|
|
60
|
+
echo "autoupdate: $PKG $current -> $latest; preparing an isolated source candidate"
|
|
61
|
+
exec python3 "$(dirname "$0")/autoupdate_stage.py" "$latest"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Prepare CI updates away from action manifests the current runner may reload."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import re
|
|
7
|
+
import shutil
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
import tempfile
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def git(root: Path, *args: str) -> str:
|
|
14
|
+
result = subprocess.run(["git", *args], cwd=root, capture_output=True,
|
|
15
|
+
text=True, check=False, timeout=60)
|
|
16
|
+
if result.returncode:
|
|
17
|
+
raise RuntimeError("autoupdate_worktree_git_failed")
|
|
18
|
+
return result.stdout.strip()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def stage(root: Path) -> None:
|
|
22
|
+
git(root, "add", "-A", "--force", "--", ".github/actions/swift-app/", ".github/actions/android-app/")
|
|
23
|
+
names = git(root, "diff", "--cached", "--name-only", "-z").split("\0")
|
|
24
|
+
bytecode = [name for name in names if "/__pycache__/" in name or name.endswith((".pyc", ".pyo"))]
|
|
25
|
+
if bytecode:
|
|
26
|
+
git(root, "reset", "--quiet", "HEAD", "--", *bytecode)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def update(version: str) -> None:
|
|
30
|
+
if not re.fullmatch(r"\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?(?:\+[A-Za-z0-9.-]+)?", version):
|
|
31
|
+
raise RuntimeError("autoupdate_version_invalid")
|
|
32
|
+
root = Path.cwd()
|
|
33
|
+
base = git(root, "rev-parse", "HEAD")
|
|
34
|
+
parent = Path(tempfile.mkdtemp(prefix="app-ci-update-", dir=os.environ["RUNNER_TEMP"]))
|
|
35
|
+
worktree = parent / "checkout"
|
|
36
|
+
added = False
|
|
37
|
+
try:
|
|
38
|
+
git(root, "worktree", "add", "--detach", str(worktree), base)
|
|
39
|
+
added = True
|
|
40
|
+
result = subprocess.run(["npx", "--yes", "gowalk-cicd@" + version], cwd=worktree,
|
|
41
|
+
check=False, timeout=180)
|
|
42
|
+
if result.returncode:
|
|
43
|
+
print("::warning::autoupdate: pinned package installation failed; keeping the current build")
|
|
44
|
+
return
|
|
45
|
+
stage(worktree)
|
|
46
|
+
script = Path(__file__).with_name("commit_bot_changes.sh")
|
|
47
|
+
result = subprocess.run(["bash", str(script)], cwd=worktree, check=False, timeout=180)
|
|
48
|
+
if result.returncode:
|
|
49
|
+
raise RuntimeError("autoupdate_source_preservation_failed")
|
|
50
|
+
finally:
|
|
51
|
+
if added:
|
|
52
|
+
git(root, "worktree", "remove", "--force", str(worktree))
|
|
53
|
+
shutil.rmtree(parent)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
try:
|
|
58
|
+
update(sys.argv[1])
|
|
59
|
+
except (RuntimeError, OSError, subprocess.SubprocessError) as exc:
|
|
60
|
+
code = str(exc) if isinstance(exc, RuntimeError) else "autoupdate_operation_unverified"
|
|
61
|
+
print("::error::" + code, file=sys.stderr)
|
|
62
|
+
raise SystemExit(1) from None
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Exercise real isolated installs and protected-remote publication without changing a live action."""
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import re
|
|
4
|
+
import subprocess
|
|
5
|
+
import textwrap
|
|
6
|
+
import unittest
|
|
7
|
+
|
|
8
|
+
from source_maintenance_fixture import MaintenanceFixture
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AutoupdateTests(MaintenanceFixture):
|
|
12
|
+
def setUp(self):
|
|
13
|
+
super().setUp()
|
|
14
|
+
self.remote_base = self.base
|
|
15
|
+
self.action = self.repo / ".github/actions/android-app/flutter-setup/action.yml"
|
|
16
|
+
self.marker = self.repo / ".github/actions/swift-app/.daemux-version"
|
|
17
|
+
self.action.parent.mkdir(parents=True)
|
|
18
|
+
self.marker.parent.mkdir(parents=True)
|
|
19
|
+
self.action.write_text("runs:\n using: composite\n steps:\n - run: old\n shell: bash\n")
|
|
20
|
+
self.marker.write_text("1.2.3")
|
|
21
|
+
self.git("add", ".github")
|
|
22
|
+
self.git("commit", "-m", "Existing action")
|
|
23
|
+
self.base = self.git("rev-parse", "HEAD").strip()
|
|
24
|
+
self.env["GITHUB_SHA"] = self.base
|
|
25
|
+
config = self.root / "checkout-auth.config"
|
|
26
|
+
self.git("config", "--file", str(config), "fixture.token", "worktree-allowed")
|
|
27
|
+
self.git("config", "includeIf.gitdir:" + str((self.repo / ".git").resolve()) + "/worktrees/*.path",
|
|
28
|
+
str(config))
|
|
29
|
+
self.scratch = self.root / "runner-temp"
|
|
30
|
+
self.scratch.mkdir()
|
|
31
|
+
self.env["RUNNER_TEMP"] = str(self.scratch)
|
|
32
|
+
binary = self.root / "bin"
|
|
33
|
+
binary.mkdir()
|
|
34
|
+
(binary / "npm").write_text("#!/bin/sh\nprintf '1.2.4\\n'\n")
|
|
35
|
+
(binary / "npm").chmod(0o755)
|
|
36
|
+
self.installer = binary / "npx"
|
|
37
|
+
self.installer.write_text("#!/bin/sh\nset -eu\n"
|
|
38
|
+
"[ \"$1\" = --yes ] && [ \"$2\" = gowalk-cicd@1.2.4 ]\n"
|
|
39
|
+
"[ \"$(git config --get fixture.token)\" = worktree-allowed ]\n"
|
|
40
|
+
"printf '1.2.4' > .github/actions/swift-app/.daemux-version\n"
|
|
41
|
+
"printf ' - run: added\\n shell: bash\\n' >> .github/actions/android-app/flutter-setup/action.yml\n"
|
|
42
|
+
"mkdir -p .github/workflows .github/actions/swift-app/scripts/__pycache__\n"
|
|
43
|
+
"printf 'never publish workflow' > .github/workflows/deploy.yml\n"
|
|
44
|
+
"printf 'never publish bytecode' > .github/actions/swift-app/scripts/__pycache__/test.pyc\n"
|
|
45
|
+
"exit ${INSTALL_EXIT:-0}\n")
|
|
46
|
+
self.installer.chmod(0o755)
|
|
47
|
+
self.env["PATH"] = str(binary) + ":" + self.env["PATH"]
|
|
48
|
+
self.script = Path(__file__).with_name("autoupdate_check.sh")
|
|
49
|
+
|
|
50
|
+
def assert_current_build_preserved(self, original, staged):
|
|
51
|
+
self.assertEqual(self.action.read_bytes(), original)
|
|
52
|
+
self.assertEqual(self.marker.read_text(), "1.2.3")
|
|
53
|
+
self.assertEqual(self.git("diff", "--cached"), staged)
|
|
54
|
+
self.assertEqual(self.git("rev-parse", "HEAD").strip(), self.base)
|
|
55
|
+
self.assertEqual(self.git("ls-remote", "origin", "refs/heads/main").split()[0], self.remote_base)
|
|
56
|
+
self.assertEqual(len(self.git("worktree", "list", "--porcelain").split("worktree ")) - 1, 1)
|
|
57
|
+
self.assertEqual(list(self.scratch.iterdir()), [])
|
|
58
|
+
|
|
59
|
+
def test_growing_composite_and_replay_preserve_current_build_and_publish_exact_update(self):
|
|
60
|
+
self.stage()
|
|
61
|
+
original, staged = self.action.read_bytes(), self.git("diff", "--cached")
|
|
62
|
+
first = self.receipt(self.run_script())
|
|
63
|
+
self.assert_current_build_preserved(original, staged)
|
|
64
|
+
updated = self.git("show", first["head_sha"] + ":.github/actions/android-app/flutter-setup/action.yml")
|
|
65
|
+
self.assertEqual(updated.count("- run:"), 2)
|
|
66
|
+
self.assertEqual(original.decode().count("- run:"), 1)
|
|
67
|
+
paths = self.git("diff", "--name-only", self.base, first["head_sha"]).splitlines()
|
|
68
|
+
self.assertEqual(paths, [".github/actions/android-app/flutter-setup/action.yml",
|
|
69
|
+
".github/actions/swift-app/.daemux-version"])
|
|
70
|
+
self.assertEqual(self.receipt(self.run_script()), first)
|
|
71
|
+
self.assert_current_build_preserved(original, staged)
|
|
72
|
+
|
|
73
|
+
def test_failed_partial_install_keeps_current_action_and_cleans_owned_worktree(self):
|
|
74
|
+
self.stage()
|
|
75
|
+
original, staged = self.action.read_bytes(), self.git("diff", "--cached")
|
|
76
|
+
self.env["INSTALL_EXIT"] = "1"
|
|
77
|
+
result = self.run_script()
|
|
78
|
+
self.assertEqual(result.returncode, 0, result.stderr)
|
|
79
|
+
self.assertIn("pinned package installation failed", result.stdout)
|
|
80
|
+
self.assertNotIn("source_maintenance_pending", result.stdout)
|
|
81
|
+
self.assert_current_build_preserved(original, staged)
|
|
82
|
+
self.assertEqual(len(self.git("ls-remote", "--heads", "origin").splitlines()), 1)
|
|
83
|
+
|
|
84
|
+
def test_unconfirmed_preservation_is_not_a_success(self):
|
|
85
|
+
self.stage()
|
|
86
|
+
original, staged = self.action.read_bytes(), self.git("diff", "--cached")
|
|
87
|
+
self.hook.write_text("#!/bin/sh\nexit 1\n")
|
|
88
|
+
result = self.run_script()
|
|
89
|
+
self.assertNotEqual(result.returncode, 0)
|
|
90
|
+
self.assertIn("autoupdate_source_preservation_failed", result.stderr)
|
|
91
|
+
self.assert_current_build_preserved(original, staged)
|
|
92
|
+
|
|
93
|
+
def test_actual_action_snapshot_can_execute_the_update_helper(self):
|
|
94
|
+
action = Path(__file__).resolve().parents[1]
|
|
95
|
+
source = (action / "action.yml").read_text()
|
|
96
|
+
block = re.search(r" - name: Stage bot-side scripts to runner temp\n(.*?)(?=\n - name:)",
|
|
97
|
+
source, re.S)[1]
|
|
98
|
+
script = textwrap.dedent(block.split(" run: |\n", 1)[1])
|
|
99
|
+
self.env.update({"SWIFT_APP_ACTION": str(action), "GITHUB_ENV": str(self.root / "environment")})
|
|
100
|
+
subprocess.run(["bash", "-e", "-c", script], env=self.env, cwd=self.repo, check=True,
|
|
101
|
+
text=True, capture_output=True, timeout=10)
|
|
102
|
+
self.script = self.scratch / "swift-app-bot-scripts/autoupdate_check.sh"
|
|
103
|
+
self.assertEqual(self.receipt(self.run_script())["base_sha"], self.base)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
if __name__ == "__main__":
|
|
107
|
+
unittest.main()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.96
|
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.96
|