Skip to content

Commit 0854871

Browse files
authored
fix(install): honor pinned releases and speed up prerelease discovery (#3681)
* fix(install): use native packages for prereleases and speed up discovery Signed-off-by: Drew Newberry <anewberry@nvidia.com> * fix(install): honor pinned versions and guard Snap migration Signed-off-by: Drew Newberry <anewberry@nvidia.com> * fix(install): omit Snap migration guard Signed-off-by: Drew Newberry <anewberry@nvidia.com> --------- Signed-off-by: Drew Newberry <anewberry@nvidia.com>
1 parent f213e9a commit 0854871

5 files changed

Lines changed: 131 additions & 118 deletions

File tree

‎README.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,8 @@ curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install.sh |
300300
OPENSHELL_VERSION=pre sh
301301
```
302302

303-
The installer downloads only the artifact for the current platform and rejects expired candidates during discovery. Installed packages retain the candidate's exact version, such as `0.1.0-pre.3`. Prerelease tags do not create entries on the GitHub Releases page.
303+
The installer checks prerelease tags from newest to oldest, selects an unexpired artifact from a successful release run for the current platform, and downloads only that artifact. Installed packages retain the candidate's exact version, such as `0.1.0-pre.3`. Prerelease tags do not create entries on the GitHub Releases page.
304+
On Linux, prereleases and explicit release tags use Debian or RPM packages even if `snap` is installed.
304305

305306
The rolling [`dev` release](https://github.com/NVIDIA/OpenShell/releases/tag/dev) does not require GitHub authentication:
306307

‎architecture/build.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,12 @@ positive canary uses system Docker; negative preflight coverage verifies that
324324
the installer rejects both missing Docker and the Docker Snap before installing
325325
OpenShell. Its Debian lane removes snapd before running the installer so Snap
326326
precedence cannot change the package under test.
327+
Explicit release tags and the `pre` alias bypass Snap selection and use the
328+
native Debian or RPM package path even when `snap` is available. The `pre` alias
329+
checks matching Git tags in version order, then looks up the exact platform
330+
artifact and verifies the release run instead of listing every repository
331+
artifact.
332+
327333
Snapd runs the gateway as a root-owned system service. Its generated client
328334
certificates reside in root-owned snap state and are unavailable to ordinary CLI
329335
users, so the Snap uses plaintext loopback transport and enables unauthenticated

‎docs/about/installation.mdx‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ Install OpenShell with a single command:
1919
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install.sh | sh
2020
```
2121

22-
The script detects your operating system and installs the OpenShell CLI, standalone policy prover, and gateway. On Linux, it installs from the Snap Store whenever the `snap` command is available. Otherwise, it uses the native Debian or RPM package. It then starts the local gateway server so you can begin creating sandboxes.
22+
The script detects your operating system and installs the OpenShell CLI, standalone policy prover, and gateway. On Linux, it installs from the Snap Store when `snap` is available and `OPENSHELL_VERSION` is unset or `dev`. Explicit release tags and prereleases use the native Debian or RPM package. It then starts the local gateway server so you can begin creating sandboxes.
2323

2424
Snap installs use `latest/stable` by default and `latest/edge` when
25-
`OPENSHELL_VERSION=dev`. Other `OPENSHELL_VERSION` values also use the stable
26-
Snap channel because Snap Store channels do not select an exact GitHub release.
25+
`OPENSHELL_VERSION=dev`. Set `OPENSHELL_VERSION` to a release tag to install
26+
that exact Debian or RPM version, even if `snap` is available.
2727

2828
You can also download release artifacts directly from the [OpenShell GitHub Releases](https://github.com/NVIDIA/OpenShell/releases) page.
2929

@@ -75,13 +75,15 @@ brew services restart openshell
7575
## Linux
7676

7777
On Linux systems with the `snap` command, the install script uses the OpenShell
78-
snap. Install and start Docker Engine from a system package or Docker's package
79-
repository before running the installer. The Docker snap is not currently
80-
compatible with OpenShell.
78+
snap when `OPENSHELL_VERSION` is unset or `dev`. Explicit release tags and the
79+
`pre` prerelease alias use Debian or RPM packages even when `snap` is available.
80+
For Snap installs, install and start Docker Engine from a system package or
81+
Docker's package repository first. The Docker snap is not currently compatible
82+
with OpenShell.
8183

82-
On Fedora and RHEL without `snap`, the install script uses RPM packages. The RPMs install the `openshell` CLI, `openshell-prover`, the `openshell-gateway` daemon, and a systemd user service.
84+
On Fedora and RHEL without `snap`, or when a release tag or `pre` is specified, the install script uses RPM packages. The RPMs install the `openshell` CLI, `openshell-prover`, the `openshell-gateway` daemon, and a systemd user service.
8385

84-
On Debian and Ubuntu without `snap`, the install script uses a Debian package. The Debian package installs the `openshell` CLI, `openshell-prover`, the `openshell-gateway` daemon, VM sandbox support, and a systemd user service.
86+
On Debian and Ubuntu without `snap`, or when a release tag or `pre` is specified, the install script uses a Debian package. The Debian package installs the `openshell` CLI, `openshell-prover`, the `openshell-gateway` daemon, VM sandbox support, and a systemd user service.
8587

8688
Linux packages require glibc 2.28 or newer. The installer checks libc before downloading packages and exits with an error on older glibc versions, Alpine, musl-based distributions, or unknown libc environments.
8789

‎install.sh‎

Lines changed: 54 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,16 @@ NOTES:
6666
When OPENSHELL_VERSION is unset, this resolves the latest tagged release
6767
from ${GITHUB_URL}/releases/latest.
6868
69-
On Linux, the installer uses the OpenShell snap whenever the snap command
70-
is available. It installs from latest/edge when OPENSHELL_VERSION=dev and
71-
from latest/stable otherwise. The OpenShell snap requires a running Docker
72-
Engine installed from a system package or Docker's package repository. The
73-
Docker snap is not currently compatible with OpenShell.
74-
75-
Without snap, Linux installs the Debian package on amd64/arm64 or the RPM
76-
packages on x86_64/aarch64, depending on the host package manager.
69+
On Linux, the installer uses the OpenShell snap when the snap command is
70+
available and OPENSHELL_VERSION is unset or dev. Snap installs use
71+
latest/stable by default and latest/edge for dev. Explicit release tags
72+
and prereleases use Debian or RPM packages. The OpenShell snap requires a
73+
running Docker Engine installed from a system package or Docker's package
74+
repository. The Docker snap is not currently compatible with OpenShell.
75+
76+
For explicit versions or without snap, Linux installs the Debian package
77+
on amd64/arm64 or the RPM packages on x86_64/aarch64, depending on the
78+
host package manager.
7779
macOS installs the release Homebrew formula on Apple Silicon and starts a
7880
brew services-backed local gateway.
7981
EOF
@@ -432,61 +434,39 @@ resolve_latest_prerelease_tag() {
432434

433435
info "resolving latest prerelease..."
434436
_artifact_platform="$(prerelease_artifact_platform)"
435-
_successful_run_ids="$(gh api --paginate \
436-
"repos/${REPO}/actions/workflows/release-tag.yml/runs?status=success&per_page=100" \
437-
--jq '.workflow_runs[] | select(.status == "completed" and .conclusion == "success") | .id')" || {
438-
error "failed to list successful Release Tag workflow runs"
437+
_release_tags="$(gh api "repos/${REPO}/git/matching-refs/tags/v" --jq '
438+
[.[].ref | sub("^refs/tags/"; "") |
439+
select(test("^v[0-9]+\\.[0-9]+\\.[0-9]+-pre\\.[1-9][0-9]*$"))] |
440+
sort_by(split("-pre.") as $parts |
441+
($parts[0] | ltrimstr("v") | split(".") | map(tonumber)) +
442+
[($parts[1] | tonumber)]) | reverse | .[]')" || {
443+
error "failed to list prerelease tags"
439444
}
440-
_artifact_records="$(gh api --paginate \
441-
"repos/${REPO}/actions/artifacts?per_page=100" \
442-
--jq '.artifacts[] | select(.expired == false) | [.workflow_run.id, .name] | @tsv')" || {
443-
error "failed to list prerelease artifacts"
444-
}
445-
_artifact_names="$(printf '%s\n--ARTIFACTS--\n%s\n' "$_successful_run_ids" "$_artifact_records" | awk -F '\t' '
446-
$0 == "--ARTIFACTS--" {
447-
reading_artifacts = 1
448-
next
449-
}
450-
!reading_artifacts {
451-
if ($1 ~ /^[0-9]+$/) successful_runs[$1] = 1
452-
next
453-
}
454-
$1 in successful_runs {
455-
print $2
456-
}
457-
')"
458-
_release_tags="$(printf '%s\n' "$_artifact_names" | sed -n "s/^openshell-\(v[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*-pre\.[1-9][0-9]*\)-${_artifact_platform}$/\1/p" | sort -u)"
459-
460-
_latest_prerelease="$(printf '%s\n' "$_release_tags" | awk '
461-
/^v[0-9]+\.[0-9]+\.[0-9]+-pre\.[1-9][0-9]*$/ {
462-
tag = $0
463-
sub(/^v/, "", tag)
464-
split(tag, version_parts, "-pre[.]")
465-
split(version_parts[1], core, "[.]")
466-
sequence = version_parts[2] + 0
467-
468-
if (!found || core[1] + 0 > major ||
469-
(core[1] + 0 == major && core[2] + 0 > minor) ||
470-
(core[1] + 0 == major && core[2] + 0 == minor && core[3] + 0 > patch) ||
471-
(core[1] + 0 == major && core[2] + 0 == minor && core[3] + 0 == patch && sequence > prerelease)) {
472-
selected = $0
473-
major = core[1] + 0
474-
minor = core[2] + 0
475-
patch = core[3] + 0
476-
prerelease = sequence
477-
found = 1
478-
}
479-
}
480-
END {
481-
if (found) print selected
445+
446+
for _tag in $_release_tags; do
447+
_artifact_name="openshell-${_tag}-${_artifact_platform}"
448+
info "checking ${_tag} for ${_artifact_platform}..."
449+
_run_ids="$(gh api \
450+
"repos/${REPO}/actions/artifacts?name=${_artifact_name}&per_page=100" \
451+
--jq '[.artifacts[] | select(.expired == false)] |
452+
sort_by(.created_at) | reverse | .[] | .workflow_run.id')" || {
453+
error "failed to find prerelease artifact ${_artifact_name}"
482454
}
483-
')"
484455

485-
if [ -z "$_latest_prerelease" ]; then
486-
error "no unexpired prerelease artifacts found"
487-
fi
456+
for _run_id in $_run_ids; do
457+
_successful="$(gh api "repos/${REPO}/actions/runs/${_run_id}" --jq '
458+
.status == "completed" and .conclusion == "success" and
459+
(.path | startswith(".github/workflows/release-tag.yml"))')" || {
460+
error "failed to check prerelease workflow run ${_run_id}"
461+
}
462+
if [ "$_successful" = "true" ]; then
463+
printf '%s\n' "$_tag"
464+
return 0
465+
fi
466+
done
467+
done
488468

