diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..641116e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# release.py runs "git add -A"; nothing here belongs in a release commit. +__pycache__/ diff --git a/README.md b/README.md index 293f8b0..0b17b4f 100644 --- a/README.md +++ b/README.md @@ -22,10 +22,13 @@ It reads the version and build number out of `amber-adult/pubspec.yaml`, copies the APK in under a versioned name, writes `manifest.json`, and commits. Push, and Coolify redeploys. -**The APKs are committed.** At ~34MB and a handful of releases a year that is -the cheaper trade against standing up artifact storage for one app; if the -repository ever gets uncomfortable, the history is disposable — only the -newest artifact is ever fetched. +**The APKs are committed**, and only the newest `KEEP_BUILDS` (3) of each +kind are kept — the release script deletes the rest. At ~34MB each that is the +cheaper trade against standing up artifact storage for one app, but only while +the working tree stays small: every artifact in `public/` is also copied into +the image nginx serves, so a release used to build a 1.3GB image. Trimming +does not shrink `.git`, which still holds every past build; that history is +disposable, since only the newest artifact is ever fetched. ## Downloading by hand @@ -35,9 +38,10 @@ newest artifact is ever fetched. | Linux x64 | `https://media-updates.petruzalekr.cz/latest-linux.tar.gz` | Both are symlinks the release script repoints, so the addresses never change. -Every past build stays reachable under its own `media-.apk` / -`media-linux-.tar.gz` name, which is what lets a device midway through a -download survive a release. +The last three builds of each kind stay reachable under their own +`media-.apk` / `media-linux-.tar.gz` names, which is what lets a +device midway through a download survive a release. Older ones are gone from +`public/` and answer 404. The Linux tarball unpacks to `amber-adult/`; run `./amber-adult/amber_adult`. **It needs libmpv on the system** — media_kit loads it at runtime rather than diff --git a/release.py b/release.py index 2441e3e..c40e937 100755 --- a/release.py +++ b/release.py @@ -17,6 +17,12 @@ import tarfile HERE = pathlib.Path(__file__).resolve().parent PUBSPEC = HERE.parent / "amber-adult" / "pubspec.yaml" BASE_URL = "https://media-updates.petruzalekr.cz" +# How many builds of each kind stay in the repository. A device midway through +# a download has to be able to finish it, so the newest is never published +# alone; past that they are only weight, because nothing ever asks for an older +# build on purpose. Each one is ~34MB in git, again in the image nginx serves, +# and again in every clone of the repository. +KEEP_BUILDS = 3 def pubspec_version() -> tuple[str, int]: @@ -27,6 +33,27 @@ def pubspec_version() -> tuple[str, int]: return m.group(1), int(m.group(2)) +def prune_old(public: pathlib.Path, prefix: str, suffix: str, + keep: int) -> list[str]: + """Delete all but the `keep` newest `` artifacts. + + The `latest` symlinks point at the build just staged, which is the highest + numbered one, so they survive this by construction. + """ + builds = {} + for path in public.glob(f"{prefix}*{suffix}"): + middle = path.name[len(prefix):len(path.name) - len(suffix)] + # Excludes the other kind: "media-*.apk" would otherwise match a + # "media-linux-*" name, whose middle is not a number. + if middle.isdigit(): + builds[int(middle)] = path + dropped = [] + for build in sorted(builds)[:-keep]: + dropped.append(builds[build].name) + builds[build].unlink() + return dropped + + def main() -> None: args = sys.argv[1:] # Linux is optional and off by default. It is another ~34MB in the @@ -96,6 +123,15 @@ def main() -> None: tar_latest.symlink_to(tar_name) print(f"staged {tar_name}") + # After the new artifacts are in place, so the build just staged counts + # towards the window it is kept by. Each kind is counted separately: Linux + # is published rarely, and its symlink has to keep resolving through the + # releases that skip it. + dropped = (prune_old(public, "media-", ".apk", KEEP_BUILDS) + + prune_old(public, "media-linux-", ".tar.gz", KEEP_BUILDS)) + if dropped: + print(f"dropped {', '.join(dropped)}") + subprocess.run(["git", "add", "-A"], cwd=HERE, check=True) subprocess.run( ["git", "commit", "-m", f"Release {version}+{build}"],