From e7dcb3682b97b7d6d0727b2c775fed71949c405c Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Thu, 15 Oct 2026 13:15:16 -0400 Subject: [PATCH] chore: build java release CLIs against Maven Central java-sdk releases don't attach cmdline.jar to their GitHub Releases, so every java release xtest used fell back to a full source build, whose sdk module runs `buf generate` against buf.build. With a dozen xtest jobs doing that at once, BSR rate limits fail the run. When a release has no cmdline.jar asset, install_java_release now checks Maven Central for io.opentdf.platform:sdk at that version and, if present, builds only the parent pom and the cmdline module of the tag (mvn -pl .,cmdline), resolving sdk* from Central. No codegen, no BSR calls. It uses its own local Maven repo so a same-versioned head installed into ~/.m2 can't shadow the release. Any failure still raises InstallError, so setup-cli-tool falls back to the full source build. java `latest` no longer resolves to head:true when no release has a cmdline.jar asset; it is a normal release row, so it takes the path above and is tested as published rather than rebuilt against the platform under test. setup-cli-tool gains a needs-build output (any head, or any release whose artifact install failed), and the java make steps gate on it instead of on heads. Previously a run whose only java versions were releases checked out the fallback source but never built it. DSPX-5177 Signed-off-by: Dave Mihalcik --- .github/workflows/xtest.yml | 6 +- otdf-sdk-mgr/README.md | 2 +- otdf-sdk-mgr/src/otdf_sdk_mgr/installers.py | 135 +++++++++++-- otdf-sdk-mgr/src/otdf_sdk_mgr/resolve.py | 31 +-- otdf-sdk-mgr/tests/test_installers.py | 203 ++++++++++++++++++++ otdf-sdk-mgr/tests/test_resolve.py | 32 +-- xtest/setup-cli-tool/action.yaml | 8 + 7 files changed, 345 insertions(+), 72 deletions(-) create mode 100644 otdf-sdk-mgr/tests/test_installers.py diff --git a/.github/workflows/xtest.yml b/.github/workflows/xtest.yml index 1f0ee6d47..be200b126 100644 --- a/.github/workflows/xtest.yml +++ b/.github/workflows/xtest.yml @@ -575,7 +575,7 @@ jobs: ####### SETUP JAVA CLI ############## - name: Prepare java cli - if: fromJson(steps.configure-java.outputs.heads)[0] != null + if: steps.configure-java.outputs.needs-build == 'true' run: | make working-directory: otdftests/xtest/sdk/java @@ -1050,7 +1050,7 @@ jobs: platform_ref: ${{ needs.resolve-versions.outputs.platform-main-sha }} - name: Build the ${{ matrix.sdk }} cli - if: fromJson(steps.configure-sdk.outputs.heads)[0] != null + if: steps.configure-sdk.outputs.needs-build == 'true' run: make working-directory: otdftests/xtest/sdk/${{ matrix.sdk }} env: @@ -1352,7 +1352,7 @@ jobs: BUF_INPUT_HTTPS_PASSWORD: ${{ secrets.PERSONAL_ACCESS_TOKEN_OPENTDF }} - name: Build the java cli - if: fromJson(steps.configure-java.outputs.heads)[0] != null + if: steps.configure-java.outputs.needs-build == 'true' run: make working-directory: otdftests/xtest/sdk/java env: diff --git a/otdf-sdk-mgr/README.md b/otdf-sdk-mgr/README.md index c99e1dfb2..af30ebc72 100644 --- a/otdf-sdk-mgr/README.md +++ b/otdf-sdk-mgr/README.md @@ -66,7 +66,7 @@ otdf-sdk-mgr java-fixup - **Go**: Writes a `.version` file containing `module-path@version`; `cli.sh`/`otdfctl.sh` use `go run @` (no local compilation needed, Go caches the binary). The resolver always targets the opentdf/platform monorepo (`github.com/opentdf/platform/otdfctl`) for v0.31.0 and newer; pre-v0.31.0 tags fall back to the archived standalone repo (`github.com/opentdf/otdfctl`) for artifact install via the Go module proxy. Bare semver inputs like `v0.32.0` are auto-prepended with `otdfctl/` when looking up platform tags. - **JS**: Runs `npm install @opentdf/ctl@{version}` into the dist directory; `cli.sh` uses `npx` from local `node_modules/` -- **Java**: Downloads `cmdline.jar` from GitHub Releases; `cli.sh` uses `java -jar cmdline.jar` +- **Java**: Downloads `cmdline.jar` from GitHub Releases if the release has one. Otherwise, if `io.opentdf.platform:sdk` is on Maven Central at that version, shallow-clones the tag and builds only the CLI (`mvn -pl .,cmdline package`) against the published sdk jars, which skips the sdk module's `buf generate` and its buf.build rate limits. That build uses its own local Maven repository (`~/.m2/otdf-sdk-mgr-releases`) so a same-versioned head `mvn install`ed into `~/.m2/repository` can't shadow the release; delete it to reclaim space. This path needs `git`, a JDK, and Maven, and takes a few minutes. If neither works, the install fails and CI falls back to a full source build. `cli.sh` uses `java -jar cmdline.jar` ## Source Builds diff --git a/otdf-sdk-mgr/src/otdf_sdk_mgr/installers.py b/otdf-sdk-mgr/src/otdf_sdk_mgr/installers.py index d2c2fcab1..d733e02f4 100644 --- a/otdf-sdk-mgr/src/otdf_sdk_mgr/installers.py +++ b/otdf-sdk-mgr/src/otdf_sdk_mgr/installers.py @@ -14,6 +14,8 @@ from otdf_sdk_mgr.config import ( GO_MODULE_PATH_PLATFORM, LTS_VERSIONS, + SDK_GIT_URLS, + SDK_MAVEN_COORDS, get_sdk_dir, get_sdk_dirs, go_module_for_tag, @@ -83,13 +85,34 @@ def install_js_release(version: str, dist_dir: Path) -> None: def install_java_release(version: str, dist_dir: Path) -> None: - """Install a Java CLI release by downloading cmdline.jar from GitHub Releases. + """Install a Java CLI release. - Raises InstallError if the artifact is not available or download fails, - so the caller can fall back to building from source. + Prefers a prebuilt cmdline.jar from GitHub Releases. Failing that, builds + only the cmdline module of the release tag against the sdk jars published + to Maven Central, which skips the sdk module's `buf generate` codegen (and + its buf.build rate limits) entirely. + + Raises InstallError if neither works, so CI (xtest/setup-cli-tool) can + fall back to a full source build. """ - java_dir = get_sdk_dir() / "java" tag = normalize_version(version) + try: + _download_java_github_release(tag, dist_dir) + except InstallError as download_err: + print(f" {download_err}", file=sys.stderr) + print(f" Building the {tag} CLI against the sdk published to Maven Central...") + try: + _build_java_cli_against_maven_central(tag, dist_dir) + except InstallError as build_err: + raise InstallError( + f"No usable GitHub cmdline.jar for {tag} ({download_err}); " + f"Maven Central build failed ({build_err})" + ) from build_err + + +def _download_java_github_release(tag: str, dist_dir: Path) -> None: + """Download cmdline.jar from a java-sdk GitHub Release into dist_dir.""" + java_dir = get_sdk_dir() / "java" url = f"https://github.com/opentdf/java-sdk/releases/download/{tag}/cmdline.jar" # Check if artifact exists before trying to download @@ -102,11 +125,10 @@ def install_java_release(version: str, dist_dir: Path) -> None: raise InstallError( f"cmdline.jar not found for {tag}. " f"The release {tag} does not include a CLI artifact. " - f"This version will need to be built from source. " f"Check: https://github.com/opentdf/java-sdk/releases/tag/{tag}" ) - raise - except (urllib.error.URLError, OSError) as e: + raise InstallError(f"Could not check GitHub for {tag} cmdline.jar: {e}") from e + except OSError as e: print(f" Warning: Could not verify artifact availability: {e}", file=sys.stderr) # Proceed with download attempt anyway @@ -124,7 +146,9 @@ def install_java_release(version: str, dist_dir: Path) -> None: f"cmdline.jar not found for {tag} (race condition?). " f"Check: https://github.com/opentdf/java-sdk/releases/tag/{tag}" ) - raise + raise InstallError(f"Downloading {tag} cmdline.jar failed: {e}") from e + except OSError as e: + raise InstallError(f"Downloading {tag} cmdline.jar failed: {e}") from e dist_dir.mkdir(parents=True, exist_ok=True) shutil.copy(java_dir / "cli.sh", dist_dir / "cli.sh") @@ -139,6 +163,87 @@ def install_java_release(version: str, dist_dir: Path) -> None: print(f" Java release {tag} installed to {dist_dir}") +# Local Maven repository for release CLI builds; see _build_java_cli_against_maven_central. +JAVA_RELEASE_MAVEN_REPO = Path.home() / ".m2" / "otdf-sdk-mgr-releases" + + +def _java_sdk_on_maven_central(tag: str) -> bool: + """Whether io.opentdf.platform:sdk is published to Maven Central at `tag`.""" + v = tag.removeprefix("v") + url = f"{SDK_MAVEN_COORDS['java']['base_url']}/{v}/sdk-{v}.pom" + try: + req = urllib.request.Request(url, method="HEAD") + with urllib.request.urlopen(req, timeout=10): + return True + except urllib.error.HTTPError as e: + if e.code == 404: + return False + raise InstallError(f"Could not check Maven Central for sdk {v}: {e}") from e + except OSError as e: + raise InstallError(f"Could not reach Maven Central to check for sdk {v}: {e}") from e + + +def _build_java_cli_against_maven_central(tag: str, dist_dir: Path) -> None: + """Build cmdline.jar from the `tag` checkout, resolving sdk* from Maven Central. + + `-pl .,cmdline` builds just the parent pom (the enforcer's reactor + convergence rule needs it) and the CLI, so the sdk module's codegen never + runs. The checkout lives in a temp dir, not under src/, so the java + Makefile won't pick it up for a second, full build. + + Maven uses its own local repository: java-sdk branches carry the version + of the last release, so a head that was `mvn install`ed into ~/.m2 would + otherwise shadow the published sdk jars this build is meant to use. + """ + if not _java_sdk_on_maven_central(tag): + raise InstallError(f"io.opentdf.platform:sdk {tag} is not published to Maven Central") + java_dir = get_sdk_dir() / "java" + with tempfile.TemporaryDirectory(prefix=f"java-sdk-{tag}-") as tmp: + src_dir = Path(tmp) / "java-sdk" + cmds = [ + [ + "git", + "clone", + "--quiet", + "--depth=1", + f"--branch={tag}", + SDK_GIT_URLS["java"], + str(src_dir), + ], + [ + "mvn", + "--batch-mode", + "--file", + str(src_dir / "pom.xml"), + "--projects", + ".,cmdline", + f"-Dmaven.repo.local={JAVA_RELEASE_MAVEN_REPO}", + "package", + "-DskipTests", + "-Dmaven.javadoc.skip=true", + ], + ] + for cmd in cmds: + try: + subprocess.check_call(cmd) + except (subprocess.CalledProcessError, FileNotFoundError) as e: + raise InstallError(f"Running {cmd[0]} for the {tag} CLI failed: {e}") from e + jar = src_dir / "cmdline" / "target" / "cmdline.jar" + if not jar.is_file(): + raise InstallError(f"Built the {tag} CLI but {jar} is missing") + try: + dist_dir.mkdir(parents=True, exist_ok=True) + shutil.copy(java_dir / "cli.sh", dist_dir / "cli.sh") + shutil.copy(jar, dist_dir / "cmdline.jar") + except OSError as e: + # A partial dist_dir would be skipped as "already installed" next time. + shutil.rmtree(dist_dir, ignore_errors=True) + raise InstallError( + f"Built the {tag} CLI but could not install it to {dist_dir}: {e}" + ) from e + print(f" Java release {tag} built against Maven Central and installed to {dist_dir}") + + INSTALLERS = { "go": install_go_release, "js": install_js_release, @@ -166,7 +271,7 @@ def install_release(sdk: str, version: str, dist_name: str | None = None) -> Pat def latest_stable_version(sdk: str) -> str | None: - """Find the latest stable version for an SDK that has a CLI available.""" + """Find the latest stable release version for an SDK.""" if sdk == "go": stable = [v for v in list_go_versions() if v.get("stable", False)] return stable[-1]["version"] if stable else None @@ -174,12 +279,12 @@ def latest_stable_version(sdk: str) -> str | None: stable = [v for v in list_js_versions() if v.get("stable", False)] return stable[-1]["version"] if stable else None elif sdk == "java": - stable_with_cli = [ - v - for v in list_java_github_releases() - if v.get("stable", False) and v.get("has_cli", False) - ] - return stable_with_cli[-1]["version"] if stable_with_cli else None + # No has_cli filter: install_java_release builds the CLI against Maven + # Central when a release has no cmdline.jar asset (and fails, for a CI + # source build, if the sdk isn't published there). The GitHub listing is + # only needed for its prerelease flag. + stable = [v for v in list_java_github_releases() if v.get("stable", False)] + return stable[-1]["version"] if stable else None return None diff --git a/otdf-sdk-mgr/src/otdf_sdk_mgr/resolve.py b/otdf-sdk-mgr/src/otdf_sdk_mgr/resolve.py index 90bb513e6..c3b1b0cd6 100644 --- a/otdf-sdk-mgr/src/otdf_sdk_mgr/resolve.py +++ b/otdf-sdk-mgr/src/otdf_sdk_mgr/resolve.py @@ -333,33 +333,10 @@ def _resolve_against( alias = version matching_tags = [] if version == "latest": - # For Java, check if CLI artifacts are available and fall back to source build if not - if sdk == "java": - from otdf_sdk_mgr.registry import list_java_github_releases - - gh_releases = list_java_github_releases() - # Find the latest version with CLI artifact - versions_with_cli = [r for r in gh_releases if r.get("has_cli", False)] - if versions_with_cli: - # Use the latest version that has CLI - latest_with_cli_tag = versions_with_cli[-1]["version"] - matching_tags = [ - (sha, tag) - for (sha, tag) in stable_tags - if tag in [latest_with_cli_tag, latest_with_cli_tag.lstrip("v")] - ] - if not matching_tags: - # No versions with CLI found, fall back to building latest from source - sha, tag = stable_tags[-1] - return { - "sdk": sdk, - "alias": alias, - "head": True, # Mark as head to trigger source checkout - "sha": sha, - "tag": tag, - } - else: - matching_tags = stable_tags[-1:] + # The newest semver tag, for every SDK. For java this holds even + # when the release has no cmdline.jar asset: the installer builds + # the CLI against the sdk on Maven Central instead. + matching_tags = stable_tags[-1:] else: if version == "lts": if sdk not in LTS_VERSIONS: diff --git a/otdf-sdk-mgr/tests/test_installers.py b/otdf-sdk-mgr/tests/test_installers.py new file mode 100644 index 000000000..8bbc6d460 --- /dev/null +++ b/otdf-sdk-mgr/tests/test_installers.py @@ -0,0 +1,203 @@ +"""Tests for the java release installer — network and subprocesses are mocked.""" + +import subprocess +import urllib.error +from collections.abc import Iterator +from pathlib import Path +from unittest.mock import MagicMock, patch + +import pytest + +from otdf_sdk_mgr import installers +from otdf_sdk_mgr.installers import InstallError, install_java_release, latest_stable_version + + +def http_error(url: str, code: int) -> urllib.error.HTTPError: + return urllib.error.HTTPError(url, code, "error", MagicMock(), None) + + +def http_404(url: str) -> urllib.error.HTTPError: + return http_error(url, 404) + + +@pytest.fixture +def sdk_dir(tmp_path: Path) -> Iterator[Path]: + java_dir = tmp_path / "java" + java_dir.mkdir() + (java_dir / "cli.sh").write_text("#!/bin/sh\n") + with patch("otdf_sdk_mgr.installers.get_sdk_dir", return_value=tmp_path): + yield tmp_path + + +def fake_build(cmd: list[str]) -> int: + """Stand-in for check_call: lay down what clone + mvn would produce.""" + if cmd[0] == "git": + Path(cmd[-1]).mkdir(parents=True) + elif cmd[0] == "mvn": + target = Path(cmd[cmd.index("--file") + 1]).parent / "cmdline" / "target" + target.mkdir(parents=True) + (target / "cmdline.jar").write_text("jar") + return 0 + + +class TestInstallJavaRelease: + def test_no_github_asset_builds_cmdline_only_against_maven_central(self, sdk_dir: Path): + dist = sdk_dir / "java" / "dist" / "v0.19.1" + with ( + patch.object( + installers, + "_download_java_github_release", + side_effect=InstallError("cmdline.jar not found"), + ), + patch.object(installers, "_java_sdk_on_maven_central", return_value=True), + patch("otdf_sdk_mgr.installers.subprocess.check_call", side_effect=fake_build) as cc, + ): + install_java_release("0.19.1", dist) + + clone, mvn = (c.args[0] for c in cc.call_args_list) + assert "--branch=v0.19.1" in clone + # Only the parent pom and the CLI: the sdk module (and its buf codegen) + # must not be in the reactor. + assert mvn[mvn.index("--projects") + 1] == ".,cmdline" + # Isolated from ~/.m2/repository, where a head build may have + # installed a same-versioned sdk. + assert f"-Dmaven.repo.local={installers.JAVA_RELEASE_MAVEN_REPO}" in mvn + assert (dist / "cmdline.jar").read_text() == "jar" + assert (dist / "cli.sh").exists() + + def test_not_on_maven_central_raises_for_source_fallback(self, sdk_dir: Path): + dist = sdk_dir / "java" / "dist" / "v0.17.0" + with ( + patch.object( + installers, + "_download_java_github_release", + side_effect=InstallError("cmdline.jar not found"), + ), + patch.object(installers, "_java_sdk_on_maven_central", return_value=False), + patch("otdf_sdk_mgr.installers.subprocess.check_call") as cc, + pytest.raises(InstallError, match="not published to Maven Central"), + ): + install_java_release("v0.17.0", dist) + cc.assert_not_called() + assert not dist.exists() + + def test_mvn_failure_raises_with_both_causes(self, sdk_dir: Path): + dist = sdk_dir / "java" / "dist" / "v0.9.0" + + def mvn_fails(cmd: list[str]) -> int: + if cmd[0] == "mvn": + raise subprocess.CalledProcessError(1, cmd) + return fake_build(cmd) + + with ( + patch.object( + installers, + "_download_java_github_release", + side_effect=InstallError("cmdline.jar not found"), + ), + patch.object(installers, "_java_sdk_on_maven_central", return_value=True), + patch("otdf_sdk_mgr.installers.subprocess.check_call", side_effect=mvn_fails), + pytest.raises(InstallError) as exc, + ): + install_java_release("v0.9.0", dist) + assert "cmdline.jar not found" in str(exc.value) + assert "Running mvn for the v0.9.0 CLI failed" in str(exc.value) + assert not dist.exists() + + def test_copy_failure_leaves_no_partial_dist(self, sdk_dir: Path): + dist = sdk_dir / "java" / "dist" / "v0.19.1" + real_copy = installers.shutil.copy + + def copy_fails_on_jar(src: Path, dst: Path) -> object: + if Path(dst).name == "cmdline.jar": + raise OSError("disk full") + return real_copy(src, dst) + + with ( + patch.object( + installers, + "_download_java_github_release", + side_effect=InstallError("cmdline.jar not found"), + ), + patch.object(installers, "_java_sdk_on_maven_central", return_value=True), + patch("otdf_sdk_mgr.installers.subprocess.check_call", side_effect=fake_build), + patch("otdf_sdk_mgr.installers.shutil.copy", side_effect=copy_fails_on_jar), + pytest.raises(InstallError, match="could not install it"), + ): + install_java_release("v0.19.1", dist) + # A leftover dist with only cli.sh would be skipped as installed next run. + assert not dist.exists() + + def test_github_asset_skips_maven_build(self, sdk_dir: Path): + dist = sdk_dir / "java" / "dist" / "v0.19.1" + with ( + patch.object(installers, "_download_java_github_release") as download, + patch("otdf_sdk_mgr.installers.subprocess.check_call") as cc, + ): + install_java_release("v0.19.1", dist) + download.assert_called_once_with("v0.19.1", dist) + cc.assert_not_called() + + +class TestDownloadJavaGithubRelease: + def test_rate_limit_raises_install_error(self, sdk_dir: Path): + # Not a bare HTTPError: install_java_release only falls through to the + # Maven Central build on InstallError. + with ( + patch( + "otdf_sdk_mgr.installers.urllib.request.urlopen", + side_effect=http_error("https://github.com/x", 403), + ), + pytest.raises(InstallError, match="Could not check GitHub"), + ): + installers._download_java_github_release("v0.19.1", sdk_dir / "dist") + + +class TestLatestStableVersion: + def test_java_ignores_has_cli(self): + releases = [ + {"version": "v0.1.0", "stable": True, "has_cli": True}, + {"version": "v0.2.0", "stable": True, "has_cli": False}, + {"version": "v0.3.0-rc1", "stable": False, "has_cli": False}, + ] + with patch.object(installers, "list_java_github_releases", return_value=releases): + assert latest_stable_version("java") == "v0.2.0" + + def test_java_none_stable(self): + with patch.object(installers, "list_java_github_releases", return_value=[]): + assert latest_stable_version("java") is None + + +class TestJavaSdkOnMavenCentral: + def test_found(self): + with patch("otdf_sdk_mgr.installers.urllib.request.urlopen", MagicMock()) as urlopen: + assert installers._java_sdk_on_maven_central("v0.19.1") is True + url = urlopen.call_args.args[0].full_url + assert url.endswith("/io/opentdf/platform/sdk/0.19.1/sdk-0.19.1.pom") + + def test_missing(self): + with patch( + "otdf_sdk_mgr.installers.urllib.request.urlopen", + side_effect=http_404("https://repo1.maven.org/x"), + ): + assert installers._java_sdk_on_maven_central("v0.17.0") is False + + def test_unreachable_raises(self): + with ( + patch( + "otdf_sdk_mgr.installers.urllib.request.urlopen", + side_effect=urllib.error.URLError("no route"), + ), + pytest.raises(InstallError, match="Could not reach Maven Central"), + ): + installers._java_sdk_on_maven_central("v0.19.1") + + def test_server_error_raises(self): + with ( + patch( + "otdf_sdk_mgr.installers.urllib.request.urlopen", + side_effect=http_error("https://repo1.maven.org/x", 503), + ), + pytest.raises(InstallError, match="Could not check Maven Central"), + ): + installers._java_sdk_on_maven_central("v0.19.1") diff --git a/otdf-sdk-mgr/tests/test_resolve.py b/otdf-sdk-mgr/tests/test_resolve.py index 6a15f67d5..eae2e355d 100644 --- a/otdf-sdk-mgr/tests/test_resolve.py +++ b/otdf-sdk-mgr/tests/test_resolve.py @@ -332,39 +332,19 @@ def test_non_java_returns_last_stable(self): assert is_resolve_success(result) assert result["tag"] == "v0.33.0" - def test_java_with_cli_available(self): + def test_java_returns_last_stable_as_release(self): + # No GitHub Release cmdline.jar lookup: the installer builds the CLI + # against Maven Central, so latest is a release row, not a head. ls = make_ls_remote( ("1" * 40, "refs/tags/v0.1.0"), ("2" * 40, "refs/tags/v0.2.0"), ) - mock_releases = [ - {"version": "v0.1.0", "has_cli": False}, - {"version": "v0.2.0", "has_cli": True}, - ] - with ( - patch_git(ls), - patch("otdf_sdk_mgr.registry.list_java_github_releases", return_value=mock_releases), - ): + with patch_git(ls): result = resolve("java", "latest", None) assert is_resolve_success(result) assert result["tag"] == "v0.2.0" - - def test_java_no_cli_available_falls_back_to_source(self): - ls = make_ls_remote( - ("1" * 40, "refs/tags/v0.1.0"), - ("2" * 40, "refs/tags/v0.2.0"), - ) - mock_releases = [ - {"version": "v0.1.0", "has_cli": False}, - {"version": "v0.2.0", "has_cli": False}, - ] - with ( - patch_git(ls), - patch("otdf_sdk_mgr.registry.list_java_github_releases", return_value=mock_releases), - ): - result = resolve("java", "latest", None) - assert is_resolve_success(result) - assert result.get("head") is True + assert result.get("release") == "v0.2.0" + assert "head" not in result # --------------------------------------------------------------------------- diff --git a/xtest/setup-cli-tool/action.yaml b/xtest/setup-cli-tool/action.yaml index c03295d04..4c8e7c5c1 100644 --- a/xtest/setup-cli-tool/action.yaml +++ b/xtest/setup-cli-tool/action.yaml @@ -32,6 +32,11 @@ outputs: heads: description: "JSON list of tags that represent main or branch heads" value: ${{ steps.resolve.outputs.heads }} + needs-build: + description: >- + "true" if any version needs a source build: a head, or a release whose + artifact install failed. Gate `make` on this rather than on heads. + value: ${{ steps.check-source.outputs.needs-build }} runs: using: composite @@ -116,6 +121,7 @@ runs: # failed. For go, the only source path is opentdf/platform's otdfctl/ # subtree — reuse the existing platform checkout when the SHAs match, # otherwise check out a fresh one. + needs_build=false for slot in a b c d; do case "$slot" in a) row=$(echo "${version_info}" | jq -rc '.[0] // empty') ;; @@ -140,6 +146,7 @@ runs: needs_source=false if [[ "$head" == "true" || "${!build_from_source_var}" == "true" ]]; then needs_source=true + needs_build=true fi is_platform=false @@ -165,6 +172,7 @@ runs: echo "checkout-repo-${slot}=${checkout_repo}" >> "$GITHUB_OUTPUT" echo "checkout-path-${slot}=${checkout_path}" >> "$GITHUB_OUTPUT" done + echo "needs-build=${needs_build}" >> "$GITHUB_OUTPUT" env: version_info: ${{ inputs.version-info }} INPUT_PATH: ${{ inputs.path }}