From 37815236da475d6a1bb9f897759893daf58fccaa Mon Sep 17 00:00:00 2001 From: Tobias Fischer Date: Sat, 10 Oct 2026 12:26:52 +1000 Subject: [PATCH 1/2] PR builds and generation skip what is already on the upload channel While uploads go to a test channel (ROBOSTACK_UPLOAD_CHANNEL), PR builds and the build-branch generation only skipped packages on the distribution's release channel, so every PR rebuilt the packages main had already built and uploaded there (jazzy linux-64: 72 recipes, all on the test channel). The upload channel now counts as published too; without the variable nothing changes. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/main.yaml | 2 ++ .github/workflows/testpr.yaml | 2 ++ tools/robostack.py | 5 ++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 1b8f7c56..1ddd9d87 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -61,6 +61,8 @@ jobs: distro: ${{ fromJSON(needs.plan.outputs.distros) }} env: DISTRO: ${{ matrix.distro }} + # while uploads go to a test channel, packages already there aren't built again + ROBOSTACK_UPLOAD_CHANNEL: ${{ vars.ROBOSTACK_UPLOAD_CHANNEL }} DRY_RUN: ${{ inputs.dry_run || 'false' }} steps: - name: Bot token (pushing workflow files needs more than GITHUB_TOKEN) diff --git a/.github/workflows/testpr.yaml b/.github/workflows/testpr.yaml index a80528b1..7c35df72 100644 --- a/.github/workflows/testpr.yaml +++ b/.github/workflows/testpr.yaml @@ -84,6 +84,8 @@ jobs: env: DISTRO: ${{ matrix.distro }} PLATFORM: ${{ matrix.target.platform }} + # while uploads go to a test channel, packages already there aren't built again + ROBOSTACK_UPLOAD_CHANNEL: ${{ vars.ROBOSTACK_UPLOAD_CHANNEL }} # rattler-build writes to distros//work/output, or to CONDA_BLD_PATH (win-64) CACHE_DIR: ${{ matrix.target.platform == 'win-64' && 'C:/bld/win-64' || format('distros/{0}/work/output/{1}', matrix.distro, matrix.target.platform) }} steps: diff --git a/tools/robostack.py b/tools/robostack.py index 5baaf169..97fda869 100644 --- a/tools/robostack.py +++ b/tools/robostack.py @@ -167,10 +167,13 @@ def prepare(distro: str) -> Path: shutil.rmtree(w / stale, ignore_errors=True) for stale in ("pkg_additional_info.yaml", "robostack.yaml", "packages_ignore.yaml", "packages-ignore.yaml"): (w / stale).unlink(missing_ok=True) + # packages already on the channel aren't built again; while uploads go to another + # channel (ROBOSTACK_UPLOAD_CHANNEL, e.g. a test channel), those count too + skip = [channel_url(distro)] + ([extra] if (extra := upload_channel_url()) else []) (w / "vinca.yaml").write_text( f"# Generated by `pixi run rs {distro} prepare`; edit distros/{distro}/vinca.yaml or shared/vinca.yaml.\n" "extends: ../vinca.yaml\n" - f"skip_existing:\n - {channel_url(distro)}/\n" + "skip_existing:\n" + "".join(f" - {url}/\n" for url in skip) ) # vinca-gha inlines ./.scripts/build_win.bat into the Windows workflow shutil.rmtree(w / ".scripts", ignore_errors=True) From 54df4df9a2d96a69c6a6176033ecf6b6d7107b40 Mon Sep 17 00:00:00 2001 From: Tobias Fischer Date: Sat, 10 Oct 2026 13:14:57 +1000 Subject: [PATCH 2/2] rs build: an empty recipes/ means nothing to build, not an error With the packages on the upload channel skipped, a PR that changes nothing a distribution builds generates no recipes; #50's check made `rs build` fail on that. Only a missing recipes/ (never generated) is an error now. Co-Authored-By: Claude Opus 5.5 --- tools/robostack.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tools/robostack.py b/tools/robostack.py index 97fda869..630a6ff8 100644 --- a/tools/robostack.py +++ b/tools/robostack.py @@ -291,10 +291,15 @@ def task(distro: str, name: str, args: list[str]) -> int: return run(py + [str(TOOLS / "check_dependency_compat.py"), *args], w) if name == "gap-report": return run(py + [str(TOOLS / "build_gap_report.py"), *args], w) - if name in ("build", "build-one") and not any((w / "recipes").glob("*/recipe.yaml")): - print(f"No recipes in {(w / 'recipes').relative_to(ROOT)}: run `pixi run rs {distro} generate-recipes` first", - file=sys.stderr) - return 1 + if name in ("build", "build-one"): + if not (w / "recipes").is_dir(): + print(f"No {(w / 'recipes').relative_to(ROOT)}: run `pixi run rs {distro} generate-recipes` first", + file=sys.stderr) + return 1 + if not any((w / "recipes").glob("*/recipe.yaml")): + # generated, but every package is already built (on the channel) + print(f"Nothing to build: {(w / 'recipes').relative_to(ROOT)} is empty") + return 0 if name == "build": return run(rattler_build(distro, ["--recipe-dir", "./recipes", *args]), w) if name == "build-ci": # used by .scripts/build_unix.sh / build_win.bat