@r1z4x/tezgah 1.9.0 → 1.9.2

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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,19 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.9.2] - 2026-10-17
10
+
11
+ ### Fixed
12
+
13
+ - **1.9.2 ships what 1.9.1 carried.** The v1.9.1 release run failed its own CI on the changelog's prose. So npm and Homebrew never received 1.9.1. This release carries the same changes.
14
+
15
+ ## [1.9.1] - 2026-10-17
16
+
17
+ ### Fixed
18
+
19
+ - **A token fetches a private repository's release.** GitHub answers `404` to `releases/download/...` from a session without one. So `brew install`, `upgrade.sh` and the `curl | sh` first install all broke when the repository went private. `upgrade.sh` and `install.sh` read `TEZGAH_GITHUB_TOKEN`, `GITHUB_TOKEN` or `GH_TOKEN`. With a token, they read the tarball through the REST asset endpoint. `install.ps1` sends the same header. The Homebrew formula names that endpoint and reads `HOMEBREW_GITHUB_API_TOKEN`, and its caveats say so. With no token, the public URLs stand and a failed fetch names the token to set. The bearer reaches curl on stdin (`-H @-`), so no other user's `ps` shows it. An install still on 1.9.0 has no token support: install the new release with the token set.
20
+ - **The formula's caveats keep their text.** Backticks in the template ran as commands in the release step's unquoted heredoc. The caveats lost the token scope and the failure line, and the step stayed green. The test now renders the template the way the step does.
21
+
9
22
  ## [1.9.0] - 2026-10-17
10
23
 
11
24
  ### Added
@@ -1206,7 +1219,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1206
1219
  the copy could not find `hooks/` and died on import. It now links into the
1207
1220
  installed tree, and also installs the command as `tezgah`.
1208
1221
 
