feat(installer): self-select release download source (#176)
This commit is contained in:
@@ -111,14 +111,36 @@ jobs:
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
# Inject the release tag into core's VERSION constant (the source for CLI --version and the install-complete
|
||||
# message); otherwise artifacts always carry the in-repo dev version and multiple installs can't be told apart.
|
||||
# BUILD_DATE is stamped alongside it with this run's UTC date (the repo keeps null for dev builds).
|
||||
# message) and both standalone installers. The stamped installers then select the same immutable OSS/GitHub
|
||||
# release when run directly. BUILD_DATE is stamped alongside core with this run's UTC date.
|
||||
- name: Stamp release version
|
||||
run: |
|
||||
TAG="${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}"
|
||||
V="${TAG#v}"
|
||||
printf '%s\n' "$TAG" | grep -Eq '^v[0-9A-Za-z][0-9A-Za-z._-]*$'
|
||||
grep -q 'export const VERSION = "' packages/core/src/index.ts
|
||||
sed -i "s/export const VERSION = \"[^\"]*\"/export const VERSION = \"$V\"/" packages/core/src/index.ts
|
||||
SH_MARKER='EMBEDDED_RELEASE_VERSION="__PENGUIN_RELEASE_VERSION__"'
|
||||
PS_MARKER='$EmbeddedReleaseVersion = "__PENGUIN_RELEASE_VERSION__"'
|
||||
if grep -Fq "$SH_MARKER" install.sh; then SH_HAS_MARKER=1; else SH_HAS_MARKER=0; fi
|
||||
if grep -Fq "$PS_MARKER" install.ps1; then PS_HAS_MARKER=1; else PS_HAS_MARKER=0; fi
|
||||
case "$SH_HAS_MARKER:$PS_HAS_MARKER" in
|
||||
1:1)
|
||||
sed -i "s/__PENGUIN_RELEASE_VERSION__/$TAG/g" install.sh
|
||||
sed -i "s/__PENGUIN_RELEASE_VERSION__/$TAG/g" install.ps1
|
||||
grep -Fq "EMBEDDED_RELEASE_VERSION=\"$TAG\"" install.sh
|
||||
grep -Fq "\$EmbeddedReleaseVersion = \"$TAG\"" install.ps1
|
||||
;;
|
||||
0:0)
|
||||
# Tags created before installer version stamping have neither marker. Preserve their
|
||||
# historical installers so a missing Release can still be rebuilt from the tag source.
|
||||
echo "Legacy tag without embedded installer versions; leaving installers unstamped."
|
||||
;;
|
||||
*)
|
||||
echo "Installer release-version markers are inconsistent." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
D="$(date -u +%Y-%m-%d)"
|
||||
# BRE: the unescaped | in these patterns is literal (grep/sed default to POSIX BRE, where | is not alternation).
|
||||
grep -q 'export const BUILD_DATE: string | null = ' packages/core/src/index.ts
|
||||
|
||||
+86
-13
@@ -3,9 +3,11 @@
|
||||
# irm https://penguin.ooo/install.ps1 | iex
|
||||
#
|
||||
# Options:
|
||||
# $env:PENGUIN_VERSION = "vX.Y.Z" pin a version (same as -Version vX.Y.Z); default is the latest Release
|
||||
# $env:PENGUIN_VERSION = "vX.Y.Z" choose a version (same as -Version vX.Y.Z); a published Release
|
||||
# installer defaults to its own version, an unstamped source copy to latest
|
||||
# $env:PENGUIN_INSTALL_DIR = "<dir>" install dir; default $env:USERPROFILE\.penguin
|
||||
# $env:PENGUIN_ARCHIVE = "<file>" install a local Release zip without network access (same as -ArchivePath)
|
||||
# $env:PENGUIN_DOWNLOAD_SOURCE = "auto|oss|github" choose the online source; default auto (OSS, then same-version GitHub)
|
||||
# $env:PENGUIN_DOWNLOAD_BASE_URL = "https://..." exact online asset directory selected by the stable forwarder
|
||||
# $env:PENGUIN_DOWNLOAD_FALLBACK_BASE_URL = "https://..." same-version fallback asset directory
|
||||
#
|
||||
@@ -35,8 +37,15 @@ $ErrorActionPreference = "Stop"
|
||||
$ProgressPreference = "SilentlyContinue" # Invoke-WebRequest progress rendering slows downloads massively on PS 5.1
|
||||
|
||||
$Repo = "https://github.com/Prism-Shadow/penguin-harness"
|
||||
$OssOrigin = "https://penguin-harness-releases.oss-cn-beijing.aliyuncs.com"
|
||||
$OssReleaseRoot = "$OssOrigin/releases"
|
||||
$GitHubReleaseRoot = "$Repo/releases/download"
|
||||
$GitHubLatestBase = "$Repo/releases/latest/download"
|
||||
$Asset = "penguin-win32-x64.zip"
|
||||
$PayloadName = "payload.zip"
|
||||
# The release workflow replaces this token with the immutable tag before publishing both the
|
||||
# standalone installer and the copy sealed inside the Windows bundle.
|
||||
$EmbeddedReleaseVersion = "__PENGUIN_RELEASE_VERSION__"
|
||||
|
||||
function Fail([string]$Message) {
|
||||
# `throw` rather than `exit`: the penguin.ooo forwarder runs this installer as an in-memory
|
||||
@@ -76,6 +85,32 @@ function Assert-HttpsUrl([string]$Name, [string]$Value) {
|
||||
}
|
||||
}
|
||||
|
||||
function Test-ReleaseTag([string]$Value) {
|
||||
return $Value -match '^v[0-9A-Za-z][0-9A-Za-z._-]*$'
|
||||
}
|
||||
|
||||
function Get-OssLatestTag([string]$ManifestPath) {
|
||||
Remove-Item -LiteralPath $ManifestPath -Force -ErrorAction SilentlyContinue
|
||||
try {
|
||||
Invoke-WebRequest -Uri "$OssOrigin/latest.json" -OutFile $ManifestPath -UseBasicParsing -TimeoutSec 8 | Out-Null
|
||||
} catch {
|
||||
Remove-Item -LiteralPath $ManifestPath -Force -ErrorAction SilentlyContinue
|
||||
return ""
|
||||
}
|
||||
try {
|
||||
$Manifest = [IO.File]::ReadAllText($ManifestPath, [Text.UTF8Encoding]::new($false)) | ConvertFrom-Json
|
||||
$CandidateTag = [string]$Manifest.tag
|
||||
$CandidateBase = ([string]$Manifest.releaseBaseUrl).TrimEnd('/')
|
||||
if ([int]$Manifest.schemaVersion -eq 1 -and
|
||||
(Test-ReleaseTag $CandidateTag) -and
|
||||
$CandidateBase -eq "$OssReleaseRoot/$CandidateTag") {
|
||||
return $CandidateTag
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
function Get-DownloadSourceLabel([string]$BaseUrl) {
|
||||
try { $HostName = ([Uri]$BaseUrl).Host } catch { return "configured mirror" }
|
||||
if ($HostName -like "*.aliyuncs.com") { return "OSS mirror" }
|
||||
@@ -141,6 +176,11 @@ $DownloadFallbackBaseUrl = if ($env:PENGUIN_DOWNLOAD_FALLBACK_BASE_URL) {
|
||||
} else {
|
||||
""
|
||||
}
|
||||
$SourceMode = if ($env:PENGUIN_DOWNLOAD_SOURCE) {
|
||||
$env:PENGUIN_DOWNLOAD_SOURCE.ToLowerInvariant()
|
||||
} else {
|
||||
"auto"
|
||||
}
|
||||
# An extracted installer bundle keeps install.cmd, this script, payload.zip and its checksum
|
||||
# together. `$PSScriptRoot` is empty for the documented `irm ... | iex` path, so online installs
|
||||
# do not accidentally pick up an unrelated archive from the caller's current directory.
|
||||
@@ -151,9 +191,19 @@ if (-not $ArchivePath -and $PSScriptRoot) {
|
||||
if ($ArchivePath -and $Version) {
|
||||
Fail "-ArchivePath/PENGUIN_ARCHIVE cannot be combined with -Version/PENGUIN_VERSION"
|
||||
}
|
||||
if ($Version -and $Version -notmatch '^v[0-9A-Za-z][0-9A-Za-z._-]*$') {
|
||||
if ($Version -and -not (Test-ReleaseTag $Version)) {
|
||||
Fail "invalid release version: $Version"
|
||||
}
|
||||
if ($SourceMode -notin @("auto", "oss", "github")) {
|
||||
Fail "PENGUIN_DOWNLOAD_SOURCE must be auto, oss, or github"
|
||||
}
|
||||
$ResolvedReleaseVersion = if ($Version) {
|
||||
$Version
|
||||
} elseif (Test-ReleaseTag $EmbeddedReleaseVersion) {
|
||||
$EmbeddedReleaseVersion
|
||||
} else {
|
||||
""
|
||||
}
|
||||
if ($DownloadBaseUrl) {
|
||||
Assert-HttpsUrl "PENGUIN_DOWNLOAD_BASE_URL" $DownloadBaseUrl
|
||||
}
|
||||
@@ -176,14 +226,10 @@ try {
|
||||
# .NET builds where the enum is immutable already default to TLS 1.2+.
|
||||
}
|
||||
|
||||
# --- Download (latest GitHub Release by default; the stable forwarder may select exact mirrors) ---
|
||||
if ($DownloadBaseUrl) {
|
||||
$BaseUrl = $DownloadBaseUrl
|
||||
} elseif ($Version) {
|
||||
$BaseUrl = "$Repo/releases/download/$Version"
|
||||
} else {
|
||||
$BaseUrl = "$Repo/releases/latest/download"
|
||||
}
|
||||
# --- Download. Explicit forwarder/configured URLs win. Otherwise a stamped Release installer
|
||||
# uses its own immutable version: auto prefers OSS and falls back only to the same GitHub
|
||||
# tag. An unstamped source-tree installer resolves latest.json first so it also locks one
|
||||
# version before downloading assets. ---
|
||||
$Tmp = Join-Path ([IO.Path]::GetTempPath()) "penguin-install-$PID"
|
||||
if (Test-Path $Tmp) { Remove-Item -Recurse -Force $Tmp }
|
||||
New-Item -ItemType Directory -Path $Tmp | Out-Null
|
||||
@@ -203,14 +249,41 @@ try {
|
||||
$ArchiveName = [IO.Path]::GetFileName($ZipPath)
|
||||
Write-Host "Using local archive $ZipPath ..."
|
||||
} else {
|
||||
$BaseUrl = ""
|
||||
$FallbackBaseUrl = $DownloadFallbackBaseUrl
|
||||
if ($DownloadBaseUrl) {
|
||||
$BaseUrl = $DownloadBaseUrl
|
||||
} elseif ($SourceMode -eq "github") {
|
||||
$BaseUrl = if ($ResolvedReleaseVersion) {
|
||||
"$GitHubReleaseRoot/$ResolvedReleaseVersion"
|
||||
} else {
|
||||
$GitHubLatestBase
|
||||
}
|
||||
} else {
|
||||
$SelectedTag = $ResolvedReleaseVersion
|
||||
if (-not $SelectedTag) {
|
||||
$SelectedTag = Get-OssLatestTag (Join-Path $Tmp "latest.json")
|
||||
}
|
||||
if ($SelectedTag) {
|
||||
$BaseUrl = "$OssReleaseRoot/$SelectedTag"
|
||||
if ($SourceMode -eq "auto" -and -not $FallbackBaseUrl) {
|
||||
$FallbackBaseUrl = "$GitHubReleaseRoot/$SelectedTag"
|
||||
}
|
||||
} elseif ($SourceMode -eq "oss") {
|
||||
Fail "the OSS mirror is unavailable or its release metadata is invalid."
|
||||
} else {
|
||||
$BaseUrl = $GitHubLatestBase
|
||||
}
|
||||
}
|
||||
|
||||
# Online: download the canonical bundle; the published checksum is mandatory.
|
||||
$ZipPath = Join-Path $Tmp $Asset
|
||||
$ArchiveName = $Asset
|
||||
$ShaPath = Join-Path $Tmp "$Asset.sha256"
|
||||
if (-not (Get-ReleasePair $BaseUrl $ZipPath $ShaPath)) {
|
||||
if ($DownloadFallbackBaseUrl -and $DownloadFallbackBaseUrl -ne $BaseUrl) {
|
||||
Write-Host "Primary download source unavailable; trying $(Get-DownloadSourceLabel $DownloadFallbackBaseUrl) ..."
|
||||
if (-not (Get-ReleasePair $DownloadFallbackBaseUrl $ZipPath $ShaPath)) {
|
||||
if ($FallbackBaseUrl -and $FallbackBaseUrl -ne $BaseUrl) {
|
||||
Write-Host "Primary download source unavailable; trying $(Get-DownloadSourceLabel $FallbackBaseUrl) ..."
|
||||
if (-not (Get-ReleasePair $FallbackBaseUrl $ZipPath $ShaPath)) {
|
||||
Fail "download failed from both the primary source and its fallback. Check your network, then retry."
|
||||
}
|
||||
} else {
|
||||
|
||||
+72
-11
@@ -4,9 +4,11 @@
|
||||
# curl -fsSL https://github.com/Prism-Shadow/penguin-harness/releases/latest/download/install.sh | sh
|
||||
#
|
||||
# Options:
|
||||
# PENGUIN_VERSION=vX.Y.Z pin a version (same as --version vX.Y.Z); default is the latest Release
|
||||
# PENGUIN_VERSION=vX.Y.Z choose a version (same as --version vX.Y.Z); a published Release
|
||||
# installer defaults to its own version, an unstamped source copy to latest
|
||||
# PENGUIN_INSTALL_DIR=<dir> install dir; default ~/.penguin
|
||||
# PENGUIN_ARCHIVE=<file> install a local Release archive without network access (same as --archive <file>)
|
||||
# PENGUIN_DOWNLOAD_SOURCE=auto|oss|github choose the online source; default auto (OSS, then same-version GitHub)
|
||||
# PENGUIN_DOWNLOAD_BASE_URL=<url> exact online asset directory selected by the stable forwarder
|
||||
# PENGUIN_DOWNLOAD_FALLBACK_BASE_URL=<url> same-version fallback asset directory
|
||||
# --universal install the universal package (no bundled Node runtime; needs system Node >= 24)
|
||||
@@ -26,14 +28,22 @@
|
||||
set -eu
|
||||
|
||||
REPO="https://github.com/Prism-Shadow/penguin-harness"
|
||||
OSS_ORIGIN="https://penguin-harness-releases.oss-cn-beijing.aliyuncs.com"
|
||||
OSS_RELEASE_ROOT="$OSS_ORIGIN/releases"
|
||||
GITHUB_RELEASE_ROOT="$REPO/releases/download"
|
||||
GITHUB_LATEST_BASE="$REPO/releases/latest/download"
|
||||
VERSION="${PENGUIN_VERSION:-}"
|
||||
INSTALL_DIR="${PENGUIN_INSTALL_DIR:-$HOME/.penguin}"
|
||||
BIN_DIR="$HOME/.local/bin"
|
||||
UNIVERSAL=0
|
||||
ARCHIVE="${PENGUIN_ARCHIVE:-}"
|
||||
SOURCE_MODE="${PENGUIN_DOWNLOAD_SOURCE:-auto}"
|
||||
DOWNLOAD_BASE_URL="${PENGUIN_DOWNLOAD_BASE_URL:-}"
|
||||
DOWNLOAD_FALLBACK_BASE_URL="${PENGUIN_DOWNLOAD_FALLBACK_BASE_URL:-}"
|
||||
PAYLOAD_NAME="payload.tar.gz"
|
||||
# The release workflow replaces this token with the immutable tag before publishing both the
|
||||
# standalone installer and the copies sealed inside the Linux, macOS and universal bundles.
|
||||
EMBEDDED_RELEASE_VERSION="__PENGUIN_RELEASE_VERSION__"
|
||||
|
||||
fail() {
|
||||
echo "error: $1" >&2
|
||||
@@ -47,14 +57,19 @@ validate_https_url() {
|
||||
esac
|
||||
}
|
||||
|
||||
validate_release_tag() {
|
||||
is_release_tag() {
|
||||
case "$1" in
|
||||
v[0-9A-Za-z]* ) ;;
|
||||
*) fail "invalid release version: $1" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
case "$1" in
|
||||
*[!0-9A-Za-z._-]*) fail "invalid release version: $1" ;;
|
||||
*[!0-9A-Za-z._-]*) return 1 ;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
validate_release_tag() {
|
||||
is_release_tag "$1" || fail "invalid release version: $1"
|
||||
}
|
||||
|
||||
download_source_label() {
|
||||
@@ -111,6 +126,14 @@ fi
|
||||
if [ -n "$VERSION" ]; then
|
||||
validate_release_tag "$VERSION"
|
||||
fi
|
||||
case "$SOURCE_MODE" in
|
||||
auto | oss | github) ;;
|
||||
*) fail "PENGUIN_DOWNLOAD_SOURCE must be auto, oss, or github" ;;
|
||||
esac
|
||||
RESOLVED_RELEASE_VERSION="$VERSION"
|
||||
if [ -z "$RESOLVED_RELEASE_VERSION" ] && is_release_tag "$EMBEDDED_RELEASE_VERSION"; then
|
||||
RESOLVED_RELEASE_VERSION="$EMBEDDED_RELEASE_VERSION"
|
||||
fi
|
||||
if [ -n "$DOWNLOAD_BASE_URL" ]; then
|
||||
DOWNLOAD_BASE_URL="${DOWNLOAD_BASE_URL%/}"
|
||||
validate_https_url PENGUIN_DOWNLOAD_BASE_URL "$DOWNLOAD_BASE_URL"
|
||||
@@ -239,6 +262,23 @@ download_release_pair() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# Resolves the OSS mirror's latest immutable tag. The advertised base URL must exactly match
|
||||
# the expected bucket path so metadata cannot redirect downloads to an arbitrary host.
|
||||
get_oss_latest_tag() {
|
||||
gol_manifest="$1"
|
||||
rm -f "$gol_manifest"
|
||||
curl -fsSL --connect-timeout 3 --max-time 8 "$OSS_ORIGIN/latest.json" -o "$gol_manifest" 2>/dev/null \
|
||||
|| return 1
|
||||
gol_schema_version="$(sed -n 's/.*"schemaVersion":[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$gol_manifest" | head -n 1)"
|
||||
gol_candidate_tag="$(sed -n 's/.*"tag":[[:space:]]*"\([^"]*\)".*/\1/p' "$gol_manifest" | head -n 1)"
|
||||
gol_candidate_base="$(sed -n 's/.*"releaseBaseUrl":[[:space:]]*"\([^"]*\)".*/\1/p' "$gol_manifest" | head -n 1)"
|
||||
[ "$gol_schema_version" = "1" ] \
|
||||
&& is_release_tag "$gol_candidate_tag" \
|
||||
&& [ "$gol_candidate_base" = "$OSS_RELEASE_ROOT/$gol_candidate_tag" ] \
|
||||
|| return 1
|
||||
printf '%s\n' "$gol_candidate_tag"
|
||||
}
|
||||
|
||||
# --- Resolve the program payload. Three entries converge on PAYLOAD_PATH:
|
||||
# (a) bundled offline: this script sits next to payload.tar.gz in an extracted bundle;
|
||||
# (b) --archive <file>: a local installer bundle, or a payload/legacy program archive;
|
||||
@@ -285,20 +325,41 @@ elif [ -n "$ARCHIVE" ]; then
|
||||
LOCAL_ARCHIVE=1
|
||||
echo "Using local archive $ARCHIVE_PATH ..."
|
||||
else
|
||||
# (c) Online: download the canonical bundle; the published checksum is mandatory.
|
||||
# (c) Online: explicit forwarder/configured URLs win. Otherwise a stamped Release installer
|
||||
# uses its own immutable version: auto prefers OSS and falls back only to the same GitHub
|
||||
# tag. An unstamped source-tree installer resolves latest.json first so it also locks one
|
||||
# version before downloading assets.
|
||||
FALLBACK_BASE_URL="$DOWNLOAD_FALLBACK_BASE_URL"
|
||||
if [ -n "$DOWNLOAD_BASE_URL" ]; then
|
||||
BASE_URL="$DOWNLOAD_BASE_URL"
|
||||
elif [ -n "$VERSION" ]; then
|
||||
BASE_URL="$REPO/releases/download/$VERSION"
|
||||
elif [ "$SOURCE_MODE" = "github" ]; then
|
||||
if [ -n "$RESOLVED_RELEASE_VERSION" ]; then
|
||||
BASE_URL="$GITHUB_RELEASE_ROOT/$RESOLVED_RELEASE_VERSION"
|
||||
else
|
||||
BASE_URL="$GITHUB_LATEST_BASE"
|
||||
fi
|
||||
else
|
||||
BASE_URL="$REPO/releases/latest/download"
|
||||
SELECTED_TAG="$RESOLVED_RELEASE_VERSION"
|
||||
if [ -z "$SELECTED_TAG" ]; then
|
||||
SELECTED_TAG="$(get_oss_latest_tag "$TMP/latest.json" || :)"
|
||||
fi
|
||||
if [ -n "$SELECTED_TAG" ]; then
|
||||
BASE_URL="$OSS_RELEASE_ROOT/$SELECTED_TAG"
|
||||
if [ "$SOURCE_MODE" = "auto" ] && [ -z "$FALLBACK_BASE_URL" ]; then
|
||||
FALLBACK_BASE_URL="$GITHUB_RELEASE_ROOT/$SELECTED_TAG"
|
||||
fi
|
||||
elif [ "$SOURCE_MODE" = "oss" ]; then
|
||||
fail "the OSS mirror is unavailable or its release metadata is invalid."
|
||||
else
|
||||
BASE_URL="$GITHUB_LATEST_BASE"
|
||||
fi
|
||||
fi
|
||||
ARCHIVE_PATH="$TMP/$ASSET"
|
||||
ARCHIVE_NAME="$ASSET"
|
||||
if ! download_release_pair "$BASE_URL"; then
|
||||
if [ -n "$DOWNLOAD_FALLBACK_BASE_URL" ] && [ "$DOWNLOAD_FALLBACK_BASE_URL" != "$BASE_URL" ]; then
|
||||
echo "Primary download source unavailable; trying $(download_source_label "$DOWNLOAD_FALLBACK_BASE_URL") ..."
|
||||
download_release_pair "$DOWNLOAD_FALLBACK_BASE_URL" \
|
||||
if [ -n "$FALLBACK_BASE_URL" ] && [ "$FALLBACK_BASE_URL" != "$BASE_URL" ]; then
|
||||
echo "Primary download source unavailable; trying $(download_source_label "$FALLBACK_BASE_URL") ..."
|
||||
download_release_pair "$FALLBACK_BASE_URL" \
|
||||
|| fail "download failed from both the primary source and its fallback. Check your network, then retry."
|
||||
else
|
||||
fail "download failed from $(download_source_label "$BASE_URL"). Check the version tag and your network, then retry."
|
||||
|
||||
@@ -21,6 +21,8 @@ The script downloads the matching `penguin-{linux,darwin}-{x64,arm64}.tar.gz`
|
||||
|
||||
The stable entry point defaults to `PENGUIN_DOWNLOAD_SOURCE=auto`: it prefers an immutable OSS release directory only after that release has been completely uploaded and verified, then falls back to the matching GitHub Release if the metadata or download is unavailable. Set the variable to `oss` or `github` to force either source. Normal installer output names the source without printing the mirror's full URL.
|
||||
|
||||
The `penguin.ooo` stable entry resolves the current stable version each time it runs. A standalone script downloaded from a versioned GitHub or OSS Release is stamped with that Release tag and defaults to the same version, keeping the installer and package format matched; set `PENGUIN_VERSION` (or `--version` on POSIX) to override it explicitly.
|
||||
|
||||
On Windows (PowerShell):
|
||||
|
||||
```powershell
|
||||
@@ -63,7 +65,7 @@ The extracted bundle keeps the installer, the program payload (`payload.tar.gz`
|
||||
| --- | --- |
|
||||
| Install dir | `~/.penguin` by default; override with the `PENGUIN_INSTALL_DIR` env var |
|
||||
| Command entry | A symlink `~/.local/bin/penguin` is created (the script warns if `~/.local/bin` is not on PATH) |
|
||||
| Version pin | `PENGUIN_VERSION=vX.Y.Z` env var, or the `--version vX.Y.Z` script flag; defaults to the latest Release |
|
||||
| Version selection | `PENGUIN_VERSION=vX.Y.Z` env var, or the `--version vX.Y.Z` script flag; the stable entry defaults to the latest Release, while a versioned Release installer defaults to its own tag |
|
||||
| Download source | `PENGUIN_DOWNLOAD_SOURCE=auto` (default), `oss`, or `github`; auto prefers OSS and falls back to the same GitHub version |
|
||||
| Local archive | `PENGUIN_ARCHIVE=<file>` or `--archive <file>`; accepts a Release bundle (self-verifying via its sealed payload checksum) or a payload/legacy program archive with an adjacent `<file>.sha256` (renamed legacy files may use the platform asset's canonical `.sha256`) |
|
||||
| Integrity check | Always on: online downloads are verified against the published `.sha256`, and bundle payloads against the checksum sealed inside the bundle |
|
||||
|
||||
@@ -21,6 +21,8 @@ curl -fsSL https://penguin.ooo/install.sh | sh
|
||||
|
||||
稳定入口默认使用 `PENGUIN_DOWNLOAD_SOURCE=auto`:优先选择已完整上传并验证的 OSS 不可变版本目录;元数据或下载不可用时,回退到同一版本的 GitHub Release。也可以将该变量设为 `oss` 或 `github` 来强制指定来源。安装器只显示来源名称,不在常规输出中打印镜像的完整 URL。
|
||||
|
||||
`penguin.ooo` 稳定入口每次执行时都会解析当前稳定版本。从 GitHub 或 OSS 的版本化 Release 中直接下载的独立安装脚本会写入该 Release tag,并默认安装同一版本,确保安装器与安装包格式匹配;如需覆盖,可显式设置 `PENGUIN_VERSION`(POSIX 也可使用 `--version`)。
|
||||
|
||||
在 Windows(PowerShell)上执行:
|
||||
|
||||
```powershell
|
||||
@@ -63,7 +65,7 @@ Linux / macOS 上执行:
|
||||
| --- | --- |
|
||||
| 安装目录 | 默认 `~/.penguin`,可用环境变量 `PENGUIN_INSTALL_DIR` 覆盖 |
|
||||
| 命令入口 | 创建符号链接 `~/.local/bin/penguin`(若 `~/.local/bin` 不在 PATH 上,脚本会给出提示) |
|
||||
| 版本固定 | 环境变量 `PENGUIN_VERSION=vX.Y.Z`,或脚本参数 `--version vX.Y.Z`;默认安装最新 Release |
|
||||
| 版本选择 | 环境变量 `PENGUIN_VERSION=vX.Y.Z`,或脚本参数 `--version vX.Y.Z`;稳定入口默认安装最新 Release,版本化 Release 安装器默认安装自身 tag |
|
||||
| 下载来源 | `PENGUIN_DOWNLOAD_SOURCE=auto`(默认)、`oss` 或 `github`;自动模式优先 OSS,并按同一版本回退到 GitHub |
|
||||
| 本地压缩包 | `PENGUIN_ARCHIVE=<file>` 或 `--archive <file>`;接受 Release 安装包(凭包内封入的负载 checksum 自校验),或旁边带 `<file>.sha256` 的负载 / 旧版程序压缩包(重命名的旧版文件可用平台标准名称的 `.sha256`) |
|
||||
| 完整性校验 | 始终进行:在线下载对照发布的 `.sha256` 校验,安装包负载对照包内封入的 checksum 校验 |
|
||||
|
||||
+41
-11
@@ -119,16 +119,18 @@ function Invoke-OnlineCase(
|
||||
[string]$Mode,
|
||||
[string]$Version,
|
||||
[bool]$ShouldSucceed,
|
||||
[int]$ExpectedRequests
|
||||
[int]$ExpectedRequests,
|
||||
[string]$InstallerPath = ""
|
||||
) {
|
||||
$Fixture.Mode = $Mode
|
||||
$Fixture.Requests.Clear()
|
||||
$InstallDir = Join-Path $WorkDir "$Name-install"
|
||||
$Arguments = @{ InstallDir = $InstallDir }
|
||||
if ($Version) { $Arguments.Version = $Version }
|
||||
if (-not $InstallerPath) { $InstallerPath = $Installer }
|
||||
$Succeeded = $true
|
||||
$Output = @()
|
||||
try { $Output = @(& $Installer @Arguments *>&1) } catch { $Succeeded = $false }
|
||||
try { $Output = @(& $InstallerPath @Arguments *>&1) } catch { $Succeeded = $false }
|
||||
Assert-True ($Succeeded -eq $ShouldSucceed) "$Name returned an unexpected result"
|
||||
Assert-True ($Fixture.Requests.Count -eq $ExpectedRequests) `
|
||||
"$Name made $($Fixture.Requests.Count) requests, expected $ExpectedRequests"
|
||||
@@ -218,6 +220,14 @@ try {
|
||||
|
||||
$Fixture.LegacyArchive = $GoodArchive
|
||||
|
||||
# Model the release workflow's installer stamping without changing the source installer.
|
||||
$StampedInstaller = Join-Path $WorkDir "install-v0.0.0-test.ps1"
|
||||
$InstallerText = [IO.File]::ReadAllText($Installer, [Text.UTF8Encoding]::new($false))
|
||||
Assert-True ($InstallerText.Contains('__PENGUIN_RELEASE_VERSION__')) `
|
||||
"Windows installer release-version token is missing"
|
||||
$InstallerText = $InstallerText.Replace('__PENGUIN_RELEASE_VERSION__', 'v0.0.0-test')
|
||||
[IO.File]::WriteAllText($StampedInstaller, $InstallerText, [Text.UTF8Encoding]::new($false))
|
||||
|
||||
# --- Local bundle via -ArchivePath: opened flat, sealed payload checksum verified. ---
|
||||
$BundleInstall = Join-Path $WorkDir "bundle-install"
|
||||
& $Installer -InstallDir $BundleInstall -ArchivePath $Fixture.GoodBundle *>&1 | Out-Null
|
||||
@@ -236,12 +246,32 @@ try {
|
||||
Assert-True ($Version -eq "fixture-old") "sibling install did not produce a working command"
|
||||
|
||||
# --- Online cases. ---
|
||||
$canonical = Invoke-OnlineCase "canonical" "canonical" "" $true 2
|
||||
Assert-True ($canonical.Requests[0] -like "*/releases/latest/download/penguin-win32-x64.zip") `
|
||||
"canonical did not request the canonical bundle"
|
||||
$canonical = Invoke-OnlineCase "canonical" "canonical" "" $true 3
|
||||
Assert-True ($canonical.Requests[0] -like "*/latest.json") `
|
||||
"unstamped installer did not resolve the OSS latest metadata"
|
||||
Assert-True ($canonical.Requests[1] -like "*/releases/v0.0.0-test/penguin-win32-x64.zip") `
|
||||
"unstamped installer did not lock the resolved OSS release"
|
||||
$Version = & (Join-Path $canonical.InstallDir "bin\penguin.cmd") --version
|
||||
Assert-True ($Version -eq "fixture-old") "canonical bundle was not installed"
|
||||
|
||||
$stamped = Invoke-OnlineCase "stamped" "canonical" "" $true 2 $StampedInstaller
|
||||
Assert-True ($stamped.Requests[0] -eq "https://penguin-harness-releases.oss-cn-beijing.aliyuncs.com/releases/v0.0.0-test/penguin-win32-x64.zip") `
|
||||
"stamped installer did not select its own immutable OSS release"
|
||||
Assert-True (-not (($stamped.Requests | Out-String) -match 'latest\.json')) `
|
||||
"stamped installer unexpectedly resolved latest metadata"
|
||||
|
||||
$stampedFallback = Invoke-OnlineCase "stamped-fallback" "primary-network" "" $true 3 $StampedInstaller
|
||||
Assert-True ($stampedFallback.Requests[0] -like "https://penguin-harness-releases.oss-cn-beijing.aliyuncs.com/releases/v0.0.0-test/*") `
|
||||
"stamped installer did not try its own OSS release first"
|
||||
Assert-True ($stampedFallback.Requests[1] -like "https://github.com/*/releases/download/v0.0.0-test/penguin-win32-x64.zip") `
|
||||
"stamped installer did not fall back to the same GitHub version"
|
||||
|
||||
$env:PENGUIN_DOWNLOAD_SOURCE = "github"
|
||||
$stampedGitHub = Invoke-OnlineCase "stamped-github" "canonical" "" $true 2 $StampedInstaller
|
||||
Assert-True ($stampedGitHub.Requests[0] -like "https://github.com/*/releases/download/v0.0.0-test/penguin-win32-x64.zip") `
|
||||
"stamped installer did not honor forced GitHub mode"
|
||||
Remove-Item Env:\PENGUIN_DOWNLOAD_SOURCE
|
||||
|
||||
$env:PENGUIN_DOWNLOAD_BASE_URL = "https://penguin-harness-releases.oss-cn-beijing.aliyuncs.com/releases/v0.0.0-test"
|
||||
$override = Invoke-OnlineCase "download-base-override" "canonical" "" $true 2
|
||||
Assert-True ($override.Requests[0] -eq "https://penguin-harness-releases.oss-cn-beijing.aliyuncs.com/releases/v0.0.0-test/penguin-win32-x64.zip") `
|
||||
@@ -292,13 +322,13 @@ try {
|
||||
"pinned installer did not keep the selected release version"
|
||||
Remove-Item Env:\PENGUIN_ARCHIVE, Env:\PENGUIN_INSTALL_DIR, Env:\PENGUIN_DOWNLOAD_SOURCE, Env:\PENGUIN_VERSION -ErrorAction SilentlyContinue
|
||||
|
||||
Invoke-OnlineCase "outer-mismatch" "outer-sha-mismatch" "" $false 2 | Out-Null
|
||||
Invoke-OnlineCase "inner-mismatch" "inner-sha-mismatch" "" $false 2 | Out-Null
|
||||
Invoke-OnlineCase "latest-404" "404" "" $false 1 | Out-Null
|
||||
Invoke-OnlineCase "pinned-network" "network" "v0.1.4" $false 1 | Out-Null
|
||||
Invoke-OnlineCase "outer-mismatch" "outer-sha-mismatch" "" $false 3 | Out-Null
|
||||
Invoke-OnlineCase "inner-mismatch" "inner-sha-mismatch" "" $false 3 | Out-Null
|
||||
Invoke-OnlineCase "latest-404" "404" "" $false 2 | Out-Null
|
||||
Invoke-OnlineCase "pinned-network" "network" "v0.1.4" $false 2 | Out-Null
|
||||
$pinned = Invoke-OnlineCase "pinned-legacy" "legacy" "v0.1.4" $true 2
|
||||
Assert-True ($pinned.Requests[0] -like "*/releases/download/v0.1.4/penguin-win32-x64.zip") `
|
||||
"pinned legacy did not request the pinned asset"
|
||||
Assert-True ($pinned.Requests[0] -like "*/releases/v0.1.4/penguin-win32-x64.zip") `
|
||||
"pinned legacy did not prefer the pinned OSS asset"
|
||||
|
||||
Write-Host "Windows installer bundle, offline, rollback and online tests passed."
|
||||
} finally {
|
||||
|
||||
+118
-11
@@ -77,6 +77,79 @@ printf '%s\n' '{"schemaVersion":1,"target":"win32-x64"}' > "$windows_payload/pac
|
||||
|
||||
sh "$ROOT_DIR/scripts/package-release-bundles.sh" "$PAYLOAD_DIR" "$ARTIFACT_DIR"
|
||||
|
||||
# Exercise the exact release-workflow stamping block against new, legacy, and inconsistent tag
|
||||
# sources. The workflow must keep this logic inline because it checks out the requested tag, which
|
||||
# may predate any helper script added to the repository.
|
||||
STAMP_SCRIPT="$WORK_DIR/stamp-release-version.sh"
|
||||
awk '
|
||||
/- name: Stamp release version/ && !found { found = 1; next }
|
||||
found && /run: \|/ { in_run = 1; next }
|
||||
in_run && /^ - name:/ { exit }
|
||||
in_run { sub(/^ /, ""); print }
|
||||
' "$ROOT_DIR/.github/workflows/release.yml" > "$STAMP_SCRIPT"
|
||||
sed -i 's/^TAG=.*/TAG="${TEST_RELEASE_TAG:?}"/' "$STAMP_SCRIPT"
|
||||
grep -q 'SH_HAS_MARKER' "$STAMP_SCRIPT" \
|
||||
|| fail_test "release workflow stamping block could not be extracted"
|
||||
|
||||
make_stamp_case() {
|
||||
case_dir="$1"
|
||||
mkdir -p "$case_dir/packages/core/src"
|
||||
printf '%s\n' \
|
||||
'export const VERSION = "0.0.0";' \
|
||||
'export const BUILD_DATE: string | null = null;' \
|
||||
> "$case_dir/packages/core/src/index.ts"
|
||||
}
|
||||
|
||||
STAMP_NEW_DIR="$WORK_DIR/stamp-new"
|
||||
make_stamp_case "$STAMP_NEW_DIR"
|
||||
printf '%s\n' 'EMBEDDED_RELEASE_VERSION="__PENGUIN_RELEASE_VERSION__"' \
|
||||
> "$STAMP_NEW_DIR/install.sh"
|
||||
printf '%s\n' '$EmbeddedReleaseVersion = "__PENGUIN_RELEASE_VERSION__"' \
|
||||
> "$STAMP_NEW_DIR/install.ps1"
|
||||
(cd "$STAMP_NEW_DIR" && TEST_RELEASE_TAG=v9.8.7 sh -e "$STAMP_SCRIPT")
|
||||
grep -Fq 'EMBEDDED_RELEASE_VERSION="v9.8.7"' "$STAMP_NEW_DIR/install.sh" \
|
||||
|| fail_test "release workflow did not stamp the POSIX installer"
|
||||
grep -Fq '$EmbeddedReleaseVersion = "v9.8.7"' "$STAMP_NEW_DIR/install.ps1" \
|
||||
|| fail_test "release workflow did not stamp the PowerShell installer"
|
||||
|
||||
STAMP_LEGACY_DIR="$WORK_DIR/stamp-legacy"
|
||||
make_stamp_case "$STAMP_LEGACY_DIR"
|
||||
printf '%s\n' 'legacy POSIX installer' > "$STAMP_LEGACY_DIR/install.sh"
|
||||
printf '%s\n' 'legacy PowerShell installer' > "$STAMP_LEGACY_DIR/install.ps1"
|
||||
(cd "$STAMP_LEGACY_DIR" && TEST_RELEASE_TAG=v9.8.7 sh -e "$STAMP_SCRIPT") \
|
||||
> "$WORK_DIR/stamp-legacy.output"
|
||||
grep -Fq 'leaving installers unstamped' "$WORK_DIR/stamp-legacy.output" \
|
||||
|| fail_test "release workflow did not use the legacy installer path"
|
||||
grep -Fq 'legacy POSIX installer' "$STAMP_LEGACY_DIR/install.sh" \
|
||||
|| fail_test "release workflow changed the legacy POSIX installer"
|
||||
grep -Fq 'legacy PowerShell installer' "$STAMP_LEGACY_DIR/install.ps1" \
|
||||
|| fail_test "release workflow changed the legacy PowerShell installer"
|
||||
|
||||
for inconsistent_side in posix powershell; do
|
||||
STAMP_INCONSISTENT_DIR="$WORK_DIR/stamp-inconsistent-$inconsistent_side"
|
||||
make_stamp_case "$STAMP_INCONSISTENT_DIR"
|
||||
printf '%s\n' 'legacy POSIX installer' > "$STAMP_INCONSISTENT_DIR/install.sh"
|
||||
printf '%s\n' 'legacy PowerShell installer' > "$STAMP_INCONSISTENT_DIR/install.ps1"
|
||||
if [ "$inconsistent_side" = posix ]; then
|
||||
printf '%s\n' 'EMBEDDED_RELEASE_VERSION="__PENGUIN_RELEASE_VERSION__"' \
|
||||
> "$STAMP_INCONSISTENT_DIR/install.sh"
|
||||
else
|
||||
printf '%s\n' '$EmbeddedReleaseVersion = "__PENGUIN_RELEASE_VERSION__"' \
|
||||
> "$STAMP_INCONSISTENT_DIR/install.ps1"
|
||||
fi
|
||||
if (cd "$STAMP_INCONSISTENT_DIR" && TEST_RELEASE_TAG=v9.8.7 sh -e "$STAMP_SCRIPT") \
|
||||
> /dev/null 2>&1; then
|
||||
fail_test "release workflow accepted inconsistent $inconsistent_side installer markers"
|
||||
fi
|
||||
done
|
||||
|
||||
# Model the release workflow's installer stamping without changing the source installer.
|
||||
STAMPED_INSTALLER="$WORK_DIR/install-v0.0.0-test.sh"
|
||||
grep -q 'EMBEDDED_RELEASE_VERSION="__PENGUIN_RELEASE_VERSION__"' "$ROOT_DIR/install.sh" \
|
||||
|| fail_test "POSIX installer release-version token is missing"
|
||||
sed 's/__PENGUIN_RELEASE_VERSION__/v0.0.0-test/' "$ROOT_DIR/install.sh" > "$STAMPED_INSTALLER"
|
||||
chmod +x "$STAMPED_INSTALLER"
|
||||
|
||||
# --- Canonical layout: flat bundles, exact member set, byte-identical installers, both
|
||||
# checksum layers valid. ---
|
||||
for target in linux-x64 linux-arm64 darwin-x64 darwin-arm64 universal; do
|
||||
@@ -137,6 +210,17 @@ HOME="$TEST_HOME" PENGUIN_INSTALL_DIR="$OFFLINE_INSTALL" PATH="$STUB_BIN:$PATH"
|
||||
[ "$("$OFFLINE_INSTALL/bin/penguin" --version)" = "fixture-old" ] \
|
||||
|| fail_test "offline install did not produce a working command"
|
||||
|
||||
# The stamped installer inside a released bundle must still prefer its sibling payload and
|
||||
# never resolve metadata or download an online asset.
|
||||
STAMPED_OFFLINE_DIR="$WORK_DIR/offline-stamped"
|
||||
STAMPED_OFFLINE_INSTALL="$WORK_DIR/offline-stamped-install"
|
||||
mkdir -p "$STAMPED_OFFLINE_DIR"
|
||||
cp "$STAMPED_INSTALLER" "$STAMPED_OFFLINE_DIR/install.sh"
|
||||
cp "$OFFLINE_DIR/payload.tar.gz" "$OFFLINE_DIR/payload.tar.gz.sha256" "$STAMPED_OFFLINE_DIR/"
|
||||
HOME="$TEST_HOME" PENGUIN_INSTALL_DIR="$STAMPED_OFFLINE_INSTALL" PATH="$STUB_BIN:$PATH" \
|
||||
sh "$STAMPED_OFFLINE_DIR/install.sh" >/dev/null \
|
||||
|| fail_test "stamped offline installer unexpectedly touched the network"
|
||||
|
||||
# A corrupted extracted payload must be rejected by the sealed checksum.
|
||||
CORRUPT_DIR="$WORK_DIR/offline-corrupt"
|
||||
mkdir -p "$CORRUPT_DIR"
|
||||
@@ -258,7 +342,7 @@ case "$MODE:$base" in
|
||||
forwarder-invalid-metadata:latest.json)
|
||||
printf '%s\n' '{"schemaVersion":1,"tag":"../invalid","releaseBaseUrl":"https://example.invalid"}' > "$output"
|
||||
;;
|
||||
forwarder-oss:latest.json | forced-oss-payload:latest.json)
|
||||
canonical:latest.json | outer-sha-mismatch:latest.json | inner-sha-mismatch:latest.json | forwarder-oss:latest.json | forced-oss-payload:latest.json)
|
||||
printf '%s\n' '{"schemaVersion":1,"tag":"v0.0.0-test","releaseBaseUrl":"https://penguin-harness-releases.oss-cn-beijing.aliyuncs.com/releases/v0.0.0-test"}' > "$output"
|
||||
;;
|
||||
forwarder-oss:install.sh | forced-oss-payload:install.sh | forwarder-auto-github:install.sh | forwarder-invalid-metadata:install.sh | canonical:install.sh) cp "$ROOT_DIR/install.sh" "$output" ;;
|
||||
@@ -290,6 +374,8 @@ run_online_case() {
|
||||
expected_requests="$5"
|
||||
download_base_url="${6:-}"
|
||||
download_fallback_base_url="${7:-}"
|
||||
installer_path="${8:-$ROOT_DIR/install.sh}"
|
||||
source_mode="${9:-auto}"
|
||||
CASE_LOG="$WORK_DIR/$name.log"
|
||||
CASE_OUTPUT="$WORK_DIR/$name.output"
|
||||
CASE_INSTALL="$WORK_DIR/$name-install"
|
||||
@@ -299,7 +385,8 @@ run_online_case() {
|
||||
HOME="$WORK_DIR/$name-home" PENGUIN_INSTALL_DIR="$CASE_INSTALL" \
|
||||
PENGUIN_VERSION="$version" PENGUIN_DOWNLOAD_BASE_URL="$download_base_url" \
|
||||
PENGUIN_DOWNLOAD_FALLBACK_BASE_URL="$download_fallback_base_url" \
|
||||
sh "$ROOT_DIR/install.sh" >"$CASE_OUTPUT" 2>&1
|
||||
PENGUIN_DOWNLOAD_SOURCE="$source_mode" \
|
||||
sh "$installer_path" >"$CASE_OUTPUT" 2>&1
|
||||
status=$?
|
||||
set -e
|
||||
if [ "$expected" = "success" ]; then
|
||||
@@ -311,11 +398,31 @@ run_online_case() {
|
||||
|| fail_test "$name made an unexpected number of requests"
|
||||
}
|
||||
|
||||
run_online_case canonical canonical "" success 2
|
||||
run_online_case canonical canonical "" success 3
|
||||
[ "$("$WORK_DIR/canonical-install/bin/penguin" --version)" = "fixture-old" ] \
|
||||
|| fail_test "canonical online install did not produce a working command"
|
||||
grep -q "/releases/latest/download/$HOST_ASSET\$" "$WORK_DIR/canonical.log" \
|
||||
|| fail_test "canonical did not request the canonical bundle"
|
||||
grep -q "/latest.json\$" "$WORK_DIR/canonical.log" \
|
||||
|| fail_test "unstamped installer did not resolve the OSS latest metadata"
|
||||
grep -q "/releases/v0.0.0-test/$HOST_ASSET\$" "$WORK_DIR/canonical.log" \
|
||||
|| fail_test "unstamped installer did not lock the resolved OSS release"
|
||||
|
||||
run_online_case stamped canonical "" success 2 "" "" "$STAMPED_INSTALLER"
|
||||
[ "$(sed -n '1p' "$WORK_DIR/stamped.log")" = \
|
||||
"https://penguin-harness-releases.oss-cn-beijing.aliyuncs.com/releases/v0.0.0-test/$HOST_ASSET" ] \
|
||||
|| fail_test "stamped installer did not select its own immutable OSS release"
|
||||
! grep -q "/latest.json\$" "$WORK_DIR/stamped.log" \
|
||||
|| fail_test "stamped installer unexpectedly resolved latest metadata"
|
||||
|
||||
run_online_case stamped-fallback primary-network "" success 3 "" "" "$STAMPED_INSTALLER"
|
||||
[ "$(sed -n '1p' "$WORK_DIR/stamped-fallback.log")" = \
|
||||
"https://penguin-harness-releases.oss-cn-beijing.aliyuncs.com/releases/v0.0.0-test/$HOST_ASSET" ] \
|
||||
|| fail_test "stamped installer did not try its own OSS release first"
|
||||
grep -q "github.com/.*/releases/download/v0.0.0-test/$HOST_ASSET\$" "$WORK_DIR/stamped-fallback.log" \
|
||||
|| fail_test "stamped installer did not fall back to the same GitHub version"
|
||||
|
||||
run_online_case stamped-github canonical "" success 2 "" "" "$STAMPED_INSTALLER" github
|
||||
grep -q "github.com/.*/releases/download/v0.0.0-test/$HOST_ASSET\$" "$WORK_DIR/stamped-github.log" \
|
||||
|| fail_test "stamped installer did not honor forced GitHub mode"
|
||||
run_online_case download-base-override canonical "" success 2 \
|
||||
"https://penguin-harness-releases.oss-cn-beijing.aliyuncs.com/releases/v0.0.0-test" ""
|
||||
grep -q "OSS mirror" "$WORK_DIR/download-base-override.output" \
|
||||
@@ -332,13 +439,13 @@ grep -q "github.com/.*/releases/download/v0.0.0-test/$HOST_ASSET\$" "$WORK_DIR/d
|
||||
|| fail_test "download fallback did not use the same-version GitHub source"
|
||||
! grep -q "aliyuncs.com" "$WORK_DIR/download-fallback.output" \
|
||||
|| fail_test "download fallback exposed the OSS URL in normal output"
|
||||
run_online_case outer-mismatch outer-sha-mismatch "" failure 2
|
||||
run_online_case inner-mismatch inner-sha-mismatch "" failure 2
|
||||
run_online_case latest-404 404 "" failure 1
|
||||
run_online_case pinned-network network v0.1.4 failure 1
|
||||
run_online_case outer-mismatch outer-sha-mismatch "" failure 3
|
||||
run_online_case inner-mismatch inner-sha-mismatch "" failure 3
|
||||
run_online_case latest-404 404 "" failure 2
|
||||
run_online_case pinned-network network v0.1.4 failure 2
|
||||
run_online_case pinned-legacy legacy v0.1.4 success 2
|
||||
grep -q "/releases/download/v0.1.4/$HOST_ASSET\$" "$WORK_DIR/pinned-legacy.log" \
|
||||
|| fail_test "pinned legacy did not request the pinned asset"
|
||||
grep -q "/releases/v0.1.4/$HOST_ASSET\$" "$WORK_DIR/pinned-legacy.log" \
|
||||
|| fail_test "pinned legacy did not prefer the pinned OSS asset"
|
||||
|
||||
# --- Stable penguin.ooo forwarder: prefer a validated immutable OSS release, but fall back to
|
||||
# GitHub when the metadata probe fails. The real installer uses a local fixture here so the
|
||||
|
||||
Reference in New Issue
Block a user