489-
printf '%s\n' "$_latest_prerelease"
469+
error "no unexpired prerelease artifacts found"
490470
}
491471

492472
is_prerelease_tag() {
@@ -678,9 +658,16 @@ local_gateway_endpoint() {
678658
}
679659

680660
linux_package_method() {
681-
if has_cmd snap; then
682-
echo "snap"
683-
elif has_cmd dpkg; then
661+
case "${OPENSHELL_VERSION:-}" in
662+
'' | dev)
663+
if has_cmd snap; then
664+
echo "snap"
665+
return 0
666+
fi
667+
;;
668+
esac
669+
670+
if has_cmd dpkg; then
684671
echo "deb"
685672
elif has_cmd rpm; then
686673
echo "rpm"
@@ -1226,14 +1213,11 @@ install_linux_rpm() {
12261213
}
12271214

12281215
openshell_snap_channel() {
1229-
if [ "${OPENSHELL_VERSION:-}" = "dev" ]; then
1230-
printf '%s\n' "latest/edge"
1231-
else
1232-
if [ -n "${OPENSHELL_VERSION:-}" ]; then
1233-
warn "OPENSHELL_VERSION=${OPENSHELL_VERSION} is ignored for Snap installs; using latest/stable"
1234-
fi
1235-
printf '%s\n' "latest/stable"
1236-
fi
1216+
case "${OPENSHELL_VERSION:-}" in
1217+
dev) printf '%s\n' "latest/edge" ;;
1218+
'') printf '%s\n' "latest/stable" ;;
1219+
*) error "Snap installs do not support OPENSHELL_VERSION=${OPENSHELL_VERSION}; use a native package" ;;
1220+
esac
12371221
}
12381222