1209
- [Unreleased]: https://github.com/r1z4x/tezgah/compare/v1.9.0...HEAD
1222
+ [Unreleased]: https://github.com/r1z4x/tezgah/compare/v1.9.2...HEAD
1223
+ [1.9.2]: https://github.com/r1z4x/tezgah/releases/tag/v1.9.2
1224
+ [1.9.1]: https://github.com/r1z4x/tezgah/releases/tag/v1.9.1
1210
1225
  [1.9.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.9.0
1211
1226
  [1.8.3]: https://github.com/r1z4x/tezgah/releases/tag/v1.8.3
1212
1227
  [1.8.2]: https://github.com/r1z4x/tezgah/releases/tag/v1.8.2
@@ -19,12 +19,12 @@ the kill switches are files, not variables, and live in
19
19
 
20
20
  | Variable | Read by | Default | Effect |
21
21
  |---|---|---|---|
22
- | `TEZGAH_PREFIX` | `packaging/install.sh:26`, `packaging/upgrade.sh`, `packaging/install.ps1`, `bin/tezgah-setup::default_prefix` | `$XDG_DATA_HOME/tezgah`, else `%LOCALAPPDATA%\tezgah` on Windows, else `~/.local/share/tezgah` | Where the artifact install unpacks its versions and the `current` link; `tezgah-setup` treats it as the install prefix. |
22
+ | `TEZGAH_PREFIX` | `packaging/install.sh:30`, `packaging/upgrade.sh`, `packaging/install.ps1`, `bin/tezgah-setup::default_prefix` | `$XDG_DATA_HOME/tezgah`, else `%LOCALAPPDATA%\tezgah` on Windows, else `~/.local/share/tezgah` | Where the artifact install unpacks its versions and the `current` link; `tezgah-setup` treats it as the install prefix. |
23
23
  | `TEZGAH_VERSION` | `packaging/install.sh`, `packaging/upgrade.sh`, `packaging/install.ps1`, `packaging/build.sh:70` | the newest GitHub release; `build.sh` falls back to `VERSION`/the CHANGELOG | The version to install, or the version `build.sh` stamps when `--version` is not given. |
24
- | `TEZGAH_REPO` | `packaging/install.sh:12`, `packaging/upgrade.sh`, `packaging/install.ps1` | `r1z4x/tezgah` | The GitHub repository releases are fetched from. |
24
+ | `TEZGAH_REPO` | `packaging/install.sh:13`, `packaging/upgrade.sh`, `packaging/install.ps1` | `r1z4x/tezgah` | The GitHub repository releases are fetched from. |
25
25
  | `TEZGAH_UPDATE_URL` | `hooks/tezgah_update.py` | `https://github.com/<TEZGAH_REPO>/releases/latest` | Where the daily release check reads the newest tag: from the redirect, or from a JSON body's `tag_name`. Tests point it at a `file://` fixture. |
26
26
  | `TEZGAH_UPDATE_CHECK` | `hooks/tezgah_update.py` | unset | `0` turns off the update chip and the daily check. The test suite sets it, so no test reaches the network. |
27
- | `TEZGAH_DIST` | `packaging/upgrade.sh:74`, `packaging/install.ps1` | unset: the release URL | A directory holding `tezgah-<V>.tar.gz` and its `.sha256` (what `build.sh --out` writes) used instead of the release download; CI exercises the real fetch-verify-unpack path with it. |
27
+ | `TEZGAH_DIST` | `packaging/upgrade.sh:105`, `packaging/install.ps1` | unset: the release URL | A directory holding `tezgah-<V>.tar.gz` and its `.sha256` (what `build.sh --out` writes) used instead of the release download; CI exercises the real fetch-verify-unpack path with it. |
28
28
  | `TEZGAH_GH` | `packaging/upgrade.sh` | `gh` on PATH | The `gh` that checks the tarball's build provenance (`gh attestation verify`). A path that does not exist reads as no `gh`: the upgrade prints `provenance not checked` and goes on. Tests point it at a stand-in. |
29
29
  | `TEZGAH_NO_DEPS` | `bin/tezgah-setup::main` | unset | Any value but empty or `0` is `--no-deps`: the optional tools (codegraph, orx, host CLIs) are not installed. |
30
30
  | `TEZGAH_NO_SYMLINK` | `bin/tezgah-setup::symlinks_refused` | unset | Any value but empty or `0` writes the copy layout a machine without symlinks gets, on a machine that has them. |
@@ -58,8 +58,8 @@ built by `packaging/build.sh` from the tracked `MANIFEST` plus a generated
58
58
  team can stay on one version and a rollback is one tree away: the older version is
59
59
  never removed, and going back is
60
60
  `ln -sfn <prefix>/<previous> <prefix>/current` (`packaging/upgrade.sh:9-11`,
61
- `packaging/upgrade.sh:118-137`). The prefix is `~/.local/share/tezgah` unless
62
- `TEZGAH_PREFIX` or `$XDG_DATA_HOME` says otherwise (`packaging/upgrade.sh:63`),
61
+ `packaging/upgrade.sh:176-196`). The prefix is `~/.local/share/tezgah` unless
62
+ `TEZGAH_PREFIX` or `$XDG_DATA_HOME` says otherwise (`packaging/upgrade.sh:94`),
63
63
  and `bin/tezgah-setup --prefix DIR` wins over both (`bin/tezgah-setup::INSTALL_PREFIX`).
64
64
  A farm link counts as tezgah's when it resolves into the checkout **or** into the
65
65
  install root, which is what keeps an upgrade from orphaning the wiring
@@ -68,30 +68,45 @@ install root, which is what keeps an upgrade from orphaning the wiring
68
68
  | Step | Command |
69
69
  |---|---|
70
70
  | first install, POSIX | `curl -fsSL https://raw.githubusercontent.com/r1z4x/tezgah/v<version>/packaging/install.sh \| sh -s -- --version <version>` |
71
- | from a tarball you hold | unpack it, then `sh packaging/install.sh --version <version>` — it finds `upgrade.sh` beside itself (`packaging/install.sh:30-32`) |
71
+ | from a tarball you hold | unpack it, then `sh packaging/install.sh --version <version>` — it finds `upgrade.sh` beside itself (`packaging/install.sh:34-36`) |
72
72
  | first install, Windows | `$env:TEZGAH_VERSION = '<version>'; irm https://raw.githubusercontent.com/r1z4x/tezgah/v<version>/packaging/install.ps1 \| iex` |
73
73
  | upgrade | `tezgah update` (any channel), `bin/tezgah-setup --upgrade [VERSION]` from the installed tree, or `sh packaging/upgrade.sh --version X.Y.Z` |
74
74
 
75
75
  `packaging/install.sh` is a bootstrap, never a second implementation: it runs
76
76
  `packaging/upgrade.sh` — the one place a version is fetched, checksum-verified,
77
77
  unpacked and made current — and then starts `bin/tezgah-setup --install` from the
78
- tree that just became current (`packaging/install.sh:44-56`). `upgrade.sh`
78
+ tree that just became current (`packaging/install.sh:54-66`). `upgrade.sh`
79
79
  compares the digest it fetched against the artifact before unpacking anything and
