@supacloud/admin 0.7.5 → 0.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +85 -28
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -25096,6 +25096,11 @@ class SshConnectionPool {
25096
25096
  } catch {}
25097
25097
  }
25098
25098
  }
25099
+ discard(conn) {
25100
+ try {
25101
+ conn.end();
25102
+ } catch {}
25103
+ }
25099
25104
  closeAll() {
25100
25105
  for (const conn of this.pool) {
25101
25106
  try {
@@ -25130,13 +25135,15 @@ class SshTransport {
25130
25135
  }
25131
25136
  auditCommand(command, this.config.host, false);
25132
25137
  const conn = await this.pool.acquire();
25138
+ let connectionReusable = true;
25133
25139
  try {
25134
25140
  return await new Promise((resolve, reject) => {
25135
25141
  const outputLimit = this.config.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
25136
25142
  const stdout = new BoundedOutputCollector(outputLimit);
25137
25143
  const stderr = new BoundedOutputCollector(outputLimit);
25138
25144
  const timer = setTimeout(() => {
25139
- conn.end();
25145
+ connectionReusable = false;
25146
+ this.pool.discard(conn);
25140
25147
  reject(new Error(`SSH command timed out after ${timeoutMs}ms`));
25141
25148
  }, timeoutMs);
25142
25149
  conn.exec(command, (err, stream) => {
@@ -25162,7 +25169,8 @@ class SshTransport {
25162
25169
  });
25163
25170
  });
25164
25171
  } finally {
25165
- this.pool.release(conn);
25172
+ if (connectionReusable)
25173
+ this.pool.release(conn);
25166
25174
  }
25167
25175
  }
25168
25176
  async upload(localPath, remotePath) {
@@ -25622,10 +25630,28 @@ SUPACLOUD_GITHUB_REPOSITORY="\${SUPACLOUD_GITHUB_REPOSITORY:-zuohuadong/supaclou
25622
25630
  SUPACLOUD_RELEASES_API="\${SUPACLOUD_RELEASES_API:-https://api.github.com/repos/\${SUPACLOUD_GITHUB_REPOSITORY}/releases}"
25623
25631
  SUPACLOUD_ATTESTATION_SIGNER_WORKFLOW="\${SUPACLOUD_ATTESTATION_SIGNER_WORKFLOW:-\${SUPACLOUD_GITHUB_REPOSITORY}/.github/workflows/release-please.yml}"
25624
25632
  SUPACLOUD_GH_VERSION="\${SUPACLOUD_GH_VERSION:-2.96.0}"
25625
- SUPACLOUD_GH_MIN_VERSION="\${SUPACLOUD_GH_MIN_VERSION:-2.51.0}"
25633
+ SUPACLOUD_GH_MIN_VERSION="\${SUPACLOUD_GH_MIN_VERSION:-2.68.0}"
25626
25634
  SUPACLOUD_GH_AMD64_SHA256="\${SUPACLOUD_GH_AMD64_SHA256:-83d5c2ccad5498f58bf6368acb1ab32588cf43ab3a4b1c301bf36328b1c8bd60}"
25627
25635
  SUPACLOUD_GH_ARM64_SHA256="\${SUPACLOUD_GH_ARM64_SHA256:-06f86ec7103d41993b76cd78072f43595c34aaa56506d971d9860e67140bf909}"
25628
25636
 
25637
+ supacloud_curl_release_json() {
25638
+ local url="$1"
25639
+ local output="$2"
25640
+ curl -fsSL --proto '=https' --proto-redir '=https' \\
25641
+ --retry 1 --retry-delay 2 --retry-max-time 60 \\
25642
+ --connect-timeout 15 --max-time 30 --speed-limit 128 --speed-time 10 \\
25643
+ -o "$output" "$url"
25644
+ }
25645
+
25646
+ supacloud_curl_release_asset() {
25647
+ local url="$1"
25648
+ local output="$2"
25649
+ curl -fL --proto '=https' --proto-redir '=https' \\
25650
+ --retry 1 --retry-delay 2 --retry-max-time 180 \\
25651
+ --connect-timeout 15 --max-time 90 --speed-limit 128 --speed-time 60 \\
25652
+ -o "$output" "$url"
25653
+ }
25654
+
25629
25655
  supacloud_component_tag() {
25630
25656
  local component="$1"
25631
25657
  local version="$2"
@@ -25691,18 +25717,15 @@ supacloud_fetch_component_release() {
25691
25717
  supacloud_select_release "$component" "\${required_assets[@]}" <<< "$response"
25692
25718
  }
25693
25719
 
25694
- supacloud_fetch_release_json() {
25720
+ supacloud_fetch_release_json() (
25695
25721
  local url="$1"
25696
- local proxy="\${SUPACLOUD_GITHUB_PROXY:-\${GH_PROXY:-}}"
25697
- if curl -fsSL --retry 3 --connect-timeout 15 "$url"; then
25698
- return 0
25699
- fi
25700
- if [[ -n "$proxy" ]]; then
25701
- curl -fsSL --retry 3 --connect-timeout 15 "\${proxy%/}/\${url}"
25702
- return
25703
- fi
25704
- return 1
25705
- }
25722
+ local response_file
25723
+ response_file=$(mktemp) || return 1
25724
+ trap 'rm -f "$response_file"' EXIT
25725
+ trap 'trap - EXIT HUP INT TERM; rm -f "$response_file"; exit 1' HUP INT TERM
25726
+ supacloud_download_release_metadata_url "$url" "$response_file" || return 1
25727
+ cat "$response_file"
25728
+ )
25706
25729
 
25707
25730
  supacloud_release_asset_url() {
25708
25731
  local release_json="$1"
@@ -25718,11 +25741,26 @@ supacloud_download_url() {
25718
25741
  local output="$2"
25719
25742
  local proxy="\${SUPACLOUD_GITHUB_PROXY:-\${GH_PROXY:-}}"
25720
25743
 
25721
- if curl -fL --retry 3 --connect-timeout 15 -o "$output" "$url"; then
25744
+ if supacloud_curl_release_asset "$url" "$output"; then
25745
+ return 0
25746
+ fi
25747
+ if [[ -n "$proxy" ]]; then
25748
+ supacloud_curl_release_asset "\${proxy%/}/\${url}" "$output"
25749
+ return
25750
+ fi
25751
+ return 1
25752
+ }
25753
+
25754
+ supacloud_download_release_metadata_url() {
25755
+ local url="$1"
25756
+ local output="$2"
25757
+ local proxy="\${SUPACLOUD_GITHUB_PROXY:-\${GH_PROXY:-}}"
25758
+
25759
+ if supacloud_curl_release_json "$url" "$output"; then
25722
25760
  return 0
25723
25761
  fi
25724
25762
  if [[ -n "$proxy" ]]; then
25725
- curl -fL --retry 3 --connect-timeout 15 -o "$output" "\${proxy%/}/\${url}"
25763
+ supacloud_curl_release_json "\${proxy%/}/\${url}" "$output"
25726
25764
  return
25727
25765
  fi
25728
25766
  return 1
@@ -25803,7 +25841,8 @@ supacloud_install_pinned_tar_xz_binary() (
25803
25841
  fi
25804
25842
 
25805
25843
  extract_dir=$(mktemp -d)
25806
- trap 'rm -rf "$extract_dir"; [[ -z "\${staged_target:-}" ]] || rm -f "$staged_target"' EXIT HUP INT TERM
25844
+ trap 'rm -rf "$extract_dir"; [[ -z "\${staged_target:-}" ]] || rm -f "$staged_target"' EXIT
25845
+ trap 'trap - EXIT HUP INT TERM; rm -rf "$extract_dir"; [[ -z "\${staged_target:-}" ]] || rm -f "$staged_target"; exit 1' HUP INT TERM
25807
25846
  if ! tar --no-same-owner --no-same-permissions -xJf "$archive" -C "$extract_dir" "$member"; then
25808
25847
  return 1
25809
25848
  fi
@@ -25992,7 +26031,8 @@ supacloud_verify_attestation() (
25992
26031
  if ! verification_output=$(gh attestation verify "$artifact_file" \\
25993
26032
  --bundle "$bundle_file" \\
25994
26033
  --repo "$SUPACLOUD_GITHUB_REPOSITORY" \\
25995
- --signer-workflow "$SUPACLOUD_ATTESTATION_SIGNER_WORKFLOW" 2>&1); then
26034
+ --signer-workflow "$SUPACLOUD_ATTESTATION_SIGNER_WORKFLOW" \\
26035
+ --source-ref "refs/heads/main" 2>&1); then
25996
26036
  echo "GitHub artifact attestation verification failed: \${verification_output}" >&2
25997
26037
  return 1
25998
26038
  fi
@@ -26011,12 +26051,15 @@ supacloud_verify_attestation() (
26011
26051
  )
26012
26052
 
26013
26053
  supacloud_attestation_verifier_available() {
26014
- local version
26054
+ local version help
26015
26055
  command -v gh >/dev/null 2>&1 || return 1
26016
26056
  version=$(supacloud_gh_version)
26017
26057
  [[ -n "$version" ]] || return 1
26018
26058
  supacloud_version_at_least "$version" "$SUPACLOUD_GH_MIN_VERSION" || return 1
26019
- gh attestation verify --help 2>&1 | grep -q -- '--signer-workflow'
26059
+ help=$(gh attestation verify --help 2>&1) || return 1
26060
+ grep -Eq -- '(^|[[:space:]])--bundle([=[:space:]]|$)' <<< "$help" || return 1
26061
+ grep -Eq -- '(^|[[:space:]])--signer-workflow([=[:space:]]|$)' <<< "$help" || return 1
26062
+ grep -Eq -- '(^|[[:space:]])--source-ref([=[:space:]]|$)' <<< "$help"
26020
26063
  }
26021
26064
 
26022
26065
  supacloud_download_release_asset() (
@@ -26031,10 +26074,11 @@ supacloud_download_release_asset() (
26031
26074
  mkdir -p "$(dirname "$destination")"
26032
26075
  temporary_artifact=$(mktemp "\${destination}.tmp.XXXXXX")
26033
26076
  temporary_checksums=$(mktemp "\${destination}.SHA256SUMS.tmp.XXXXXX")
26034
- trap 'rm -f "\${temporary_artifact:-}" "\${temporary_checksums:-}"' EXIT HUP INT TERM
26077
+ trap 'rm -f "\${temporary_artifact:-}" "\${temporary_checksums:-}"' EXIT
26078
+ trap 'trap - EXIT HUP INT TERM; rm -f "\${temporary_artifact:-}" "\${temporary_checksums:-}"; exit 1' HUP INT TERM
26035
26079
 
26036
26080
  if ! supacloud_download_url "$asset_url" "$temporary_artifact" \\
26037
- || ! supacloud_download_url "$checksum_url" "$temporary_checksums" \\
26081
+ || ! supacloud_download_release_metadata_url "$checksum_url" "$temporary_checksums" \\
26038
26082
  || ! supacloud_verify_checksum "$temporary_artifact" "$asset_name" "$temporary_checksums"; then
26039
26083
  rm -f "$temporary_artifact" "$temporary_checksums"
26040
26084
  return 1
@@ -26076,6 +26120,8 @@ var SAFE_HOSTNAME = /^(?=.{1,253}$)(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9
26076
26120
  var SAFE_SYSTEMD_UNIT = /^[a-zA-Z0-9][a-zA-Z0-9_.@:-]{0,127}$/;
26077
26121
  var SAFE_DB_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_-]{0,62}$/;
26078
26122
  var MINIMUM_COMPONENT_UPGRADE_VERSION = "0.50.27";
26123
+ var DIRECT_UPGRADE_SSH_TIMEOUT_MS = 720000;
26124
+ var PROXIED_UPGRADE_SSH_TIMEOUT_MS = 1320000;
26079
26125
  function hostnameSchema(fieldName) {
26080
26126
  return decodedSchema(Type.String(), Type.String({ minLength: 1, maxLength: 253 }), (value) => {
26081
26127
  const normalized = value.trim();
@@ -26171,6 +26217,12 @@ function componentPreflightCommands(request) {
26171
26217
  `test "$EDGE_RUNTIME_MODE_VALUE" = external || { echo 'Edge Runtime component upgrade supports persisted external mode only' >&2; exit 1; }`
26172
26218
  ] : [];
26173
26219
  }
26220
+ function signalSafeCleanupTraps(cleanupCommand) {
26221
+ return [
26222
+ `trap '${cleanupCommand}' EXIT`,
26223
+ `trap 'trap - EXIT HUP INT TERM; ${cleanupCommand}; exit 1' HUP INT TERM`
26224
+ ];
26225
+ }
26174
26226
  function buildRootUpgradeScript(request) {
26175
26227
  const envAssignments = upgradeEnvAssignments(request);
26176
26228
  return [
@@ -26179,12 +26231,12 @@ function buildRootUpgradeScript(request) {
26179
26231
  "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
26180
26232
  "export PATH",
26181
26233
  "unset SUPACLOUD_ALLOW_UNVERIFIED_RELEASE SUPACLOUD_GITHUB_REPOSITORY SUPACLOUD_RELEASES_API SUPACLOUD_ATTESTATION_SIGNER_WORKFLOW SUPACLOUD_GH_VERSION SUPACLOUD_GH_MIN_VERSION SUPACLOUD_GH_AMD64_SHA256 SUPACLOUD_GH_ARM64_SHA256 GH_PROXY",
26182
- request.githubProxy ? `export SUPACLOUD_GITHUB_PROXY=${quoteEnvValue(request.githubProxy)}` : "unset SUPACLOUD_GITHUB_PROXY",
26234
+ request.githubProxy ? `export SUPACLOUD_GITHUB_PROXY=${quoteEnvValue(request.githubProxy)}` : "unset SUPACLOUD_GITHUB_PROXY SUPACLOUD_GITHUB_PROXIES",
26183
26235
  'for tool in curl jq file sha256sum tar; do command -v "$tool" >/dev/null 2>&1 || { echo "Required upgrade tool is missing: $tool" >&2; exit 127; }; done',
26184
26236
  "test -x /usr/local/bin/supacloud || { echo 'SupaCloud binary not found at /usr/local/bin/supacloud; run ssh install first.' >&2; exit 127; }",
26185
26237
  ...componentPreflightCommands(request),
26186
26238
  "STAGED_MANAGEMENT=''",
26187
- `trap 'test -z "$STAGED_MANAGEMENT" || rm -f "$STAGED_MANAGEMENT"' EXIT HUP INT TERM`,
26239
+ ...signalSafeCleanupTraps('test -z "$STAGED_MANAGEMENT" || rm -f "$STAGED_MANAGEMENT"'),
26188
26240
  `source ${quoteEnvValue(request.helperPath)}`,
26189
26241
  "if ! supacloud_attestation_verifier_available; then supacloud_install_pinned_gh /usr/local/bin/gh; fi",
26190
26242
  "supacloud_attestation_verifier_available || { echo 'Pinned GitHub attestation verifier is unavailable' >&2; exit 1; }",
@@ -26195,7 +26247,11 @@ function buildRootUpgradeScript(request) {
26195
26247
  }
26196
26248
  function buildOfficialUpgradeCommand(request) {
26197
26249
  const rootScript = buildRootUpgradeScript(request);
26198
- return `set -e; trap 'rm -f ${request.helperPath}' EXIT HUP INT TERM; ` + 'if [ "$(id -u)" -eq 0 ]; then ' + `bash -c ${quoteEnvValue(rootScript)}; ` + "else sudo -n true; " + `sudo -n bash -c ${quoteEnvValue(rootScript)}; fi`;
26250
+ return [
26251
+ "set -e",
26252
+ ...signalSafeCleanupTraps(`rm -f ${request.helperPath}`),
26253
+ 'if [ "$(id -u)" -eq 0 ]; then ' + `bash -c ${quoteEnvValue(rootScript)}; ` + "else sudo -n true; " + `sudo -n bash -c ${quoteEnvValue(rootScript)}; fi`
26254
+ ].join("; ");
26199
26255
  }
26200
26256
  async function removeRemoteUpgradeHelper(ssh, helperPath) {
26201
26257
  const cleanup = await ssh.exec(`rm -f ${quoteEnvValue(helperPath)}`);
@@ -26224,12 +26280,12 @@ function officialUpgradeOutcome(execution, executionError, cleanupError) {
26224
26280
  throw remoteUpgradeFailure(execution);
26225
26281
  return execution;
26226
26282
  }
26227
- async function executeOfficialUpgrade(ssh, helperPath, command) {
26283
+ async function executeOfficialUpgrade(ssh, helperPath, command, timeoutMs) {
26228
26284
  let execution;
26229
26285
  let executionError;
26230
26286
  try {
26231
26287
  await ssh.uploadText(helperPath, release_assets_default, 384);
26232
- execution = await ssh.exec(command, 600000);
26288
+ execution = await ssh.exec(command, timeoutMs);
26233
26289
  } catch (error) {
26234
26290
  executionError = error;
26235
26291
  }
@@ -26544,7 +26600,8 @@ ${result.stderr.slice(-500)}`;
26544
26600
  githubProxy,
26545
26601
  helperPath
26546
26602
  });
26547
- const upgradeExecution = await executeOfficialUpgrade(ssh, helperPath, cmd);
26603
+ const upgradeTimeoutMs = githubProxy ? PROXIED_UPGRADE_SSH_TIMEOUT_MS : DIRECT_UPGRADE_SSH_TIMEOUT_MS;
26604
+ const upgradeExecution = await executeOfficialUpgrade(ssh, helperPath, cmd, upgradeTimeoutMs);
26548
26605
  const edgeBoundary = edgeRuntimeVersion ? "" : `
26549
26606
  ⚠️ Edge Runtime was not upgraded; provide --edge_runtime_version for a component transaction.`;
26550
26607
  text = `✅ Upgrade done
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/admin",
3
- "version": "0.7.5",
3
+ "version": "0.7.7",
4
4
  "description": "Platform administration CLI for SupaCloud operators",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",