12391223
ensure_snap_gateway_config() {

‎tasks/scripts/test-install-sh.sh‎

Lines changed: 59 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,15 @@ assert_glibc_preflight_fails \
102102

103103
assert_linux_package_method() {
104104
local name=$1
105-
local snap_present=$2
106-
local dpkg_present=$3
107-
local rpm_present=$4
108-
local expected=$5
105+
local requested_version=$2
106+
local snap_present=$3
107+
local dpkg_present=$4
108+
local rpm_present=$5
109+
local expected=$6
109110
local actual
110111

111112
actual="$(
113+
export OPENSHELL_VERSION="$requested_version"
112114
has_cmd() {
113115
case "$1" in
114116
snap) [ "$snap_present" = "1" ] ;;
@@ -125,9 +127,16 @@ assert_linux_package_method() {
125127
fi
126128
}
127129

128-
assert_linux_package_method "snap takes precedence over deb and rpm" 1 1 1 snap
129-
assert_linux_package_method "deb is selected without snap" 0 1 1 deb
130-
assert_linux_package_method "rpm is selected without snap or deb" 0 0 1 rpm
130+
assert_linux_package_method "snap takes precedence over deb and rpm" "" 1 1 1 snap
131+
assert_linux_package_method "dev uses snap" dev 1 1 1 snap
132+
assert_linux_package_method "pre uses deb despite snap" pre 1 1 1 deb
133+
assert_linux_package_method "numbered prerelease uses deb despite snap" v0.1.0-pre.3 1 1 1 deb
134+
assert_linux_package_method "pre uses rpm despite snap" pre 1 0 1 rpm
135+
assert_linux_package_method "pinned stable uses deb despite snap" v1.2.3 1 1 1 deb
136+
assert_linux_package_method "pinned stable uses rpm despite snap" v1.2.3 1 0 1 rpm
137+
assert_linux_package_method "deb is selected without snap" "" 0 1 1 deb
138+
assert_linux_package_method "dev uses deb without snap" dev 0 1 1 deb
139+
assert_linux_package_method "rpm is selected without snap or deb" "" 0 0 1 rpm
131140

132141
if ! (
133142
find_existing_native_openshell_bin() { return 1; }
@@ -223,22 +232,15 @@ if [ -s "$err" ]; then
223232
exit 1
224233
fi
225234

226-
for requested_version in pre v1.2.3; do
227-
if ! OPENSHELL_VERSION="$requested_version" openshell_snap_channel >"$out" 2>"$err"; then
228-
echo "FAIL: '${requested_version}' must select the latest/stable Snap channel" >&2
229-
cat "$err" >&2 || true
230-
exit 1
231-
fi
232-
if [ "$(cat "$out")" != "latest/stable" ]; then
233-
echo "FAIL: '${requested_version}' must select the latest/stable Snap channel" >&2
234-
exit 1
235-
fi
236-
if ! grep -Fq "OPENSHELL_VERSION=${requested_version} is ignored for Snap installs" "$err"; then
237-
echo "FAIL: '${requested_version}' must warn that the requested version is ignored" >&2
238-
cat "$err" >&2 || true
239-
exit 1
240-
fi
241-
done
235+
if (OPENSHELL_VERSION=v1.2.3 openshell_snap_channel) >"$out" 2>"$err"; then
236+
echo "FAIL: pinned release must not select a Snap channel" >&2
237+
exit 1
238+
fi
239+
if ! grep -Fq "Snap installs do not support OPENSHELL_VERSION=v1.2.3" "$err"; then
240+
echo "FAIL: pinned release Snap rejection was not explained" >&2
241+
cat "$err" >&2
242+
exit 1
243+
fi
242244
rm -f "$out" "$err"
243245

244246
assert_snap_install_flow() {
@@ -299,7 +301,7 @@ wait:gateway-status"
299301

300302
assert_snap_install_flow \
301303
"existing OpenShell snap is refreshed" \
302-
1 1 pre \
304+
1 1 "" \
303305
"wait:docker
304306
root:snap refresh openshell --channel=latest/stable
305307
ensure:gateway-config
@@ -502,19 +504,21 @@ gh() {
502504
;;
503505
api:*)
504506
case "$*" in
505-
*"?name="*) printf '123456\n' ;;
506-
*"actions/workflows/release-tag.yml/runs?status=success"*)
507-
printf '%s\n' 100 101
507+
*"git/matching-refs/tags/v"*)
508+
printf '%s\n' v2.0.0-pre.1 v1.0.0-pre.2 v0.1.0-pre.9
509+
;;
510+
*"?name=openshell-v2.0.0-pre.1-linux-amd64-deb"*)
511+
[ "${MOCK_NO_PRERELEASE:-0}" = "1" ] || printf '999\n'
512+
;;
513+
*"?name=openshell-v1.0.0-pre.2-linux-amd64-deb"*)
514+
[ "${MOCK_NO_PRERELEASE:-0}" = "1" ] || printf '101\n'
508515
;;
516+
*"?name=openshell-v0.1.0-pre.9-linux-amd64-deb"*)
517+
[ "${MOCK_NO_PRERELEASE:-0}" = "1" ] || printf '123456\n'
518+
;;
519+
*"actions/runs/999"*) printf 'false\n' ;;
520+
*"actions/runs/101"*) printf 'true\n' ;;
509521
*)
510-
if [ "${MOCK_NO_PRERELEASE:-0}" != "1" ]; then
511-
printf '%b\n' \
512-
'100\topenshell-v0.1.0-pre.9-linux-amd64-deb' \
513-
'101\topenshell-v1.0.0-pre.2-linux-amd64-deb' \
514-
'101\topenshell-v1.0.0-pre.1-macos-arm64' \
515-
'101\topenshell-v0.2.0-pre.10-linux-aarch64-rpm' \
516-
'999\topenshell-v2.0.0-pre.1-linux-amd64-deb'
517-
fi
518522
;;
519523
esac
520524
;;
@@ -539,13 +543,29 @@ if [ "$resolved_prerelease" != "v1.0.0-pre.2" ]; then
539543
echo "FAIL: pre alias resolved to ${resolved_prerelease}, expected v1.0.0-pre.2" >&2
540544
exit 1
541545
fi
542-
if ! grep -Fq 'actions/workflows/release-tag.yml/runs?status=success' "$mock_gh_log"; then
543-
echo "FAIL: pre alias did not query successful Release Tag workflow runs" >&2
546+
if ! grep -Fq 'git/matching-refs/tags/v' "$mock_gh_log"; then
547+
echo "FAIL: pre alias did not query prerelease tags" >&2
548+
cat "$mock_gh_log" >&2
549+
exit 1
550+
fi
551+
if ! grep -Fq 'actions/artifacts?name=openshell-v1.0.0-pre.2-linux-amd64-deb' "$mock_gh_log"; then
552+
echo "FAIL: pre alias did not query the platform artifact by name" >&2
553+
cat "$mock_gh_log" >&2
554+
exit 1
555+
fi
556+
if ! grep -Fq 'actions/runs/999' "$mock_gh_log" ||
557+
! grep -Fq 'actions/runs/101' "$mock_gh_log"; then
558+
echo "FAIL: pre alias did not skip an unsuccessful run" >&2
559+
cat "$mock_gh_log" >&2
560+
exit 1
561+
fi
562+
if ! grep -Fq '.conclusion == "success"' "$mock_gh_log"; then
563+
echo "FAIL: pre alias did not require a successful workflow run" >&2
544564
cat "$mock_gh_log" >&2
545565
exit 1
546566
fi
547-
if ! grep -Fq 'select(.status == "completed" and .conclusion == "success")' "$mock_gh_log"; then
548-
echo "FAIL: pre alias did not require completed successful workflow runs" >&2
567+
if grep -Fq -- '--paginate' "$mock_gh_log"; then
568+
echo "FAIL: pre alias must not scan every workflow run or artifact" >&2
549569
cat "$mock_gh_log" >&2
550570
exit 1
551571
fi

0 commit comments

Comments
 (0)