80
- stops, printing both, on a mismatch (`packaging/upgrade.sh:88-96`). It then
80
+ stops, printing both, on a mismatch (`packaging/upgrade.sh:148-152`). It then
81
81
  checks the tarball's build provenance with `gh attestation verify` when `gh` is
82
82
  on PATH. Without `gh` it prints `provenance not checked`, and a `gh` that cannot
83
83
  answer is a warning: the checksum stays the gate.
84
84
  `TEZGAH_DIST` points `upgrade.sh` and `install.ps1` at a directory holding
85
85
  `tezgah-<version>.tar.gz` + `.sha256` instead of the release URL — the seam that
86
86
  installs a locally built artifact with no published release, which is what CI
87
- uses (`packaging/upgrade.sh:17-21`, `packaging/upgrade.sh:74-78`).
87
+ uses (`packaging/upgrade.sh:17-24`, `packaging/upgrade.sh:105-111`).
88
+
89
+ **A private `TEZGAH_REPO` needs a token.** GitHub answers `404` to
90
+ `releases/download/...` from a session without one. Measured 2026-10-17: the token
91
+ in the URL and a plain bearer header answered the same. So a token in
92
+ `TEZGAH_GITHUB_TOKEN`, `GITHUB_TOKEN` or `GH_TOKEN` sends the fetch to the REST
93
+ asset endpoint instead. That endpoint wants `Accept: application/octet-stream` plus
94
+ `Authorization: Bearer`, and the script reads the asset's id out of the release
95
+ JSON of the tag. The bearer reaches curl on stdin (`-H @-`). No other user's `ps`
96
+ shows it as an argument. `packaging/install.sh` carries the same bearer for the
97
+ `raw.githubusercontent.com` bootstrap. `packaging/install.ps1` carries it for the
98
+ release JSON and the asset blobs. With no token, the public URLs stand and a failed
99
+ fetch names the token to set. The brew formula takes the same shape. It names the
100
+ asset endpoint and reads `HOMEBREW_GITHUB_API_TOKEN`. Homebrew leaves that variable
101
+ in the environment it reads a formula with, so the token resolves at evaluation.
102
+ Homebrew redacts it from the commands it logs.
88
103
 
89
104
  Windows goes through `packaging/install.ps1`, which assumes no `sh`, no `python3`
90
105
  and no symlink privilege: it unpacks with the `tar.exe` that Windows 10 1803+
91
106
  ships, materialises `current` as a directory junction, copies the tree where a
92
107
  junction is refused, and starts the installer with the first of `py -3`,
93
108
  `python`, `python3` it finds, passing `--prefix` on (`packaging/install.ps1:12-23`,
94
- `packaging/install.ps1:128-163`). The installer knows both shapes of `current`
109
+ `packaging/install.ps1:179-195`). The installer knows both shapes of `current`
95
110
  (`bin/tezgah-setup::running_prefix`). A junction answers to
96
111
  `os.path.isjunction`, or to its reparse tag below Python 3.12. A copy is the
97
112
  running tree at `<prefix>/current` with no `.git`, whose `VERSION` names a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@r1z4x/tezgah",
3
- "version": "1.9.0",
3
+ "version": "1.9.2",
4
4
  "description": "One shared working contract for every AI coding assistant you run: Claude Code, Codex, Cursor, opencode, dsh and oh-my-pi (omp).",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -27,6 +27,11 @@
27
27
 
28
28
  $env:TEZGAH_VERSION = '0.1.0'; irm https://raw.githubusercontent.com/r1z4x/tezgah/main/packaging/install.ps1 | iex
29
29
 
30
+ The tezgah repository is private, so that fetch and the release below need a
31
+ token with read access, in `TEZGAH_GITHUB_TOKEN`, `GITHUB_TOKEN` or `GH_TOKEN`:
32
+
33
+ $env:GITHUB_TOKEN = 'ghp_...'
34
+
30
35
  .EXAMPLE
31
36
  .\install.ps1 -Version 0.1.0 -Prefix D:\tezgah
32
37
  #>
@@ -59,16 +64,29 @@ function Remove-Current([string]$Path) {
59
64
  }
60
65
 
61
66
  if ($env:TEZGAH_REPO) { $Repo = $env:TEZGAH_REPO }
