prep for initial release
This commit is contained in:
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Jason Swank
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
88
README.md
Normal file
88
README.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# binst
|
||||||
|
|
||||||
|
A collection of installation scripts for binary tools, generated using [binstaller](https://github.com/binary-install/binstaller). These scripts are portable POSIX shell scripts that work across Linux, macOS, and Windows (Git Bash/WSL) and are designed to be a simple, secure way to install self-contained binaries from GitHub releases.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
Install any binary using its generated script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# install trufflehog
|
||||||
|
$ scripts/trufflehog-install.sh
|
||||||
|
|
||||||
|
# install task
|
||||||
|
$ scripts/task-install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflows
|
||||||
|
|
||||||
|
Common workflows for the creation, maintantenance, usage of the installation scripts are encapsulated in [Task](https://taskfile.dev) tasks.
|
||||||
|
|
||||||
|
```
|
||||||
|
# list availale tasks
|
||||||
|
$ task --list
|
||||||
|
|
||||||
|
task: Available tasks for this project:
|
||||||
|
* default: Create a new installation script for a binary
|
||||||
|
* embed-checksums: Embed checksums into a binst configuration file.
|
||||||
|
* gen: Generate installation script from binst configuration.
|
||||||
|
* init: Initialize binst configuration for a GitHub project.
|
||||||
|
* install-*: Install a binary using its installation script.
|
||||||
|
* latest-release: Determine the latest release available for a given repo.
|
||||||
|
|
||||||
|
# install a binary
|
||||||
|
$ task install-trufflehog
|
||||||
|
```
|
||||||
|
|
||||||
|
### Creating New Installation Scripts
|
||||||
|
|
||||||
|
The default task creates a new installation script for a binary from its GitHub
|
||||||
|
repository. This is often the only step needed to add a new installation script
|
||||||
|
to the collection.
|
||||||
|
|
||||||
|
```
|
||||||
|
# create a new installation script by providing the GitHub repository as an argument:
|
||||||
|
$ task REPO=owner/repo-name
|
||||||
|
|
||||||
|
# summarize the default task
|
||||||
|
$ task --summary
|
||||||
|
task: default
|
||||||
|
|
||||||
|
Create a new installation script for a binary by initializing a binst config,
|
||||||
|
embedding checksums, and generating the installation script. The latest release, as
|
||||||
|
determined by the latest-release task, will be used unless a specific version is
|
||||||
|
provided.
|
||||||
|
|
||||||
|
Invoke this task like: task default REPO=trufflesecurity/trufflehog
|
||||||
|
|
||||||
|
vars:
|
||||||
|
CONFIG_DIR: "./config"
|
||||||
|
SCRIPT_DIR: "./scripts"
|
||||||
|
BINARY: "{{.REPO | base}}"
|
||||||
|
VERSION: "latest"
|
||||||
|
|
||||||
|
requires:
|
||||||
|
vars:
|
||||||
|
- REPO
|
||||||
|
|
||||||
|
commands:
|
||||||
|
- Task: init
|
||||||
|
- Task: embed-checksums
|
||||||
|
- Task: gen
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
### Binary Names
|
||||||
|
|
||||||
|
Sometimes the binary executable name differs from the GitHub repository name. When this occurs, the `asset.binaries` section in the configuration file defines the actual binary name. If this is the case, the generated installation script will need to be manually edited.
|
||||||
|
|
||||||
|
As an example, the released artifact for [stacklok/toolhive](https://github.com/stacklok/toolhive) is named `thv` rather than `toolhive`. The [configuration file](config/toolhive.binstaller.yml) specifies the correct name for the binary in the `asset.binaries` section.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
repo: stacklok/toolhive
|
||||||
|
asset:
|
||||||
|
binaries:
|
||||||
|
- name: thv
|
||||||
|
path: thv
|
||||||
|
```
|
||||||
@@ -6,6 +6,34 @@ vars:
|
|||||||
SCRIPT_DIR: ./scripts
|
SCRIPT_DIR: ./scripts
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
|
|
||||||
|
default:
|
||||||
|
desc: Create a new installation script for a binary
|
||||||
|
summary: |
|
||||||
|
Create a new installation script for a binary by initializing a binst config,
|
||||||
|
embedding checksums, and generating the installation script. The latest release, as
|
||||||
|
determined by the latest-release task, will be used unless a specific version is
|
||||||
|
provided.
|
||||||
|
|
||||||
|
Invoke this task like: task default REPO=trufflesecurity/trufflehog
|
||||||
|
vars:
|
||||||
|
BINARY: "{{.REPO | base}}"
|
||||||
|
VERSION: '{{.VERSION | default "latest"}}'
|
||||||
|
cmds:
|
||||||
|
- task: init
|
||||||
|
vars:
|
||||||
|
REPO: "{{.REPO}}"
|
||||||
|
- task: embed-checksums
|
||||||
|
vars:
|
||||||
|
BINARY: "{{.BINARY}}"
|
||||||
|
# use 'latest' as version unless VERSION is explicitly provided
|
||||||
|
VERSION: "{{.VERSION }}"
|
||||||
|
- task: gen
|
||||||
|
vars:
|
||||||
|
BINARY: "{{.BINARY}}"
|
||||||
|
requires:
|
||||||
|
vars: ["REPO"]
|
||||||
|
|
||||||
init:
|
init:
|
||||||
desc: Initialize binst configuration for a GitHub project.
|
desc: Initialize binst configuration for a GitHub project.
|
||||||
summary: |
|
summary: |
|
||||||
@@ -26,17 +54,40 @@ tasks:
|
|||||||
requires:
|
requires:
|
||||||
vars: ["REPO"]
|
vars: ["REPO"]
|
||||||
|
|
||||||
|
latest-release:
|
||||||
|
desc: Determine the latest release available for a given repo.
|
||||||
|
summary: |
|
||||||
|
Determine the latest release available for a given GitHub repository.
|
||||||
|
|
||||||
|
Invoke this task like:
|
||||||
|
task latest-release REPO=trufflesecurity/trufflehog
|
||||||
|
cmd: |
|
||||||
|
gh release view -R {{.REPO}} --json tagName --jq .tagName
|
||||||
|
requires:
|
||||||
|
vars: ["REPO"]
|
||||||
|
|
||||||
embed-checksums:
|
embed-checksums:
|
||||||
desc: Embed checksums into a binst configuration file.
|
desc: Embed checksums into a binst configuration file.
|
||||||
summary: |
|
summary: |
|
||||||
Embed checksums into a binst configuration file for a specific version.
|
Embed checksums into a binst configuration file for a specific version.
|
||||||
|
|
||||||
|
If VERSION is not specified, the latest release ("latest") will be used.
|
||||||
|
|
||||||
Invoke this task like:
|
Invoke this task like:
|
||||||
task embed-checksums BINARY=trufflehog VERSION=v3.92.4
|
task embed-checksums BINARY=trufflehog VERSION=v3.92.4
|
||||||
|
|
||||||
|
If --mode download fails, fallbac to --mode calculate
|
||||||
cmd: |
|
cmd: |
|
||||||
binst embed-checksums --config {{.CONFIG_DIR}}/{{.BINARY}}.binstaller.yml --version {{.VERSION}} --mode download
|
set +o errexit
|
||||||
|
binst embed-checksums --config {{.CONFIG_DIR}}/{{.BINARY}}.binstaller.yml --version {{.VERSION}} --mode download
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
binst embed-checksums --config {{.CONFIG_DIR}}/{{.BINARY}}.binstaller.yml --version {{.VERSION}} --mode calculate
|
||||||
|
fi
|
||||||
|
|
||||||
|
vars:
|
||||||
|
VERSION: "latest"
|
||||||
requires:
|
requires:
|
||||||
vars: ["BINARY", "VERSION"]
|
vars: ["BINARY"]
|
||||||
|
|
||||||
gen:
|
gen:
|
||||||
desc: Generate installation script from binst configuration.
|
desc: Generate installation script from binst configuration.
|
||||||
@@ -52,3 +103,14 @@ tasks:
|
|||||||
requires:
|
requires:
|
||||||
vars: ["BINARY"]
|
vars: ["BINARY"]
|
||||||
|
|
||||||
|
install-*:
|
||||||
|
desc: Install a binary using its installation script.
|
||||||
|
summary: |
|
||||||
|
Install a binary using its installation script located in the scripts directory.
|
||||||
|
|
||||||
|
Invoke this task like:
|
||||||
|
task install-trufflehog
|
||||||
|
cmd: |
|
||||||
|
bash {{.SCRIPT_DIR}}/{{.BINARY}}-install.sh
|
||||||
|
vars:
|
||||||
|
BINARY: "{{index .MATCH 0}}"
|
||||||
|
|||||||
25
config/aichat.binstaller.yml
Normal file
25
config/aichat.binstaller.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/binary-install/binstaller/main/schema/InstallSpec.json
|
||||||
|
schema: v1
|
||||||
|
repo: sigoden/aichat
|
||||||
|
asset:
|
||||||
|
template: aichat-${TAG}-${ARCH}-${OS}${EXT}
|
||||||
|
default_extension: .tar.gz
|
||||||
|
rules:
|
||||||
|
- when:
|
||||||
|
arch: amd64
|
||||||
|
arch: x86_64
|
||||||
|
- when:
|
||||||
|
arch: arm64
|
||||||
|
arch: aarch64
|
||||||
|
- when:
|
||||||
|
os: darwin
|
||||||
|
os: apple-darwin
|
||||||
|
- when:
|
||||||
|
os: linux
|
||||||
|
os: unknown-linux-musl
|
||||||
|
- when:
|
||||||
|
os: windows
|
||||||
|
os: pc-windows-msvc
|
||||||
|
- when:
|
||||||
|
os: windows
|
||||||
|
ext: .zip
|
||||||
39
config/binstaller.binstaller.yml
Normal file
39
config/binstaller.binstaller.yml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/binary-install/binstaller/main/schema/InstallSpec.json
|
||||||
|
schema: v1
|
||||||
|
repo: binary-install/binstaller
|
||||||
|
asset:
|
||||||
|
template: binst_${OS}_${ARCH}${EXT}
|
||||||
|
default_extension: .tar.gz
|
||||||
|
rules:
|
||||||
|
- when:
|
||||||
|
arch: amd64
|
||||||
|
arch: x86_64
|
||||||
|
- when:
|
||||||
|
os: darwin
|
||||||
|
os: Darwin
|
||||||
|
- when:
|
||||||
|
os: linux
|
||||||
|
os: Linux
|
||||||
|
- when:
|
||||||
|
os: windows
|
||||||
|
os: Windows
|
||||||
|
- when:
|
||||||
|
os: windows
|
||||||
|
ext: .zip
|
||||||
|
checksums:
|
||||||
|
algorithm: sha256
|
||||||
|
template: checksums.txt
|
||||||
|
embedded_checksums:
|
||||||
|
v0.12.0:
|
||||||
|
- filename: binst_Darwin_arm64.tar.gz
|
||||||
|
hash: 2c3a8cf9b8f95edb5d97b905ccb0204800b25f1a9bc2b4ad704596e756e41eb9
|
||||||
|
- filename: binst_Darwin_x86_64.tar.gz
|
||||||
|
hash: bd9eae149f5735671bf1f32204f5ba9f10de269a290755f21f93bf1bb5d09b03
|
||||||
|
- filename: binst_Linux_arm64.tar.gz
|
||||||
|
hash: 76bca4884ee3fa229d915473ddd53ab4d04c34c9e4b5a1eda36d9751763af916
|
||||||
|
- filename: binst_Linux_x86_64.tar.gz
|
||||||
|
hash: af36ebb92c01ad4b9867a79eb45d1a130efbce20930896ec7578b3746416ee41
|
||||||
|
- filename: binst_Windows_arm64.zip
|
||||||
|
hash: 133784089efd57f5a0164b84ac74c4924c299342c88eb40be65155193ca37761
|
||||||
|
- filename: binst_Windows_x86_64.zip
|
||||||
|
hash: afcc9eab78de6bfa1ce360aebbf1a48738c519cdbe6a9560e6979ec3a4fcb4ef
|
||||||
36
config/eksctl.binstaller.yml
Normal file
36
config/eksctl.binstaller.yml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/binary-install/binstaller/main/schema/InstallSpec.json
|
||||||
|
schema: v1
|
||||||
|
repo: eksctl-io/eksctl
|
||||||
|
asset:
|
||||||
|
template: eksctl_${OS}_${ARCH}${EXT}
|
||||||
|
default_extension: .tar.gz
|
||||||
|
rules:
|
||||||
|
- when:
|
||||||
|
os: darwin
|
||||||
|
os: Darwin
|
||||||
|
- when:
|
||||||
|
os: linux
|
||||||
|
os: Linux
|
||||||
|
- when:
|
||||||
|
os: windows
|
||||||
|
os: Windows
|
||||||
|
- when:
|
||||||
|
os: windows
|
||||||
|
ext: .zip
|
||||||
|
checksums:
|
||||||
|
algorithm: sha256
|
||||||
|
template: eksctl_checksums.txt
|
||||||
|
embedded_checksums:
|
||||||
|
v0.221.0:
|
||||||
|
- filename: eksctl_Darwin_amd64.tar.gz
|
||||||
|
hash: 9a5a3370ed79ffa646e25a0115aa7ee7fa203b284dffe058135b353be1542752
|
||||||
|
- filename: eksctl_Darwin_arm64.tar.gz
|
||||||
|
hash: 80a5f4ec7c58aad8884665318f448e88f1861a9e0eb2a467e0b879161d90bcc2
|
||||||
|
- filename: eksctl_Linux_amd64.tar.gz
|
||||||
|
hash: 9c04d16e5a0be350f9c383088ed99eac2cf1af830d25360c34588854e9e06cda
|
||||||
|
- filename: eksctl_Linux_arm64.tar.gz
|
||||||
|
hash: 3c8cfe3954c1a88a49d7f09cc03f69dceab689b1d396b9d0bcba80228d12978e
|
||||||
|
- filename: eksctl_Windows_amd64.zip
|
||||||
|
hash: c247283dc064ac55e44222191d71800f189a16b438b42cb520fb1489d1b80513
|
||||||
|
- filename: eksctl_Windows_arm64.zip
|
||||||
|
hash: f1dbc8383281c0c318e4ee99eb688438f1d7a19dbbe2b2a03e93863740dc0f73
|
||||||
30
config/gh-md-toc.binstaller.yml
Normal file
30
config/gh-md-toc.binstaller.yml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/binary-install/binstaller/main/schema/InstallSpec.json
|
||||||
|
schema: v1
|
||||||
|
repo: ekalinin/github-markdown-toc.go
|
||||||
|
asset:
|
||||||
|
template: gh-md-toc_${VERSION}_${OS}_${ARCH}${EXT}
|
||||||
|
default_extension: .tar.gz
|
||||||
|
binaries:
|
||||||
|
- name: gh-md-toc
|
||||||
|
path: gh-md-toc
|
||||||
|
checksums:
|
||||||
|
algorithm: sha256
|
||||||
|
template: checksums.txt
|
||||||
|
embedded_checksums:
|
||||||
|
v2.0.0:
|
||||||
|
- filename: gh-md-toc_2.0.0_darwin_amd64.tar.gz
|
||||||
|
hash: 622626f7880f6b604e93c06638e948500f4b458703d4066e91bae5b857934666
|
||||||
|
- filename: gh-md-toc_2.0.0_darwin_arm64.tar.gz
|
||||||
|
hash: 6a610cfe0e30e46ba49027da8eef3869fd2b98be9403b9d231df8c619bb59659
|
||||||
|
- filename: gh-md-toc_2.0.0_linux_amd64.tar.gz
|
||||||
|
hash: 7546a6e1146ba53d23a657cd6e93f2ed6ed0cc06bcf882c8f2983116f758adf5
|
||||||
|
- filename: gh-md-toc_2.0.0_linux_arm64.tar.gz
|
||||||
|
hash: 30487683256f1a0a923433b97730b74110552e857506e3fee5cc2d11ee7366ed
|
||||||
|
- filename: gh-md-toc_2.0.0_linux_armv6.tar.gz
|
||||||
|
hash: c715d4432e95dda4a06232c9b3c01e945e07ec00a11355b5f1d4804f36794bcb
|
||||||
|
- filename: gh-md-toc_2.0.0_windows_amd64.tar.gz
|
||||||
|
hash: d06b047b3314c875034f8e29f07a1c8498e3bfaab3b01cbfa06f7d37e013d222
|
||||||
|
- filename: gh-md-toc_2.0.0_windows_arm64.tar.gz
|
||||||
|
hash: 22d045c736b3fedc90e42467c92dbb43bef3d8a625f05587070f6ca0b2d4f1d1
|
||||||
|
- filename: gh-md-toc_2.0.0_windows_armv6.tar.gz
|
||||||
|
hash: 24c85ede2c0d897993174d5df9905ee6d9f78d95a0f5d36a1324ab55e0dac735
|
||||||
39
config/opentofu.binstaller.yml
Normal file
39
config/opentofu.binstaller.yml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/binary-install/binstaller/main/schema/InstallSpec.json
|
||||||
|
schema: v1
|
||||||
|
repo: opentofu/opentofu
|
||||||
|
asset:
|
||||||
|
template: tofu_${VERSION}_${OS}_${ARCH}${EXT}
|
||||||
|
default_extension: .tar.gz
|
||||||
|
checksums:
|
||||||
|
algorithm: sha256
|
||||||
|
template: tofu_${VERSION}_SHA256SUMS
|
||||||
|
embedded_checksums:
|
||||||
|
v1.11.3:
|
||||||
|
- filename: tofu_1.11.3_darwin_amd64.tar.gz
|
||||||
|
hash: c895c837af131f91ec6a493e5ab29e702bf38500ebccb5c3bf31e2ae823ab5c4
|
||||||
|
- filename: tofu_1.11.3_darwin_arm64.tar.gz
|
||||||
|
hash: ed13cc2c919a0e3532eeb71fd5cd275d44d9552d8e57f18e5867917149146490
|
||||||
|
- filename: tofu_1.11.3_freebsd_386.tar.gz
|
||||||
|
hash: 9e94e9d4c1c0c6371069e55e7d85b9b80e31fb7651bdf6d86d6f6fd3c33d078e
|
||||||
|
- filename: tofu_1.11.3_freebsd_amd64.tar.gz
|
||||||
|
hash: 8ce117bd91d28608369e7f057b7ea10c638bef1625d55eb8bbc6d0ab1a57382d
|
||||||
|
- filename: tofu_1.11.3_freebsd_arm.tar.gz
|
||||||
|
hash: 6d60c289445d11f51bff7a828a8eaa3eeed6db1209e88eadd436c49852932775
|
||||||
|
- filename: tofu_1.11.3_linux_386.tar.gz
|
||||||
|
hash: 15f5721d928af1c53a11a2cbcca700a16c3d81ddd0b1b6d205d662d3d8f6b320
|
||||||
|
- filename: tofu_1.11.3_linux_amd64.tar.gz
|
||||||
|
hash: 46b567512d4cfa631551b936f3616c02277954fece29d91a52d264537399ad03
|
||||||
|
- filename: tofu_1.11.3_linux_arm.tar.gz
|
||||||
|
hash: f670ddb67a1c187735bfe4d9e4642a1f35903e3f3548a5604fe15c0c4f3b0fb8
|
||||||
|
- filename: tofu_1.11.3_linux_arm64.tar.gz
|
||||||
|
hash: abb19fdbb4516a30f303f0686cd156f2ff768b34c8d1e433ce5e6f1d81399425
|
||||||
|
- filename: tofu_1.11.3_openbsd_386.tar.gz
|
||||||
|
hash: e2b74b05a5e3faf5f957361382b47129a411840b68be07cce216b6ce723af7aa
|
||||||
|
- filename: tofu_1.11.3_openbsd_amd64.tar.gz
|
||||||
|
hash: e4e21e05db28441c41d94267495d65ae098f791df365c6fcd2924e65d0f946d2
|
||||||
|
- filename: tofu_1.11.3_solaris_amd64.tar.gz
|
||||||
|
hash: a4a88ee5bdbd0afa47488d8dabc463733a3efd5a28be57199ead6b04fdd5bd9b
|
||||||
|
- filename: tofu_1.11.3_windows_386.tar.gz
|
||||||
|
hash: dad3090337bebc23bbb0d645c86a36316827d743ae26b5ecc925c33d2d454868
|
||||||
|
- filename: tofu_1.11.3_windows_amd64.tar.gz
|
||||||
|
hash: 5e97b41ee5d9387f1e4532b6e2de6ae244e93f70c9d48fb46ec606c7cacb7a07
|
||||||
45
config/task.binstaller.yml
Normal file
45
config/task.binstaller.yml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/binary-install/binstaller/main/schema/InstallSpec.json
|
||||||
|
schema: v1
|
||||||
|
repo: go-task/task
|
||||||
|
asset:
|
||||||
|
template: task_${OS}_${ARCH}${EXT}
|
||||||
|
default_extension: .tar.gz
|
||||||
|
rules:
|
||||||
|
- when:
|
||||||
|
os: windows
|
||||||
|
ext: .zip
|
||||||
|
checksums:
|
||||||
|
algorithm: sha256
|
||||||
|
template: task_checksums.txt
|
||||||
|
embedded_checksums:
|
||||||
|
v3.46.4:
|
||||||
|
- filename: task_darwin_amd64.tar.gz
|
||||||
|
hash: 6e3d1bf5fcc03e50b2c02c92335923b715d3f61e27fef5b3048b59ed97a18721
|
||||||
|
- filename: task_darwin_arm64.tar.gz
|
||||||
|
hash: ffe2b4a3c54467172ab15734d0b451b1fc0472d0d48bb3538870b433c700bc96
|
||||||
|
- filename: task_freebsd_386.tar.gz
|
||||||
|
hash: 37efbab3e10723fbba85114ffb5cd39938ed489b69674e15605ec96989c047b5
|
||||||
|
- filename: task_freebsd_amd64.tar.gz
|
||||||
|
hash: cd0be727b17943a53004c9984526649010aa7710cd4fac4a84b805ae25f1f35c
|
||||||
|
- filename: task_freebsd_arm.tar.gz
|
||||||
|
hash: f267218e5ae49f7bbbe41c94377b3c0694c79411b9180fccc21b43cc8ef45284
|
||||||
|
- filename: task_freebsd_arm64.tar.gz
|
||||||
|
hash: e68251346bc140f367f42a34fdc327edb63612a66bd782cd713a1c812531a13c
|
||||||
|
- filename: task_linux_386.tar.gz
|
||||||
|
hash: b0e93c6290ed91af882516117114999ff0b21ac08d8c4e7e6f63ed6a12ef95b5
|
||||||
|
- filename: task_linux_amd64.tar.gz
|
||||||
|
hash: 4b0098862292de03d568c851b207bce5fdb8a59cb533d6ed4132386151926c46
|
||||||
|
- filename: task_linux_arm.tar.gz
|
||||||
|
hash: f08d96178a6612e6c548e103c022740c82a313d04f7f90421d7550356ac8ee02
|
||||||
|
- filename: task_linux_arm64.tar.gz
|
||||||
|
hash: 9079bbdfd439f2168ee380d9167fd77f13f9fab7afb516205ac3a578f16bcb59
|
||||||
|
- filename: task_linux_riscv64.tar.gz
|
||||||
|
hash: c05fb7d47c0c5d1b8222362bf2edaae6cbe6d33c3b38f0ec034306425ee51761
|
||||||
|
- filename: task_windows_386.zip
|
||||||
|
hash: 1398d3b96f5b7dfb71b2a126dbb5849369da130099783ebd899a162ff091f6a3
|
||||||
|
- filename: task_windows_amd64.zip
|
||||||
|
hash: 790420c11a6457a27f135c292371d6a352436dfce7dba5b5ffa8ae399ecfe04b
|
||||||
|
- filename: task_windows_arm.zip
|
||||||
|
hash: 450bf906119ddf741e99c44569329b9493c4b6f55f4c3d736297b3d57826fb32
|
||||||
|
- filename: task_windows_arm64.zip
|
||||||
|
hash: 4ef119dfc6ed5133170f6fc20085f0602701a37c90b440ee54331b6370f2914d
|
||||||
30
config/toolhive.binstaller.yml
Normal file
30
config/toolhive.binstaller.yml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/binary-install/binstaller/main/schema/InstallSpec.json
|
||||||
|
schema: v1
|
||||||
|
repo: stacklok/toolhive
|
||||||
|
asset:
|
||||||
|
template: toolhive_${VERSION}_${OS}_${ARCH}${EXT}
|
||||||
|
default_extension: .tar.gz
|
||||||
|
binaries:
|
||||||
|
- name: thv
|
||||||
|
path: thv
|
||||||
|
rules:
|
||||||
|
- when:
|
||||||
|
os: windows
|
||||||
|
ext: .zip
|
||||||
|
checksums:
|
||||||
|
algorithm: sha256
|
||||||
|
template: toolhive_${VERSION}_checksums.txt
|
||||||
|
embedded_checksums:
|
||||||
|
v0.7.2:
|
||||||
|
- filename: toolhive_0.7.2_darwin_amd64.tar.gz
|
||||||
|
hash: a32b18485d33acefc0d69641a8f99e43c9d91ed45c6279cf158f220763840ef5
|
||||||
|
- filename: toolhive_0.7.2_darwin_arm64.tar.gz
|
||||||
|
hash: 20b3fd98df6abdbd4898c6aa1f2fa7d01f58901055f85c374cccf110a014be1d
|
||||||
|
- filename: toolhive_0.7.2_linux_amd64.tar.gz
|
||||||
|
hash: 84a89a735f3c5d7eaa4fe0f4cba164eb2a04b83f2525c9709dc60dc86fc9dab1
|
||||||
|
- filename: toolhive_0.7.2_linux_arm64.tar.gz
|
||||||
|
hash: 821fb80c672701b62bf3efcb4c744c685fbbde80845b6256d2116a4fe561ac21
|
||||||
|
- filename: toolhive_0.7.2_windows_amd64.zip
|
||||||
|
hash: d6d639453938383a3399888bbe0a60449ae1a5aa196b9cd2187a1ef089b4beb6
|
||||||
|
- filename: toolhive_0.7.2_windows_arm64.zip
|
||||||
|
hash: d291feff936764db8a02868461431bd54a840ecac7a78b303e30956609c54631
|
||||||
13
config/zk.binstaller.yml
Normal file
13
config/zk.binstaller.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/binary-install/binstaller/main/schema/InstallSpec.json
|
||||||
|
schema: v1
|
||||||
|
repo: zk-org/zk
|
||||||
|
asset:
|
||||||
|
template: zk-${TAG}-${OS}-${ARCH}${EXT}
|
||||||
|
default_extension: .tar.gz
|
||||||
|
rules:
|
||||||
|
- when:
|
||||||
|
arch: amd64
|
||||||
|
arch: x86_64
|
||||||
|
- when:
|
||||||
|
os: darwin
|
||||||
|
os: macos
|
||||||
620
scripts/aichat-install.sh
Executable file
620
scripts/aichat-install.sh
Executable file
@@ -0,0 +1,620 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Code generated by binstaller. DO NOT EDIT.
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
usage() {
|
||||||
|
this=$1
|
||||||
|
cat <<EOF
|
||||||
|
$this: download ${NAME} from ${REPO}
|
||||||
|
|
||||||
|
Usage: $this [-b bindir] [-d] [-q] [-n] [tag]
|
||||||
|
-b sets bindir or installation directory, Defaults to ${BINSTALLER_BIN:-${HOME}/.local/bin}
|
||||||
|
-d turns on debug logging
|
||||||
|
-q turns on quiet mode (errors only)
|
||||||
|
-n turns on dry run mode
|
||||||
|
[tag] is a tag from
|
||||||
|
https://github.com/sigoden/aichat/releases
|
||||||
|
If tag is missing, then latest will be used.
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
BINSTALLER_NO_PROGRESS=1 Disable progress indicators
|
||||||
|
BINSTALLER_OS=... Override OS detection
|
||||||
|
BINSTALLER_ARCH=... Override architecture detection
|
||||||
|
|
||||||
|
Generated by binstaller
|
||||||
|
https://github.com/binary-install/binstaller
|
||||||
|
EOF
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
https://github.com/client9/shlib - portable posix shell functions
|
||||||
|
Public domain - http://unlicense.org
|
||||||
|
https://github.com/client9/shlib/blob/master/LICENSE.md
|
||||||
|
but credit (and pull requests) appreciated.
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
is_command() {
|
||||||
|
command -v "$1" >/dev/null
|
||||||
|
}
|
||||||
|
echoerr() {
|
||||||
|
echo "$@" 1>&2
|
||||||
|
}
|
||||||
|
_logp=6
|
||||||
|
log_set_priority() {
|
||||||
|
_logp="$1"
|
||||||
|
}
|
||||||
|
log_priority() {
|
||||||
|
if test -z "$1"; then
|
||||||
|
echo "$_logp"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
[ "$1" -le "$_logp" ]
|
||||||
|
}
|
||||||
|
log_tag() {
|
||||||
|
case $1 in
|
||||||
|
0) echo "emerg" ;;
|
||||||
|
1) echo "alert" ;;
|
||||||
|
2) echo "crit" ;;
|
||||||
|
3) echo "err" ;;
|
||||||
|
4) echo "warning" ;;
|
||||||
|
5) echo "notice" ;;
|
||||||
|
6) echo "info" ;;
|
||||||
|
7) echo "debug" ;;
|
||||||
|
*) echo "$1" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
log_debug() {
|
||||||
|
log_priority 7 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
|
||||||
|
}
|
||||||
|
log_info() {
|
||||||
|
log_priority 6 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
|
||||||
|
}
|
||||||
|
log_err() {
|
||||||
|
log_priority 3 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
|
||||||
|
}
|
||||||
|
log_crit() {
|
||||||
|
log_priority 2 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
|
||||||
|
}
|
||||||
|
uname_os() {
|
||||||
|
os=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
case "$os" in
|
||||||
|
msys*) os="windows" ;;
|
||||||
|
mingw*) os="windows" ;;
|
||||||
|
cygwin*) os="windows" ;;
|
||||||
|
esac
|
||||||
|
if [ "$os" = "sunos" ]; then
|
||||||
|
if [ "$(uname -o)" = "illumos" ]; then
|
||||||
|
os="illumos"
|
||||||
|
else
|
||||||
|
os="solaris"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo "$os"
|
||||||
|
}
|
||||||
|
uname_arch() {
|
||||||
|
arch=$(uname -m)
|
||||||
|
case $arch in
|
||||||
|
x86_64) arch="amd64" ;;
|
||||||
|
i86pc) arch="amd64" ;;
|
||||||
|
x86) arch="386" ;;
|
||||||
|
i686) arch="386" ;;
|
||||||
|
i386) arch="386" ;;
|
||||||
|
aarch64) arch="arm64" ;;
|
||||||
|
armv5*) arch="armv5" ;;
|
||||||
|
armv6*) arch="armv6" ;;
|
||||||
|
armv7*) arch="armv7" ;;
|
||||||
|
esac
|
||||||
|
echo "${arch}"
|
||||||
|
}
|
||||||
|
uname_os_check() {
|
||||||
|
os=$(uname_os)
|
||||||
|
case "$os" in
|
||||||
|
darwin) return 0 ;;
|
||||||
|
dragonfly) return 0 ;;
|
||||||
|
freebsd) return 0 ;;
|
||||||
|
linux) return 0 ;;
|
||||||
|
android) return 0 ;;
|
||||||
|
midnightbsd) return 0 ;;
|
||||||
|
nacl) return 0 ;;
|
||||||
|
netbsd) return 0 ;;
|
||||||
|
openbsd) return 0 ;;
|
||||||
|
plan9) return 0 ;;
|
||||||
|
solaris) return 0 ;;
|
||||||
|
illumos) return 0 ;;
|
||||||
|
windows) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
uname_arch_check() {
|
||||||
|
arch=$(uname_arch)
|
||||||
|
case "$arch" in
|
||||||
|
386) return 0 ;;
|
||||||
|
amd64) return 0 ;;
|
||||||
|
arm64) return 0 ;;
|
||||||
|
armv5) return 0 ;;
|
||||||
|
armv6) return 0 ;;
|
||||||
|
armv7) return 0 ;;
|
||||||
|
ppc64) return 0 ;;
|
||||||
|
ppc64le) return 0 ;;
|
||||||
|
mips) return 0 ;;
|
||||||
|
mipsle) return 0 ;;
|
||||||
|
mips64) return 0 ;;
|
||||||
|
mips64le) return 0 ;;
|
||||||
|
s390x) return 0 ;;
|
||||||
|
amd64p32) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
End of functions from https://github.com/client9/shlib
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
hash_sha256() {
|
||||||
|
TARGET=${1:-/dev/stdin}
|
||||||
|
if is_command gsha256sum; then
|
||||||
|
hash=$(gsha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command sha256sum; then
|
||||||
|
hash=$(sha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command shasum; then
|
||||||
|
hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command openssl; then
|
||||||
|
hash=$(openssl dgst -sha256 "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 2
|
||||||
|
else
|
||||||
|
log_crit "hash_sha256 unable to find command to compute sha-256 hash"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_compute() {
|
||||||
|
hash_sha256 "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# shellcheck shell=sh
|
||||||
|
# Terminal progress reporting functions
|
||||||
|
progress_init() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
# OSC 9;4 sequences are safely ignored by unsupporting terminals
|
||||||
|
# Only need special handling for tmux passthrough
|
||||||
|
if [ -n "$TMUX" ]; then
|
||||||
|
# Tmux passthrough: DCS tmux; <doubled ESC sequence> ST
|
||||||
|
# ESC characters in the wrapped sequence must be doubled
|
||||||
|
# Format: ESC P tmux; ESC ESC ] 9;4; ... ESC ESC \ ESC \
|
||||||
|
PROGRESS_START=$(printf '\033Ptmux;\033\033]9;4;')
|
||||||
|
# shellcheck disable=SC1003
|
||||||
|
PROGRESS_END=$(printf '\033\033\\\033\\')
|
||||||
|
else
|
||||||
|
# Direct OSC 9;4 - terminals that don't support it will safely ignore
|
||||||
|
PROGRESS_START=$(printf '\033]9;4;')
|
||||||
|
PROGRESS_END=$(printf '\007')
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start pulsing progress animation
|
||||||
|
progress_pulse_start() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Send OSC 9;4 with state 3 (indeterminate/pulsing) once
|
||||||
|
# The terminal will handle the continuous animation
|
||||||
|
printf "%s3%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clear progress indicator
|
||||||
|
progress_clear() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
printf "%s0;%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
untar() {
|
||||||
|
tarball=$1
|
||||||
|
strip_components=${2:-0} # default 0
|
||||||
|
case "${tarball}" in
|
||||||
|
*.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.xz) tar --no-same-owner -xJf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.bz2) tar --no-same-owner -xjf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar) tar --no-same-owner -xf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.gz) gunzip "${tarball}" ;;
|
||||||
|
*.zip)
|
||||||
|
# unzip doesn't have a standard --strip-components
|
||||||
|
# Workaround: extract to a subdir and move contents up if stripping
|
||||||
|
if [ "$strip_components" -gt 0 ]; then
|
||||||
|
extract_dir=$(basename "${tarball%.zip}")_extracted
|
||||||
|
unzip -q "${tarball}" -d "${extract_dir}"
|
||||||
|
# Move contents of the *first* directory found inside extract_dir up
|
||||||
|
# This assumes wrap_in_directory=true convention
|
||||||
|
first_subdir=$(find "${extract_dir}" -mindepth 1 -maxdepth 1 -type d -print -quit)
|
||||||
|
if [ -n "$first_subdir" ]; then
|
||||||
|
# Move all contents (* includes hidden files)
|
||||||
|
mv "${first_subdir}"/* .
|
||||||
|
# Optionally remove the now-empty subdir and the extract_dir
|
||||||
|
rmdir "${first_subdir}"
|
||||||
|
rmdir "${extract_dir}"
|
||||||
|
else
|
||||||
|
log_warn "Could not find subdirectory in zip to strip components from ${extract_dir}"
|
||||||
|
# Files are extracted in current dir anyway, proceed
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
unzip -q "${tarball}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_err "untar unknown archive format for ${tarball}"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
hash_verify() {
|
||||||
|
TARGET_PATH=$1
|
||||||
|
SUMFILE=$2
|
||||||
|
if [ -z "${SUMFILE}" ]; then
|
||||||
|
log_err "hash_verify checksum file not specified in arg2"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
got=$(hash_compute "$TARGET_PATH")
|
||||||
|
if [ -z "${got}" ]; then
|
||||||
|
log_err "failed to calculate hash: ${TARGET_PATH}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BASENAME=${TARGET_PATH##*/}
|
||||||
|
|
||||||
|
# Check for line matches in checksum file
|
||||||
|
# Format: "<hash> <filename>" or "<hash> *<filename>"
|
||||||
|
# Filename may include path prefix (e.g., "deployment/m2/file.tar.gz")
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
# Normalize tabs to spaces
|
||||||
|
line=$(echo "$line" | tr '\t' ' ')
|
||||||
|
|
||||||
|
# Remove trailing spaces for hash-only line check
|
||||||
|
line_trimmed=$(echo "$line" | sed 's/[[:space:]]*$//')
|
||||||
|
|
||||||
|
# Check for hash-only line (no filename) - early return
|
||||||
|
if [ "$line_trimmed" = "$got" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract hash and filename parts
|
||||||
|
# First field is the hash, rest is filename (which may contain spaces)
|
||||||
|
line_hash=$(echo "$line" | cut -d' ' -f1)
|
||||||
|
|
||||||
|
# Skip if hash doesn't match
|
||||||
|
if [ "$line_hash" != "$got" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Hash matches, now check filename
|
||||||
|
# Remove the hash part from the beginning of the line
|
||||||
|
line_rest="${line#"$got"}"
|
||||||
|
# Remove leading spaces
|
||||||
|
while [ "${line_rest#[ ]}" != "$line_rest" ]; do
|
||||||
|
line_rest="${line_rest#[ ]}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove leading asterisk if present (binary mode indicator)
|
||||||
|
if [ "${line_rest#\*}" != "$line_rest" ]; then
|
||||||
|
line_rest="${line_rest#\*}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract just the filename without any path
|
||||||
|
line_filename="${line_rest##*/}"
|
||||||
|
|
||||||
|
# Check if the filename matches
|
||||||
|
if [ "$line_filename" = "$BASENAME" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done < "$SUMFILE"
|
||||||
|
|
||||||
|
log_err "hash_verify checksum for '$TARGET_PATH' did not verify"
|
||||||
|
log_err " Expected hash: ${got}"
|
||||||
|
log_err " Checksum file content:"
|
||||||
|
cat "$SUMFILE" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# GitHub HTTP download functions with GITHUB_TOKEN support
|
||||||
|
github_http_download_curl() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download_wget() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download() {
|
||||||
|
log_debug "github_http_download $2"
|
||||||
|
if is_command curl; then
|
||||||
|
github_http_download_curl "$@"
|
||||||
|
return
|
||||||
|
elif is_command wget; then
|
||||||
|
github_http_download_wget "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
log_crit "github_http_download unable to find wget or curl"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
github_http_copy() {
|
||||||
|
tmp=$(mktemp)
|
||||||
|
github_http_download "${tmp}" "$@" || return 1
|
||||||
|
body=$(cat "$tmp")
|
||||||
|
rm -f "${tmp}"
|
||||||
|
echo "$body"
|
||||||
|
}
|
||||||
|
github_release() {
|
||||||
|
owner_repo=$1
|
||||||
|
version=$2
|
||||||
|
test -z "$version" && version="latest"
|
||||||
|
giturl="https://github.com/${owner_repo}/releases/${version}"
|
||||||
|
json=$(github_http_copy "$giturl" "Accept:application/json")
|
||||||
|
test -z "$json" && return 1
|
||||||
|
version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
|
||||||
|
test -z "$version" && return 1
|
||||||
|
echo "$version"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Embedded Checksums (Format: VERSION:FILENAME:HASH) ---
|
||||||
|
EMBEDDED_CHECKSUMS=""
|
||||||
|
|
||||||
|
# Find embedded checksum for a given version and filename
|
||||||
|
find_embedded_checksum() {
|
||||||
|
version="$1"
|
||||||
|
filename="$2"
|
||||||
|
echo "$EMBEDDED_CHECKSUMS" | grep -E "^${version}:${filename}:" | cut -d':' -f3
|
||||||
|
}
|
||||||
|
parse_args() {
|
||||||
|
BINDIR="${BINSTALLER_BIN:-${HOME}/.local/bin}"
|
||||||
|
DRY_RUN=0
|
||||||
|
while getopts "b:dqh?xn" arg; do
|
||||||
|
case "$arg" in
|
||||||
|
b) BINDIR="$OPTARG" ;;
|
||||||
|
d) log_set_priority 10 ;;
|
||||||
|
q) log_set_priority 3 ;;
|
||||||
|
h | \?) usage "$0" ;;
|
||||||
|
x) set -x ;;
|
||||||
|
n) DRY_RUN=1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND - 1))
|
||||||
|
TAG="${1:-latest}"
|
||||||
|
}
|
||||||
|
tag_to_version() {
|
||||||
|
if [ "$TAG" = "latest" ]; then
|
||||||
|
log_info "checking GitHub for latest tag"
|
||||||
|
REALTAG=$(github_release "${REPO}" "${TAG}") && true
|
||||||
|
test -n "$REALTAG" || {
|
||||||
|
log_crit "Could not determine latest tag for ${REPO}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
# Assume TAG is a valid tag/version string
|
||||||
|
REALTAG="$TAG"
|
||||||
|
fi
|
||||||
|
if test -z "$REALTAG"; then
|
||||||
|
log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${REPO}/releases for details"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
VERSION=${REALTAG#v} # Strip leading 'v'
|
||||||
|
TAG="$REALTAG" # Use the resolved tag
|
||||||
|
log_info "Resolved version: ${VERSION} (tag: ${TAG})"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
resolve_asset_filename() {
|
||||||
|
|
||||||
|
# --- Apply Rules ---
|
||||||
|
ASSET_FILENAME=""
|
||||||
|
if [ "${UNAME_ARCH}" = 'amd64' ] && true
|
||||||
|
then
|
||||||
|
ARCH='x86_64'
|
||||||
|
fi
|
||||||
|
if [ "${UNAME_ARCH}" = 'arm64' ] && true
|
||||||
|
then
|
||||||
|
ARCH='aarch64'
|
||||||
|
fi
|
||||||
|
if [ "${UNAME_OS}" = 'darwin' ] && true
|
||||||
|
then
|
||||||
|
OS='apple-darwin'
|
||||||
|
fi
|
||||||
|
if [ "${UNAME_OS}" = 'linux' ] && true
|
||||||
|
then
|
||||||
|
OS='unknown-linux-musl'
|
||||||
|
fi
|
||||||
|
if [ "${UNAME_OS}" = 'windows' ] && true
|
||||||
|
then
|
||||||
|
OS='pc-windows-msvc'
|
||||||
|
fi
|
||||||
|
if [ "${UNAME_OS}" = 'windows' ] && true
|
||||||
|
then
|
||||||
|
EXT='.zip'
|
||||||
|
fi
|
||||||
|
if [ -z "${ASSET_FILENAME}" ]; then
|
||||||
|
ASSET_FILENAME="aichat-${TAG}-${ARCH}-${OS}${EXT}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
# Cleanup function to remove temporary files and stop progress
|
||||||
|
cleanup() {
|
||||||
|
# Stop progress animation
|
||||||
|
progress_clear
|
||||||
|
|
||||||
|
if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
|
||||||
|
log_debug "Cleaning up temporary directory: $TMPDIR"
|
||||||
|
rm -rf -- "$TMPDIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
STRIP_COMPONENTS=0
|
||||||
|
CHECKSUM_FILENAME=""
|
||||||
|
|
||||||
|
# --- Construct URLs ---
|
||||||
|
GITHUB_DOWNLOAD="https://github.com/${REPO}/releases/download"
|
||||||
|
ASSET_URL="${GITHUB_DOWNLOAD}/${TAG}/${ASSET_FILENAME}"
|
||||||
|
CHECKSUM_URL=""
|
||||||
|
if [ -n "$CHECKSUM_FILENAME" ]; then
|
||||||
|
CHECKSUM_URL="${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM_FILENAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Download and Verify ---
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
# Set up cleanup trap that includes progress clearing
|
||||||
|
trap cleanup EXIT HUP INT TERM
|
||||||
|
log_debug "Downloading files into ${TMPDIR}"
|
||||||
|
log_info "Downloading ${ASSET_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${ASSET_FILENAME}" "${ASSET_URL}"
|
||||||
|
|
||||||
|
# Try to find embedded checksum first
|
||||||
|
EMBEDDED_HASH=$(find_embedded_checksum "$VERSION" "$ASSET_FILENAME")
|
||||||
|
|
||||||
|
if [ -n "$EMBEDDED_HASH" ]; then
|
||||||
|
log_info "Using embedded checksum for verification"
|
||||||
|
|
||||||
|
# Verify using embedded hash
|
||||||
|
got=$(hash_compute "${TMPDIR}/${ASSET_FILENAME}")
|
||||||
|
if [ "$got" != "$EMBEDDED_HASH" ]; then
|
||||||
|
log_crit "Checksum verification failed for ${ASSET_FILENAME}"
|
||||||
|
log_crit "Expected: ${EMBEDDED_HASH}"
|
||||||
|
log_crit "Got: ${got}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
log_info "Checksum verification successful"
|
||||||
|
elif [ -n "$CHECKSUM_URL" ]; then
|
||||||
|
# Fall back to downloading checksum file
|
||||||
|
log_info "Downloading checksums from ${CHECKSUM_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${CHECKSUM_FILENAME}" "${CHECKSUM_URL}"
|
||||||
|
log_info "Verifying checksum ..."
|
||||||
|
hash_verify "${TMPDIR}/${ASSET_FILENAME}" "${TMPDIR}/${CHECKSUM_FILENAME}"
|
||||||
|
else
|
||||||
|
log_info "No checksum found, skipping verification."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
log_debug "Target is raw binary"
|
||||||
|
else
|
||||||
|
log_info "Extracting ${ASSET_FILENAME}..."
|
||||||
|
(cd "${TMPDIR}" && untar "${ASSET_FILENAME}" "${STRIP_COMPONENTS}")
|
||||||
|
fi
|
||||||
|
BINARY_NAME='aichat'
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
BINARY_PATH="${TMPDIR}/${ASSET_FILENAME}"
|
||||||
|
else
|
||||||
|
BINARY_PATH="${TMPDIR}/aichat"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${UNAME_OS}" = "windows" ]; then
|
||||||
|
case "${BINARY_NAME}" in *.exe) ;; *) BINARY_NAME="${BINARY_NAME}.exe" ;; esac
|
||||||
|
case "${BINARY_PATH}" in *.exe) ;; *) BINARY_PATH="${BINARY_PATH}.exe" ;; esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${BINARY_PATH}" ]; then
|
||||||
|
log_crit "Binary not found: ${BINARY_PATH}"
|
||||||
|
log_crit "Listing contents of ${TMPDIR} ..."
|
||||||
|
if command -v find >/dev/null 2>&1; then
|
||||||
|
cd "${TMPDIR}" && find .
|
||||||
|
else
|
||||||
|
cd "${TMPDIR}" && ls -R .
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
progress_clear
|
||||||
|
# Install the binary
|
||||||
|
INSTALL_PATH="${BINDIR}/${BINARY_NAME}"
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" = "1" ]; then
|
||||||
|
log_info "[DRY RUN] ${BINARY_NAME} dry-run installation succeeded! (Would install to: ${INSTALL_PATH})"
|
||||||
|
else
|
||||||
|
log_info "Installing binary to ${INSTALL_PATH}"
|
||||||
|
test ! -d "${BINDIR}" && install -d "${BINDIR}"
|
||||||
|
install "${BINARY_PATH}" "${INSTALL_PATH}"
|
||||||
|
log_info "${BINARY_NAME} installation complete!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Configuration ---
|
||||||
|
NAME='aichat'
|
||||||
|
REPO='sigoden/aichat'
|
||||||
|
EXT='.tar.gz'
|
||||||
|
|
||||||
|
# use in logging routines
|
||||||
|
log_prefix() {
|
||||||
|
echo "${REPO}"
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args "$@"
|
||||||
|
|
||||||
|
progress_init
|
||||||
|
progress_pulse_start
|
||||||
|
|
||||||
|
# --- Determine target platform ---
|
||||||
|
OS="${BINSTALLER_OS:-$(uname_os)}"
|
||||||
|
UNAME_OS="${OS}"
|
||||||
|
|
||||||
|
ARCH="${BINSTALLER_ARCH:-$(uname_arch)}"
|
||||||
|
UNAME_ARCH="${ARCH}"
|
||||||
|
log_info "Detected Platform: ${OS}/${ARCH}"
|
||||||
|
|
||||||
|
# --- Validate platform ---
|
||||||
|
uname_os_check "$OS"
|
||||||
|
uname_arch_check "$ARCH"
|
||||||
|
|
||||||
|
tag_to_version
|
||||||
|
|
||||||
|
resolve_asset_filename
|
||||||
|
execute
|
||||||
622
scripts/binstaller-install.sh
Executable file
622
scripts/binstaller-install.sh
Executable file
@@ -0,0 +1,622 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Code generated by binstaller. DO NOT EDIT.
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
usage() {
|
||||||
|
this=$1
|
||||||
|
cat <<EOF
|
||||||
|
$this: download ${NAME} from ${REPO}
|
||||||
|
|
||||||
|
Usage: $this [-b bindir] [-d] [-q] [-n] [tag]
|
||||||
|
-b sets bindir or installation directory, Defaults to ${BINSTALLER_BIN:-${HOME}/.local/bin}
|
||||||
|
-d turns on debug logging
|
||||||
|
-q turns on quiet mode (errors only)
|
||||||
|
-n turns on dry run mode
|
||||||
|
[tag] is a tag from
|
||||||
|
https://github.com/binary-install/binstaller/releases
|
||||||
|
If tag is missing, then latest will be used.
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
BINSTALLER_NO_PROGRESS=1 Disable progress indicators
|
||||||
|
BINSTALLER_OS=... Override OS detection
|
||||||
|
BINSTALLER_ARCH=... Override architecture detection
|
||||||
|
|
||||||
|
Generated by binstaller
|
||||||
|
https://github.com/binary-install/binstaller
|
||||||
|
EOF
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
https://github.com/client9/shlib - portable posix shell functions
|
||||||
|
Public domain - http://unlicense.org
|
||||||
|
https://github.com/client9/shlib/blob/master/LICENSE.md
|
||||||
|
but credit (and pull requests) appreciated.
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
is_command() {
|
||||||
|
command -v "$1" >/dev/null
|
||||||
|
}
|
||||||
|
echoerr() {
|
||||||
|
echo "$@" 1>&2
|
||||||
|
}
|
||||||
|
_logp=6
|
||||||
|
log_set_priority() {
|
||||||
|
_logp="$1"
|
||||||
|
}
|
||||||
|
log_priority() {
|
||||||
|
if test -z "$1"; then
|
||||||
|
echo "$_logp"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
[ "$1" -le "$_logp" ]
|
||||||
|
}
|
||||||
|
log_tag() {
|
||||||
|
case $1 in
|
||||||
|
0) echo "emerg" ;;
|
||||||
|
1) echo "alert" ;;
|
||||||
|
2) echo "crit" ;;
|
||||||
|
3) echo "err" ;;
|
||||||
|
4) echo "warning" ;;
|
||||||
|
5) echo "notice" ;;
|
||||||
|
6) echo "info" ;;
|
||||||
|
7) echo "debug" ;;
|
||||||
|
*) echo "$1" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
log_debug() {
|
||||||
|
log_priority 7 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
|
||||||
|
}
|
||||||
|
log_info() {
|
||||||
|
log_priority 6 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
|
||||||
|
}
|
||||||
|
log_err() {
|
||||||
|
log_priority 3 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
|
||||||
|
}
|
||||||
|
log_crit() {
|
||||||
|
log_priority 2 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
|
||||||
|
}
|
||||||
|
uname_os() {
|
||||||
|
os=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
case "$os" in
|
||||||
|
msys*) os="windows" ;;
|
||||||
|
mingw*) os="windows" ;;
|
||||||
|
cygwin*) os="windows" ;;
|
||||||
|
esac
|
||||||
|
if [ "$os" = "sunos" ]; then
|
||||||
|
if [ "$(uname -o)" = "illumos" ]; then
|
||||||
|
os="illumos"
|
||||||
|
else
|
||||||
|
os="solaris"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo "$os"
|
||||||
|
}
|
||||||
|
uname_arch() {
|
||||||
|
arch=$(uname -m)
|
||||||
|
case $arch in
|
||||||
|
x86_64) arch="amd64" ;;
|
||||||
|
i86pc) arch="amd64" ;;
|
||||||
|
x86) arch="386" ;;
|
||||||
|
i686) arch="386" ;;
|
||||||
|
i386) arch="386" ;;
|
||||||
|
aarch64) arch="arm64" ;;
|
||||||
|
armv5*) arch="armv5" ;;
|
||||||
|
armv6*) arch="armv6" ;;
|
||||||
|
armv7*) arch="armv7" ;;
|
||||||
|
esac
|
||||||
|
echo "${arch}"
|
||||||
|
}
|
||||||
|
uname_os_check() {
|
||||||
|
os=$(uname_os)
|
||||||
|
case "$os" in
|
||||||
|
darwin) return 0 ;;
|
||||||
|
dragonfly) return 0 ;;
|
||||||
|
freebsd) return 0 ;;
|
||||||
|
linux) return 0 ;;
|
||||||
|
android) return 0 ;;
|
||||||
|
midnightbsd) return 0 ;;
|
||||||
|
nacl) return 0 ;;
|
||||||
|
netbsd) return 0 ;;
|
||||||
|
openbsd) return 0 ;;
|
||||||
|
plan9) return 0 ;;
|
||||||
|
solaris) return 0 ;;
|
||||||
|
illumos) return 0 ;;
|
||||||
|
windows) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
uname_arch_check() {
|
||||||
|
arch=$(uname_arch)
|
||||||
|
case "$arch" in
|
||||||
|
386) return 0 ;;
|
||||||
|
amd64) return 0 ;;
|
||||||
|
arm64) return 0 ;;
|
||||||
|
armv5) return 0 ;;
|
||||||
|
armv6) return 0 ;;
|
||||||
|
armv7) return 0 ;;
|
||||||
|
ppc64) return 0 ;;
|
||||||
|
ppc64le) return 0 ;;
|
||||||
|
mips) return 0 ;;
|
||||||
|
mipsle) return 0 ;;
|
||||||
|
mips64) return 0 ;;
|
||||||
|
mips64le) return 0 ;;
|
||||||
|
s390x) return 0 ;;
|
||||||
|
amd64p32) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
End of functions from https://github.com/client9/shlib
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
hash_sha256() {
|
||||||
|
TARGET=${1:-/dev/stdin}
|
||||||
|
if is_command gsha256sum; then
|
||||||
|
hash=$(gsha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command sha256sum; then
|
||||||
|
hash=$(sha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command shasum; then
|
||||||
|
hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command openssl; then
|
||||||
|
hash=$(openssl dgst -sha256 "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 2
|
||||||
|
else
|
||||||
|
log_crit "hash_sha256 unable to find command to compute sha-256 hash"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_compute() {
|
||||||
|
hash_sha256 "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# shellcheck shell=sh
|
||||||
|
# Terminal progress reporting functions
|
||||||
|
progress_init() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
# OSC 9;4 sequences are safely ignored by unsupporting terminals
|
||||||
|
# Only need special handling for tmux passthrough
|
||||||
|
if [ -n "$TMUX" ]; then
|
||||||
|
# Tmux passthrough: DCS tmux; <doubled ESC sequence> ST
|
||||||
|
# ESC characters in the wrapped sequence must be doubled
|
||||||
|
# Format: ESC P tmux; ESC ESC ] 9;4; ... ESC ESC \ ESC \
|
||||||
|
PROGRESS_START=$(printf '\033Ptmux;\033\033]9;4;')
|
||||||
|
# shellcheck disable=SC1003
|
||||||
|
PROGRESS_END=$(printf '\033\033\\\033\\')
|
||||||
|
else
|
||||||
|
# Direct OSC 9;4 - terminals that don't support it will safely ignore
|
||||||
|
PROGRESS_START=$(printf '\033]9;4;')
|
||||||
|
PROGRESS_END=$(printf '\007')
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start pulsing progress animation
|
||||||
|
progress_pulse_start() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Send OSC 9;4 with state 3 (indeterminate/pulsing) once
|
||||||
|
# The terminal will handle the continuous animation
|
||||||
|
printf "%s3%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clear progress indicator
|
||||||
|
progress_clear() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
printf "%s0;%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
untar() {
|
||||||
|
tarball=$1
|
||||||
|
strip_components=${2:-0} # default 0
|
||||||
|
case "${tarball}" in
|
||||||
|
*.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.xz) tar --no-same-owner -xJf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.bz2) tar --no-same-owner -xjf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar) tar --no-same-owner -xf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.gz) gunzip "${tarball}" ;;
|
||||||
|
*.zip)
|
||||||
|
# unzip doesn't have a standard --strip-components
|
||||||
|
# Workaround: extract to a subdir and move contents up if stripping
|
||||||
|
if [ "$strip_components" -gt 0 ]; then
|
||||||
|
extract_dir=$(basename "${tarball%.zip}")_extracted
|
||||||
|
unzip -q "${tarball}" -d "${extract_dir}"
|
||||||
|
# Move contents of the *first* directory found inside extract_dir up
|
||||||
|
# This assumes wrap_in_directory=true convention
|
||||||
|
first_subdir=$(find "${extract_dir}" -mindepth 1 -maxdepth 1 -type d -print -quit)
|
||||||
|
if [ -n "$first_subdir" ]; then
|
||||||
|
# Move all contents (* includes hidden files)
|
||||||
|
mv "${first_subdir}"/* .
|
||||||
|
# Optionally remove the now-empty subdir and the extract_dir
|
||||||
|
rmdir "${first_subdir}"
|
||||||
|
rmdir "${extract_dir}"
|
||||||
|
else
|
||||||
|
log_warn "Could not find subdirectory in zip to strip components from ${extract_dir}"
|
||||||
|
# Files are extracted in current dir anyway, proceed
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
unzip -q "${tarball}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_err "untar unknown archive format for ${tarball}"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
hash_verify() {
|
||||||
|
TARGET_PATH=$1
|
||||||
|
SUMFILE=$2
|
||||||
|
if [ -z "${SUMFILE}" ]; then
|
||||||
|
log_err "hash_verify checksum file not specified in arg2"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
got=$(hash_compute "$TARGET_PATH")
|
||||||
|
if [ -z "${got}" ]; then
|
||||||
|
log_err "failed to calculate hash: ${TARGET_PATH}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BASENAME=${TARGET_PATH##*/}
|
||||||
|
|
||||||
|
# Check for line matches in checksum file
|
||||||
|
# Format: "<hash> <filename>" or "<hash> *<filename>"
|
||||||
|
# Filename may include path prefix (e.g., "deployment/m2/file.tar.gz")
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
# Normalize tabs to spaces
|
||||||
|
line=$(echo "$line" | tr '\t' ' ')
|
||||||
|
|
||||||
|
# Remove trailing spaces for hash-only line check
|
||||||
|
line_trimmed=$(echo "$line" | sed 's/[[:space:]]*$//')
|
||||||
|
|
||||||
|
# Check for hash-only line (no filename) - early return
|
||||||
|
if [ "$line_trimmed" = "$got" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract hash and filename parts
|
||||||
|
# First field is the hash, rest is filename (which may contain spaces)
|
||||||
|
line_hash=$(echo "$line" | cut -d' ' -f1)
|
||||||
|
|
||||||
|
# Skip if hash doesn't match
|
||||||
|
if [ "$line_hash" != "$got" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Hash matches, now check filename
|
||||||
|
# Remove the hash part from the beginning of the line
|
||||||
|
line_rest="${line#"$got"}"
|
||||||
|
# Remove leading spaces
|
||||||
|
while [ "${line_rest#[ ]}" != "$line_rest" ]; do
|
||||||
|
line_rest="${line_rest#[ ]}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove leading asterisk if present (binary mode indicator)
|
||||||
|
if [ "${line_rest#\*}" != "$line_rest" ]; then
|
||||||
|
line_rest="${line_rest#\*}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract just the filename without any path
|
||||||
|
line_filename="${line_rest##*/}"
|
||||||
|
|
||||||
|
# Check if the filename matches
|
||||||
|
if [ "$line_filename" = "$BASENAME" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done < "$SUMFILE"
|
||||||
|
|
||||||
|
log_err "hash_verify checksum for '$TARGET_PATH' did not verify"
|
||||||
|
log_err " Expected hash: ${got}"
|
||||||
|
log_err " Checksum file content:"
|
||||||
|
cat "$SUMFILE" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# GitHub HTTP download functions with GITHUB_TOKEN support
|
||||||
|
github_http_download_curl() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download_wget() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download() {
|
||||||
|
log_debug "github_http_download $2"
|
||||||
|
if is_command curl; then
|
||||||
|
github_http_download_curl "$@"
|
||||||
|
return
|
||||||
|
elif is_command wget; then
|
||||||
|
github_http_download_wget "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
log_crit "github_http_download unable to find wget or curl"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
github_http_copy() {
|
||||||
|
tmp=$(mktemp)
|
||||||
|
github_http_download "${tmp}" "$@" || return 1
|
||||||
|
body=$(cat "$tmp")
|
||||||
|
rm -f "${tmp}"
|
||||||
|
echo "$body"
|
||||||
|
}
|
||||||
|
github_release() {
|
||||||
|
owner_repo=$1
|
||||||
|
version=$2
|
||||||
|
test -z "$version" && version="latest"
|
||||||
|
giturl="https://github.com/${owner_repo}/releases/${version}"
|
||||||
|
json=$(github_http_copy "$giturl" "Accept:application/json")
|
||||||
|
test -z "$json" && return 1
|
||||||
|
version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
|
||||||
|
test -z "$version" && return 1
|
||||||
|
echo "$version"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Embedded Checksums (Format: VERSION:FILENAME:HASH) ---
|
||||||
|
EMBEDDED_CHECKSUMS="
|
||||||
|
0.12.0:binst_Darwin_arm64.tar.gz:2c3a8cf9b8f95edb5d97b905ccb0204800b25f1a9bc2b4ad704596e756e41eb9
|
||||||
|
0.12.0:binst_Darwin_x86_64.tar.gz:bd9eae149f5735671bf1f32204f5ba9f10de269a290755f21f93bf1bb5d09b03
|
||||||
|
0.12.0:binst_Linux_arm64.tar.gz:76bca4884ee3fa229d915473ddd53ab4d04c34c9e4b5a1eda36d9751763af916
|
||||||
|
0.12.0:binst_Linux_x86_64.tar.gz:af36ebb92c01ad4b9867a79eb45d1a130efbce20930896ec7578b3746416ee41
|
||||||
|
0.12.0:binst_Windows_arm64.zip:133784089efd57f5a0164b84ac74c4924c299342c88eb40be65155193ca37761
|
||||||
|
0.12.0:binst_Windows_x86_64.zip:afcc9eab78de6bfa1ce360aebbf1a48738c519cdbe6a9560e6979ec3a4fcb4ef"
|
||||||
|
|
||||||
|
# Find embedded checksum for a given version and filename
|
||||||
|
find_embedded_checksum() {
|
||||||
|
version="$1"
|
||||||
|
filename="$2"
|
||||||
|
echo "$EMBEDDED_CHECKSUMS" | grep -E "^${version}:${filename}:" | cut -d':' -f3
|
||||||
|
}
|
||||||
|
parse_args() {
|
||||||
|
BINDIR="${BINSTALLER_BIN:-${HOME}/.local/bin}"
|
||||||
|
DRY_RUN=0
|
||||||
|
while getopts "b:dqh?xn" arg; do
|
||||||
|
case "$arg" in
|
||||||
|
b) BINDIR="$OPTARG" ;;
|
||||||
|
d) log_set_priority 10 ;;
|
||||||
|
q) log_set_priority 3 ;;
|
||||||
|
h | \?) usage "$0" ;;
|
||||||
|
x) set -x ;;
|
||||||
|
n) DRY_RUN=1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND - 1))
|
||||||
|
TAG="${1:-latest}"
|
||||||
|
}
|
||||||
|
tag_to_version() {
|
||||||
|
if [ "$TAG" = "latest" ]; then
|
||||||
|
log_info "checking GitHub for latest tag"
|
||||||
|
REALTAG=$(github_release "${REPO}" "${TAG}") && true
|
||||||
|
test -n "$REALTAG" || {
|
||||||
|
log_crit "Could not determine latest tag for ${REPO}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
# Assume TAG is a valid tag/version string
|
||||||
|
REALTAG="$TAG"
|
||||||
|
fi
|
||||||
|
if test -z "$REALTAG"; then
|
||||||
|
log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${REPO}/releases for details"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
VERSION=${REALTAG#v} # Strip leading 'v'
|
||||||
|
TAG="$REALTAG" # Use the resolved tag
|
||||||
|
log_info "Resolved version: ${VERSION} (tag: ${TAG})"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
resolve_asset_filename() {
|
||||||
|
|
||||||
|
# --- Apply Rules ---
|
||||||
|
ASSET_FILENAME=""
|
||||||
|
if [ "${UNAME_ARCH}" = 'amd64' ] && true
|
||||||
|
then
|
||||||
|
ARCH='x86_64'
|
||||||
|
fi
|
||||||
|
if [ "${UNAME_OS}" = 'darwin' ] && true
|
||||||
|
then
|
||||||
|
OS='Darwin'
|
||||||
|
fi
|
||||||
|
if [ "${UNAME_OS}" = 'linux' ] && true
|
||||||
|
then
|
||||||
|
OS='Linux'
|
||||||
|
fi
|
||||||
|
if [ "${UNAME_OS}" = 'windows' ] && true
|
||||||
|
then
|
||||||
|
OS='Windows'
|
||||||
|
fi
|
||||||
|
if [ "${UNAME_OS}" = 'windows' ] && true
|
||||||
|
then
|
||||||
|
EXT='.zip'
|
||||||
|
fi
|
||||||
|
if [ -z "${ASSET_FILENAME}" ]; then
|
||||||
|
ASSET_FILENAME="binst_${OS}_${ARCH}${EXT}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
# Cleanup function to remove temporary files and stop progress
|
||||||
|
cleanup() {
|
||||||
|
# Stop progress animation
|
||||||
|
progress_clear
|
||||||
|
|
||||||
|
if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
|
||||||
|
log_debug "Cleaning up temporary directory: $TMPDIR"
|
||||||
|
rm -rf -- "$TMPDIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
STRIP_COMPONENTS=0
|
||||||
|
CHECKSUM_FILENAME="checksums.txt"
|
||||||
|
|
||||||
|
# --- Construct URLs ---
|
||||||
|
GITHUB_DOWNLOAD="https://github.com/${REPO}/releases/download"
|
||||||
|
ASSET_URL="${GITHUB_DOWNLOAD}/${TAG}/${ASSET_FILENAME}"
|
||||||
|
CHECKSUM_URL=""
|
||||||
|
if [ -n "$CHECKSUM_FILENAME" ]; then
|
||||||
|
CHECKSUM_URL="${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM_FILENAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Download and Verify ---
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
# Set up cleanup trap that includes progress clearing
|
||||||
|
trap cleanup EXIT HUP INT TERM
|
||||||
|
log_debug "Downloading files into ${TMPDIR}"
|
||||||
|
log_info "Downloading ${ASSET_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${ASSET_FILENAME}" "${ASSET_URL}"
|
||||||
|
|
||||||
|
# Try to find embedded checksum first
|
||||||
|
EMBEDDED_HASH=$(find_embedded_checksum "$VERSION" "$ASSET_FILENAME")
|
||||||
|
|
||||||
|
if [ -n "$EMBEDDED_HASH" ]; then
|
||||||
|
log_info "Using embedded checksum for verification"
|
||||||
|
|
||||||
|
# Verify using embedded hash
|
||||||
|
got=$(hash_compute "${TMPDIR}/${ASSET_FILENAME}")
|
||||||
|
if [ "$got" != "$EMBEDDED_HASH" ]; then
|
||||||
|
log_crit "Checksum verification failed for ${ASSET_FILENAME}"
|
||||||
|
log_crit "Expected: ${EMBEDDED_HASH}"
|
||||||
|
log_crit "Got: ${got}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
log_info "Checksum verification successful"
|
||||||
|
elif [ -n "$CHECKSUM_URL" ]; then
|
||||||
|
# Fall back to downloading checksum file
|
||||||
|
log_info "Downloading checksums from ${CHECKSUM_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${CHECKSUM_FILENAME}" "${CHECKSUM_URL}"
|
||||||
|
log_info "Verifying checksum ..."
|
||||||
|
hash_verify "${TMPDIR}/${ASSET_FILENAME}" "${TMPDIR}/${CHECKSUM_FILENAME}"
|
||||||
|
else
|
||||||
|
log_info "No checksum found, skipping verification."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
log_debug "Target is raw binary"
|
||||||
|
else
|
||||||
|
log_info "Extracting ${ASSET_FILENAME}..."
|
||||||
|
(cd "${TMPDIR}" && untar "${ASSET_FILENAME}" "${STRIP_COMPONENTS}")
|
||||||
|
fi
|
||||||
|
BINARY_NAME='binstaller'
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
BINARY_PATH="${TMPDIR}/${ASSET_FILENAME}"
|
||||||
|
else
|
||||||
|
BINARY_PATH="${TMPDIR}/binstaller"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${UNAME_OS}" = "windows" ]; then
|
||||||
|
case "${BINARY_NAME}" in *.exe) ;; *) BINARY_NAME="${BINARY_NAME}.exe" ;; esac
|
||||||
|
case "${BINARY_PATH}" in *.exe) ;; *) BINARY_PATH="${BINARY_PATH}.exe" ;; esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${BINARY_PATH}" ]; then
|
||||||
|
log_crit "Binary not found: ${BINARY_PATH}"
|
||||||
|
log_crit "Listing contents of ${TMPDIR} ..."
|
||||||
|
if command -v find >/dev/null 2>&1; then
|
||||||
|
cd "${TMPDIR}" && find .
|
||||||
|
else
|
||||||
|
cd "${TMPDIR}" && ls -R .
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
progress_clear
|
||||||
|
# Install the binary
|
||||||
|
INSTALL_PATH="${BINDIR}/${BINARY_NAME}"
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" = "1" ]; then
|
||||||
|
log_info "[DRY RUN] ${BINARY_NAME} dry-run installation succeeded! (Would install to: ${INSTALL_PATH})"
|
||||||
|
else
|
||||||
|
log_info "Installing binary to ${INSTALL_PATH}"
|
||||||
|
test ! -d "${BINDIR}" && install -d "${BINDIR}"
|
||||||
|
install "${BINARY_PATH}" "${INSTALL_PATH}"
|
||||||
|
log_info "${BINARY_NAME} installation complete!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Configuration ---
|
||||||
|
NAME='binstaller'
|
||||||
|
REPO='binary-install/binstaller'
|
||||||
|
EXT='.tar.gz'
|
||||||
|
|
||||||
|
# use in logging routines
|
||||||
|
log_prefix() {
|
||||||
|
echo "${REPO}"
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args "$@"
|
||||||
|
|
||||||
|
progress_init
|
||||||
|
progress_pulse_start
|
||||||
|
|
||||||
|
# --- Determine target platform ---
|
||||||
|
OS="${BINSTALLER_OS:-$(uname_os)}"
|
||||||
|
UNAME_OS="${OS}"
|
||||||
|
|
||||||
|
ARCH="${BINSTALLER_ARCH:-$(uname_arch)}"
|
||||||
|
UNAME_ARCH="${ARCH}"
|
||||||
|
log_info "Detected Platform: ${OS}/${ARCH}"
|
||||||
|
|
||||||
|
# --- Validate platform ---
|
||||||
|
uname_os_check "$OS"
|
||||||
|
uname_arch_check "$ARCH"
|
||||||
|
|
||||||
|
tag_to_version
|
||||||
|
|
||||||
|
resolve_asset_filename
|
||||||
|
execute
|
||||||
5
scripts/claude-install.sh
Executable file
5
scripts/claude-install.sh
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
|
||||||
|
curl -fsSL https://claude.ai/install.sh | bash
|
||||||
618
scripts/eksctl-install.sh
Executable file
618
scripts/eksctl-install.sh
Executable file
@@ -0,0 +1,618 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Code generated by binstaller. DO NOT EDIT.
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
usage() {
|
||||||
|
this=$1
|
||||||
|
cat <<EOF
|
||||||
|
$this: download ${NAME} from ${REPO}
|
||||||
|
|
||||||
|
Usage: $this [-b bindir] [-d] [-q] [-n] [tag]
|
||||||
|
-b sets bindir or installation directory, Defaults to ${BINSTALLER_BIN:-${HOME}/.local/bin}
|
||||||
|
-d turns on debug logging
|
||||||
|
-q turns on quiet mode (errors only)
|
||||||
|
-n turns on dry run mode
|
||||||
|
[tag] is a tag from
|
||||||
|
https://github.com/eksctl-io/eksctl/releases
|
||||||
|
If tag is missing, then latest will be used.
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
BINSTALLER_NO_PROGRESS=1 Disable progress indicators
|
||||||
|
BINSTALLER_OS=... Override OS detection
|
||||||
|
BINSTALLER_ARCH=... Override architecture detection
|
||||||
|
|
||||||
|
Generated by binstaller
|
||||||
|
https://github.com/binary-install/binstaller
|
||||||
|
EOF
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
https://github.com/client9/shlib - portable posix shell functions
|
||||||
|
Public domain - http://unlicense.org
|
||||||
|
https://github.com/client9/shlib/blob/master/LICENSE.md
|
||||||
|
but credit (and pull requests) appreciated.
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
is_command() {
|
||||||
|
command -v "$1" >/dev/null
|
||||||
|
}
|
||||||
|
echoerr() {
|
||||||
|
echo "$@" 1>&2
|
||||||
|
}
|
||||||
|
_logp=6
|
||||||
|
log_set_priority() {
|
||||||
|
_logp="$1"
|
||||||
|
}
|
||||||
|
log_priority() {
|
||||||
|
if test -z "$1"; then
|
||||||
|
echo "$_logp"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
[ "$1" -le "$_logp" ]
|
||||||
|
}
|
||||||
|
log_tag() {
|
||||||
|
case $1 in
|
||||||
|
0) echo "emerg" ;;
|
||||||
|
1) echo "alert" ;;
|
||||||
|
2) echo "crit" ;;
|
||||||
|
3) echo "err" ;;
|
||||||
|
4) echo "warning" ;;
|
||||||
|
5) echo "notice" ;;
|
||||||
|
6) echo "info" ;;
|
||||||
|
7) echo "debug" ;;
|
||||||
|
*) echo "$1" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
log_debug() {
|
||||||
|
log_priority 7 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
|
||||||
|
}
|
||||||
|
log_info() {
|
||||||
|
log_priority 6 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
|
||||||
|
}
|
||||||
|
log_err() {
|
||||||
|
log_priority 3 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
|
||||||
|
}
|
||||||
|
log_crit() {
|
||||||
|
log_priority 2 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
|
||||||
|
}
|
||||||
|
uname_os() {
|
||||||
|
os=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
case "$os" in
|
||||||
|
msys*) os="windows" ;;
|
||||||
|
mingw*) os="windows" ;;
|
||||||
|
cygwin*) os="windows" ;;
|
||||||
|
esac
|
||||||
|
if [ "$os" = "sunos" ]; then
|
||||||
|
if [ "$(uname -o)" = "illumos" ]; then
|
||||||
|
os="illumos"
|
||||||
|
else
|
||||||
|
os="solaris"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo "$os"
|
||||||
|
}
|
||||||
|
uname_arch() {
|
||||||
|
arch=$(uname -m)
|
||||||
|
case $arch in
|
||||||
|
x86_64) arch="amd64" ;;
|
||||||
|
i86pc) arch="amd64" ;;
|
||||||
|
x86) arch="386" ;;
|
||||||
|
i686) arch="386" ;;
|
||||||
|
i386) arch="386" ;;
|
||||||
|
aarch64) arch="arm64" ;;
|
||||||
|
armv5*) arch="armv5" ;;
|
||||||
|
armv6*) arch="armv6" ;;
|
||||||
|
armv7*) arch="armv7" ;;
|
||||||
|
esac
|
||||||
|
echo "${arch}"
|
||||||
|
}
|
||||||
|
uname_os_check() {
|
||||||
|
os=$(uname_os)
|
||||||
|
case "$os" in
|
||||||
|
darwin) return 0 ;;
|
||||||
|
dragonfly) return 0 ;;
|
||||||
|
freebsd) return 0 ;;
|
||||||
|
linux) return 0 ;;
|
||||||
|
android) return 0 ;;
|
||||||
|
midnightbsd) return 0 ;;
|
||||||
|
nacl) return 0 ;;
|
||||||
|
netbsd) return 0 ;;
|
||||||
|
openbsd) return 0 ;;
|
||||||
|
plan9) return 0 ;;
|
||||||
|
solaris) return 0 ;;
|
||||||
|
illumos) return 0 ;;
|
||||||
|
windows) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
uname_arch_check() {
|
||||||
|
arch=$(uname_arch)
|
||||||
|
case "$arch" in
|
||||||
|
386) return 0 ;;
|
||||||
|
amd64) return 0 ;;
|
||||||
|
arm64) return 0 ;;
|
||||||
|
armv5) return 0 ;;
|
||||||
|
armv6) return 0 ;;
|
||||||
|
armv7) return 0 ;;
|
||||||
|
ppc64) return 0 ;;
|
||||||
|
ppc64le) return 0 ;;
|
||||||
|
mips) return 0 ;;
|
||||||
|
mipsle) return 0 ;;
|
||||||
|
mips64) return 0 ;;
|
||||||
|
mips64le) return 0 ;;
|
||||||
|
s390x) return 0 ;;
|
||||||
|
amd64p32) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
End of functions from https://github.com/client9/shlib
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
hash_sha256() {
|
||||||
|
TARGET=${1:-/dev/stdin}
|
||||||
|
if is_command gsha256sum; then
|
||||||
|
hash=$(gsha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command sha256sum; then
|
||||||
|
hash=$(sha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command shasum; then
|
||||||
|
hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command openssl; then
|
||||||
|
hash=$(openssl dgst -sha256 "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 2
|
||||||
|
else
|
||||||
|
log_crit "hash_sha256 unable to find command to compute sha-256 hash"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_compute() {
|
||||||
|
hash_sha256 "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# shellcheck shell=sh
|
||||||
|
# Terminal progress reporting functions
|
||||||
|
progress_init() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
# OSC 9;4 sequences are safely ignored by unsupporting terminals
|
||||||
|
# Only need special handling for tmux passthrough
|
||||||
|
if [ -n "$TMUX" ]; then
|
||||||
|
# Tmux passthrough: DCS tmux; <doubled ESC sequence> ST
|
||||||
|
# ESC characters in the wrapped sequence must be doubled
|
||||||
|
# Format: ESC P tmux; ESC ESC ] 9;4; ... ESC ESC \ ESC \
|
||||||
|
PROGRESS_START=$(printf '\033Ptmux;\033\033]9;4;')
|
||||||
|
# shellcheck disable=SC1003
|
||||||
|
PROGRESS_END=$(printf '\033\033\\\033\\')
|
||||||
|
else
|
||||||
|
# Direct OSC 9;4 - terminals that don't support it will safely ignore
|
||||||
|
PROGRESS_START=$(printf '\033]9;4;')
|
||||||
|
PROGRESS_END=$(printf '\007')
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start pulsing progress animation
|
||||||
|
progress_pulse_start() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Send OSC 9;4 with state 3 (indeterminate/pulsing) once
|
||||||
|
# The terminal will handle the continuous animation
|
||||||
|
printf "%s3%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clear progress indicator
|
||||||
|
progress_clear() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
printf "%s0;%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
untar() {
|
||||||
|
tarball=$1
|
||||||
|
strip_components=${2:-0} # default 0
|
||||||
|
case "${tarball}" in
|
||||||
|
*.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.xz) tar --no-same-owner -xJf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.bz2) tar --no-same-owner -xjf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar) tar --no-same-owner -xf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.gz) gunzip "${tarball}" ;;
|
||||||
|
*.zip)
|
||||||
|
# unzip doesn't have a standard --strip-components
|
||||||
|
# Workaround: extract to a subdir and move contents up if stripping
|
||||||
|
if [ "$strip_components" -gt 0 ]; then
|
||||||
|
extract_dir=$(basename "${tarball%.zip}")_extracted
|
||||||
|
unzip -q "${tarball}" -d "${extract_dir}"
|
||||||
|
# Move contents of the *first* directory found inside extract_dir up
|
||||||
|
# This assumes wrap_in_directory=true convention
|
||||||
|
first_subdir=$(find "${extract_dir}" -mindepth 1 -maxdepth 1 -type d -print -quit)
|
||||||
|
if [ -n "$first_subdir" ]; then
|
||||||
|
# Move all contents (* includes hidden files)
|
||||||
|
mv "${first_subdir}"/* .
|
||||||
|
# Optionally remove the now-empty subdir and the extract_dir
|
||||||
|
rmdir "${first_subdir}"
|
||||||
|
rmdir "${extract_dir}"
|
||||||
|
else
|
||||||
|
log_warn "Could not find subdirectory in zip to strip components from ${extract_dir}"
|
||||||
|
# Files are extracted in current dir anyway, proceed
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
unzip -q "${tarball}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_err "untar unknown archive format for ${tarball}"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
hash_verify() {
|
||||||
|
TARGET_PATH=$1
|
||||||
|
SUMFILE=$2
|
||||||
|
if [ -z "${SUMFILE}" ]; then
|
||||||
|
log_err "hash_verify checksum file not specified in arg2"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
got=$(hash_compute "$TARGET_PATH")
|
||||||
|
if [ -z "${got}" ]; then
|
||||||
|
log_err "failed to calculate hash: ${TARGET_PATH}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BASENAME=${TARGET_PATH##*/}
|
||||||
|
|
||||||
|
# Check for line matches in checksum file
|
||||||
|
# Format: "<hash> <filename>" or "<hash> *<filename>"
|
||||||
|
# Filename may include path prefix (e.g., "deployment/m2/file.tar.gz")
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
# Normalize tabs to spaces
|
||||||
|
line=$(echo "$line" | tr '\t' ' ')
|
||||||
|
|
||||||
|
# Remove trailing spaces for hash-only line check
|
||||||
|
line_trimmed=$(echo "$line" | sed 's/[[:space:]]*$//')
|
||||||
|
|
||||||
|
# Check for hash-only line (no filename) - early return
|
||||||
|
if [ "$line_trimmed" = "$got" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract hash and filename parts
|
||||||
|
# First field is the hash, rest is filename (which may contain spaces)
|
||||||
|
line_hash=$(echo "$line" | cut -d' ' -f1)
|
||||||
|
|
||||||
|
# Skip if hash doesn't match
|
||||||
|
if [ "$line_hash" != "$got" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Hash matches, now check filename
|
||||||
|
# Remove the hash part from the beginning of the line
|
||||||
|
line_rest="${line#"$got"}"
|
||||||
|
# Remove leading spaces
|
||||||
|
while [ "${line_rest#[ ]}" != "$line_rest" ]; do
|
||||||
|
line_rest="${line_rest#[ ]}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove leading asterisk if present (binary mode indicator)
|
||||||
|
if [ "${line_rest#\*}" != "$line_rest" ]; then
|
||||||
|
line_rest="${line_rest#\*}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract just the filename without any path
|
||||||
|
line_filename="${line_rest##*/}"
|
||||||
|
|
||||||
|
# Check if the filename matches
|
||||||
|
if [ "$line_filename" = "$BASENAME" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done < "$SUMFILE"
|
||||||
|
|
||||||
|
log_err "hash_verify checksum for '$TARGET_PATH' did not verify"
|
||||||
|
log_err " Expected hash: ${got}"
|
||||||
|
log_err " Checksum file content:"
|
||||||
|
cat "$SUMFILE" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# GitHub HTTP download functions with GITHUB_TOKEN support
|
||||||
|
github_http_download_curl() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download_wget() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download() {
|
||||||
|
log_debug "github_http_download $2"
|
||||||
|
if is_command curl; then
|
||||||
|
github_http_download_curl "$@"
|
||||||
|
return
|
||||||
|
elif is_command wget; then
|
||||||
|
github_http_download_wget "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
log_crit "github_http_download unable to find wget or curl"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
github_http_copy() {
|
||||||
|
tmp=$(mktemp)
|
||||||
|
github_http_download "${tmp}" "$@" || return 1
|
||||||
|
body=$(cat "$tmp")
|
||||||
|
rm -f "${tmp}"
|
||||||
|
echo "$body"
|
||||||
|
}
|
||||||
|
github_release() {
|
||||||
|
owner_repo=$1
|
||||||
|
version=$2
|
||||||
|
test -z "$version" && version="latest"
|
||||||
|
giturl="https://github.com/${owner_repo}/releases/${version}"
|
||||||
|
json=$(github_http_copy "$giturl" "Accept:application/json")
|
||||||
|
test -z "$json" && return 1
|
||||||
|
version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
|
||||||
|
test -z "$version" && return 1
|
||||||
|
echo "$version"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Embedded Checksums (Format: VERSION:FILENAME:HASH) ---
|
||||||
|
EMBEDDED_CHECKSUMS="
|
||||||
|
0.221.0:eksctl_Darwin_amd64.tar.gz:9a5a3370ed79ffa646e25a0115aa7ee7fa203b284dffe058135b353be1542752
|
||||||
|
0.221.0:eksctl_Darwin_arm64.tar.gz:80a5f4ec7c58aad8884665318f448e88f1861a9e0eb2a467e0b879161d90bcc2
|
||||||
|
0.221.0:eksctl_Linux_amd64.tar.gz:9c04d16e5a0be350f9c383088ed99eac2cf1af830d25360c34588854e9e06cda
|
||||||
|
0.221.0:eksctl_Linux_arm64.tar.gz:3c8cfe3954c1a88a49d7f09cc03f69dceab689b1d396b9d0bcba80228d12978e
|
||||||
|
0.221.0:eksctl_Windows_amd64.zip:c247283dc064ac55e44222191d71800f189a16b438b42cb520fb1489d1b80513
|
||||||
|
0.221.0:eksctl_Windows_arm64.zip:f1dbc8383281c0c318e4ee99eb688438f1d7a19dbbe2b2a03e93863740dc0f73"
|
||||||
|
|
||||||
|
# Find embedded checksum for a given version and filename
|
||||||
|
find_embedded_checksum() {
|
||||||
|
version="$1"
|
||||||
|
filename="$2"
|
||||||
|
echo "$EMBEDDED_CHECKSUMS" | grep -E "^${version}:${filename}:" | cut -d':' -f3
|
||||||
|
}
|
||||||
|
parse_args() {
|
||||||
|
BINDIR="${BINSTALLER_BIN:-${HOME}/.local/bin}"
|
||||||
|
DRY_RUN=0
|
||||||
|
while getopts "b:dqh?xn" arg; do
|
||||||
|
case "$arg" in
|
||||||
|
b) BINDIR="$OPTARG" ;;
|
||||||
|
d) log_set_priority 10 ;;
|
||||||
|
q) log_set_priority 3 ;;
|
||||||
|
h | \?) usage "$0" ;;
|
||||||
|
x) set -x ;;
|
||||||
|
n) DRY_RUN=1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND - 1))
|
||||||
|
TAG="${1:-latest}"
|
||||||
|
}
|
||||||
|
tag_to_version() {
|
||||||
|
if [ "$TAG" = "latest" ]; then
|
||||||
|
log_info "checking GitHub for latest tag"
|
||||||
|
REALTAG=$(github_release "${REPO}" "${TAG}") && true
|
||||||
|
test -n "$REALTAG" || {
|
||||||
|
log_crit "Could not determine latest tag for ${REPO}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
# Assume TAG is a valid tag/version string
|
||||||
|
REALTAG="$TAG"
|
||||||
|
fi
|
||||||
|
if test -z "$REALTAG"; then
|
||||||
|
log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${REPO}/releases for details"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
VERSION=${REALTAG#v} # Strip leading 'v'
|
||||||
|
TAG="$REALTAG" # Use the resolved tag
|
||||||
|
log_info "Resolved version: ${VERSION} (tag: ${TAG})"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
resolve_asset_filename() {
|
||||||
|
|
||||||
|
# --- Apply Rules ---
|
||||||
|
ASSET_FILENAME=""
|
||||||
|
if [ "${UNAME_OS}" = 'darwin' ] && true
|
||||||
|
then
|
||||||
|
OS='Darwin'
|
||||||
|
fi
|
||||||
|
if [ "${UNAME_OS}" = 'linux' ] && true
|
||||||
|
then
|
||||||
|
OS='Linux'
|
||||||
|
fi
|
||||||
|
if [ "${UNAME_OS}" = 'windows' ] && true
|
||||||
|
then
|
||||||
|
OS='Windows'
|
||||||
|
fi
|
||||||
|
if [ "${UNAME_OS}" = 'windows' ] && true
|
||||||
|
then
|
||||||
|
EXT='.zip'
|
||||||
|
fi
|
||||||
|
if [ -z "${ASSET_FILENAME}" ]; then
|
||||||
|
ASSET_FILENAME="eksctl_${OS}_${ARCH}${EXT}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
# Cleanup function to remove temporary files and stop progress
|
||||||
|
cleanup() {
|
||||||
|
# Stop progress animation
|
||||||
|
progress_clear
|
||||||
|
|
||||||
|
if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
|
||||||
|
log_debug "Cleaning up temporary directory: $TMPDIR"
|
||||||
|
rm -rf -- "$TMPDIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
STRIP_COMPONENTS=0
|
||||||
|
CHECKSUM_FILENAME="eksctl_checksums.txt"
|
||||||
|
|
||||||
|
# --- Construct URLs ---
|
||||||
|
GITHUB_DOWNLOAD="https://github.com/${REPO}/releases/download"
|
||||||
|
ASSET_URL="${GITHUB_DOWNLOAD}/${TAG}/${ASSET_FILENAME}"
|
||||||
|
CHECKSUM_URL=""
|
||||||
|
if [ -n "$CHECKSUM_FILENAME" ]; then
|
||||||
|
CHECKSUM_URL="${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM_FILENAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Download and Verify ---
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
# Set up cleanup trap that includes progress clearing
|
||||||
|
trap cleanup EXIT HUP INT TERM
|
||||||
|
log_debug "Downloading files into ${TMPDIR}"
|
||||||
|
log_info "Downloading ${ASSET_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${ASSET_FILENAME}" "${ASSET_URL}"
|
||||||
|
|
||||||
|
# Try to find embedded checksum first
|
||||||
|
EMBEDDED_HASH=$(find_embedded_checksum "$VERSION" "$ASSET_FILENAME")
|
||||||
|
|
||||||
|
if [ -n "$EMBEDDED_HASH" ]; then
|
||||||
|
log_info "Using embedded checksum for verification"
|
||||||
|
|
||||||
|
# Verify using embedded hash
|
||||||
|
got=$(hash_compute "${TMPDIR}/${ASSET_FILENAME}")
|
||||||
|
if [ "$got" != "$EMBEDDED_HASH" ]; then
|
||||||
|
log_crit "Checksum verification failed for ${ASSET_FILENAME}"
|
||||||
|
log_crit "Expected: ${EMBEDDED_HASH}"
|
||||||
|
log_crit "Got: ${got}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
log_info "Checksum verification successful"
|
||||||
|
elif [ -n "$CHECKSUM_URL" ]; then
|
||||||
|
# Fall back to downloading checksum file
|
||||||
|
log_info "Downloading checksums from ${CHECKSUM_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${CHECKSUM_FILENAME}" "${CHECKSUM_URL}"
|
||||||
|
log_info "Verifying checksum ..."
|
||||||
|
hash_verify "${TMPDIR}/${ASSET_FILENAME}" "${TMPDIR}/${CHECKSUM_FILENAME}"
|
||||||
|
else
|
||||||
|
log_info "No checksum found, skipping verification."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
log_debug "Target is raw binary"
|
||||||
|
else
|
||||||
|
log_info "Extracting ${ASSET_FILENAME}..."
|
||||||
|
(cd "${TMPDIR}" && untar "${ASSET_FILENAME}" "${STRIP_COMPONENTS}")
|
||||||
|
fi
|
||||||
|
BINARY_NAME='eksctl'
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
BINARY_PATH="${TMPDIR}/${ASSET_FILENAME}"
|
||||||
|
else
|
||||||
|
BINARY_PATH="${TMPDIR}/eksctl"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${UNAME_OS}" = "windows" ]; then
|
||||||
|
case "${BINARY_NAME}" in *.exe) ;; *) BINARY_NAME="${BINARY_NAME}.exe" ;; esac
|
||||||
|
case "${BINARY_PATH}" in *.exe) ;; *) BINARY_PATH="${BINARY_PATH}.exe" ;; esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${BINARY_PATH}" ]; then
|
||||||
|
log_crit "Binary not found: ${BINARY_PATH}"
|
||||||
|
log_crit "Listing contents of ${TMPDIR} ..."
|
||||||
|
if command -v find >/dev/null 2>&1; then
|
||||||
|
cd "${TMPDIR}" && find .
|
||||||
|
else
|
||||||
|
cd "${TMPDIR}" && ls -R .
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
progress_clear
|
||||||
|
# Install the binary
|
||||||
|
INSTALL_PATH="${BINDIR}/${BINARY_NAME}"
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" = "1" ]; then
|
||||||
|
log_info "[DRY RUN] ${BINARY_NAME} dry-run installation succeeded! (Would install to: ${INSTALL_PATH})"
|
||||||
|
else
|
||||||
|
log_info "Installing binary to ${INSTALL_PATH}"
|
||||||
|
test ! -d "${BINDIR}" && install -d "${BINDIR}"
|
||||||
|
install "${BINARY_PATH}" "${INSTALL_PATH}"
|
||||||
|
log_info "${BINARY_NAME} installation complete!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Configuration ---
|
||||||
|
NAME='eksctl'
|
||||||
|
REPO='eksctl-io/eksctl'
|
||||||
|
EXT='.tar.gz'
|
||||||
|
|
||||||
|
# use in logging routines
|
||||||
|
log_prefix() {
|
||||||
|
echo "${REPO}"
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args "$@"
|
||||||
|
|
||||||
|
progress_init
|
||||||
|
progress_pulse_start
|
||||||
|
|
||||||
|
# --- Determine target platform ---
|
||||||
|
OS="${BINSTALLER_OS:-$(uname_os)}"
|
||||||
|
UNAME_OS="${OS}"
|
||||||
|
|
||||||
|
ARCH="${BINSTALLER_ARCH:-$(uname_arch)}"
|
||||||
|
|
||||||
|
log_info "Detected Platform: ${OS}/${ARCH}"
|
||||||
|
|
||||||
|
# --- Validate platform ---
|
||||||
|
uname_os_check "$OS"
|
||||||
|
uname_arch_check "$ARCH"
|
||||||
|
|
||||||
|
tag_to_version
|
||||||
|
|
||||||
|
resolve_asset_filename
|
||||||
|
execute
|
||||||
604
scripts/gh-md-toc-install.sh
Executable file
604
scripts/gh-md-toc-install.sh
Executable file
@@ -0,0 +1,604 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Code generated by binstaller. DO NOT EDIT.
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
usage() {
|
||||||
|
this=$1
|
||||||
|
cat <<EOF
|
||||||
|
$this: download ${NAME} from ${REPO}
|
||||||
|
|
||||||
|
Usage: $this [-b bindir] [-d] [-q] [-n] [tag]
|
||||||
|
-b sets bindir or installation directory, Defaults to ${BINSTALLER_BIN:-${HOME}/.local/bin}
|
||||||
|
-d turns on debug logging
|
||||||
|
-q turns on quiet mode (errors only)
|
||||||
|
-n turns on dry run mode
|
||||||
|
[tag] is a tag from
|
||||||
|
https://github.com/ekalinin/github-markdown-toc.go/releases
|
||||||
|
If tag is missing, then latest will be used.
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
BINSTALLER_NO_PROGRESS=1 Disable progress indicators
|
||||||
|
BINSTALLER_OS=... Override OS detection
|
||||||
|
BINSTALLER_ARCH=... Override architecture detection
|
||||||
|
|
||||||
|
Generated by binstaller
|
||||||
|
https://github.com/binary-install/binstaller
|
||||||
|
EOF
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
https://github.com/client9/shlib - portable posix shell functions
|
||||||
|
Public domain - http://unlicense.org
|
||||||
|
https://github.com/client9/shlib/blob/master/LICENSE.md
|
||||||
|
but credit (and pull requests) appreciated.
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
is_command() {
|
||||||
|
command -v "$1" >/dev/null
|
||||||
|
}
|
||||||
|
echoerr() {
|
||||||
|
echo "$@" 1>&2
|
||||||
|
}
|
||||||
|
_logp=6
|
||||||
|
log_set_priority() {
|
||||||
|
_logp="$1"
|
||||||
|
}
|
||||||
|
log_priority() {
|
||||||
|
if test -z "$1"; then
|
||||||
|
echo "$_logp"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
[ "$1" -le "$_logp" ]
|
||||||
|
}
|
||||||
|
log_tag() {
|
||||||
|
case $1 in
|
||||||
|
0) echo "emerg" ;;
|
||||||
|
1) echo "alert" ;;
|
||||||
|
2) echo "crit" ;;
|
||||||
|
3) echo "err" ;;
|
||||||
|
4) echo "warning" ;;
|
||||||
|
5) echo "notice" ;;
|
||||||
|
6) echo "info" ;;
|
||||||
|
7) echo "debug" ;;
|
||||||
|
*) echo "$1" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
log_debug() {
|
||||||
|
log_priority 7 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
|
||||||
|
}
|
||||||
|
log_info() {
|
||||||
|
log_priority 6 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
|
||||||
|
}
|
||||||
|
log_err() {
|
||||||
|
log_priority 3 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
|
||||||
|
}
|
||||||
|
log_crit() {
|
||||||
|
log_priority 2 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
|
||||||
|
}
|
||||||
|
uname_os() {
|
||||||
|
os=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
case "$os" in
|
||||||
|
msys*) os="windows" ;;
|
||||||
|
mingw*) os="windows" ;;
|
||||||
|
cygwin*) os="windows" ;;
|
||||||
|
esac
|
||||||
|
if [ "$os" = "sunos" ]; then
|
||||||
|
if [ "$(uname -o)" = "illumos" ]; then
|
||||||
|
os="illumos"
|
||||||
|
else
|
||||||
|
os="solaris"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo "$os"
|
||||||
|
}
|
||||||
|
uname_arch() {
|
||||||
|
arch=$(uname -m)
|
||||||
|
case $arch in
|
||||||
|
x86_64) arch="amd64" ;;
|
||||||
|
i86pc) arch="amd64" ;;
|
||||||
|
x86) arch="386" ;;
|
||||||
|
i686) arch="386" ;;
|
||||||
|
i386) arch="386" ;;
|
||||||
|
aarch64) arch="arm64" ;;
|
||||||
|
armv5*) arch="armv5" ;;
|
||||||
|
armv6*) arch="armv6" ;;
|
||||||
|
armv7*) arch="armv7" ;;
|
||||||
|
esac
|
||||||
|
echo "${arch}"
|
||||||
|
}
|
||||||
|
uname_os_check() {
|
||||||
|
os=$(uname_os)
|
||||||
|
case "$os" in
|
||||||
|
darwin) return 0 ;;
|
||||||
|
dragonfly) return 0 ;;
|
||||||
|
freebsd) return 0 ;;
|
||||||
|
linux) return 0 ;;
|
||||||
|
android) return 0 ;;
|
||||||
|
midnightbsd) return 0 ;;
|
||||||
|
nacl) return 0 ;;
|
||||||
|
netbsd) return 0 ;;
|
||||||
|
openbsd) return 0 ;;
|
||||||
|
plan9) return 0 ;;
|
||||||
|
solaris) return 0 ;;
|
||||||
|
illumos) return 0 ;;
|
||||||
|
windows) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
uname_arch_check() {
|
||||||
|
arch=$(uname_arch)
|
||||||
|
case "$arch" in
|
||||||
|
386) return 0 ;;
|
||||||
|
amd64) return 0 ;;
|
||||||
|
arm64) return 0 ;;
|
||||||
|
armv5) return 0 ;;
|
||||||
|
armv6) return 0 ;;
|
||||||
|
armv7) return 0 ;;
|
||||||
|
ppc64) return 0 ;;
|
||||||
|
ppc64le) return 0 ;;
|
||||||
|
mips) return 0 ;;
|
||||||
|
mipsle) return 0 ;;
|
||||||
|
mips64) return 0 ;;
|
||||||
|
mips64le) return 0 ;;
|
||||||
|
s390x) return 0 ;;
|
||||||
|
amd64p32) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
End of functions from https://github.com/client9/shlib
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
hash_sha256() {
|
||||||
|
TARGET=${1:-/dev/stdin}
|
||||||
|
if is_command gsha256sum; then
|
||||||
|
hash=$(gsha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command sha256sum; then
|
||||||
|
hash=$(sha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command shasum; then
|
||||||
|
hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command openssl; then
|
||||||
|
hash=$(openssl dgst -sha256 "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 2
|
||||||
|
else
|
||||||
|
log_crit "hash_sha256 unable to find command to compute sha-256 hash"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_compute() {
|
||||||
|
hash_sha256 "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# shellcheck shell=sh
|
||||||
|
# Terminal progress reporting functions
|
||||||
|
progress_init() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
# OSC 9;4 sequences are safely ignored by unsupporting terminals
|
||||||
|
# Only need special handling for tmux passthrough
|
||||||
|
if [ -n "$TMUX" ]; then
|
||||||
|
# Tmux passthrough: DCS tmux; <doubled ESC sequence> ST
|
||||||
|
# ESC characters in the wrapped sequence must be doubled
|
||||||
|
# Format: ESC P tmux; ESC ESC ] 9;4; ... ESC ESC \ ESC \
|
||||||
|
PROGRESS_START=$(printf '\033Ptmux;\033\033]9;4;')
|
||||||
|
# shellcheck disable=SC1003
|
||||||
|
PROGRESS_END=$(printf '\033\033\\\033\\')
|
||||||
|
else
|
||||||
|
# Direct OSC 9;4 - terminals that don't support it will safely ignore
|
||||||
|
PROGRESS_START=$(printf '\033]9;4;')
|
||||||
|
PROGRESS_END=$(printf '\007')
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start pulsing progress animation
|
||||||
|
progress_pulse_start() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Send OSC 9;4 with state 3 (indeterminate/pulsing) once
|
||||||
|
# The terminal will handle the continuous animation
|
||||||
|
printf "%s3%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clear progress indicator
|
||||||
|
progress_clear() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
printf "%s0;%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
untar() {
|
||||||
|
tarball=$1
|
||||||
|
strip_components=${2:-0} # default 0
|
||||||
|
case "${tarball}" in
|
||||||
|
*.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.xz) tar --no-same-owner -xJf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.bz2) tar --no-same-owner -xjf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar) tar --no-same-owner -xf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.gz) gunzip "${tarball}" ;;
|
||||||
|
*.zip)
|
||||||
|
# unzip doesn't have a standard --strip-components
|
||||||
|
# Workaround: extract to a subdir and move contents up if stripping
|
||||||
|
if [ "$strip_components" -gt 0 ]; then
|
||||||
|
extract_dir=$(basename "${tarball%.zip}")_extracted
|
||||||
|
unzip -q "${tarball}" -d "${extract_dir}"
|
||||||
|
# Move contents of the *first* directory found inside extract_dir up
|
||||||
|
# This assumes wrap_in_directory=true convention
|
||||||
|
first_subdir=$(find "${extract_dir}" -mindepth 1 -maxdepth 1 -type d -print -quit)
|
||||||
|
if [ -n "$first_subdir" ]; then
|
||||||
|
# Move all contents (* includes hidden files)
|
||||||
|
mv "${first_subdir}"/* .
|
||||||
|
# Optionally remove the now-empty subdir and the extract_dir
|
||||||
|
rmdir "${first_subdir}"
|
||||||
|
rmdir "${extract_dir}"
|
||||||
|
else
|
||||||
|
log_warn "Could not find subdirectory in zip to strip components from ${extract_dir}"
|
||||||
|
# Files are extracted in current dir anyway, proceed
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
unzip -q "${tarball}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_err "untar unknown archive format for ${tarball}"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
hash_verify() {
|
||||||
|
TARGET_PATH=$1
|
||||||
|
SUMFILE=$2
|
||||||
|
if [ -z "${SUMFILE}" ]; then
|
||||||
|
log_err "hash_verify checksum file not specified in arg2"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
got=$(hash_compute "$TARGET_PATH")
|
||||||
|
if [ -z "${got}" ]; then
|
||||||
|
log_err "failed to calculate hash: ${TARGET_PATH}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BASENAME=${TARGET_PATH##*/}
|
||||||
|
|
||||||
|
# Check for line matches in checksum file
|
||||||
|
# Format: "<hash> <filename>" or "<hash> *<filename>"
|
||||||
|
# Filename may include path prefix (e.g., "deployment/m2/file.tar.gz")
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
# Normalize tabs to spaces
|
||||||
|
line=$(echo "$line" | tr '\t' ' ')
|
||||||
|
|
||||||
|
# Remove trailing spaces for hash-only line check
|
||||||
|
line_trimmed=$(echo "$line" | sed 's/[[:space:]]*$//')
|
||||||
|
|
||||||
|
# Check for hash-only line (no filename) - early return
|
||||||
|
if [ "$line_trimmed" = "$got" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract hash and filename parts
|
||||||
|
# First field is the hash, rest is filename (which may contain spaces)
|
||||||
|
line_hash=$(echo "$line" | cut -d' ' -f1)
|
||||||
|
|
||||||
|
# Skip if hash doesn't match
|
||||||
|
if [ "$line_hash" != "$got" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Hash matches, now check filename
|
||||||
|
# Remove the hash part from the beginning of the line
|
||||||
|
line_rest="${line#"$got"}"
|
||||||
|
# Remove leading spaces
|
||||||
|
while [ "${line_rest#[ ]}" != "$line_rest" ]; do
|
||||||
|
line_rest="${line_rest#[ ]}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove leading asterisk if present (binary mode indicator)
|
||||||
|
if [ "${line_rest#\*}" != "$line_rest" ]; then
|
||||||
|
line_rest="${line_rest#\*}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract just the filename without any path
|
||||||
|
line_filename="${line_rest##*/}"
|
||||||
|
|
||||||
|
# Check if the filename matches
|
||||||
|
if [ "$line_filename" = "$BASENAME" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done < "$SUMFILE"
|
||||||
|
|
||||||
|
log_err "hash_verify checksum for '$TARGET_PATH' did not verify"
|
||||||
|
log_err " Expected hash: ${got}"
|
||||||
|
log_err " Checksum file content:"
|
||||||
|
cat "$SUMFILE" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# GitHub HTTP download functions with GITHUB_TOKEN support
|
||||||
|
github_http_download_curl() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download_wget() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download() {
|
||||||
|
log_debug "github_http_download $2"
|
||||||
|
if is_command curl; then
|
||||||
|
github_http_download_curl "$@"
|
||||||
|
return
|
||||||
|
elif is_command wget; then
|
||||||
|
github_http_download_wget "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
log_crit "github_http_download unable to find wget or curl"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
github_http_copy() {
|
||||||
|
tmp=$(mktemp)
|
||||||
|
github_http_download "${tmp}" "$@" || return 1
|
||||||
|
body=$(cat "$tmp")
|
||||||
|
rm -f "${tmp}"
|
||||||
|
echo "$body"
|
||||||
|
}
|
||||||
|
github_release() {
|
||||||
|
owner_repo=$1
|
||||||
|
version=$2
|
||||||
|
test -z "$version" && version="latest"
|
||||||
|
giturl="https://github.com/${owner_repo}/releases/${version}"
|
||||||
|
json=$(github_http_copy "$giturl" "Accept:application/json")
|
||||||
|
test -z "$json" && return 1
|
||||||
|
version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
|
||||||
|
test -z "$version" && return 1
|
||||||
|
echo "$version"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Embedded Checksums (Format: VERSION:FILENAME:HASH) ---
|
||||||
|
EMBEDDED_CHECKSUMS="
|
||||||
|
2.0.0:gh-md-toc_2.0.0_darwin_amd64.tar.gz:622626f7880f6b604e93c06638e948500f4b458703d4066e91bae5b857934666
|
||||||
|
2.0.0:gh-md-toc_2.0.0_darwin_arm64.tar.gz:6a610cfe0e30e46ba49027da8eef3869fd2b98be9403b9d231df8c619bb59659
|
||||||
|
2.0.0:gh-md-toc_2.0.0_linux_amd64.tar.gz:7546a6e1146ba53d23a657cd6e93f2ed6ed0cc06bcf882c8f2983116f758adf5
|
||||||
|
2.0.0:gh-md-toc_2.0.0_linux_arm64.tar.gz:30487683256f1a0a923433b97730b74110552e857506e3fee5cc2d11ee7366ed
|
||||||
|
2.0.0:gh-md-toc_2.0.0_linux_armv6.tar.gz:c715d4432e95dda4a06232c9b3c01e945e07ec00a11355b5f1d4804f36794bcb
|
||||||
|
2.0.0:gh-md-toc_2.0.0_windows_amd64.tar.gz:d06b047b3314c875034f8e29f07a1c8498e3bfaab3b01cbfa06f7d37e013d222
|
||||||
|
2.0.0:gh-md-toc_2.0.0_windows_arm64.tar.gz:22d045c736b3fedc90e42467c92dbb43bef3d8a625f05587070f6ca0b2d4f1d1
|
||||||
|
2.0.0:gh-md-toc_2.0.0_windows_armv6.tar.gz:24c85ede2c0d897993174d5df9905ee6d9f78d95a0f5d36a1324ab55e0dac735"
|
||||||
|
|
||||||
|
# Find embedded checksum for a given version and filename
|
||||||
|
find_embedded_checksum() {
|
||||||
|
version="$1"
|
||||||
|
filename="$2"
|
||||||
|
echo "$EMBEDDED_CHECKSUMS" | grep -E "^${version}:${filename}:" | cut -d':' -f3
|
||||||
|
}
|
||||||
|
parse_args() {
|
||||||
|
BINDIR="${BINSTALLER_BIN:-${HOME}/.local/bin}"
|
||||||
|
DRY_RUN=0
|
||||||
|
while getopts "b:dqh?xn" arg; do
|
||||||
|
case "$arg" in
|
||||||
|
b) BINDIR="$OPTARG" ;;
|
||||||
|
d) log_set_priority 10 ;;
|
||||||
|
q) log_set_priority 3 ;;
|
||||||
|
h | \?) usage "$0" ;;
|
||||||
|
x) set -x ;;
|
||||||
|
n) DRY_RUN=1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND - 1))
|
||||||
|
TAG="${1:-latest}"
|
||||||
|
}
|
||||||
|
tag_to_version() {
|
||||||
|
if [ "$TAG" = "latest" ]; then
|
||||||
|
log_info "checking GitHub for latest tag"
|
||||||
|
REALTAG=$(github_release "${REPO}" "${TAG}") && true
|
||||||
|
test -n "$REALTAG" || {
|
||||||
|
log_crit "Could not determine latest tag for ${REPO}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
# Assume TAG is a valid tag/version string
|
||||||
|
REALTAG="$TAG"
|
||||||
|
fi
|
||||||
|
if test -z "$REALTAG"; then
|
||||||
|
log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${REPO}/releases for details"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
VERSION=${REALTAG#v} # Strip leading 'v'
|
||||||
|
TAG="$REALTAG" # Use the resolved tag
|
||||||
|
log_info "Resolved version: ${VERSION} (tag: ${TAG})"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
resolve_asset_filename() {
|
||||||
|
|
||||||
|
# --- Apply Rules ---
|
||||||
|
ASSET_FILENAME=""
|
||||||
|
if [ -z "${ASSET_FILENAME}" ]; then
|
||||||
|
ASSET_FILENAME="gh-md-toc_${VERSION}_${OS}_${ARCH}${EXT}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
# Cleanup function to remove temporary files and stop progress
|
||||||
|
cleanup() {
|
||||||
|
# Stop progress animation
|
||||||
|
progress_clear
|
||||||
|
|
||||||
|
if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
|
||||||
|
log_debug "Cleaning up temporary directory: $TMPDIR"
|
||||||
|
rm -rf -- "$TMPDIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
STRIP_COMPONENTS=0
|
||||||
|
CHECKSUM_FILENAME="checksums.txt"
|
||||||
|
|
||||||
|
# --- Construct URLs ---
|
||||||
|
GITHUB_DOWNLOAD="https://github.com/${REPO}/releases/download"
|
||||||
|
ASSET_URL="${GITHUB_DOWNLOAD}/${TAG}/${ASSET_FILENAME}"
|
||||||
|
CHECKSUM_URL=""
|
||||||
|
if [ -n "$CHECKSUM_FILENAME" ]; then
|
||||||
|
CHECKSUM_URL="${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM_FILENAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Download and Verify ---
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
# Set up cleanup trap that includes progress clearing
|
||||||
|
trap cleanup EXIT HUP INT TERM
|
||||||
|
log_debug "Downloading files into ${TMPDIR}"
|
||||||
|
log_info "Downloading ${ASSET_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${ASSET_FILENAME}" "${ASSET_URL}"
|
||||||
|
|
||||||
|
# Try to find embedded checksum first
|
||||||
|
EMBEDDED_HASH=$(find_embedded_checksum "$VERSION" "$ASSET_FILENAME")
|
||||||
|
|
||||||
|
if [ -n "$EMBEDDED_HASH" ]; then
|
||||||
|
log_info "Using embedded checksum for verification"
|
||||||
|
|
||||||
|
# Verify using embedded hash
|
||||||
|
got=$(hash_compute "${TMPDIR}/${ASSET_FILENAME}")
|
||||||
|
if [ "$got" != "$EMBEDDED_HASH" ]; then
|
||||||
|
log_crit "Checksum verification failed for ${ASSET_FILENAME}"
|
||||||
|
log_crit "Expected: ${EMBEDDED_HASH}"
|
||||||
|
log_crit "Got: ${got}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
log_info "Checksum verification successful"
|
||||||
|
elif [ -n "$CHECKSUM_URL" ]; then
|
||||||
|
# Fall back to downloading checksum file
|
||||||
|
log_info "Downloading checksums from ${CHECKSUM_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${CHECKSUM_FILENAME}" "${CHECKSUM_URL}"
|
||||||
|
log_info "Verifying checksum ..."
|
||||||
|
hash_verify "${TMPDIR}/${ASSET_FILENAME}" "${TMPDIR}/${CHECKSUM_FILENAME}"
|
||||||
|
else
|
||||||
|
log_info "No checksum found, skipping verification."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
log_debug "Target is raw binary"
|
||||||
|
else
|
||||||
|
log_info "Extracting ${ASSET_FILENAME}..."
|
||||||
|
(cd "${TMPDIR}" && untar "${ASSET_FILENAME}" "${STRIP_COMPONENTS}")
|
||||||
|
fi
|
||||||
|
BINARY_NAME='gh-md-toc'
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
BINARY_PATH="${TMPDIR}/${ASSET_FILENAME}"
|
||||||
|
else
|
||||||
|
BINARY_PATH="${TMPDIR}/gh-md-toc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${UNAME_OS}" = "windows" ]; then
|
||||||
|
case "${BINARY_NAME}" in *.exe) ;; *) BINARY_NAME="${BINARY_NAME}.exe" ;; esac
|
||||||
|
case "${BINARY_PATH}" in *.exe) ;; *) BINARY_PATH="${BINARY_PATH}.exe" ;; esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${BINARY_PATH}" ]; then
|
||||||
|
log_crit "Binary not found: ${BINARY_PATH}"
|
||||||
|
log_crit "Listing contents of ${TMPDIR} ..."
|
||||||
|
if command -v find >/dev/null 2>&1; then
|
||||||
|
cd "${TMPDIR}" && find .
|
||||||
|
else
|
||||||
|
cd "${TMPDIR}" && ls -R .
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
progress_clear
|
||||||
|
# Install the binary
|
||||||
|
INSTALL_PATH="${BINDIR}/${BINARY_NAME}"
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" = "1" ]; then
|
||||||
|
log_info "[DRY RUN] ${BINARY_NAME} dry-run installation succeeded! (Would install to: ${INSTALL_PATH})"
|
||||||
|
else
|
||||||
|
log_info "Installing binary to ${INSTALL_PATH}"
|
||||||
|
test ! -d "${BINDIR}" && install -d "${BINDIR}"
|
||||||
|
install "${BINARY_PATH}" "${INSTALL_PATH}"
|
||||||
|
log_info "${BINARY_NAME} installation complete!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Configuration ---
|
||||||
|
NAME='github-markdown-toc.go'
|
||||||
|
REPO='ekalinin/github-markdown-toc.go'
|
||||||
|
EXT='.tar.gz'
|
||||||
|
|
||||||
|
# use in logging routines
|
||||||
|
log_prefix() {
|
||||||
|
echo "${REPO}"
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args "$@"
|
||||||
|
|
||||||
|
progress_init
|
||||||
|
progress_pulse_start
|
||||||
|
|
||||||
|
# --- Determine target platform ---
|
||||||
|
OS="${BINSTALLER_OS:-$(uname_os)}"
|
||||||
|
UNAME_OS="${OS}"
|
||||||
|
|
||||||
|
ARCH="${BINSTALLER_ARCH:-$(uname_arch)}"
|
||||||
|
|
||||||
|
log_info "Detected Platform: ${OS}/${ARCH}"
|
||||||
|
|
||||||
|
# --- Validate platform ---
|
||||||
|
uname_os_check "$OS"
|
||||||
|
uname_arch_check "$ARCH"
|
||||||
|
|
||||||
|
tag_to_version
|
||||||
|
|
||||||
|
resolve_asset_filename
|
||||||
|
execute
|
||||||
610
scripts/opentofu-install.sh
Executable file
610
scripts/opentofu-install.sh
Executable file
@@ -0,0 +1,610 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Code generated by binstaller. DO NOT EDIT.
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
usage() {
|
||||||
|
this=$1
|
||||||
|
cat <<EOF
|
||||||
|
$this: download ${NAME} from ${REPO}
|
||||||
|
|
||||||
|
Usage: $this [-b bindir] [-d] [-q] [-n] [tag]
|
||||||
|
-b sets bindir or installation directory, Defaults to ${BINSTALLER_BIN:-${HOME}/.local/bin}
|
||||||
|
-d turns on debug logging
|
||||||
|
-q turns on quiet mode (errors only)
|
||||||
|
-n turns on dry run mode
|
||||||
|
[tag] is a tag from
|
||||||
|
https://github.com/opentofu/opentofu/releases
|
||||||
|
If tag is missing, then latest will be used.
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
BINSTALLER_NO_PROGRESS=1 Disable progress indicators
|
||||||
|
BINSTALLER_OS=... Override OS detection
|
||||||
|
BINSTALLER_ARCH=... Override architecture detection
|
||||||
|
|
||||||
|
Generated by binstaller
|
||||||
|
https://github.com/binary-install/binstaller
|
||||||
|
EOF
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
https://github.com/client9/shlib - portable posix shell functions
|
||||||
|
Public domain - http://unlicense.org
|
||||||
|
https://github.com/client9/shlib/blob/master/LICENSE.md
|
||||||
|
but credit (and pull requests) appreciated.
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
is_command() {
|
||||||
|
command -v "$1" >/dev/null
|
||||||
|
}
|
||||||
|
echoerr() {
|
||||||
|
echo "$@" 1>&2
|
||||||
|
}
|
||||||
|
_logp=6
|
||||||
|
log_set_priority() {
|
||||||
|
_logp="$1"
|
||||||
|
}
|
||||||
|
log_priority() {
|
||||||
|
if test -z "$1"; then
|
||||||
|
echo "$_logp"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
[ "$1" -le "$_logp" ]
|
||||||
|
}
|
||||||
|
log_tag() {
|
||||||
|
case $1 in
|
||||||
|
0) echo "emerg" ;;
|
||||||
|
1) echo "alert" ;;
|
||||||
|
2) echo "crit" ;;
|
||||||
|
3) echo "err" ;;
|
||||||
|
4) echo "warning" ;;
|
||||||
|
5) echo "notice" ;;
|
||||||
|
6) echo "info" ;;
|
||||||
|
7) echo "debug" ;;
|
||||||
|
*) echo "$1" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
log_debug() {
|
||||||
|
log_priority 7 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
|
||||||
|
}
|
||||||
|
log_info() {
|
||||||
|
log_priority 6 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
|
||||||
|
}
|
||||||
|
log_err() {
|
||||||
|
log_priority 3 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
|
||||||
|
}
|
||||||
|
log_crit() {
|
||||||
|
log_priority 2 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
|
||||||
|
}
|
||||||
|
uname_os() {
|
||||||
|
os=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
case "$os" in
|
||||||
|
msys*) os="windows" ;;
|
||||||
|
mingw*) os="windows" ;;
|
||||||
|
cygwin*) os="windows" ;;
|
||||||
|
esac
|
||||||
|
if [ "$os" = "sunos" ]; then
|
||||||
|
if [ "$(uname -o)" = "illumos" ]; then
|
||||||
|
os="illumos"
|
||||||
|
else
|
||||||
|
os="solaris"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo "$os"
|
||||||
|
}
|
||||||
|
uname_arch() {
|
||||||
|
arch=$(uname -m)
|
||||||
|
case $arch in
|
||||||
|
x86_64) arch="amd64" ;;
|
||||||
|
i86pc) arch="amd64" ;;
|
||||||
|
x86) arch="386" ;;
|
||||||
|
i686) arch="386" ;;
|
||||||
|
i386) arch="386" ;;
|
||||||
|
aarch64) arch="arm64" ;;
|
||||||
|
armv5*) arch="armv5" ;;
|
||||||
|
armv6*) arch="armv6" ;;
|
||||||
|
armv7*) arch="armv7" ;;
|
||||||
|
esac
|
||||||
|
echo "${arch}"
|
||||||
|
}
|
||||||
|
uname_os_check() {
|
||||||
|
os=$(uname_os)
|
||||||
|
case "$os" in
|
||||||
|
darwin) return 0 ;;
|
||||||
|
dragonfly) return 0 ;;
|
||||||
|
freebsd) return 0 ;;
|
||||||
|
linux) return 0 ;;
|
||||||
|
android) return 0 ;;
|
||||||
|
midnightbsd) return 0 ;;
|
||||||
|
nacl) return 0 ;;
|
||||||
|
netbsd) return 0 ;;
|
||||||
|
openbsd) return 0 ;;
|
||||||
|
plan9) return 0 ;;
|
||||||
|
solaris) return 0 ;;
|
||||||
|
illumos) return 0 ;;
|
||||||
|
windows) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
uname_arch_check() {
|
||||||
|
arch=$(uname_arch)
|
||||||
|
case "$arch" in
|
||||||
|
386) return 0 ;;
|
||||||
|
amd64) return 0 ;;
|
||||||
|
arm64) return 0 ;;
|
||||||
|
armv5) return 0 ;;
|
||||||
|
armv6) return 0 ;;
|
||||||
|
armv7) return 0 ;;
|
||||||
|
ppc64) return 0 ;;
|
||||||
|
ppc64le) return 0 ;;
|
||||||
|
mips) return 0 ;;
|
||||||
|
mipsle) return 0 ;;
|
||||||
|
mips64) return 0 ;;
|
||||||
|
mips64le) return 0 ;;
|
||||||
|
s390x) return 0 ;;
|
||||||
|
amd64p32) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
End of functions from https://github.com/client9/shlib
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
hash_sha256() {
|
||||||
|
TARGET=${1:-/dev/stdin}
|
||||||
|
if is_command gsha256sum; then
|
||||||
|
hash=$(gsha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command sha256sum; then
|
||||||
|
hash=$(sha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command shasum; then
|
||||||
|
hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command openssl; then
|
||||||
|
hash=$(openssl dgst -sha256 "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 2
|
||||||
|
else
|
||||||
|
log_crit "hash_sha256 unable to find command to compute sha-256 hash"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_compute() {
|
||||||
|
hash_sha256 "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# shellcheck shell=sh
|
||||||
|
# Terminal progress reporting functions
|
||||||
|
progress_init() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
# OSC 9;4 sequences are safely ignored by unsupporting terminals
|
||||||
|
# Only need special handling for tmux passthrough
|
||||||
|
if [ -n "$TMUX" ]; then
|
||||||
|
# Tmux passthrough: DCS tmux; <doubled ESC sequence> ST
|
||||||
|
# ESC characters in the wrapped sequence must be doubled
|
||||||
|
# Format: ESC P tmux; ESC ESC ] 9;4; ... ESC ESC \ ESC \
|
||||||
|
PROGRESS_START=$(printf '\033Ptmux;\033\033]9;4;')
|
||||||
|
# shellcheck disable=SC1003
|
||||||
|
PROGRESS_END=$(printf '\033\033\\\033\\')
|
||||||
|
else
|
||||||
|
# Direct OSC 9;4 - terminals that don't support it will safely ignore
|
||||||
|
PROGRESS_START=$(printf '\033]9;4;')
|
||||||
|
PROGRESS_END=$(printf '\007')
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start pulsing progress animation
|
||||||
|
progress_pulse_start() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Send OSC 9;4 with state 3 (indeterminate/pulsing) once
|
||||||
|
# The terminal will handle the continuous animation
|
||||||
|
printf "%s3%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clear progress indicator
|
||||||
|
progress_clear() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
printf "%s0;%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
untar() {
|
||||||
|
tarball=$1
|
||||||
|
strip_components=${2:-0} # default 0
|
||||||
|
case "${tarball}" in
|
||||||
|
*.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.xz) tar --no-same-owner -xJf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.bz2) tar --no-same-owner -xjf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar) tar --no-same-owner -xf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.gz) gunzip "${tarball}" ;;
|
||||||
|
*.zip)
|
||||||
|
# unzip doesn't have a standard --strip-components
|
||||||
|
# Workaround: extract to a subdir and move contents up if stripping
|
||||||
|
if [ "$strip_components" -gt 0 ]; then
|
||||||
|
extract_dir=$(basename "${tarball%.zip}")_extracted
|
||||||
|
unzip -q "${tarball}" -d "${extract_dir}"
|
||||||
|
# Move contents of the *first* directory found inside extract_dir up
|
||||||
|
# This assumes wrap_in_directory=true convention
|
||||||
|
first_subdir=$(find "${extract_dir}" -mindepth 1 -maxdepth 1 -type d -print -quit)
|
||||||
|
if [ -n "$first_subdir" ]; then
|
||||||
|
# Move all contents (* includes hidden files)
|
||||||
|
mv "${first_subdir}"/* .
|
||||||
|
# Optionally remove the now-empty subdir and the extract_dir
|
||||||
|
rmdir "${first_subdir}"
|
||||||
|
rmdir "${extract_dir}"
|
||||||
|
else
|
||||||
|
log_warn "Could not find subdirectory in zip to strip components from ${extract_dir}"
|
||||||
|
# Files are extracted in current dir anyway, proceed
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
unzip -q "${tarball}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_err "untar unknown archive format for ${tarball}"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
hash_verify() {
|
||||||
|
TARGET_PATH=$1
|
||||||
|
SUMFILE=$2
|
||||||
|
if [ -z "${SUMFILE}" ]; then
|
||||||
|
log_err "hash_verify checksum file not specified in arg2"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
got=$(hash_compute "$TARGET_PATH")
|
||||||
|
if [ -z "${got}" ]; then
|
||||||
|
log_err "failed to calculate hash: ${TARGET_PATH}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BASENAME=${TARGET_PATH##*/}
|
||||||
|
|
||||||
|
# Check for line matches in checksum file
|
||||||
|
# Format: "<hash> <filename>" or "<hash> *<filename>"
|
||||||
|
# Filename may include path prefix (e.g., "deployment/m2/file.tar.gz")
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
# Normalize tabs to spaces
|
||||||
|
line=$(echo "$line" | tr '\t' ' ')
|
||||||
|
|
||||||
|
# Remove trailing spaces for hash-only line check
|
||||||
|
line_trimmed=$(echo "$line" | sed 's/[[:space:]]*$//')
|
||||||
|
|
||||||
|
# Check for hash-only line (no filename) - early return
|
||||||
|
if [ "$line_trimmed" = "$got" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract hash and filename parts
|
||||||
|
# First field is the hash, rest is filename (which may contain spaces)
|
||||||
|
line_hash=$(echo "$line" | cut -d' ' -f1)
|
||||||
|
|
||||||
|
# Skip if hash doesn't match
|
||||||
|
if [ "$line_hash" != "$got" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Hash matches, now check filename
|
||||||
|
# Remove the hash part from the beginning of the line
|
||||||
|
line_rest="${line#"$got"}"
|
||||||
|
# Remove leading spaces
|
||||||
|
while [ "${line_rest#[ ]}" != "$line_rest" ]; do
|
||||||
|
line_rest="${line_rest#[ ]}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove leading asterisk if present (binary mode indicator)
|
||||||
|
if [ "${line_rest#\*}" != "$line_rest" ]; then
|
||||||
|
line_rest="${line_rest#\*}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract just the filename without any path
|
||||||
|
line_filename="${line_rest##*/}"
|
||||||
|
|
||||||
|
# Check if the filename matches
|
||||||
|
if [ "$line_filename" = "$BASENAME" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done < "$SUMFILE"
|
||||||
|
|
||||||
|
log_err "hash_verify checksum for '$TARGET_PATH' did not verify"
|
||||||
|
log_err " Expected hash: ${got}"
|
||||||
|
log_err " Checksum file content:"
|
||||||
|
cat "$SUMFILE" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# GitHub HTTP download functions with GITHUB_TOKEN support
|
||||||
|
github_http_download_curl() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download_wget() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download() {
|
||||||
|
log_debug "github_http_download $2"
|
||||||
|
if is_command curl; then
|
||||||
|
github_http_download_curl "$@"
|
||||||
|
return
|
||||||
|
elif is_command wget; then
|
||||||
|
github_http_download_wget "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
log_crit "github_http_download unable to find wget or curl"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
github_http_copy() {
|
||||||
|
tmp=$(mktemp)
|
||||||
|
github_http_download "${tmp}" "$@" || return 1
|
||||||
|
body=$(cat "$tmp")
|
||||||
|
rm -f "${tmp}"
|
||||||
|
echo "$body"
|
||||||
|
}
|
||||||
|
github_release() {
|
||||||
|
owner_repo=$1
|
||||||
|
version=$2
|
||||||
|
test -z "$version" && version="latest"
|
||||||
|
giturl="https://github.com/${owner_repo}/releases/${version}"
|
||||||
|
json=$(github_http_copy "$giturl" "Accept:application/json")
|
||||||
|
test -z "$json" && return 1
|
||||||
|
version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
|
||||||
|
test -z "$version" && return 1
|
||||||
|
echo "$version"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Embedded Checksums (Format: VERSION:FILENAME:HASH) ---
|
||||||
|
EMBEDDED_CHECKSUMS="
|
||||||
|
1.11.3:tofu_1.11.3_darwin_amd64.tar.gz:c895c837af131f91ec6a493e5ab29e702bf38500ebccb5c3bf31e2ae823ab5c4
|
||||||
|
1.11.3:tofu_1.11.3_darwin_arm64.tar.gz:ed13cc2c919a0e3532eeb71fd5cd275d44d9552d8e57f18e5867917149146490
|
||||||
|
1.11.3:tofu_1.11.3_freebsd_386.tar.gz:9e94e9d4c1c0c6371069e55e7d85b9b80e31fb7651bdf6d86d6f6fd3c33d078e
|
||||||
|
1.11.3:tofu_1.11.3_freebsd_amd64.tar.gz:8ce117bd91d28608369e7f057b7ea10c638bef1625d55eb8bbc6d0ab1a57382d
|
||||||
|
1.11.3:tofu_1.11.3_freebsd_arm.tar.gz:6d60c289445d11f51bff7a828a8eaa3eeed6db1209e88eadd436c49852932775
|
||||||
|
1.11.3:tofu_1.11.3_linux_386.tar.gz:15f5721d928af1c53a11a2cbcca700a16c3d81ddd0b1b6d205d662d3d8f6b320
|
||||||
|
1.11.3:tofu_1.11.3_linux_amd64.tar.gz:46b567512d4cfa631551b936f3616c02277954fece29d91a52d264537399ad03
|
||||||
|
1.11.3:tofu_1.11.3_linux_arm.tar.gz:f670ddb67a1c187735bfe4d9e4642a1f35903e3f3548a5604fe15c0c4f3b0fb8
|
||||||
|
1.11.3:tofu_1.11.3_linux_arm64.tar.gz:abb19fdbb4516a30f303f0686cd156f2ff768b34c8d1e433ce5e6f1d81399425
|
||||||
|
1.11.3:tofu_1.11.3_openbsd_386.tar.gz:e2b74b05a5e3faf5f957361382b47129a411840b68be07cce216b6ce723af7aa
|
||||||
|
1.11.3:tofu_1.11.3_openbsd_amd64.tar.gz:e4e21e05db28441c41d94267495d65ae098f791df365c6fcd2924e65d0f946d2
|
||||||
|
1.11.3:tofu_1.11.3_solaris_amd64.tar.gz:a4a88ee5bdbd0afa47488d8dabc463733a3efd5a28be57199ead6b04fdd5bd9b
|
||||||
|
1.11.3:tofu_1.11.3_windows_386.tar.gz:dad3090337bebc23bbb0d645c86a36316827d743ae26b5ecc925c33d2d454868
|
||||||
|
1.11.3:tofu_1.11.3_windows_amd64.tar.gz:5e97b41ee5d9387f1e4532b6e2de6ae244e93f70c9d48fb46ec606c7cacb7a07"
|
||||||
|
|
||||||
|
# Find embedded checksum for a given version and filename
|
||||||
|
find_embedded_checksum() {
|
||||||
|
version="$1"
|
||||||
|
filename="$2"
|
||||||
|
echo "$EMBEDDED_CHECKSUMS" | grep -E "^${version}:${filename}:" | cut -d':' -f3
|
||||||
|
}
|
||||||
|
parse_args() {
|
||||||
|
BINDIR="${BINSTALLER_BIN:-${HOME}/.local/bin}"
|
||||||
|
DRY_RUN=0
|
||||||
|
while getopts "b:dqh?xn" arg; do
|
||||||
|
case "$arg" in
|
||||||
|
b) BINDIR="$OPTARG" ;;
|
||||||
|
d) log_set_priority 10 ;;
|
||||||
|
q) log_set_priority 3 ;;
|
||||||
|
h | \?) usage "$0" ;;
|
||||||
|
x) set -x ;;
|
||||||
|
n) DRY_RUN=1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND - 1))
|
||||||
|
TAG="${1:-latest}"
|
||||||
|
}
|
||||||
|
tag_to_version() {
|
||||||
|
if [ "$TAG" = "latest" ]; then
|
||||||
|
log_info "checking GitHub for latest tag"
|
||||||
|
REALTAG=$(github_release "${REPO}" "${TAG}") && true
|
||||||
|
test -n "$REALTAG" || {
|
||||||
|
log_crit "Could not determine latest tag for ${REPO}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
# Assume TAG is a valid tag/version string
|
||||||
|
REALTAG="$TAG"
|
||||||
|
fi
|
||||||
|
if test -z "$REALTAG"; then
|
||||||
|
log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${REPO}/releases for details"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
VERSION=${REALTAG#v} # Strip leading 'v'
|
||||||
|
TAG="$REALTAG" # Use the resolved tag
|
||||||
|
log_info "Resolved version: ${VERSION} (tag: ${TAG})"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
resolve_asset_filename() {
|
||||||
|
|
||||||
|
# --- Apply Rules ---
|
||||||
|
ASSET_FILENAME=""
|
||||||
|
if [ -z "${ASSET_FILENAME}" ]; then
|
||||||
|
ASSET_FILENAME="tofu_${VERSION}_${OS}_${ARCH}${EXT}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
# Cleanup function to remove temporary files and stop progress
|
||||||
|
cleanup() {
|
||||||
|
# Stop progress animation
|
||||||
|
progress_clear
|
||||||
|
|
||||||
|
if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
|
||||||
|
log_debug "Cleaning up temporary directory: $TMPDIR"
|
||||||
|
rm -rf -- "$TMPDIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
STRIP_COMPONENTS=0
|
||||||
|
CHECKSUM_FILENAME="tofu_${VERSION}_SHA256SUMS"
|
||||||
|
|
||||||
|
# --- Construct URLs ---
|
||||||
|
GITHUB_DOWNLOAD="https://github.com/${REPO}/releases/download"
|
||||||
|
ASSET_URL="${GITHUB_DOWNLOAD}/${TAG}/${ASSET_FILENAME}"
|
||||||
|
CHECKSUM_URL=""
|
||||||
|
if [ -n "$CHECKSUM_FILENAME" ]; then
|
||||||
|
CHECKSUM_URL="${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM_FILENAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Download and Verify ---
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
# Set up cleanup trap that includes progress clearing
|
||||||
|
trap cleanup EXIT HUP INT TERM
|
||||||
|
log_debug "Downloading files into ${TMPDIR}"
|
||||||
|
log_info "Downloading ${ASSET_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${ASSET_FILENAME}" "${ASSET_URL}"
|
||||||
|
|
||||||
|
# Try to find embedded checksum first
|
||||||
|
EMBEDDED_HASH=$(find_embedded_checksum "$VERSION" "$ASSET_FILENAME")
|
||||||
|
|
||||||
|
if [ -n "$EMBEDDED_HASH" ]; then
|
||||||
|
log_info "Using embedded checksum for verification"
|
||||||
|
|
||||||
|
# Verify using embedded hash
|
||||||
|
got=$(hash_compute "${TMPDIR}/${ASSET_FILENAME}")
|
||||||
|
if [ "$got" != "$EMBEDDED_HASH" ]; then
|
||||||
|
log_crit "Checksum verification failed for ${ASSET_FILENAME}"
|
||||||
|
log_crit "Expected: ${EMBEDDED_HASH}"
|
||||||
|
log_crit "Got: ${got}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
log_info "Checksum verification successful"
|
||||||
|
elif [ -n "$CHECKSUM_URL" ]; then
|
||||||
|
# Fall back to downloading checksum file
|
||||||
|
log_info "Downloading checksums from ${CHECKSUM_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${CHECKSUM_FILENAME}" "${CHECKSUM_URL}"
|
||||||
|
log_info "Verifying checksum ..."
|
||||||
|
hash_verify "${TMPDIR}/${ASSET_FILENAME}" "${TMPDIR}/${CHECKSUM_FILENAME}"
|
||||||
|
else
|
||||||
|
log_info "No checksum found, skipping verification."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
log_debug "Target is raw binary"
|
||||||
|
else
|
||||||
|
log_info "Extracting ${ASSET_FILENAME}..."
|
||||||
|
(cd "${TMPDIR}" && untar "${ASSET_FILENAME}" "${STRIP_COMPONENTS}")
|
||||||
|
fi
|
||||||
|
BINARY_NAME='opentofu'
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
BINARY_PATH="${TMPDIR}/${ASSET_FILENAME}"
|
||||||
|
else
|
||||||
|
BINARY_PATH="${TMPDIR}/opentofu"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${UNAME_OS}" = "windows" ]; then
|
||||||
|
case "${BINARY_NAME}" in *.exe) ;; *) BINARY_NAME="${BINARY_NAME}.exe" ;; esac
|
||||||
|
case "${BINARY_PATH}" in *.exe) ;; *) BINARY_PATH="${BINARY_PATH}.exe" ;; esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${BINARY_PATH}" ]; then
|
||||||
|
log_crit "Binary not found: ${BINARY_PATH}"
|
||||||
|
log_crit "Listing contents of ${TMPDIR} ..."
|
||||||
|
if command -v find >/dev/null 2>&1; then
|
||||||
|
cd "${TMPDIR}" && find .
|
||||||
|
else
|
||||||
|
cd "${TMPDIR}" && ls -R .
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
progress_clear
|
||||||
|
# Install the binary
|
||||||
|
INSTALL_PATH="${BINDIR}/${BINARY_NAME}"
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" = "1" ]; then
|
||||||
|
log_info "[DRY RUN] ${BINARY_NAME} dry-run installation succeeded! (Would install to: ${INSTALL_PATH})"
|
||||||
|
else
|
||||||
|
log_info "Installing binary to ${INSTALL_PATH}"
|
||||||
|
test ! -d "${BINDIR}" && install -d "${BINDIR}"
|
||||||
|
install "${BINARY_PATH}" "${INSTALL_PATH}"
|
||||||
|
log_info "${BINARY_NAME} installation complete!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Configuration ---
|
||||||
|
NAME='opentofu'
|
||||||
|
REPO='opentofu/opentofu'
|
||||||
|
EXT='.tar.gz'
|
||||||
|
|
||||||
|
# use in logging routines
|
||||||
|
log_prefix() {
|
||||||
|
echo "${REPO}"
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args "$@"
|
||||||
|
|
||||||
|
progress_init
|
||||||
|
progress_pulse_start
|
||||||
|
|
||||||
|
# --- Determine target platform ---
|
||||||
|
OS="${BINSTALLER_OS:-$(uname_os)}"
|
||||||
|
UNAME_OS="${OS}"
|
||||||
|
|
||||||
|
ARCH="${BINSTALLER_ARCH:-$(uname_arch)}"
|
||||||
|
|
||||||
|
log_info "Detected Platform: ${OS}/${ARCH}"
|
||||||
|
|
||||||
|
# --- Validate platform ---
|
||||||
|
uname_os_check "$OS"
|
||||||
|
uname_arch_check "$ARCH"
|
||||||
|
|
||||||
|
tag_to_version
|
||||||
|
|
||||||
|
resolve_asset_filename
|
||||||
|
execute
|
||||||
615
scripts/task-install.sh
Executable file
615
scripts/task-install.sh
Executable file
@@ -0,0 +1,615 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Code generated by binstaller. DO NOT EDIT.
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
usage() {
|
||||||
|
this=$1
|
||||||
|
cat <<EOF
|
||||||
|
$this: download ${NAME} from ${REPO}
|
||||||
|
|
||||||
|
Usage: $this [-b bindir] [-d] [-q] [-n] [tag]
|
||||||
|
-b sets bindir or installation directory, Defaults to ${BINSTALLER_BIN:-${HOME}/.local/bin}
|
||||||
|
-d turns on debug logging
|
||||||
|
-q turns on quiet mode (errors only)
|
||||||
|
-n turns on dry run mode
|
||||||
|
[tag] is a tag from
|
||||||
|
https://github.com/go-task/task/releases
|
||||||
|
If tag is missing, then latest will be used.
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
BINSTALLER_NO_PROGRESS=1 Disable progress indicators
|
||||||
|
BINSTALLER_OS=... Override OS detection
|
||||||
|
BINSTALLER_ARCH=... Override architecture detection
|
||||||
|
|
||||||
|
Generated by binstaller
|
||||||
|
https://github.com/binary-install/binstaller
|
||||||
|
EOF
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
https://github.com/client9/shlib - portable posix shell functions
|
||||||
|
Public domain - http://unlicense.org
|
||||||
|
https://github.com/client9/shlib/blob/master/LICENSE.md
|
||||||
|
but credit (and pull requests) appreciated.
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
is_command() {
|
||||||
|
command -v "$1" >/dev/null
|
||||||
|
}
|
||||||
|
echoerr() {
|
||||||
|
echo "$@" 1>&2
|
||||||
|
}
|
||||||
|
_logp=6
|
||||||
|
log_set_priority() {
|
||||||
|
_logp="$1"
|
||||||
|
}
|
||||||
|
log_priority() {
|
||||||
|
if test -z "$1"; then
|
||||||
|
echo "$_logp"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
[ "$1" -le "$_logp" ]
|
||||||
|
}
|
||||||
|
log_tag() {
|
||||||
|
case $1 in
|
||||||
|
0) echo "emerg" ;;
|
||||||
|
1) echo "alert" ;;
|
||||||
|
2) echo "crit" ;;
|
||||||
|
3) echo "err" ;;
|
||||||
|
4) echo "warning" ;;
|
||||||
|
5) echo "notice" ;;
|
||||||
|
6) echo "info" ;;
|
||||||
|
7) echo "debug" ;;
|
||||||
|
*) echo "$1" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
log_debug() {
|
||||||
|
log_priority 7 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
|
||||||
|
}
|
||||||
|
log_info() {
|
||||||
|
log_priority 6 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
|
||||||
|
}
|
||||||
|
log_err() {
|
||||||
|
log_priority 3 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
|
||||||
|
}
|
||||||
|
log_crit() {
|
||||||
|
log_priority 2 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
|
||||||
|
}
|
||||||
|
uname_os() {
|
||||||
|
os=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
case "$os" in
|
||||||
|
msys*) os="windows" ;;
|
||||||
|
mingw*) os="windows" ;;
|
||||||
|
cygwin*) os="windows" ;;
|
||||||
|
esac
|
||||||
|
if [ "$os" = "sunos" ]; then
|
||||||
|
if [ "$(uname -o)" = "illumos" ]; then
|
||||||
|
os="illumos"
|
||||||
|
else
|
||||||
|
os="solaris"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo "$os"
|
||||||
|
}
|
||||||
|
uname_arch() {
|
||||||
|
arch=$(uname -m)
|
||||||
|
case $arch in
|
||||||
|
x86_64) arch="amd64" ;;
|
||||||
|
i86pc) arch="amd64" ;;
|
||||||
|
x86) arch="386" ;;
|
||||||
|
i686) arch="386" ;;
|
||||||
|
i386) arch="386" ;;
|
||||||
|
aarch64) arch="arm64" ;;
|
||||||
|
armv5*) arch="armv5" ;;
|
||||||
|
armv6*) arch="armv6" ;;
|
||||||
|
armv7*) arch="armv7" ;;
|
||||||
|
esac
|
||||||
|
echo "${arch}"
|
||||||
|
}
|
||||||
|
uname_os_check() {
|
||||||
|
os=$(uname_os)
|
||||||
|
case "$os" in
|
||||||
|
darwin) return 0 ;;
|
||||||
|
dragonfly) return 0 ;;
|
||||||
|
freebsd) return 0 ;;
|
||||||
|
linux) return 0 ;;
|
||||||
|
android) return 0 ;;
|
||||||
|
midnightbsd) return 0 ;;
|
||||||
|
nacl) return 0 ;;
|
||||||
|
netbsd) return 0 ;;
|
||||||
|
openbsd) return 0 ;;
|
||||||
|
plan9) return 0 ;;
|
||||||
|
solaris) return 0 ;;
|
||||||
|
illumos) return 0 ;;
|
||||||
|
windows) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
uname_arch_check() {
|
||||||
|
arch=$(uname_arch)
|
||||||
|
case "$arch" in
|
||||||
|
386) return 0 ;;
|
||||||
|
amd64) return 0 ;;
|
||||||
|
arm64) return 0 ;;
|
||||||
|
armv5) return 0 ;;
|
||||||
|
armv6) return 0 ;;
|
||||||
|
armv7) return 0 ;;
|
||||||
|
ppc64) return 0 ;;
|
||||||
|
ppc64le) return 0 ;;
|
||||||
|
mips) return 0 ;;
|
||||||
|
mipsle) return 0 ;;
|
||||||
|
mips64) return 0 ;;
|
||||||
|
mips64le) return 0 ;;
|
||||||
|
s390x) return 0 ;;
|
||||||
|
amd64p32) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
End of functions from https://github.com/client9/shlib
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
hash_sha256() {
|
||||||
|
TARGET=${1:-/dev/stdin}
|
||||||
|
if is_command gsha256sum; then
|
||||||
|
hash=$(gsha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command sha256sum; then
|
||||||
|
hash=$(sha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command shasum; then
|
||||||
|
hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command openssl; then
|
||||||
|
hash=$(openssl dgst -sha256 "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 2
|
||||||
|
else
|
||||||
|
log_crit "hash_sha256 unable to find command to compute sha-256 hash"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_compute() {
|
||||||
|
hash_sha256 "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# shellcheck shell=sh
|
||||||
|
# Terminal progress reporting functions
|
||||||
|
progress_init() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
# OSC 9;4 sequences are safely ignored by unsupporting terminals
|
||||||
|
# Only need special handling for tmux passthrough
|
||||||
|
if [ -n "$TMUX" ]; then
|
||||||
|
# Tmux passthrough: DCS tmux; <doubled ESC sequence> ST
|
||||||
|
# ESC characters in the wrapped sequence must be doubled
|
||||||
|
# Format: ESC P tmux; ESC ESC ] 9;4; ... ESC ESC \ ESC \
|
||||||
|
PROGRESS_START=$(printf '\033Ptmux;\033\033]9;4;')
|
||||||
|
# shellcheck disable=SC1003
|
||||||
|
PROGRESS_END=$(printf '\033\033\\\033\\')
|
||||||
|
else
|
||||||
|
# Direct OSC 9;4 - terminals that don't support it will safely ignore
|
||||||
|
PROGRESS_START=$(printf '\033]9;4;')
|
||||||
|
PROGRESS_END=$(printf '\007')
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start pulsing progress animation
|
||||||
|
progress_pulse_start() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Send OSC 9;4 with state 3 (indeterminate/pulsing) once
|
||||||
|
# The terminal will handle the continuous animation
|
||||||
|
printf "%s3%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clear progress indicator
|
||||||
|
progress_clear() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
printf "%s0;%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
untar() {
|
||||||
|
tarball=$1
|
||||||
|
strip_components=${2:-0} # default 0
|
||||||
|
case "${tarball}" in
|
||||||
|
*.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.xz) tar --no-same-owner -xJf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.bz2) tar --no-same-owner -xjf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar) tar --no-same-owner -xf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.gz) gunzip "${tarball}" ;;
|
||||||
|
*.zip)
|
||||||
|
# unzip doesn't have a standard --strip-components
|
||||||
|
# Workaround: extract to a subdir and move contents up if stripping
|
||||||
|
if [ "$strip_components" -gt 0 ]; then
|
||||||
|
extract_dir=$(basename "${tarball%.zip}")_extracted
|
||||||
|
unzip -q "${tarball}" -d "${extract_dir}"
|
||||||
|
# Move contents of the *first* directory found inside extract_dir up
|
||||||
|
# This assumes wrap_in_directory=true convention
|
||||||
|
first_subdir=$(find "${extract_dir}" -mindepth 1 -maxdepth 1 -type d -print -quit)
|
||||||
|
if [ -n "$first_subdir" ]; then
|
||||||
|
# Move all contents (* includes hidden files)
|
||||||
|
mv "${first_subdir}"/* .
|
||||||
|
# Optionally remove the now-empty subdir and the extract_dir
|
||||||
|
rmdir "${first_subdir}"
|
||||||
|
rmdir "${extract_dir}"
|
||||||
|
else
|
||||||
|
log_warn "Could not find subdirectory in zip to strip components from ${extract_dir}"
|
||||||
|
# Files are extracted in current dir anyway, proceed
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
unzip -q "${tarball}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_err "untar unknown archive format for ${tarball}"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
hash_verify() {
|
||||||
|
TARGET_PATH=$1
|
||||||
|
SUMFILE=$2
|
||||||
|
if [ -z "${SUMFILE}" ]; then
|
||||||
|
log_err "hash_verify checksum file not specified in arg2"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
got=$(hash_compute "$TARGET_PATH")
|
||||||
|
if [ -z "${got}" ]; then
|
||||||
|
log_err "failed to calculate hash: ${TARGET_PATH}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BASENAME=${TARGET_PATH##*/}
|
||||||
|
|
||||||
|
# Check for line matches in checksum file
|
||||||
|
# Format: "<hash> <filename>" or "<hash> *<filename>"
|
||||||
|
# Filename may include path prefix (e.g., "deployment/m2/file.tar.gz")
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
# Normalize tabs to spaces
|
||||||
|
line=$(echo "$line" | tr '\t' ' ')
|
||||||
|
|
||||||
|
# Remove trailing spaces for hash-only line check
|
||||||
|
line_trimmed=$(echo "$line" | sed 's/[[:space:]]*$//')
|
||||||
|
|
||||||
|
# Check for hash-only line (no filename) - early return
|
||||||
|
if [ "$line_trimmed" = "$got" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract hash and filename parts
|
||||||
|
# First field is the hash, rest is filename (which may contain spaces)
|
||||||
|
line_hash=$(echo "$line" | cut -d' ' -f1)
|
||||||
|
|
||||||
|
# Skip if hash doesn't match
|
||||||
|
if [ "$line_hash" != "$got" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Hash matches, now check filename
|
||||||
|
# Remove the hash part from the beginning of the line
|
||||||
|
line_rest="${line#"$got"}"
|
||||||
|
# Remove leading spaces
|
||||||
|
while [ "${line_rest#[ ]}" != "$line_rest" ]; do
|
||||||
|
line_rest="${line_rest#[ ]}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove leading asterisk if present (binary mode indicator)
|
||||||
|
if [ "${line_rest#\*}" != "$line_rest" ]; then
|
||||||
|
line_rest="${line_rest#\*}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract just the filename without any path
|
||||||
|
line_filename="${line_rest##*/}"
|
||||||
|
|
||||||
|
# Check if the filename matches
|
||||||
|
if [ "$line_filename" = "$BASENAME" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done < "$SUMFILE"
|
||||||
|
|
||||||
|
log_err "hash_verify checksum for '$TARGET_PATH' did not verify"
|
||||||
|
log_err " Expected hash: ${got}"
|
||||||
|
log_err " Checksum file content:"
|
||||||
|
cat "$SUMFILE" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# GitHub HTTP download functions with GITHUB_TOKEN support
|
||||||
|
github_http_download_curl() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download_wget() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download() {
|
||||||
|
log_debug "github_http_download $2"
|
||||||
|
if is_command curl; then
|
||||||
|
github_http_download_curl "$@"
|
||||||
|
return
|
||||||
|
elif is_command wget; then
|
||||||
|
github_http_download_wget "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
log_crit "github_http_download unable to find wget or curl"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
github_http_copy() {
|
||||||
|
tmp=$(mktemp)
|
||||||
|
github_http_download "${tmp}" "$@" || return 1
|
||||||
|
body=$(cat "$tmp")
|
||||||
|
rm -f "${tmp}"
|
||||||
|
echo "$body"
|
||||||
|
}
|
||||||
|
github_release() {
|
||||||
|
owner_repo=$1
|
||||||
|
version=$2
|
||||||
|
test -z "$version" && version="latest"
|
||||||
|
giturl="https://github.com/${owner_repo}/releases/${version}"
|
||||||
|
json=$(github_http_copy "$giturl" "Accept:application/json")
|
||||||
|
test -z "$json" && return 1
|
||||||
|
version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
|
||||||
|
test -z "$version" && return 1
|
||||||
|
echo "$version"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Embedded Checksums (Format: VERSION:FILENAME:HASH) ---
|
||||||
|
EMBEDDED_CHECKSUMS="
|
||||||
|
3.46.4:task_darwin_amd64.tar.gz:6e3d1bf5fcc03e50b2c02c92335923b715d3f61e27fef5b3048b59ed97a18721
|
||||||
|
3.46.4:task_darwin_arm64.tar.gz:ffe2b4a3c54467172ab15734d0b451b1fc0472d0d48bb3538870b433c700bc96
|
||||||
|
3.46.4:task_freebsd_386.tar.gz:37efbab3e10723fbba85114ffb5cd39938ed489b69674e15605ec96989c047b5
|
||||||
|
3.46.4:task_freebsd_amd64.tar.gz:cd0be727b17943a53004c9984526649010aa7710cd4fac4a84b805ae25f1f35c
|
||||||
|
3.46.4:task_freebsd_arm.tar.gz:f267218e5ae49f7bbbe41c94377b3c0694c79411b9180fccc21b43cc8ef45284
|
||||||
|
3.46.4:task_freebsd_arm64.tar.gz:e68251346bc140f367f42a34fdc327edb63612a66bd782cd713a1c812531a13c
|
||||||
|
3.46.4:task_linux_386.tar.gz:b0e93c6290ed91af882516117114999ff0b21ac08d8c4e7e6f63ed6a12ef95b5
|
||||||
|
3.46.4:task_linux_amd64.tar.gz:4b0098862292de03d568c851b207bce5fdb8a59cb533d6ed4132386151926c46
|
||||||
|
3.46.4:task_linux_arm.tar.gz:f08d96178a6612e6c548e103c022740c82a313d04f7f90421d7550356ac8ee02
|
||||||
|
3.46.4:task_linux_arm64.tar.gz:9079bbdfd439f2168ee380d9167fd77f13f9fab7afb516205ac3a578f16bcb59
|
||||||
|
3.46.4:task_linux_riscv64.tar.gz:c05fb7d47c0c5d1b8222362bf2edaae6cbe6d33c3b38f0ec034306425ee51761
|
||||||
|
3.46.4:task_windows_386.zip:1398d3b96f5b7dfb71b2a126dbb5849369da130099783ebd899a162ff091f6a3
|
||||||
|
3.46.4:task_windows_amd64.zip:790420c11a6457a27f135c292371d6a352436dfce7dba5b5ffa8ae399ecfe04b
|
||||||
|
3.46.4:task_windows_arm.zip:450bf906119ddf741e99c44569329b9493c4b6f55f4c3d736297b3d57826fb32
|
||||||
|
3.46.4:task_windows_arm64.zip:4ef119dfc6ed5133170f6fc20085f0602701a37c90b440ee54331b6370f2914d"
|
||||||
|
|
||||||
|
# Find embedded checksum for a given version and filename
|
||||||
|
find_embedded_checksum() {
|
||||||
|
version="$1"
|
||||||
|
filename="$2"
|
||||||
|
echo "$EMBEDDED_CHECKSUMS" | grep -E "^${version}:${filename}:" | cut -d':' -f3
|
||||||
|
}
|
||||||
|
parse_args() {
|
||||||
|
BINDIR="${BINSTALLER_BIN:-${HOME}/.local/bin}"
|
||||||
|
DRY_RUN=0
|
||||||
|
while getopts "b:dqh?xn" arg; do
|
||||||
|
case "$arg" in
|
||||||
|
b) BINDIR="$OPTARG" ;;
|
||||||
|
d) log_set_priority 10 ;;
|
||||||
|
q) log_set_priority 3 ;;
|
||||||
|
h | \?) usage "$0" ;;
|
||||||
|
x) set -x ;;
|
||||||
|
n) DRY_RUN=1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND - 1))
|
||||||
|
TAG="${1:-latest}"
|
||||||
|
}
|
||||||
|
tag_to_version() {
|
||||||
|
if [ "$TAG" = "latest" ]; then
|
||||||
|
log_info "checking GitHub for latest tag"
|
||||||
|
REALTAG=$(github_release "${REPO}" "${TAG}") && true
|
||||||
|
test -n "$REALTAG" || {
|
||||||
|
log_crit "Could not determine latest tag for ${REPO}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
# Assume TAG is a valid tag/version string
|
||||||
|
REALTAG="$TAG"
|
||||||
|
fi
|
||||||
|
if test -z "$REALTAG"; then
|
||||||
|
log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${REPO}/releases for details"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
VERSION=${REALTAG#v} # Strip leading 'v'
|
||||||
|
TAG="$REALTAG" # Use the resolved tag
|
||||||
|
log_info "Resolved version: ${VERSION} (tag: ${TAG})"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
resolve_asset_filename() {
|
||||||
|
|
||||||
|
# --- Apply Rules ---
|
||||||
|
ASSET_FILENAME=""
|
||||||
|
if [ "${UNAME_OS}" = 'windows' ] && true
|
||||||
|
then
|
||||||
|
EXT='.zip'
|
||||||
|
fi
|
||||||
|
if [ -z "${ASSET_FILENAME}" ]; then
|
||||||
|
ASSET_FILENAME="task_${OS}_${ARCH}${EXT}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
# Cleanup function to remove temporary files and stop progress
|
||||||
|
cleanup() {
|
||||||
|
# Stop progress animation
|
||||||
|
progress_clear
|
||||||
|
|
||||||
|
if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
|
||||||
|
log_debug "Cleaning up temporary directory: $TMPDIR"
|
||||||
|
rm -rf -- "$TMPDIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
STRIP_COMPONENTS=0
|
||||||
|
CHECKSUM_FILENAME="task_checksums.txt"
|
||||||
|
|
||||||
|
# --- Construct URLs ---
|
||||||
|
GITHUB_DOWNLOAD="https://github.com/${REPO}/releases/download"
|
||||||
|
ASSET_URL="${GITHUB_DOWNLOAD}/${TAG}/${ASSET_FILENAME}"
|
||||||
|
CHECKSUM_URL=""
|
||||||
|
if [ -n "$CHECKSUM_FILENAME" ]; then
|
||||||
|
CHECKSUM_URL="${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM_FILENAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Download and Verify ---
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
# Set up cleanup trap that includes progress clearing
|
||||||
|
trap cleanup EXIT HUP INT TERM
|
||||||
|
log_debug "Downloading files into ${TMPDIR}"
|
||||||
|
log_info "Downloading ${ASSET_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${ASSET_FILENAME}" "${ASSET_URL}"
|
||||||
|
|
||||||
|
# Try to find embedded checksum first
|
||||||
|
EMBEDDED_HASH=$(find_embedded_checksum "$VERSION" "$ASSET_FILENAME")
|
||||||
|
|
||||||
|
if [ -n "$EMBEDDED_HASH" ]; then
|
||||||
|
log_info "Using embedded checksum for verification"
|
||||||
|
|
||||||
|
# Verify using embedded hash
|
||||||
|
got=$(hash_compute "${TMPDIR}/${ASSET_FILENAME}")
|
||||||
|
if [ "$got" != "$EMBEDDED_HASH" ]; then
|
||||||
|
log_crit "Checksum verification failed for ${ASSET_FILENAME}"
|
||||||
|
log_crit "Expected: ${EMBEDDED_HASH}"
|
||||||
|
log_crit "Got: ${got}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
log_info "Checksum verification successful"
|
||||||
|
elif [ -n "$CHECKSUM_URL" ]; then
|
||||||
|
# Fall back to downloading checksum file
|
||||||
|
log_info "Downloading checksums from ${CHECKSUM_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${CHECKSUM_FILENAME}" "${CHECKSUM_URL}"
|
||||||
|
log_info "Verifying checksum ..."
|
||||||
|
hash_verify "${TMPDIR}/${ASSET_FILENAME}" "${TMPDIR}/${CHECKSUM_FILENAME}"
|
||||||
|
else
|
||||||
|
log_info "No checksum found, skipping verification."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
log_debug "Target is raw binary"
|
||||||
|
else
|
||||||
|
log_info "Extracting ${ASSET_FILENAME}..."
|
||||||
|
(cd "${TMPDIR}" && untar "${ASSET_FILENAME}" "${STRIP_COMPONENTS}")
|
||||||
|
fi
|
||||||
|
BINARY_NAME='task'
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
BINARY_PATH="${TMPDIR}/${ASSET_FILENAME}"
|
||||||
|
else
|
||||||
|
BINARY_PATH="${TMPDIR}/task"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${UNAME_OS}" = "windows" ]; then
|
||||||
|
case "${BINARY_NAME}" in *.exe) ;; *) BINARY_NAME="${BINARY_NAME}.exe" ;; esac
|
||||||
|
case "${BINARY_PATH}" in *.exe) ;; *) BINARY_PATH="${BINARY_PATH}.exe" ;; esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${BINARY_PATH}" ]; then
|
||||||
|
log_crit "Binary not found: ${BINARY_PATH}"
|
||||||
|
log_crit "Listing contents of ${TMPDIR} ..."
|
||||||
|
if command -v find >/dev/null 2>&1; then
|
||||||
|
cd "${TMPDIR}" && find .
|
||||||
|
else
|
||||||
|
cd "${TMPDIR}" && ls -R .
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
progress_clear
|
||||||
|
# Install the binary
|
||||||
|
INSTALL_PATH="${BINDIR}/${BINARY_NAME}"
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" = "1" ]; then
|
||||||
|
log_info "[DRY RUN] ${BINARY_NAME} dry-run installation succeeded! (Would install to: ${INSTALL_PATH})"
|
||||||
|
else
|
||||||
|
log_info "Installing binary to ${INSTALL_PATH}"
|
||||||
|
test ! -d "${BINDIR}" && install -d "${BINDIR}"
|
||||||
|
install "${BINARY_PATH}" "${INSTALL_PATH}"
|
||||||
|
log_info "${BINARY_NAME} installation complete!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Configuration ---
|
||||||
|
NAME='task'
|
||||||
|
REPO='go-task/task'
|
||||||
|
EXT='.tar.gz'
|
||||||
|
|
||||||
|
# use in logging routines
|
||||||
|
log_prefix() {
|
||||||
|
echo "${REPO}"
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args "$@"
|
||||||
|
|
||||||
|
progress_init
|
||||||
|
progress_pulse_start
|
||||||
|
|
||||||
|
# --- Determine target platform ---
|
||||||
|
OS="${BINSTALLER_OS:-$(uname_os)}"
|
||||||
|
UNAME_OS="${OS}"
|
||||||
|
|
||||||
|
ARCH="${BINSTALLER_ARCH:-$(uname_arch)}"
|
||||||
|
|
||||||
|
log_info "Detected Platform: ${OS}/${ARCH}"
|
||||||
|
|
||||||
|
# --- Validate platform ---
|
||||||
|
uname_os_check "$OS"
|
||||||
|
uname_arch_check "$ARCH"
|
||||||
|
|
||||||
|
tag_to_version
|
||||||
|
|
||||||
|
resolve_asset_filename
|
||||||
|
execute
|
||||||
606
scripts/toolhive-install.sh
Executable file
606
scripts/toolhive-install.sh
Executable file
@@ -0,0 +1,606 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Code generated by binstaller. DO NOT EDIT.
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
usage() {
|
||||||
|
this=$1
|
||||||
|
cat <<EOF
|
||||||
|
$this: download ${NAME} from ${REPO}
|
||||||
|
|
||||||
|
Usage: $this [-b bindir] [-d] [-q] [-n] [tag]
|
||||||
|
-b sets bindir or installation directory, Defaults to ${BINSTALLER_BIN:-${HOME}/.local/bin}
|
||||||
|
-d turns on debug logging
|
||||||
|
-q turns on quiet mode (errors only)
|
||||||
|
-n turns on dry run mode
|
||||||
|
[tag] is a tag from
|
||||||
|
https://github.com/stacklok/toolhive/releases
|
||||||
|
If tag is missing, then latest will be used.
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
BINSTALLER_NO_PROGRESS=1 Disable progress indicators
|
||||||
|
BINSTALLER_OS=... Override OS detection
|
||||||
|
BINSTALLER_ARCH=... Override architecture detection
|
||||||
|
|
||||||
|
Generated by binstaller
|
||||||
|
https://github.com/binary-install/binstaller
|
||||||
|
EOF
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
https://github.com/client9/shlib - portable posix shell functions
|
||||||
|
Public domain - http://unlicense.org
|
||||||
|
https://github.com/client9/shlib/blob/master/LICENSE.md
|
||||||
|
but credit (and pull requests) appreciated.
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
is_command() {
|
||||||
|
command -v "$1" >/dev/null
|
||||||
|
}
|
||||||
|
echoerr() {
|
||||||
|
echo "$@" 1>&2
|
||||||
|
}
|
||||||
|
_logp=6
|
||||||
|
log_set_priority() {
|
||||||
|
_logp="$1"
|
||||||
|
}
|
||||||
|
log_priority() {
|
||||||
|
if test -z "$1"; then
|
||||||
|
echo "$_logp"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
[ "$1" -le "$_logp" ]
|
||||||
|
}
|
||||||
|
log_tag() {
|
||||||
|
case $1 in
|
||||||
|
0) echo "emerg" ;;
|
||||||
|
1) echo "alert" ;;
|
||||||
|
2) echo "crit" ;;
|
||||||
|
3) echo "err" ;;
|
||||||
|
4) echo "warning" ;;
|
||||||
|
5) echo "notice" ;;
|
||||||
|
6) echo "info" ;;
|
||||||
|
7) echo "debug" ;;
|
||||||
|
*) echo "$1" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
log_debug() {
|
||||||
|
log_priority 7 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
|
||||||
|
}
|
||||||
|
log_info() {
|
||||||
|
log_priority 6 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
|
||||||
|
}
|
||||||
|
log_err() {
|
||||||
|
log_priority 3 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
|
||||||
|
}
|
||||||
|
log_crit() {
|
||||||
|
log_priority 2 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
|
||||||
|
}
|
||||||
|
uname_os() {
|
||||||
|
os=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
case "$os" in
|
||||||
|
msys*) os="windows" ;;
|
||||||
|
mingw*) os="windows" ;;
|
||||||
|
cygwin*) os="windows" ;;
|
||||||
|
esac
|
||||||
|
if [ "$os" = "sunos" ]; then
|
||||||
|
if [ "$(uname -o)" = "illumos" ]; then
|
||||||
|
os="illumos"
|
||||||
|
else
|
||||||
|
os="solaris"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo "$os"
|
||||||
|
}
|
||||||
|
uname_arch() {
|
||||||
|
arch=$(uname -m)
|
||||||
|
case $arch in
|
||||||
|
x86_64) arch="amd64" ;;
|
||||||
|
i86pc) arch="amd64" ;;
|
||||||
|
x86) arch="386" ;;
|
||||||
|
i686) arch="386" ;;
|
||||||
|
i386) arch="386" ;;
|
||||||
|
aarch64) arch="arm64" ;;
|
||||||
|
armv5*) arch="armv5" ;;
|
||||||
|
armv6*) arch="armv6" ;;
|
||||||
|
armv7*) arch="armv7" ;;
|
||||||
|
esac
|
||||||
|
echo "${arch}"
|
||||||
|
}
|
||||||
|
uname_os_check() {
|
||||||
|
os=$(uname_os)
|
||||||
|
case "$os" in
|
||||||
|
darwin) return 0 ;;
|
||||||
|
dragonfly) return 0 ;;
|
||||||
|
freebsd) return 0 ;;
|
||||||
|
linux) return 0 ;;
|
||||||
|
android) return 0 ;;
|
||||||
|
midnightbsd) return 0 ;;
|
||||||
|
nacl) return 0 ;;
|
||||||
|
netbsd) return 0 ;;
|
||||||
|
openbsd) return 0 ;;
|
||||||
|
plan9) return 0 ;;
|
||||||
|
solaris) return 0 ;;
|
||||||
|
illumos) return 0 ;;
|
||||||
|
windows) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
uname_arch_check() {
|
||||||
|
arch=$(uname_arch)
|
||||||
|
case "$arch" in
|
||||||
|
386) return 0 ;;
|
||||||
|
amd64) return 0 ;;
|
||||||
|
arm64) return 0 ;;
|
||||||
|
armv5) return 0 ;;
|
||||||
|
armv6) return 0 ;;
|
||||||
|
armv7) return 0 ;;
|
||||||
|
ppc64) return 0 ;;
|
||||||
|
ppc64le) return 0 ;;
|
||||||
|
mips) return 0 ;;
|
||||||
|
mipsle) return 0 ;;
|
||||||
|
mips64) return 0 ;;
|
||||||
|
mips64le) return 0 ;;
|
||||||
|
s390x) return 0 ;;
|
||||||
|
amd64p32) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
End of functions from https://github.com/client9/shlib
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
hash_sha256() {
|
||||||
|
TARGET=${1:-/dev/stdin}
|
||||||
|
if is_command gsha256sum; then
|
||||||
|
hash=$(gsha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command sha256sum; then
|
||||||
|
hash=$(sha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command shasum; then
|
||||||
|
hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command openssl; then
|
||||||
|
hash=$(openssl dgst -sha256 "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 2
|
||||||
|
else
|
||||||
|
log_crit "hash_sha256 unable to find command to compute sha-256 hash"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_compute() {
|
||||||
|
hash_sha256 "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# shellcheck shell=sh
|
||||||
|
# Terminal progress reporting functions
|
||||||
|
progress_init() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
# OSC 9;4 sequences are safely ignored by unsupporting terminals
|
||||||
|
# Only need special handling for tmux passthrough
|
||||||
|
if [ -n "$TMUX" ]; then
|
||||||
|
# Tmux passthrough: DCS tmux; <doubled ESC sequence> ST
|
||||||
|
# ESC characters in the wrapped sequence must be doubled
|
||||||
|
# Format: ESC P tmux; ESC ESC ] 9;4; ... ESC ESC \ ESC \
|
||||||
|
PROGRESS_START=$(printf '\033Ptmux;\033\033]9;4;')
|
||||||
|
# shellcheck disable=SC1003
|
||||||
|
PROGRESS_END=$(printf '\033\033\\\033\\')
|
||||||
|
else
|
||||||
|
# Direct OSC 9;4 - terminals that don't support it will safely ignore
|
||||||
|
PROGRESS_START=$(printf '\033]9;4;')
|
||||||
|
PROGRESS_END=$(printf '\007')
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start pulsing progress animation
|
||||||
|
progress_pulse_start() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Send OSC 9;4 with state 3 (indeterminate/pulsing) once
|
||||||
|
# The terminal will handle the continuous animation
|
||||||
|
printf "%s3%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clear progress indicator
|
||||||
|
progress_clear() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
printf "%s0;%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
untar() {
|
||||||
|
tarball=$1
|
||||||
|
strip_components=${2:-0} # default 0
|
||||||
|
case "${tarball}" in
|
||||||
|
*.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.xz) tar --no-same-owner -xJf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.bz2) tar --no-same-owner -xjf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar) tar --no-same-owner -xf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.gz) gunzip "${tarball}" ;;
|
||||||
|
*.zip)
|
||||||
|
# unzip doesn't have a standard --strip-components
|
||||||
|
# Workaround: extract to a subdir and move contents up if stripping
|
||||||
|
if [ "$strip_components" -gt 0 ]; then
|
||||||
|
extract_dir=$(basename "${tarball%.zip}")_extracted
|
||||||
|
unzip -q "${tarball}" -d "${extract_dir}"
|
||||||
|
# Move contents of the *first* directory found inside extract_dir up
|
||||||
|
# This assumes wrap_in_directory=true convention
|
||||||
|
first_subdir=$(find "${extract_dir}" -mindepth 1 -maxdepth 1 -type d -print -quit)
|
||||||
|
if [ -n "$first_subdir" ]; then
|
||||||
|
# Move all contents (* includes hidden files)
|
||||||
|
mv "${first_subdir}"/* .
|
||||||
|
# Optionally remove the now-empty subdir and the extract_dir
|
||||||
|
rmdir "${first_subdir}"
|
||||||
|
rmdir "${extract_dir}"
|
||||||
|
else
|
||||||
|
log_warn "Could not find subdirectory in zip to strip components from ${extract_dir}"
|
||||||
|
# Files are extracted in current dir anyway, proceed
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
unzip -q "${tarball}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_err "untar unknown archive format for ${tarball}"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
hash_verify() {
|
||||||
|
TARGET_PATH=$1
|
||||||
|
SUMFILE=$2
|
||||||
|
if [ -z "${SUMFILE}" ]; then
|
||||||
|
log_err "hash_verify checksum file not specified in arg2"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
got=$(hash_compute "$TARGET_PATH")
|
||||||
|
if [ -z "${got}" ]; then
|
||||||
|
log_err "failed to calculate hash: ${TARGET_PATH}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BASENAME=${TARGET_PATH##*/}
|
||||||
|
|
||||||
|
# Check for line matches in checksum file
|
||||||
|
# Format: "<hash> <filename>" or "<hash> *<filename>"
|
||||||
|
# Filename may include path prefix (e.g., "deployment/m2/file.tar.gz")
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
# Normalize tabs to spaces
|
||||||
|
line=$(echo "$line" | tr '\t' ' ')
|
||||||
|
|
||||||
|
# Remove trailing spaces for hash-only line check
|
||||||
|
line_trimmed=$(echo "$line" | sed 's/[[:space:]]*$//')
|
||||||
|
|
||||||
|
# Check for hash-only line (no filename) - early return
|
||||||
|
if [ "$line_trimmed" = "$got" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract hash and filename parts
|
||||||
|
# First field is the hash, rest is filename (which may contain spaces)
|
||||||
|
line_hash=$(echo "$line" | cut -d' ' -f1)
|
||||||
|
|
||||||
|
# Skip if hash doesn't match
|
||||||
|
if [ "$line_hash" != "$got" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Hash matches, now check filename
|
||||||
|
# Remove the hash part from the beginning of the line
|
||||||
|
line_rest="${line#"$got"}"
|
||||||
|
# Remove leading spaces
|
||||||
|
while [ "${line_rest#[ ]}" != "$line_rest" ]; do
|
||||||
|
line_rest="${line_rest#[ ]}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove leading asterisk if present (binary mode indicator)
|
||||||
|
if [ "${line_rest#\*}" != "$line_rest" ]; then
|
||||||
|
line_rest="${line_rest#\*}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract just the filename without any path
|
||||||
|
line_filename="${line_rest##*/}"
|
||||||
|
|
||||||
|
# Check if the filename matches
|
||||||
|
if [ "$line_filename" = "$BASENAME" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done < "$SUMFILE"
|
||||||
|
|
||||||
|
log_err "hash_verify checksum for '$TARGET_PATH' did not verify"
|
||||||
|
log_err " Expected hash: ${got}"
|
||||||
|
log_err " Checksum file content:"
|
||||||
|
cat "$SUMFILE" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# GitHub HTTP download functions with GITHUB_TOKEN support
|
||||||
|
github_http_download_curl() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download_wget() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download() {
|
||||||
|
log_debug "github_http_download $2"
|
||||||
|
if is_command curl; then
|
||||||
|
github_http_download_curl "$@"
|
||||||
|
return
|
||||||
|
elif is_command wget; then
|
||||||
|
github_http_download_wget "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
log_crit "github_http_download unable to find wget or curl"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
github_http_copy() {
|
||||||
|
tmp=$(mktemp)
|
||||||
|
github_http_download "${tmp}" "$@" || return 1
|
||||||
|
body=$(cat "$tmp")
|
||||||
|
rm -f "${tmp}"
|
||||||
|
echo "$body"
|
||||||
|
}
|
||||||
|
github_release() {
|
||||||
|
owner_repo=$1
|
||||||
|
version=$2
|
||||||
|
test -z "$version" && version="latest"
|
||||||
|
giturl="https://github.com/${owner_repo}/releases/${version}"
|
||||||
|
json=$(github_http_copy "$giturl" "Accept:application/json")
|
||||||
|
test -z "$json" && return 1
|
||||||
|
version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
|
||||||
|
test -z "$version" && return 1
|
||||||
|
echo "$version"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Embedded Checksums (Format: VERSION:FILENAME:HASH) ---
|
||||||
|
EMBEDDED_CHECKSUMS="
|
||||||
|
0.7.2:toolhive_0.7.2_darwin_amd64.tar.gz:a32b18485d33acefc0d69641a8f99e43c9d91ed45c6279cf158f220763840ef5
|
||||||
|
0.7.2:toolhive_0.7.2_darwin_arm64.tar.gz:20b3fd98df6abdbd4898c6aa1f2fa7d01f58901055f85c374cccf110a014be1d
|
||||||
|
0.7.2:toolhive_0.7.2_linux_amd64.tar.gz:84a89a735f3c5d7eaa4fe0f4cba164eb2a04b83f2525c9709dc60dc86fc9dab1
|
||||||
|
0.7.2:toolhive_0.7.2_linux_arm64.tar.gz:821fb80c672701b62bf3efcb4c744c685fbbde80845b6256d2116a4fe561ac21
|
||||||
|
0.7.2:toolhive_0.7.2_windows_amd64.zip:d6d639453938383a3399888bbe0a60449ae1a5aa196b9cd2187a1ef089b4beb6
|
||||||
|
0.7.2:toolhive_0.7.2_windows_arm64.zip:d291feff936764db8a02868461431bd54a840ecac7a78b303e30956609c54631"
|
||||||
|
|
||||||
|
# Find embedded checksum for a given version and filename
|
||||||
|
find_embedded_checksum() {
|
||||||
|
version="$1"
|
||||||
|
filename="$2"
|
||||||
|
echo "$EMBEDDED_CHECKSUMS" | grep -E "^${version}:${filename}:" | cut -d':' -f3
|
||||||
|
}
|
||||||
|
parse_args() {
|
||||||
|
BINDIR="${BINSTALLER_BIN:-${HOME}/.local/bin}"
|
||||||
|
DRY_RUN=0
|
||||||
|
while getopts "b:dqh?xn" arg; do
|
||||||
|
case "$arg" in
|
||||||
|
b) BINDIR="$OPTARG" ;;
|
||||||
|
d) log_set_priority 10 ;;
|
||||||
|
q) log_set_priority 3 ;;
|
||||||
|
h | \?) usage "$0" ;;
|
||||||
|
x) set -x ;;
|
||||||
|
n) DRY_RUN=1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND - 1))
|
||||||
|
TAG="${1:-latest}"
|
||||||
|
}
|
||||||
|
tag_to_version() {
|
||||||
|
if [ "$TAG" = "latest" ]; then
|
||||||
|
log_info "checking GitHub for latest tag"
|
||||||
|
REALTAG=$(github_release "${REPO}" "${TAG}") && true
|
||||||
|
test -n "$REALTAG" || {
|
||||||
|
log_crit "Could not determine latest tag for ${REPO}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
# Assume TAG is a valid tag/version string
|
||||||
|
REALTAG="$TAG"
|
||||||
|
fi
|
||||||
|
if test -z "$REALTAG"; then
|
||||||
|
log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${REPO}/releases for details"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
VERSION=${REALTAG#v} # Strip leading 'v'
|
||||||
|
TAG="$REALTAG" # Use the resolved tag
|
||||||
|
log_info "Resolved version: ${VERSION} (tag: ${TAG})"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
resolve_asset_filename() {
|
||||||
|
|
||||||
|
# --- Apply Rules ---
|
||||||
|
ASSET_FILENAME=""
|
||||||
|
if [ "${UNAME_OS}" = 'windows' ] && true
|
||||||
|
then
|
||||||
|
EXT='.zip'
|
||||||
|
fi
|
||||||
|
if [ -z "${ASSET_FILENAME}" ]; then
|
||||||
|
ASSET_FILENAME="toolhive_${VERSION}_${OS}_${ARCH}${EXT}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
# Cleanup function to remove temporary files and stop progress
|
||||||
|
cleanup() {
|
||||||
|
# Stop progress animation
|
||||||
|
progress_clear
|
||||||
|
|
||||||
|
if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
|
||||||
|
log_debug "Cleaning up temporary directory: $TMPDIR"
|
||||||
|
rm -rf -- "$TMPDIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
STRIP_COMPONENTS=0
|
||||||
|
CHECKSUM_FILENAME="toolhive_${VERSION}_checksums.txt"
|
||||||
|
|
||||||
|
# --- Construct URLs ---
|
||||||
|
GITHUB_DOWNLOAD="https://github.com/${REPO}/releases/download"
|
||||||
|
ASSET_URL="${GITHUB_DOWNLOAD}/${TAG}/${ASSET_FILENAME}"
|
||||||
|
CHECKSUM_URL=""
|
||||||
|
if [ -n "$CHECKSUM_FILENAME" ]; then
|
||||||
|
CHECKSUM_URL="${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM_FILENAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Download and Verify ---
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
# Set up cleanup trap that includes progress clearing
|
||||||
|
trap cleanup EXIT HUP INT TERM
|
||||||
|
log_debug "Downloading files into ${TMPDIR}"
|
||||||
|
log_info "Downloading ${ASSET_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${ASSET_FILENAME}" "${ASSET_URL}"
|
||||||
|
|
||||||
|
# Try to find embedded checksum first
|
||||||
|
EMBEDDED_HASH=$(find_embedded_checksum "$VERSION" "$ASSET_FILENAME")
|
||||||
|
|
||||||
|
if [ -n "$EMBEDDED_HASH" ]; then
|
||||||
|
log_info "Using embedded checksum for verification"
|
||||||
|
|
||||||
|
# Verify using embedded hash
|
||||||
|
got=$(hash_compute "${TMPDIR}/${ASSET_FILENAME}")
|
||||||
|
if [ "$got" != "$EMBEDDED_HASH" ]; then
|
||||||
|
log_crit "Checksum verification failed for ${ASSET_FILENAME}"
|
||||||
|
log_crit "Expected: ${EMBEDDED_HASH}"
|
||||||
|
log_crit "Got: ${got}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
log_info "Checksum verification successful"
|
||||||
|
elif [ -n "$CHECKSUM_URL" ]; then
|
||||||
|
# Fall back to downloading checksum file
|
||||||
|
log_info "Downloading checksums from ${CHECKSUM_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${CHECKSUM_FILENAME}" "${CHECKSUM_URL}"
|
||||||
|
log_info "Verifying checksum ..."
|
||||||
|
hash_verify "${TMPDIR}/${ASSET_FILENAME}" "${TMPDIR}/${CHECKSUM_FILENAME}"
|
||||||
|
else
|
||||||
|
log_info "No checksum found, skipping verification."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
log_debug "Target is raw binary"
|
||||||
|
else
|
||||||
|
log_info "Extracting ${ASSET_FILENAME}..."
|
||||||
|
(cd "${TMPDIR}" && untar "${ASSET_FILENAME}" "${STRIP_COMPONENTS}")
|
||||||
|
fi
|
||||||
|
BINARY_NAME='thv'
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
BINARY_PATH="${TMPDIR}/${ASSET_FILENAME}"
|
||||||
|
else
|
||||||
|
BINARY_PATH="${TMPDIR}/thv"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${UNAME_OS}" = "windows" ]; then
|
||||||
|
case "${BINARY_NAME}" in *.exe) ;; *) BINARY_NAME="${BINARY_NAME}.exe" ;; esac
|
||||||
|
case "${BINARY_PATH}" in *.exe) ;; *) BINARY_PATH="${BINARY_PATH}.exe" ;; esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${BINARY_PATH}" ]; then
|
||||||
|
log_crit "Binary not found: ${BINARY_PATH}"
|
||||||
|
log_crit "Listing contents of ${TMPDIR} ..."
|
||||||
|
if command -v find >/dev/null 2>&1; then
|
||||||
|
cd "${TMPDIR}" && find .
|
||||||
|
else
|
||||||
|
cd "${TMPDIR}" && ls -R .
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
progress_clear
|
||||||
|
# Install the binary
|
||||||
|
INSTALL_PATH="${BINDIR}/${BINARY_NAME}"
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" = "1" ]; then
|
||||||
|
log_info "[DRY RUN] ${BINARY_NAME} dry-run installation succeeded! (Would install to: ${INSTALL_PATH})"
|
||||||
|
else
|
||||||
|
log_info "Installing binary to ${INSTALL_PATH}"
|
||||||
|
test ! -d "${BINDIR}" && install -d "${BINDIR}"
|
||||||
|
install "${BINARY_PATH}" "${INSTALL_PATH}"
|
||||||
|
log_info "${BINARY_NAME} installation complete!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Configuration ---
|
||||||
|
NAME='toolhive'
|
||||||
|
REPO='stacklok/toolhive'
|
||||||
|
EXT='.tar.gz'
|
||||||
|
|
||||||
|
# use in logging routines
|
||||||
|
log_prefix() {
|
||||||
|
echo "${REPO}"
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args "$@"
|
||||||
|
|
||||||
|
progress_init
|
||||||
|
progress_pulse_start
|
||||||
|
|
||||||
|
# --- Determine target platform ---
|
||||||
|
OS="${BINSTALLER_OS:-$(uname_os)}"
|
||||||
|
UNAME_OS="${OS}"
|
||||||
|
|
||||||
|
ARCH="${BINSTALLER_ARCH:-$(uname_arch)}"
|
||||||
|
|
||||||
|
log_info "Detected Platform: ${OS}/${ARCH}"
|
||||||
|
|
||||||
|
# --- Validate platform ---
|
||||||
|
uname_os_check "$OS"
|
||||||
|
uname_arch_check "$ARCH"
|
||||||
|
|
||||||
|
tag_to_version
|
||||||
|
|
||||||
|
resolve_asset_filename
|
||||||
|
execute
|
||||||
604
scripts/zk-install.sh
Executable file
604
scripts/zk-install.sh
Executable file
@@ -0,0 +1,604 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Code generated by binstaller. DO NOT EDIT.
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
usage() {
|
||||||
|
this=$1
|
||||||
|
cat <<EOF
|
||||||
|
$this: download ${NAME} from ${REPO}
|
||||||
|
|
||||||
|
Usage: $this [-b bindir] [-d] [-q] [-n] [tag]
|
||||||
|
-b sets bindir or installation directory, Defaults to ${BINSTALLER_BIN:-${HOME}/.local/bin}
|
||||||
|
-d turns on debug logging
|
||||||
|
-q turns on quiet mode (errors only)
|
||||||
|
-n turns on dry run mode
|
||||||
|
[tag] is a tag from
|
||||||
|
https://github.com/zk-org/zk/releases
|
||||||
|
If tag is missing, then latest will be used.
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
BINSTALLER_NO_PROGRESS=1 Disable progress indicators
|
||||||
|
BINSTALLER_OS=... Override OS detection
|
||||||
|
BINSTALLER_ARCH=... Override architecture detection
|
||||||
|
|
||||||
|
Generated by binstaller
|
||||||
|
https://github.com/binary-install/binstaller
|
||||||
|
EOF
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
https://github.com/client9/shlib - portable posix shell functions
|
||||||
|
Public domain - http://unlicense.org
|
||||||
|
https://github.com/client9/shlib/blob/master/LICENSE.md
|
||||||
|
but credit (and pull requests) appreciated.
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
is_command() {
|
||||||
|
command -v "$1" >/dev/null
|
||||||
|
}
|
||||||
|
echoerr() {
|
||||||
|
echo "$@" 1>&2
|
||||||
|
}
|
||||||
|
_logp=6
|
||||||
|
log_set_priority() {
|
||||||
|
_logp="$1"
|
||||||
|
}
|
||||||
|
log_priority() {
|
||||||
|
if test -z "$1"; then
|
||||||
|
echo "$_logp"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
[ "$1" -le "$_logp" ]
|
||||||
|
}
|
||||||
|
log_tag() {
|
||||||
|
case $1 in
|
||||||
|
0) echo "emerg" ;;
|
||||||
|
1) echo "alert" ;;
|
||||||
|
2) echo "crit" ;;
|
||||||
|
3) echo "err" ;;
|
||||||
|
4) echo "warning" ;;
|
||||||
|
5) echo "notice" ;;
|
||||||
|
6) echo "info" ;;
|
||||||
|
7) echo "debug" ;;
|
||||||
|
*) echo "$1" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
log_debug() {
|
||||||
|
log_priority 7 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
|
||||||
|
}
|
||||||
|
log_info() {
|
||||||
|
log_priority 6 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
|
||||||
|
}
|
||||||
|
log_err() {
|
||||||
|
log_priority 3 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
|
||||||
|
}
|
||||||
|
log_crit() {
|
||||||
|
log_priority 2 || return 0
|
||||||
|
echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
|
||||||
|
}
|
||||||
|
uname_os() {
|
||||||
|
os=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
case "$os" in
|
||||||
|
msys*) os="windows" ;;
|
||||||
|
mingw*) os="windows" ;;
|
||||||
|
cygwin*) os="windows" ;;
|
||||||
|
esac
|
||||||
|
if [ "$os" = "sunos" ]; then
|
||||||
|
if [ "$(uname -o)" = "illumos" ]; then
|
||||||
|
os="illumos"
|
||||||
|
else
|
||||||
|
os="solaris"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo "$os"
|
||||||
|
}
|
||||||
|
uname_arch() {
|
||||||
|
arch=$(uname -m)
|
||||||
|
case $arch in
|
||||||
|
x86_64) arch="amd64" ;;
|
||||||
|
i86pc) arch="amd64" ;;
|
||||||
|
x86) arch="386" ;;
|
||||||
|
i686) arch="386" ;;
|
||||||
|
i386) arch="386" ;;
|
||||||
|
aarch64) arch="arm64" ;;
|
||||||
|
armv5*) arch="armv5" ;;
|
||||||
|
armv6*) arch="armv6" ;;
|
||||||
|
armv7*) arch="armv7" ;;
|
||||||
|
esac
|
||||||
|
echo "${arch}"
|
||||||
|
}
|
||||||
|
uname_os_check() {
|
||||||
|
os=$(uname_os)
|
||||||
|
case "$os" in
|
||||||
|
darwin) return 0 ;;
|
||||||
|
dragonfly) return 0 ;;
|
||||||
|
freebsd) return 0 ;;
|
||||||
|
linux) return 0 ;;
|
||||||
|
android) return 0 ;;
|
||||||
|
midnightbsd) return 0 ;;
|
||||||
|
nacl) return 0 ;;
|
||||||
|
netbsd) return 0 ;;
|
||||||
|
openbsd) return 0 ;;
|
||||||
|
plan9) return 0 ;;
|
||||||
|
solaris) return 0 ;;
|
||||||
|
illumos) return 0 ;;
|
||||||
|
windows) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
uname_arch_check() {
|
||||||
|
arch=$(uname_arch)
|
||||||
|
case "$arch" in
|
||||||
|
386) return 0 ;;
|
||||||
|
amd64) return 0 ;;
|
||||||
|
arm64) return 0 ;;
|
||||||
|
armv5) return 0 ;;
|
||||||
|
armv6) return 0 ;;
|
||||||
|
armv7) return 0 ;;
|
||||||
|
ppc64) return 0 ;;
|
||||||
|
ppc64le) return 0 ;;
|
||||||
|
mips) return 0 ;;
|
||||||
|
mipsle) return 0 ;;
|
||||||
|
mips64) return 0 ;;
|
||||||
|
mips64le) return 0 ;;
|
||||||
|
s390x) return 0 ;;
|
||||||
|
amd64p32) return 0 ;;
|
||||||
|
esac
|
||||||
|
log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
cat /dev/null <<EOF
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
End of functions from https://github.com/client9/shlib
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
hash_sha256() {
|
||||||
|
TARGET=${1:-/dev/stdin}
|
||||||
|
if is_command gsha256sum; then
|
||||||
|
hash=$(gsha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command sha256sum; then
|
||||||
|
hash=$(sha256sum "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command shasum; then
|
||||||
|
hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 1
|
||||||
|
elif is_command openssl; then
|
||||||
|
hash=$(openssl dgst -sha256 "$TARGET") || return 1
|
||||||
|
echo "$hash" | cut -d ' ' -f 2
|
||||||
|
else
|
||||||
|
log_crit "hash_sha256 unable to find command to compute sha-256 hash"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_compute() {
|
||||||
|
hash_sha256 "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# shellcheck shell=sh
|
||||||
|
# Terminal progress reporting functions
|
||||||
|
progress_init() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
# OSC 9;4 sequences are safely ignored by unsupporting terminals
|
||||||
|
# Only need special handling for tmux passthrough
|
||||||
|
if [ -n "$TMUX" ]; then
|
||||||
|
# Tmux passthrough: DCS tmux; <doubled ESC sequence> ST
|
||||||
|
# ESC characters in the wrapped sequence must be doubled
|
||||||
|
# Format: ESC P tmux; ESC ESC ] 9;4; ... ESC ESC \ ESC \
|
||||||
|
PROGRESS_START=$(printf '\033Ptmux;\033\033]9;4;')
|
||||||
|
# shellcheck disable=SC1003
|
||||||
|
PROGRESS_END=$(printf '\033\033\\\033\\')
|
||||||
|
else
|
||||||
|
# Direct OSC 9;4 - terminals that don't support it will safely ignore
|
||||||
|
PROGRESS_START=$(printf '\033]9;4;')
|
||||||
|
PROGRESS_END=$(printf '\007')
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start pulsing progress animation
|
||||||
|
progress_pulse_start() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Send OSC 9;4 with state 3 (indeterminate/pulsing) once
|
||||||
|
# The terminal will handle the continuous animation
|
||||||
|
printf "%s3%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clear progress indicator
|
||||||
|
progress_clear() {
|
||||||
|
# Only show progress on interactive terminals and when not disabled
|
||||||
|
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
printf "%s0;%s" "$PROGRESS_START" "$PROGRESS_END" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
untar() {
|
||||||
|
tarball=$1
|
||||||
|
strip_components=${2:-0} # default 0
|
||||||
|
case "${tarball}" in
|
||||||
|
*.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.xz) tar --no-same-owner -xJf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar.bz2) tar --no-same-owner -xjf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.tar) tar --no-same-owner -xf "${tarball}" --strip-components "${strip_components}" ;;
|
||||||
|
*.gz) gunzip "${tarball}" ;;
|
||||||
|
*.zip)
|
||||||
|
# unzip doesn't have a standard --strip-components
|
||||||
|
# Workaround: extract to a subdir and move contents up if stripping
|
||||||
|
if [ "$strip_components" -gt 0 ]; then
|
||||||
|
extract_dir=$(basename "${tarball%.zip}")_extracted
|
||||||
|
unzip -q "${tarball}" -d "${extract_dir}"
|
||||||
|
# Move contents of the *first* directory found inside extract_dir up
|
||||||
|
# This assumes wrap_in_directory=true convention
|
||||||
|
first_subdir=$(find "${extract_dir}" -mindepth 1 -maxdepth 1 -type d -print -quit)
|
||||||
|
if [ -n "$first_subdir" ]; then
|
||||||
|
# Move all contents (* includes hidden files)
|
||||||
|
mv "${first_subdir}"/* .
|
||||||
|
# Optionally remove the now-empty subdir and the extract_dir
|
||||||
|
rmdir "${first_subdir}"
|
||||||
|
rmdir "${extract_dir}"
|
||||||
|
else
|
||||||
|
log_warn "Could not find subdirectory in zip to strip components from ${extract_dir}"
|
||||||
|
# Files are extracted in current dir anyway, proceed
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
unzip -q "${tarball}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_err "untar unknown archive format for ${tarball}"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
hash_verify() {
|
||||||
|
TARGET_PATH=$1
|
||||||
|
SUMFILE=$2
|
||||||
|
if [ -z "${SUMFILE}" ]; then
|
||||||
|
log_err "hash_verify checksum file not specified in arg2"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
got=$(hash_compute "$TARGET_PATH")
|
||||||
|
if [ -z "${got}" ]; then
|
||||||
|
log_err "failed to calculate hash: ${TARGET_PATH}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BASENAME=${TARGET_PATH##*/}
|
||||||
|
|
||||||
|
# Check for line matches in checksum file
|
||||||
|
# Format: "<hash> <filename>" or "<hash> *<filename>"
|
||||||
|
# Filename may include path prefix (e.g., "deployment/m2/file.tar.gz")
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
# Normalize tabs to spaces
|
||||||
|
line=$(echo "$line" | tr '\t' ' ')
|
||||||
|
|
||||||
|
# Remove trailing spaces for hash-only line check
|
||||||
|
line_trimmed=$(echo "$line" | sed 's/[[:space:]]*$//')
|
||||||
|
|
||||||
|
# Check for hash-only line (no filename) - early return
|
||||||
|
if [ "$line_trimmed" = "$got" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract hash and filename parts
|
||||||
|
# First field is the hash, rest is filename (which may contain spaces)
|
||||||
|
line_hash=$(echo "$line" | cut -d' ' -f1)
|
||||||
|
|
||||||
|
# Skip if hash doesn't match
|
||||||
|
if [ "$line_hash" != "$got" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Hash matches, now check filename
|
||||||
|
# Remove the hash part from the beginning of the line
|
||||||
|
line_rest="${line#"$got"}"
|
||||||
|
# Remove leading spaces
|
||||||
|
while [ "${line_rest#[ ]}" != "$line_rest" ]; do
|
||||||
|
line_rest="${line_rest#[ ]}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove leading asterisk if present (binary mode indicator)
|
||||||
|
if [ "${line_rest#\*}" != "$line_rest" ]; then
|
||||||
|
line_rest="${line_rest#\*}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract just the filename without any path
|
||||||
|
line_filename="${line_rest##*/}"
|
||||||
|
|
||||||
|
# Check if the filename matches
|
||||||
|
if [ "$line_filename" = "$BASENAME" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done < "$SUMFILE"
|
||||||
|
|
||||||
|
log_err "hash_verify checksum for '$TARGET_PATH' did not verify"
|
||||||
|
log_err " Expected hash: ${got}"
|
||||||
|
log_err " Checksum file content:"
|
||||||
|
cat "$SUMFILE" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# GitHub HTTP download functions with GITHUB_TOKEN support
|
||||||
|
github_http_download_curl() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
curl -fsSL -o "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
curl -fsSL -H "$header" -o "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download_wget() {
|
||||||
|
local_file=$1
|
||||||
|
source_url=$2
|
||||||
|
header=$3
|
||||||
|
if [ -n "$GITHUB_TOKEN" ]; then
|
||||||
|
log_debug "Using GITHUB_TOKEN for authentication"
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$header" ]; then
|
||||||
|
wget -q -O "$local_file" "$source_url"
|
||||||
|
else
|
||||||
|
wget -q --header "$header" -O "$local_file" "$source_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
github_http_download() {
|
||||||
|
log_debug "github_http_download $2"
|
||||||
|
if is_command curl; then
|
||||||
|
github_http_download_curl "$@"
|
||||||
|
return
|
||||||
|
elif is_command wget; then
|
||||||
|
github_http_download_wget "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
log_crit "github_http_download unable to find wget or curl"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
github_http_copy() {
|
||||||
|
tmp=$(mktemp)
|
||||||
|
github_http_download "${tmp}" "$@" || return 1
|
||||||
|
body=$(cat "$tmp")
|
||||||
|
rm -f "${tmp}"
|
||||||
|
echo "$body"
|
||||||
|
}
|
||||||
|
github_release() {
|
||||||
|
owner_repo=$1
|
||||||
|
version=$2
|
||||||
|
test -z "$version" && version="latest"
|
||||||
|
giturl="https://github.com/${owner_repo}/releases/${version}"
|
||||||
|
json=$(github_http_copy "$giturl" "Accept:application/json")
|
||||||
|
test -z "$json" && return 1
|
||||||
|
version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
|
||||||
|
test -z "$version" && return 1
|
||||||
|
echo "$version"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Embedded Checksums (Format: VERSION:FILENAME:HASH) ---
|
||||||
|
EMBEDDED_CHECKSUMS=""
|
||||||
|
|
||||||
|
# Find embedded checksum for a given version and filename
|
||||||
|
find_embedded_checksum() {
|
||||||
|
version="$1"
|
||||||
|
filename="$2"
|
||||||
|
echo "$EMBEDDED_CHECKSUMS" | grep -E "^${version}:${filename}:" | cut -d':' -f3
|
||||||
|
}
|
||||||
|
parse_args() {
|
||||||
|
BINDIR="${BINSTALLER_BIN:-${HOME}/.local/bin}"
|
||||||
|
DRY_RUN=0
|
||||||
|
while getopts "b:dqh?xn" arg; do
|
||||||
|
case "$arg" in
|
||||||
|
b) BINDIR="$OPTARG" ;;
|
||||||
|
d) log_set_priority 10 ;;
|
||||||
|
q) log_set_priority 3 ;;
|
||||||
|
h | \?) usage "$0" ;;
|
||||||
|
x) set -x ;;
|
||||||
|
n) DRY_RUN=1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND - 1))
|
||||||
|
TAG="${1:-latest}"
|
||||||
|
}
|
||||||
|
tag_to_version() {
|
||||||
|
if [ "$TAG" = "latest" ]; then
|
||||||
|
log_info "checking GitHub for latest tag"
|
||||||
|
REALTAG=$(github_release "${REPO}" "${TAG}") && true
|
||||||
|
test -n "$REALTAG" || {
|
||||||
|
log_crit "Could not determine latest tag for ${REPO}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
# Assume TAG is a valid tag/version string
|
||||||
|
REALTAG="$TAG"
|
||||||
|
fi
|
||||||
|
if test -z "$REALTAG"; then
|
||||||
|
log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${REPO}/releases for details"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
VERSION=${REALTAG#v} # Strip leading 'v'
|
||||||
|
TAG="$REALTAG" # Use the resolved tag
|
||||||
|
log_info "Resolved version: ${VERSION} (tag: ${TAG})"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
resolve_asset_filename() {
|
||||||
|
|
||||||
|
# --- Apply Rules ---
|
||||||
|
ASSET_FILENAME=""
|
||||||
|
if [ "${UNAME_ARCH}" = 'amd64' ] && true
|
||||||
|
then
|
||||||
|
ARCH='x86_64'
|
||||||
|
fi
|
||||||
|
if [ "${UNAME_OS}" = 'darwin' ] && true
|
||||||
|
then
|
||||||
|
OS='macos'
|
||||||
|
fi
|
||||||
|
if [ -z "${ASSET_FILENAME}" ]; then
|
||||||
|
ASSET_FILENAME="zk-${TAG}-${OS}-${ARCH}${EXT}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
# Cleanup function to remove temporary files and stop progress
|
||||||
|
cleanup() {
|
||||||
|
# Stop progress animation
|
||||||
|
progress_clear
|
||||||
|
|
||||||
|
if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
|
||||||
|
log_debug "Cleaning up temporary directory: $TMPDIR"
|
||||||
|
rm -rf -- "$TMPDIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
STRIP_COMPONENTS=0
|
||||||
|
CHECKSUM_FILENAME=""
|
||||||
|
|
||||||
|
# --- Construct URLs ---
|
||||||
|
GITHUB_DOWNLOAD="https://github.com/${REPO}/releases/download"
|
||||||
|
ASSET_URL="${GITHUB_DOWNLOAD}/${TAG}/${ASSET_FILENAME}"
|
||||||
|
CHECKSUM_URL=""
|
||||||
|
if [ -n "$CHECKSUM_FILENAME" ]; then
|
||||||
|
CHECKSUM_URL="${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM_FILENAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Download and Verify ---
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
# Set up cleanup trap that includes progress clearing
|
||||||
|
trap cleanup EXIT HUP INT TERM
|
||||||
|
log_debug "Downloading files into ${TMPDIR}"
|
||||||
|
log_info "Downloading ${ASSET_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${ASSET_FILENAME}" "${ASSET_URL}"
|
||||||
|
|
||||||
|
# Try to find embedded checksum first
|
||||||
|
EMBEDDED_HASH=$(find_embedded_checksum "$VERSION" "$ASSET_FILENAME")
|
||||||
|
|
||||||
|
if [ -n "$EMBEDDED_HASH" ]; then
|
||||||
|
log_info "Using embedded checksum for verification"
|
||||||
|
|
||||||
|
# Verify using embedded hash
|
||||||
|
got=$(hash_compute "${TMPDIR}/${ASSET_FILENAME}")
|
||||||
|
if [ "$got" != "$EMBEDDED_HASH" ]; then
|
||||||
|
log_crit "Checksum verification failed for ${ASSET_FILENAME}"
|
||||||
|
log_crit "Expected: ${EMBEDDED_HASH}"
|
||||||
|
log_crit "Got: ${got}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
log_info "Checksum verification successful"
|
||||||
|
elif [ -n "$CHECKSUM_URL" ]; then
|
||||||
|
# Fall back to downloading checksum file
|
||||||
|
log_info "Downloading checksums from ${CHECKSUM_URL}"
|
||||||
|
github_http_download "${TMPDIR}/${CHECKSUM_FILENAME}" "${CHECKSUM_URL}"
|
||||||
|
log_info "Verifying checksum ..."
|
||||||
|
hash_verify "${TMPDIR}/${ASSET_FILENAME}" "${TMPDIR}/${CHECKSUM_FILENAME}"
|
||||||
|
else
|
||||||
|
log_info "No checksum found, skipping verification."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
log_debug "Target is raw binary"
|
||||||
|
else
|
||||||
|
log_info "Extracting ${ASSET_FILENAME}..."
|
||||||
|
(cd "${TMPDIR}" && untar "${ASSET_FILENAME}" "${STRIP_COMPONENTS}")
|
||||||
|
fi
|
||||||
|
BINARY_NAME='zk'
|
||||||
|
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
|
||||||
|
BINARY_PATH="${TMPDIR}/${ASSET_FILENAME}"
|
||||||
|
else
|
||||||
|
BINARY_PATH="${TMPDIR}/zk"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${UNAME_OS}" = "windows" ]; then
|
||||||
|
case "${BINARY_NAME}" in *.exe) ;; *) BINARY_NAME="${BINARY_NAME}.exe" ;; esac
|
||||||
|
case "${BINARY_PATH}" in *.exe) ;; *) BINARY_PATH="${BINARY_PATH}.exe" ;; esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${BINARY_PATH}" ]; then
|
||||||
|
log_crit "Binary not found: ${BINARY_PATH}"
|
||||||
|
log_crit "Listing contents of ${TMPDIR} ..."
|
||||||
|
if command -v find >/dev/null 2>&1; then
|
||||||
|
cd "${TMPDIR}" && find .
|
||||||
|
else
|
||||||
|
cd "${TMPDIR}" && ls -R .
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
progress_clear
|
||||||
|
# Install the binary
|
||||||
|
INSTALL_PATH="${BINDIR}/${BINARY_NAME}"
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" = "1" ]; then
|
||||||
|
log_info "[DRY RUN] ${BINARY_NAME} dry-run installation succeeded! (Would install to: ${INSTALL_PATH})"
|
||||||
|
else
|
||||||
|
log_info "Installing binary to ${INSTALL_PATH}"
|
||||||
|
test ! -d "${BINDIR}" && install -d "${BINDIR}"
|
||||||
|
install "${BINARY_PATH}" "${INSTALL_PATH}"
|
||||||
|
log_info "${BINARY_NAME} installation complete!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Configuration ---
|
||||||
|
NAME='zk'
|
||||||
|
REPO='zk-org/zk'
|
||||||
|
EXT='.tar.gz'
|
||||||
|
|
||||||
|
# use in logging routines
|
||||||
|
log_prefix() {
|
||||||
|
echo "${REPO}"
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args "$@"
|
||||||
|
|
||||||
|
progress_init
|
||||||
|
progress_pulse_start
|
||||||
|
|
||||||
|
# --- Determine target platform ---
|
||||||
|
OS="${BINSTALLER_OS:-$(uname_os)}"
|
||||||
|
UNAME_OS="${OS}"
|
||||||
|
|
||||||
|
ARCH="${BINSTALLER_ARCH:-$(uname_arch)}"
|
||||||
|
UNAME_ARCH="${ARCH}"
|
||||||
|
log_info "Detected Platform: ${OS}/${ARCH}"
|
||||||
|
|
||||||
|
# --- Validate platform ---
|
||||||
|
uname_os_check "$OS"
|
||||||
|
uname_arch_check "$ARCH"
|
||||||
|
|
||||||
|
tag_to_version
|
||||||
|
|
||||||
|
resolve_asset_filename
|
||||||
|
execute
|
||||||
Reference in New Issue
Block a user