diff --git a/.github/workflows/native.yml b/.github/workflows/native.yml index c901adc6f..4047bdf3c 100644 --- a/.github/workflows/native.yml +++ b/.github/workflows/native.yml @@ -67,10 +67,19 @@ jobs: run: | go build -o "$RUNNER_TEMP/oac${{ runner.os == 'Windows' && '.exe' || '' }}" ./services/core/cmd/oac go test ./services/core/cmd/oac -count=1 -timeout=3m - - name: Exercise the Windows Core launcher + - name: Exercise the Unix Core launchers with the system bash + if: runner.os != 'Windows' + run: | + python3 deploy/test_install.py + node --test website/tests/install.test.mjs + - name: Exercise the Windows Core launcher in PowerShell 7 if: runner.os == 'Windows' shell: pwsh run: ./deploy/test_install.ps1 -Binary "$env:RUNNER_TEMP/oac.exe" + - name: Exercise the Windows Core launcher in Windows PowerShell 5.1 + if: runner.os == 'Windows' + shell: powershell + run: ./deploy/test_install.ps1 -Binary "$env:RUNNER_TEMP/oac.exe" - name: Verify native download bootstrap and recovery run: go test ./services/core/internal/nativeinstaller -count=1 -timeout=3m - name: Native filesystem, authentication and process lifecycle diff --git a/deploy/install.ps1 b/deploy/install.ps1 index 02d14975b..4fe1c05e6 100644 --- a/deploy/install.ps1 +++ b/deploy/install.ps1 @@ -1,31 +1,52 @@ # The same native oac command owns installation on Windows. -$ErrorActionPreference = 'Stop' -$installArgs = $args -$version = 'latest' -for ($i = 0; $i -lt $installArgs.Count; $i++) { - if ($installArgs[$i] -eq '--version') { - if ($i + 1 -ge $installArgs.Count) { throw 'Missing release tag' } - $version = $installArgs[$i + 1] +# The body runs in its own scope so `& ([scriptblock]::Create((irm ))) ` works; a failure sets the exit code and exits only when run as a file, never closing an interactive session. +& { + $ErrorActionPreference = 'Stop' + $ProgressPreference = 'SilentlyContinue' + $failure = $null + $directory = $null + $version = 'latest' + function Get-Release([string]$name) { + for ($attempt = 1; $attempt -le 5; $attempt++) { + try { Invoke-WebRequest -UseBasicParsing -Uri "$base/$name" -OutFile (Join-Path $directory $name); return } + catch { + if ($_.Exception.Response -and [int]$_.Exception.Response.StatusCode -eq 404) { throw "Release $version not found." } + Start-Sleep -Seconds 3 + } + } + throw "Could not download $name from GitHub. Check the network (set HTTPS_PROXY if a proxy is required) and rerun the installer." } -} -$architecture = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant() -if ($architecture -ne 'x64') { throw 'Windows x64 is required.' } -$repository = $env:OAC_REPOSITORY -if (-not $repository) { $repository = 'MiniMax-AI/OpenAgentCore' } -$base = "/$repository/releases/latest/download" -if ($version -ne 'latest') { $base = "/$repository/releases/download/$version" } -$directory = Join-Path ([IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString()) -New-Item -ItemType Directory -Path $directory | Out-Null -try { - $asset = 'oac-windows-amd64.exe' - foreach ($name in @($asset, "$asset.sha256")) { - Invoke-WebRequest -UseBasicParsing -Uri "$base/$name" -OutFile (Join-Path $directory $name) + try { + for ($i = 0; $i -lt $args.Count; $i++) { + if ($args[$i] -eq '--version') { + if ($i + 1 -ge $args.Count) { throw 'Missing release tag after --version.' } + $version = $args[$i + 1] + } elseif ($args[$i] -like '--version=*') { + $version = $args[$i].Substring('--version='.Length) + } + } + $architecture = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant() + if ($architecture -ne 'x64') { throw 'Windows x64 is required.' } + $base = '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/MiniMax-AI/OpenAgentCore/releases/latest/download' + if ($version -ne 'latest') { $base = "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/MiniMax-AI/OpenAgentCore/releases/download/$version" } + $directory = Join-Path ([IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString()) + New-Item -ItemType Directory -Path $directory | Out-Null + $asset = 'oac-windows-amd64.exe' + Get-Release $asset + Get-Release "$asset.sha256" + $binary = Join-Path $directory $asset + $actual = (Get-FileHash -Algorithm SHA256 $binary).Hash.ToLowerInvariant() + " $asset" + if ($actual -cne (Get-Content -Raw (Join-Path $directory "$asset.sha256")).Trim()) { throw 'Installer checksum mismatch. Rerun the installer to download it again.' } + & $binary install @args + if ($LASTEXITCODE -ne 0) { throw "Installation failed (exit $LASTEXITCODE). Fix the reported error and rerun." } + } catch { + $failure = $_.Exception.Message + } finally { + if ($directory) { Remove-Item -Recurse -Force $directory -ErrorAction SilentlyContinue } } - $binary = Join-Path $directory $asset - $actual = (Get-FileHash -Algorithm SHA256 $binary).Hash.ToLowerInvariant() + " $asset" - if ($actual -cne (Get-Content -Raw (Join-Path $directory "$asset.sha256")).Trim()) { throw 'Installer checksum mismatch.' } - & $binary install @installArgs - if ($LASTEXITCODE -ne 0) { throw "Installation failed (exit $LASTEXITCODE). Fix the reported error and rerun." } -} finally { - Remove-Item -Recurse -Force $directory -} + if ($failure) { + $global:LASTEXITCODE = 1 + Write-Error $failure -ErrorAction Continue + if ($PSCommandPath) { exit 1 } + } +} @args diff --git a/deploy/install.sh b/deploy/install.sh index 58ef0226c..9cd2d63ee 100755 --- a/deploy/install.sh +++ b/deploy/install.sh @@ -3,23 +3,38 @@ set -euo pipefail case "$(uname -s)" in Linux) os=linux;; Darwin) os=darwin;; *) echo 'Use install.ps1 on Windows.' >&2; exit 1;; esac case "$(uname -m)" in x86_64) arch=amd64;; arm64|aarch64) arch=arm64;; *) echo 'Unsupported CPU architecture.' >&2; exit 1;; esac -version=latest -args=("$@") -while [[ $# -gt 0 ]]; do - case "$1" in --version) version="${2:?Missing release tag}"; shift 2;; *) shift;; esac +# Read the release tag without copying "$@": an empty array aborts bash < 4.4 under set -u. +version=latest previous= +for argument in "$@"; do + case "$previous/$argument" in --version/*) version="$argument";; */--version=*) version="${argument#--version=}";; esac + previous="$argument" done -base="/${OAC_REPOSITORY:-MiniMax-AI/OpenAgentCore}/releases/latest/download" -if [[ "$version" != latest ]]; then base="/${OAC_REPOSITORY:-MiniMax-AI/OpenAgentCore}/releases/download/$version"; fi +[[ "$previous" != --version ]] || { echo 'Missing release tag after --version.' >&2; exit 1; } +base="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/MiniMax-AI/OpenAgentCore/releases/latest/download" +if [[ "$version" != latest ]]; then base="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/MiniMax-AI/OpenAgentCore/releases/download/$version"; fi umask 077 -temporary="$(mktemp -d)" +temporary="$(mktemp -d "${TMPDIR:-/tmp}/oac.XXXXXX")" trap 'rm -rf "$temporary"' EXIT +# Resume after resets, fail only when a download stalls (no total time cap), and give up after five attempts. +download() { + local name="$1" progress="$2" attempt code + for attempt in 1 2 3 4 5; do + if code="$(curl --fail "$progress" --location --proto '=https' --proto-redir '=https' --connect-timeout 15 \ + --speed-limit 1024 --speed-time 60 --continue-at - --write-out '%{http_code}' \ + --output "$temporary/$name" "$base/$name")"; then return 0; fi + [[ "$code" != 404 ]] || { echo "Release $version not found." >&2; exit 1; } + sleep 3 + done + echo "Could not download $name from GitHub. Check the network (set HTTPS_PROXY if a proxy is required) and rerun the installer." >&2 + exit 1 +} asset="oac-$os-$arch" -for name in "$asset" "$asset.sha256"; do - curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' \ - --connect-timeout 15 --max-time 180 --retry 2 "$base/$name" --output "$temporary/$name" -done +progress=--silent +if [[ -t 2 ]]; then progress=--progress-bar; fi +download "$asset.sha256" --silent +download "$asset" "$progress" cd "$temporary" if command -v sha256sum >/dev/null; then actual="$(sha256sum "$asset")"; else actual="$(shasum -a 256 "$asset")"; fi -[[ "$actual" == "$(cat "$asset.sha256")" ]] || { echo 'Installer checksum mismatch.' >&2; exit 1; } +[[ "$actual" == "$(cat "$asset.sha256")" ]] || { rm -f "$asset"; echo 'Installer checksum mismatch. Rerun the installer to download it again.' >&2; exit 1; } chmod 700 "$asset" -"./$asset" install "${args[@]}" +"./$asset" install "$@" diff --git a/deploy/test_install.ps1 b/deploy/test_install.ps1 index bed472823..bf7423671 100644 --- a/deploy/test_install.ps1 +++ b/deploy/test_install.ps1 @@ -1,9 +1,15 @@ # Exercise the actual launcher with a native oac binary and local release assets. param([Parameter(Mandatory=$true)][string]$Binary) $ErrorActionPreference = 'Stop' -$testAssets = @{ Binary = $Binary; Corrupt = $false } +$testAssets = @{ Binary = $Binary; Corrupt = $false; Status = 0 } +function Start-Sleep { param([int]$Seconds) } function Invoke-WebRequest { param([switch]$UseBasicParsing, [string]$Uri, [string]$OutFile) + if ($testAssets.Status) { + $failure = [Exception]::new('request failed') + $failure | Add-Member -NotePropertyName Response -NotePropertyValue ([pscustomobject]@{ StatusCode = $testAssets.Status }) + throw $failure + } if ($Uri.EndsWith('.sha256')) { $digest = (Get-FileHash -Algorithm SHA256 $testAssets.Binary).Hash.ToLowerInvariant() if ($testAssets.Corrupt) { $digest = '0' * 64 } @@ -12,23 +18,22 @@ function Invoke-WebRequest { Copy-Item $testAssets.Binary $OutFile } } +# A failure writes one error and sets a non-zero exit code; the session stays open. +function Assert-Failure([string]$Pattern, [string[]]$Arguments = @('--help')) { + $global:LASTEXITCODE = 0 + $output = (& "$PSScriptRoot/install.ps1" @Arguments 2>&1 | Out-String) + if ($LASTEXITCODE -eq 0 -or $output -notlike $Pattern) { throw "Expected a failure matching $Pattern, got exit $LASTEXITCODE`: $output" } +} & "$PSScriptRoot/install.ps1" --help +if ($LASTEXITCODE -ne 0) { throw 'The launcher failed on a valid installation.' } $testAssets.Corrupt = $true -$caught = $false -try { & "$PSScriptRoot/install.ps1" --help } -catch { - if ($_.Exception.Message -notlike '*checksum mismatch*') { throw } - $caught = $true -} -if (-not $caught) { throw 'A corrupt binary was executed.' } - +Assert-Failure '*checksum mismatch*' $testAssets.Corrupt = $false -$caught = $false -try { & "$PSScriptRoot/install.ps1" --unknown-option } -catch { - if ($_.Exception.Message -notlike '*Installation failed*') { throw } - $caught = $true -} -if (-not $caught) { throw 'A failed native command was reported as successful.' } +Assert-Failure '*Installation failed*' @('--unknown-option') +Assert-Failure '*Missing release tag*' @('--version') +$testAssets.Status = 404 +Assert-Failure '*Release v9.9.9 not found*' @('--version=v9.9.9') +$testAssets.Status = 503 +Assert-Failure '*Check the network*' exit 0 diff --git a/deploy/test_install.py b/deploy/test_install.py index 5b3a3ec05..0db560185 100644 --- a/deploy/test_install.py +++ b/deploy/test_install.py @@ -10,7 +10,7 @@ class LauncherTests(unittest.TestCase): - def run_launcher(self, system='Darwin', machine='arm64', corrupt=False): + def run_launcher(self, system='Darwin', machine='arm64', corrupt=False, arguments=('--install-dir', '/path with spaces/core', '--version', 'v1.2.3'), missing=False, outage=False): with tempfile.TemporaryDirectory() as directory: root = Path(directory) binary = b'#!/bin/sh\nprintf "%s\\n" "$@"\n' @@ -20,17 +20,21 @@ def run_launcher(self, system='Darwin', machine='arm64', corrupt=False): 'curl': '''#!/usr/bin/env python3 import pathlib,sys args=sys.argv -url=args[-3]; target=pathlib.Path(args[-1]) +url=args[-1]; target=pathlib.Path(args[args.index('--output') + 1]) asset=url.rsplit('/',1)[1] if asset.endswith('.sha256'): target.write_text(DIGEST + ' ' + asset.removesuffix('.sha256') + '\\n') else: target.write_bytes(BINARY) -'''.replace('DIGEST', repr('0' * 64 if corrupt else digest)).replace('BINARY', repr(binary)), +if OUTAGE: sys.exit(22) +if MISSING: + sys.stdout.write('404'); sys.exit(22) +'''.replace('OUTAGE', repr(outage)).replace('MISSING', repr(missing)).replace('DIGEST', repr('0' * 64 if corrupt else digest)).replace('BINARY', repr(binary)), } + scripts['sleep'] = '#!/bin/sh\n' for name, contents in scripts.items(): (root / name).write_text(contents); (root / name).chmod(0o700) - return subprocess.run(['bash', str(SCRIPT), '--install-dir', '/path with spaces/core', '--version', 'v1.2.3'], + return subprocess.run(['/bin/bash', str(SCRIPT), *arguments], env={**os.environ, 'PATH': str(root) + os.pathsep + os.environ['PATH']}, capture_output=True, text=True) def test_linux_and_mac_share_argument_forwarding(self): @@ -40,6 +44,27 @@ def test_linux_and_mac_share_argument_forwarding(self): self.assertEqual(result.returncode, 0, result.stderr) self.assertEqual(result.stdout.splitlines(), ['install', '--install-dir', '/path with spaces/core', '--version', 'v1.2.3']) + def test_no_arguments_run_the_installer(self): + result = self.run_launcher(arguments=()) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual(result.stdout.splitlines(), ['install']) + + def test_version_forms_and_dangling_version(self): + for arguments in [('--version=v1.2.3',), ('--version', 'v1.2.3')]: + self.assertEqual(self.run_launcher(arguments=arguments).returncode, 0) + result = self.run_launcher(arguments=('--upgrade', '--version')) + self.assertEqual(result.returncode, 1) + self.assertIn('Missing release tag', result.stderr) + + def test_download_failures_say_what_to_do(self): + result = self.run_launcher(missing=True) + self.assertEqual(result.returncode, 1) + self.assertIn('Release v1.2.3 not found', result.stderr) + result = self.run_launcher(outage=True) + self.assertEqual(result.returncode, 1) + self.assertIn('Check the network', result.stderr) + self.assertEqual(result.stdout, '') + def test_corrupt_binary_is_never_executed(self): result = self.run_launcher(corrupt=True) self.assertNotEqual(result.returncode, 0) diff --git a/docs/getting-started/install-options.md b/docs/getting-started/install-options.md index 44dd87493..994c9f5ef 100644 --- a/docs/getting-started/install-options.md +++ b/docs/getting-started/install-options.md @@ -10,7 +10,7 @@ Pass options to the downloaded script: ./install.sh --public-url https://core.example ``` -On Windows, download `install.ps1` and pass the same flags with `& ./install.ps1 --public-url https://core.example`. With the Unix one-line command, append them after `bash -s --`. `--version TAG` selects a published release; otherwise the script selects the latest stable release and verifies the native command and Compose files against their SHA-256 checksums. +On Windows, `irm … | iex` cannot pass flags and the default execution policy blocks a downloaded `.ps1` file, so run the script as a script block: `& ([scriptblock]::Create((irm https://github.com/MiniMax-AI/OpenAgentCore/releases/latest/download/install.ps1))) --public-url https://core.example`. With the Unix one-line command, append them after `bash -s --`. `--version TAG` selects a published release; otherwise the script selects the latest stable release and verifies the native command and Compose files against their SHA-256 checksums. ## Docker Compose and hosting platforms diff --git a/docs/getting-started/install.md b/docs/getting-started/install.md index 997cc6c41..c390872f4 100644 --- a/docs/getting-started/install.md +++ b/docs/getting-started/install.md @@ -41,6 +41,8 @@ Windows PowerShell: irm https://github.com/MiniMax-AI/OpenAgentCore/releases/latest/download/install.ps1 | iex ``` +To pass flags on Windows, see [Install options](./install-options.md). + When binding to all IPv4 addresses, the installer uses the private address of the default route if available; otherwise the console address is `http://localhost:8080`. To choose another reachable origin, pass `--public-url`; if a reverse proxy already serves this host, use its HTTPS address: ```sh diff --git a/docs/getting-started/operations.md b/docs/getting-started/operations.md index 6779d1976..f84c6eb6a 100644 --- a/docs/getting-started/operations.md +++ b/docs/getting-started/operations.md @@ -178,7 +178,7 @@ An installation of v0.0.10 or later upgrades in place to a newer release. It kee curl -fsSL https://openagentcore.dev/install.sh | bash -s -- --upgrade --install-dir "$HOME/.oac/core" ``` - `--version TAG` selects a release other than the latest. On Windows, pass the same flags to the downloaded `install.ps1`. For an installation from a [source bundle](../development.md#install-a-checkout-without-publishing-a-release), run the new bundle's installer: `python3 /install.py --install-dir --upgrade`. + `--version TAG` selects a release other than the latest. On Windows, run `& ([scriptblock]::Create((irm https://github.com/MiniMax-AI/OpenAgentCore/releases/latest/download/install.ps1))) --upgrade --install-dir "$HOME\.oac\core"`. For an installation from a [source bundle](../development.md#install-a-checkout-without-publishing-a-release), run the new bundle's installer: `python3 /install.py --install-dir --upgrade`. 4. When you are ready, [apply the release's Runtime](#runtime-after-an-upgrade). Without `--upgrade`, another release's installer changes nothing and stops with `This installation runs release ; this installer is . Back up the installation (Upgrade in the operations guide), then rerun with --upgrade.` (`install.py` says `this bundle is`). The installed release's installer resumes the installation, with or without the flag. Installation and mutating `oac` commands share the [installation lock](../configuration.md#installation-directory); if another command is running, wait for it to finish before retrying. diff --git a/docs/zh/getting-started/install-options.md b/docs/zh/getting-started/install-options.md index 662da17a5..a70daa900 100644 --- a/docs/zh/getting-started/install-options.md +++ b/docs/zh/getting-started/install-options.md @@ -1,7 +1,7 @@ --- title: "安装选项" source: docs/getting-started/install-options.md -source_hash: f4d146c2e939103b489c20709d15eae5ec8916f1bc30dd90ad1b6b4dadc80bbc +source_hash: b3802e02538475497cc07acc8b5a20646cb555e3f77bc03e16eef4f82adac456 --- [默认安装](install.md)无需任何选项。本页介绍安装选项、Compose 部署和反向代理配置。 @@ -12,7 +12,7 @@ source_hash: f4d146c2e939103b489c20709d15eae5ec8916f1bc30dd90ad1b6b4dadc80bbc ./install.sh --public-url https://core.example ``` -Windows 可下载 `install.ps1`,然后使用相同参数,例如 `& ./install.ps1 --public-url https://core.example`。Unix 单行命令的参数追加在 `bash -s --` 后。`--version TAG` 选择已发布的版本;默认选择最新稳定版,并校验原生命令和 Compose 文件的 SHA-256。 +Windows 上 `irm … | iex` 无法传参,默认执行策略也会拦截下载的 `.ps1` 文件,因此以脚本块运行:`& ([scriptblock]::Create((irm https://github.com/MiniMax-AI/OpenAgentCore/releases/latest/download/install.ps1))) --public-url https://core.example`。Unix 单行命令的参数追加在 `bash -s --` 后。`--version TAG` 选择已发布的版本;默认选择最新稳定版,并校验原生命令和 Compose 文件的 SHA-256。 ## Docker Compose 与托管平台 {#docker-compose-and-hosting-platforms} diff --git a/docs/zh/getting-started/install.md b/docs/zh/getting-started/install.md index d40031ed5..ec9786376 100644 --- a/docs/zh/getting-started/install.md +++ b/docs/zh/getting-started/install.md @@ -1,7 +1,7 @@ --- title: "安装 Core 和 Web" source: docs/getting-started/install.md -source_hash: d816781ff3fa3b765f0e207efd9c993fcb3ea194d88a42c7c4bba9d5913d8d17 +source_hash: de77f596cad24c26ddb2ddfa71ef104ace08dfe62d57a785b26f692df8f5a3ed --- 一条命令即可在 Linux、macOS 或 Windows 上安装 Core、Web 控制台和 PostgreSQL。用 Core 密钥登录 Web,设置默认模型并签发项目 API 密钥。应用使用这些密钥调用 Core。Session 在你添加的节点上的沙箱中运行,也可以在 E2B 上运行。 @@ -43,6 +43,8 @@ Windows PowerShell: irm https://github.com/MiniMax-AI/OpenAgentCore/releases/latest/download/install.ps1 | iex ``` +Windows 上传参的方式见[安装选项](./install-options.md)。 + 绑定所有 IPv4 地址时,安装器会使用默认路由的私网地址;如果没有可用私网地址,则使用 `http://localhost:8080`。也可以通过 `--public-url` 指定可访问的源地址;如果已有反向代理,就使用它的 HTTPS 地址: ```sh diff --git a/docs/zh/getting-started/operations.md b/docs/zh/getting-started/operations.md index 58672b65f..9f5cb3c18 100644 --- a/docs/zh/getting-started/operations.md +++ b/docs/zh/getting-started/operations.md @@ -1,7 +1,7 @@ --- title: "运维" source: docs/getting-started/operations.md -source_hash: 635fa6022d6fd132ee0af8ce329298355b924803e421db1d6e41add73c970f4f +source_hash: 1d79122c41ef06b70dfd592c55d6b474dc53485f4500518871d8e4a7aba43b91 --- 安装运维人员负责 Core 主机、存储和可用性。节点主机运行各自的服务;参阅[节点](nodes.md)。设置见[配置参考](../configuration.md)。 @@ -180,7 +180,7 @@ v0.0.10 及更高版本的安装可以原地升级到更新的发布版本,并 curl -fsSL https://openagentcore.dev/install.sh | bash -s -- --upgrade --install-dir "$HOME/.oac/core" ``` - `--version TAG` 选择最新版本以外的发布版本。Windows 上,将相同参数传给下载的 `install.ps1`。对于从[源码包](../development.md#install-a-checkout-without-publishing-a-release)安装的实例,运行新源码包的安装程序:`python3 /install.py --install-dir --upgrade`。 + `--version TAG` 选择最新版本以外的发布版本。Windows 上运行 `& ([scriptblock]::Create((irm https://github.com/MiniMax-AI/OpenAgentCore/releases/latest/download/install.ps1))) --upgrade --install-dir "$HOME\.oac\core"`。对于从[源码包](../development.md#install-a-checkout-without-publishing-a-release)安装的实例,运行新源码包的安装程序:`python3 /install.py --install-dir --upgrade`。 4. 准备好后,[应用该版本的 Runtime](#runtime-after-an-upgrade)。 不带 `--upgrade` 时,其他版本的安装程序不做任何更改,并以 `This installation runs release ; this installer is . Back up the installation (Upgrade in the operations guide), then rerun with --upgrade.` 停止(`install.py` 的提示为 `this bundle is`)。无论是否带该参数,已安装版本的安装程序都会继续该安装。安装程序和修改状态的 `oac` 命令共用[安装锁](../configuration.md#installation-directory);其他命令正在运行时,等待其结束后重试。 diff --git a/scripts/build-core-distribution.sh b/scripts/build-core-distribution.sh index 75df48957..df064929c 100755 --- a/scripts/build-core-distribution.sh +++ b/scripts/build-core-distribution.sh @@ -285,6 +285,6 @@ for platform in linux-amd64 linux-arm64 darwin-amd64 darwin-arm64 windows-amd64; if [[ "$platform" == windows-* ]]; then extension=.exe; fi asset="oac-$platform$extension" GOOS="${platform%-*}" GOARCH="${platform#*-}" CGO_ENABLED=0 go build -mod=readonly -trimpath \ - -ldflags "-X main.buildRevision=$revision" -o "$output_dir/$asset" ./services/core/cmd/oac + -ldflags "-s -w -X main.buildRevision=$revision" -o "$output_dir/$asset" ./services/core/cmd/oac (cd "$output_dir" && sha256sum "$asset" > "$asset.sha256") done diff --git a/website/install.sh b/website/install.sh index 17f985828..d2299e52a 100644 --- a/website/install.sh +++ b/website/install.sh @@ -2,10 +2,17 @@ # Keep installation behavior in the published release launcher. set -euo pipefail umask 077 -temporary="$(mktemp -d)" +temporary="$(mktemp -d "${TMPDIR:-/tmp}/oac.XXXXXX")" trap 'rm -rf "$temporary"' EXIT -curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' \ - --connect-timeout 15 --max-time 180 --retry 2 \ - https://github.com/MiniMax-AI/OpenAgentCore/releases/latest/download/install.sh \ - --output "$temporary/install.sh" -bash "$temporary/install.sh" "$@" +for attempt in 1 2 3 4 5; do + if curl --fail --silent --location --proto '=https' --proto-redir '=https' --connect-timeout 15 \ + --speed-limit 1024 --speed-time 60 --continue-at - \ + https://github.com/MiniMax-AI/OpenAgentCore/releases/latest/download/install.sh \ + --output "$temporary/install.sh"; then + bash "$temporary/install.sh" "$@" + exit + fi + sleep 3 +done +echo 'Could not download the installer from GitHub. Check the network (set HTTPS_PROXY if a proxy is required) and rerun the command.' >&2 +exit 1 diff --git a/website/tests/install.test.mjs b/website/tests/install.test.mjs index 6d93c7440..5c1452ab7 100644 --- a/website/tests/install.test.mjs +++ b/website/tests/install.test.mjs @@ -24,14 +24,17 @@ done printf '%s\\n' '#!/usr/bin/env bash' 'printf "%s\\n" "$@"' 'exit 17' > "$target" exit "$DOWNLOAD_STATUS" `) + writeFileSync(join(root, 'sleep'), '#!/bin/sh\n') + chmodSync(join(root, 'sleep'), 0o700) chmodSync(join(root, 'curl'), 0o700) const args = ['--upgrade', '--install-dir', '/path with spaces/core', '--version', 'v0.0.10'] for (const downloadStatus of [0, 22]) { - const result = spawnSync('bash', ['-s', '--', ...args], { + const result = spawnSync('/bin/bash', ['-s', '--', ...args], { input: readFileSync(script), encoding: 'utf8', env: { ...process.env, PATH: `${root}:${process.env.PATH}`, TMPDIR: temporary, CURL_ARGS: join(root, 'curl-args'), DOWNLOAD_STATUS: String(downloadStatus) }, }) - assert.equal(result.status, downloadStatus || 17, result.stderr) + assert.equal(result.status, downloadStatus ? 1 : 17, result.stderr) + if (downloadStatus) assert.match(result.stderr, /Check the network/) assert.equal(result.stdout, downloadStatus ? '' : args.join('\n') + '\n') const curlArgs = readFileSync(join(root, 'curl-args'), 'utf8').trim().split('\n') assert.ok(curlArgs.includes(release))