67
+ # A private repository serves no release file to an anonymous session, so the
68
+ # token decides which URL is read: the REST asset endpoint with it, the plain
69
+ # download URL without. `releases/download/...` answers 404 to both a token in
70
+ # the URL and a bearer header (measured 2026-10-17), so the id comes first.
71
+ $Token = $env:TEZGAH_GITHUB_TOKEN
72
+ if (-not $Token) { $Token = $env:GITHUB_TOKEN }
73
+ if (-not $Token) { $Token = $env:GH_TOKEN }
74
+ $Headers = @{}
75
+ $AssetHeaders = @{ Accept = 'application/octet-stream' }
76
+ if ($Token) {
77
+ $Headers.Authorization = "Bearer $Token"
78
+ $AssetHeaders.Authorization = "Bearer $Token"
79
+ }
62
80
  if (-not $Version) { $Version = $env:TEZGAH_VERSION }
63
81
  if (-not $Prefix) { $Prefix = $env:TEZGAH_PREFIX }
64
82
  if (-not $Prefix) { $Prefix = Join-Path $env:LOCALAPPDATA 'tezgah' } # Windows' data dir
65
83
  $Prefix = Resolve-Full $Prefix
66
84
  if (-not $Version) {
67
85
  try {
68
- $rel = Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest"
86
+ $rel = Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest" -Headers $Headers
69
87
  $Version = "$($rel.tag_name)".TrimStart('v')
70
88
  } catch {
71
- throw "tezgah: cannot read the newest release of $Repo (no release yet, or no network): $_"
89
+ throw "tezgah: cannot read the newest release of $Repo (no release yet, or no network; a private repository needs TEZGAH_GITHUB_TOKEN, GITHUB_TOKEN or GH_TOKEN): $_"
72
90
  }
73
91
  }
74
92
  if (-not $Version -or $Version -match '[\\/]' -or $Version -in @('.', '..')) {
@@ -86,13 +104,28 @@ try {
86
104
  if ($env:TEZGAH_DIST) {
87
105
  $archive = Join-Path (Resolve-Full $env:TEZGAH_DIST) $name
88
106
  if (-not (Test-Path -LiteralPath $archive)) { throw "tezgah: no $archive" }
107
+ } elseif ($Token) {
108
+ $release = Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/tags/v$Version" -Headers $Headers
109
+ $asset = $release.assets | Where-Object { $_.name -eq $name }
110
+ $sidecarAsset = $release.assets | Where-Object { $_.name -eq "$name.sha256" }
111
+ if (-not $asset -or -not $sidecarAsset) {
112
+ throw "tezgah: release v$Version of $Repo carries no $name"
113
+ }
114
+ try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } catch { }
115
+ Invoke-WebRequest -UseBasicParsing -Headers $AssetHeaders `
116
+ "https://api.github.com/repos/$Repo/releases/assets/$($asset.id)" -OutFile $archive
89
117
  } else {
90
118
  $base = "https://github.com/$Repo/releases/download/v$Version"
91
119
  try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } catch { }
92
120
  Invoke-WebRequest -UseBasicParsing "$base/$name" -OutFile $archive
93
121
  }
94
122
  $sidecar = "$archive.sha256"
95
- if (-not $env:TEZGAH_DIST) {
123
+ if ($env:TEZGAH_DIST) {
124
+ # already beside the archive
125
+ } elseif ($Token) {
126
+ Invoke-WebRequest -UseBasicParsing -Headers $AssetHeaders `
127
+ "https://api.github.com/repos/$Repo/releases/assets/$($sidecarAsset.id)" -OutFile $sidecar
128
+ } else {
96
129
  Invoke-WebRequest -UseBasicParsing "$base/$name.sha256" -OutFile $sidecar
97
130
  }
98
131
 
@@ -6,10 +6,14 @@
6
6
  # Runs packaging/upgrade.sh (the one place a version is fetched, verified,
7
7
  # unpacked and made current), then the installer from the tree it made current.
8
8
  # Needs sh and curl; tar and sha256sum are upgrade.sh's. Env, unlike its flags:
9
- # TEZGAH_REPO, TEZGAH_DIST, TEZGAH_PREFIX, as in upgrade.sh.
9
+ # TEZGAH_REPO, TEZGAH_DIST, TEZGAH_PREFIX, as in upgrade.sh, and the token below
10
+ # (a private TEZGAH_REPO serves no file without one).
10
11
  set -eu
11
12
 
12
13
  REPO=${TEZGAH_REPO:-r1z4x/tezgah}
14
+ # raw.githubusercontent.com answers 404 to a session without a token when the
15
+ # repository is private, and serves the same file to one that carries the bearer.
16
+ token=${TEZGAH_GITHUB_TOKEN:-${GITHUB_TOKEN:-${GH_TOKEN:-}}}
13
17
  VERSION_ARG=
14
18
  PREFIX_ARG=
15
19
  REPLY_ARG=
@@ -37,8 +41,14 @@ else
37
41
  [ -z "$VERSION_ARG" ] || ref=v$VERSION_ARG
38
42
  work=$(mktemp -d "${TMPDIR:-/tmp}/tezgah-install.XXXXXX") || exit 1
39
43
  up=$work/upgrade.sh
40
- curl -fsSL "https://raw.githubusercontent.com/$REPO/$ref/packaging/upgrade.sh" -o "$up" \
41
- || { echo "tezgah: cannot fetch packaging/upgrade.sh at $ref" >&2; exit 1; }
44
+ url="https://raw.githubusercontent.com/$REPO/$ref/packaging/upgrade.sh"
45
+ if [ -n "$token" ]; then
46
+ printf 'Authorization: Bearer %s\n' "$token" | curl -fsSL -H @- "$url" -o "$up" \
47
+ || { echo "tezgah: cannot fetch packaging/upgrade.sh at $ref - if $REPO is private, set TEZGAH_GITHUB_TOKEN, GITHUB_TOKEN or GH_TOKEN" >&2; exit 1; }
48
+ else
49
+ curl -fsSL "$url" -o "$up" \
50
+ || { echo "tezgah: cannot fetch packaging/upgrade.sh at $ref - if $REPO is private, set TEZGAH_GITHUB_TOKEN, GITHUB_TOKEN or GH_TOKEN" >&2; exit 1; }
51
+ fi
42
52
  fi
43
53
 
44
54
  set -- --prefix "$PREFIX" # explicit flags: a prefix may hold a space
@@ -12,13 +12,15 @@
12
12
  # Callers: packaging/install.sh (a first install) and `tezgah-setup --upgrade`.
13
13
  # Neither re-implements the fetch, the checksum or the flip.
14
14
  #
15
- # Needs: sh, curl, tar, and sha256sum or shasum. No python. Uses `gh` when it
15
+ # Needs: sh, curl, tar, awk, and sha256sum or shasum. No python. Uses `gh` when it
16
16
  # is on PATH to check the release's build provenance; without it, says so.
17
17
  # Env: TEZGAH_PREFIX, XDG_DATA_HOME, TEZGAH_VERSION, TEZGAH_REPO, TEZGAH_DIST
18
18
  # (a directory holding tezgah-<V>.tar.gz + .sha256 - what build.sh --out
19
19
  # writes; it swaps the release URL for that directory, one code path, and
20
20
  # is how upgrade.sh is exercised without a published release), TEZGAH_GH
21
- # (the gh to run for the provenance check; default `gh` on PATH).
21
+ # (the gh to run for the provenance check; default `gh` on PATH),
22
+ # TEZGAH_GITHUB_TOKEN / GITHUB_TOKEN / GH_TOKEN (the first one set is sent
23
+ # as the bearer a private $TEZGAH_REPO needs; with none, the public URLs).
22
24
  set -eu
23
25
 
24
26
  REPO=${TEZGAH_REPO:-r1z4x/tezgah}
@@ -39,10 +41,40 @@ die() {
39
41
  exit 1
40
42
  }
41
43
 
44
+ # A private repository answers 404 to `releases/download/...` from a session without
45
+ # a token (measured 2026-10-17, with the token as URL userinfo and as a header
46
+ # alike), while the REST asset endpoint returns the blob for `Accept:
47
+ # application/octet-stream` plus `Authorization: Bearer`. So the token decides which
48
+ # URL is read, and a public repository - no token - keeps exactly the URL it had.
49
+ token=${TEZGAH_GITHUB_TOKEN:-${GITHUB_TOKEN:-${GH_TOKEN:-}}}
50
+
51
+ # Every read goes through here, so the bearer cannot be forgotten on one call. It
52
+ # reaches curl on stdin (`-H @-`, curl 7.55+): an argument is in every user's `ps`.
53
+ get() { # get <url> [curl args...]
54
+ if [ -n "$token" ]; then
55
+ printf 'Authorization: Bearer %s\n' "$token" | curl -fsSL -H @- "$@"
56
+ else
57
+ curl -fsSL "$@"
58
+ fi
59
+ }
60
+
61
+ # The id of asset `$2` in the JSON of one release (`$1`). An asset's id is written
62
+ # down in one place, its own `url` field, so the JSON is flattened, split at each
63
+ # `"url":` - one fragment per asset, from its url to the next one - and the id read
64
+ # from the fragment that names the file wanted. Empty, not an error, when the
65
+ # release carries no such asset; the caller says so.
66
+ asset_id() {
67
+ printf '%s\n' "$1" | tr -d '[:space:]' | awk -v want="\"name\":\"$2\"" '
68
+ { n = split($0, o, "\"url\":")
69
+ for (i = 1; i <= n; i++)
70
+ if (index(o[i], want) && match(o[i], /"id":[0-9]+/))
71
+ { print substr(o[i], RSTART + 5, RLENGTH - 5); exit } }'
72
+ }
73
+
42
74
  # The newest release tag. The API answers `tag_name`; the raw redirect would
43
75
  # too, but GitHub answers this without a rate-limit surprise on a repeat run.
44
76
  latest_release() {
45
- body=$(curl -fsSL "https://api.github.com/repos/$REPO/releases/latest" 2>/dev/null) \
77
+ body=$(get "https://api.github.com/repos/$REPO/releases/latest" 2>/dev/null) \
46
78
  || return 1
47
79
  tag=$(printf '%s\n' "$body" \
48
80
  | sed -n 's/.*"tag_name":[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1)
@@ -77,18 +109,40 @@ if [ -n "${TEZGAH_DIST:-}" ]; then
77
109
  *) dist=$PWD/$TEZGAH_DIST ;;
78
110
  esac
79
111
  base="file://$dist"
112
+ assets=
113
+ elif [ -n "$token" ]; then
114
+ # The asset endpoint is addressed by id, and the release JSON is the only
115
+ # place that id is written down.
116
+ rel=$(get "https://api.github.com/repos/$REPO/releases/tags/v$VERSION") \
117
+ || die "cannot read release v$VERSION of $REPO with the token in TEZGAH_GITHUB_TOKEN/GITHUB_TOKEN/GH_TOKEN"
118
+ id=$(asset_id "$rel" "tezgah-$VERSION.tar.gz")
119
+ id_sidecar=$(asset_id "$rel" "tezgah-$VERSION.tar.gz.sha256")
120
+ [ -n "$id" ] && [ -n "$id_sidecar" ] \
121
+ || die "release v$VERSION of $REPO carries no tezgah-$VERSION.tar.gz"
122
+ base="https://api.github.com/repos/$REPO/releases/assets"
123
+ assets=$base
80
124
  else
81
125
  base="https://github.com/$REPO/releases/download/v$VERSION"
126
+ assets=
82
127
  fi
83
128
 
84
129
  work=$(mktemp -d "${TMPDIR:-/tmp}/tezgah-upgrade.XXXXXX") || die "cannot create a temp dir"
85
130
  trap 'rm -rf "$work"' EXIT INT TERM
86
131
 
87
132
  name=tezgah-$VERSION.tar.gz
88
- curl -fsSL "$base/$name" -o "$work/$name" \
89
- || die "cannot fetch $name from $base"
90
- curl -fsSL "$base/$name.sha256" -o "$work/$name.sha256" \
91
- || die "cannot fetch $name.sha256 from $base"
133
+ if [ -n "$assets" ]; then
134
+ # Both files come from the same release, and neither blob is served without
135
+ # this Accept: the endpoint answers JSON metadata otherwise.
136
+ get -H "Accept: application/octet-stream" "$assets/$id" -o "$work/$name" \
137
+ || die "cannot fetch $name from $assets/$id"
138
+ get -H "Accept: application/octet-stream" "$assets/$id_sidecar" -o "$work/$name.sha256" \
139
+ || die "cannot fetch $name.sha256 from $assets/$id_sidecar"
140
+ else
141
+ get "$base/$name" -o "$work/$name" \
142
+ || die "cannot fetch $name from $base - if $REPO is private, set TEZGAH_GITHUB_TOKEN, GITHUB_TOKEN or GH_TOKEN"
143
+ get "$base/$name.sha256" -o "$work/$name.sha256" \
144
+ || die "cannot fetch $name.sha256 from $base - if $REPO is private, set TEZGAH_GITHUB_TOKEN, GITHUB_TOKEN or GH_TOKEN"
145
+ fi
92
146
 
93
147
  want=$(sed -n '1s/[[:space:]].*//p' "$work/$name.sha256")
94
148
  got=$(sha256_of "$work/$name")