Skip to content

Commit c63b137

Browse files
authored
Add bash installation script (#5)
* Add bash installation script * fmt * add shfmt config * add shfmt config * add shfmt config * update shfmt * update shfmt * update shfmt * fmt * fmt
1 parent d194bd2 commit c63b137

File tree

8 files changed

+267
-14
lines changed

8 files changed

+267
-14
lines changed

.editorconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# top-most EditorConfig file
2+
root = true
3+
4+
# General settings for all file types
5+
[*]
6+
trim_trailing_whitespace = true
7+
8+
# Settings for shell scripts
9+
[*.sh]
10+
indent_size = 2
11+
indent_style = space
12+
13+
switch_case_indent = true
14+
space_redirects = true

.github/workflows/pull_request.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@ jobs:
2626
VALIDATE_YAML: false
2727
VALIDATE_YAML_PRETTIER: false
2828
VALIDATE_MARKDOWN_PRETTIER: false
29+
VALIDATE_GO_RELEASER: false
2930
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3031
FILTER_REGEX_EXCLUDE: ".*(demo)/.*"

.goreleaser.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ archives:
4545
- zip
4646
name_template: "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
4747
id: windows
48-
ids:
48+
ids:
4949
- windows
5050

5151
checksum:

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ RUN CGO_ENABLED=0 GOOS="$TARGETOS" GOARCH="$TARGETARCH" go build -v -o canary-ga
3838
################################
3939
# Main image
4040
################################
41-
FROM scratch
41+
FROM scratch
4242
ARG VERSION
4343
ARG COMMIT_HASH
4444
ARG BUILD_DATE

README.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
22
[![Build and Test](https://github.com/KongZ/canary-gate/actions/workflows/code_build.yml/badge.svg)](https://github.com/KongZ/canary-gate/actions/workflows/code_build.yml)
33

4-
54
<img src="https://raw.githubusercontent.com/KongZ/canary-gate/main/docs/gopher-canary-gate.png" alt="Canary Gate logo" width="200"/>
65

76
# Canary Gate
@@ -189,6 +188,32 @@ brew tap KongZ/homebrew-tap
189188
brew install canary-gate
190189
```
191190

191+
### Bash Script (macOS and Linux)
192+
193+
It installs the canary-gate binary to the default location
194+
195+
```bash
196+
curl -sSL https://raw.githubusercontent.com/KongZ/canary-gate/refs/heads/main/docs/install.sh | sudo bash
197+
```
198+
199+
If you want to install the binary to a different directory (e.g., /opt/bin), you can set the INSTALL_PATH environment variable.
200+
201+
```bash
202+
curl -sSL https://raw.githubusercontent.com/KongZ/canary-gate/refs/heads/main/docs/install.sh | sudo INSTALL_PATH=/opt/bin bash
203+
```
204+
205+
To remove the binary, pass the uninstall parameter to the script.
206+
207+
```bash
208+
curl -sSL https://raw.githubusercontent.com/KongZ/canary-gate/refs/heads/main/docs/install.sh | sudo bash -s -- uninstall
209+
```
210+
211+
If you installed it to a custom path, you must provide the same INSTALL_PATH variable during uninstallation.
212+
213+
```bash
214+
curl -sSL https://raw.githubusercontent.com/KongZ/canary-gate/refs/heads/main/docs/install.sh | sudo INSTALL_PATH=/opt/bin bash -s -- uninstall
215+
```
216+
192217
### Downloading CLI Binary Files
193218

194219
You can download the binary files from [releases](https://github.com/KongZ/canary-gate/releases).

cli/main.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,13 @@ func createCliApp() *cli.Command {
113113
Usage: "A CLI tool to interact with canary gate in the Flagger",
114114
UsageText: `canary-gate [command] <gate-name> <global-options>
115115
116-
Example:
116+
Example:
117117
# CanaryGate is located within the 'gate-namespace' namespace, with the name 'my-deployment' on the 'my-cluster' cluster.
118118
119-
# Open the confirm-rollout gate.
119+
# Open the confirm-rollout gate.
120120
canary-gate open confirm-rollout --cluster my-cluster --namespace gate-namespace --deployment my-deployment
121121
122-
# Close the confirm-promotion gate.
122+
# Close the confirm-promotion gate.
123123
canary-gate close confirm-promotion --cluster my-cluster --namespace gate-namespace --deployment my-deployment
124124
125125
# Check the status of the confirm-traffic-increase gate.
@@ -137,10 +137,10 @@ canary-gate status all --cluster my-cluster --namespace gate-namespace --deploym
137137
Usage: "Open a canary gate.",
138138
UsageText: `canary-gate open <gate-name> <global-options>
139139
140-
Example:
140+
Example:
141141
# CanaryGate is located within the 'gate-namespace' namespace, with the name 'my-deployment' on the 'my-cluster' cluster.
142142
143-
# Open the confirm-rollout gate.
143+
# Open the confirm-rollout gate.
144144
canary-gate open confirm-rollout --cluster my-cluster --namespace gate-namespace --deployment my-deployment`,
145145
Flags: flags,
146146
Commands: []*cli.Command{
@@ -209,10 +209,10 @@ canary-gate open confirm-rollout --cluster my-cluster --namespace gate-namespace
209209
Usage: "Close a canary gate.",
210210
UsageText: `canary-gate close <gate-name> <global-options>
211211
212-
Example:
212+
Example:
213213
# CanaryGate is located within the 'gate-namespace' namespace, with the name 'my-deployment' on the 'my-cluster' cluster.
214214
215-
# Close the confirm-rollout gate.
215+
# Close the confirm-rollout gate.
216216
canary-gate close confirm-rollout --cluster my-cluster --namespace gate-namespace --deployment my-deployment`,
217217
Flags: flags,
218218
Commands: []*cli.Command{
@@ -281,10 +281,10 @@ canary-gate close confirm-rollout --cluster my-cluster --namespace gate-namespac
281281
Usage: "Check status of a canary gate.",
282282
UsageText: `canary-gate status <gate-name> <global-options>
283283
284-
Example:
284+
Example:
285285
# CanaryGate is located within the 'gate-namespace' namespace, with the name 'my-deployment' on the 'my-cluster' cluster..
286286
287-
# Check the status of a specific gate.
287+
# Check the status of a specific gate.
288288
canary-gate status confirm-rollout --cluster my-cluster --namespace gate-namespace --deployment my-deployment
289289
290290
# Check the status of a all gates

docs/canarygate-crd.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ spec:
3232
type: string
3333
rollback:
3434
type: string
35-
target:
35+
target:
3636
type: object
3737
required:
3838
- namespace
@@ -41,7 +41,7 @@ spec:
4141
namespace:
4242
type: string
4343
name:
44-
type: string
44+
type: string
4545
flagger:
4646
description: Contains the raw spec for the Flagger Canary resource.
4747
type: object

docs/install.sh

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
#!/bin/bash
2+
#
3+
# This script handles the installation and uninstallation of the 'canary-gate' binary.
4+
# It automatically detects the OS and architecture, downloads the appropriate release
5+
# from GitHub, verifies the checksum, and installs it.
6+
#
7+
# Usage:
8+
# To Install:
9+
# curl -sSL https://raw.githubusercontent.com/KongZ/canary-gate/refs/heads/main/docs/install.sh | sudo bash
10+
#
11+
# To Uninstall:
12+
# curl -sSL https://raw.githubusercontent.com/KongZ/canary-gate/refs/heads/main/docs/install.sh | sudo bash -s -- uninstall
13+
#
14+
# Dry Run (see what the script will do without making changes):
15+
# # For installation
16+
# curl -sSL https://raw.githubusercontent.com/KongZ/canary-gate/refs/heads/main/docs/install.sh | bash -s -- --dry-run
17+
# # For uninstallation
18+
# curl -sSL https://raw.githubusercontent.com/KongZ/canary-gate/refs/heads/main/docs/install.sh | bash -s -- uninstall --dry-run
19+
#
20+
# Custom Install Path:
21+
# Set the INSTALL_PATH environment variable. This works with all commands.
22+
# curl -sSL https://raw.githubusercontent.com/KongZ/canary-gate/refs/heads/main/docs/install.sh | sudo INSTALL_PATH=/opt/bin bash
23+
#
24+
25+
set -e # Exit immediately if a command exits with a non-zero status.
26+
set -o pipefail # Return value of a pipeline is the value of the last command to exit with a non-zero status.
27+
# set -x
28+
29+
REPO="KongZ/canary-gate"
30+
BINARY_NAME="canary-gate"
31+
INSTALL_DIR=${INSTALL_PATH:-/usr/local/bin}
32+
33+
#####################
34+
## Helper Functions
35+
#####################
36+
37+
info() {
38+
echo "INFO: ✅ $1"
39+
}
40+
41+
error() {
42+
echo "ERROR: ❌ $1" >&2
43+
exit 1
44+
}
45+
46+
check_command() {
47+
if ! command -v "$1" &> /dev/null; then
48+
error "Required command '$1' is not installed. Please install it to continue."
49+
fi
50+
}
51+
52+
get_release_info() {
53+
info "Detecting platform and fetching latest release information..."
54+
55+
local OS_KERNEL
56+
OS_KERNEL=$(uname -s)
57+
local ARCH
58+
ARCH=$(uname -m)
59+
60+
case "$OS_KERNEL" in
61+
Linux) PLATFORM_OS="linux" ;;
62+
Darwin) PLATFORM_OS="darwin" ;;
63+
*) error "Unsupported operating system: $OS_KERNEL." ;;
64+
esac
65+
66+
case "$ARCH" in
67+
x86_64) PLATFORM_ARCH="x86_64" ;;
68+
arm64 | aarch64) PLATFORM_ARCH="arm64" ;;
69+
*) error "Unsupported architecture: $ARCH. Only x86_64 and arm64/aarch64 are supported." ;;
70+
esac
71+
72+
local API_URL="https://api.github.com/repos/${REPO}/releases/latest"
73+
local RELEASE_INFO
74+
RELEASE_INFO=$(curl -sL "$API_URL")
75+
76+
LATEST_TAG=$(echo "$RELEASE_INFO" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
77+
if [ -z "$LATEST_TAG" ]; then
78+
error "Could not determine the latest release tag. Check repository and network connection."
79+
fi
80+
81+
local VERSION=${LATEST_TAG}
82+
ARCHIVE_NAME="${BINARY_NAME}_${VERSION}_${PLATFORM_OS}_${PLATFORM_ARCH}.tar.gz"
83+
CHECKSUMS_URL="https://github.com/${REPO}/releases/download/${LATEST_TAG}/checksums.txt"
84+
85+
DOWNLOAD_URL_ARCHIVE=$(echo "$RELEASE_INFO" | grep '"browser_download_url":' | grep "$ARCHIVE_NAME" | sed -E 's/.*"([^"]+)".*/\1/')
86+
87+
if [ -z "$DOWNLOAD_URL_ARCHIVE" ]; then
88+
error "Could not find download URLs for version ${LATEST_TAG} on platform ${PLATFORM_OS}/${PLATFORM_ARCH}."
89+
fi
90+
}
91+
92+
#####################
93+
## Installation
94+
#####################
95+
96+
do_install() {
97+
check_command "curl"
98+
get_release_info
99+
100+
if [ "$DRY_RUN" = "true" ]; then
101+
echo
102+
info "--- 💧 Dry Run Mode ---"
103+
info "No files will be downloaded or installed."
104+
echo
105+
echo " Latest Version: ${LATEST_TAG}"
106+
echo " Platform: ${PLATFORM_OS}/${PLATFORM_ARCH}"
107+
echo " Download URL (Archive): ${DOWNLOAD_URL_ARCHIVE}"
108+
echo " Download URL (Checksums): ${CHECKSUMS_URL}"
109+
echo " Temporary Directory: /tmp/..."
110+
echo " Installation Target: ${INSTALL_DIR}/${BINARY_NAME}"
111+
echo
112+
return
113+
fi
114+
115+
info "Starting canary-gate installation..."
116+
if [ "$(id -u)" -ne 0 ]; then
117+
error "This script must be run as root for installation. Please use 'sudo'."
118+
fi
119+
120+
CHECKSUMS_FILENAME="checksums.txt"
121+
TMP_DIR=$(mktemp -d)
122+
trap 'rm -rf "$TMP_DIR"' EXIT
123+
cd "$TMP_DIR"
124+
125+
info "Downloading ${ARCHIVE_NAME}..."
126+
curl -sSL -o "$ARCHIVE_NAME" "$DOWNLOAD_URL_ARCHIVE"
127+
128+
info "Downloading checksums from ${CHECKSUMS_URL}..."
129+
curl -sSL -o "$CHECKSUMS_FILENAME" "$CHECKSUMS_URL"
130+
131+
info "Verifying file integrity..."
132+
if command -v sha256sum &> /dev/null; then
133+
SHASUM_CMD="sha256sum"
134+
elif command -v shasum &> /dev/null; then
135+
SHASUM_CMD="shasum -a 256"
136+
else
137+
error "Could not find a SHA256 checksum command (sha256sum or shasum)."
138+
fi
139+
140+
EXPECTED_HASH=$(grep "$ARCHIVE_NAME" "$CHECKSUMS_FILENAME" | awk '{print $1}')
141+
CALCULATED_HASH=$($SHASUM_CMD "$ARCHIVE_NAME" | awk '{print $1}')
142+
143+
if [ "$EXPECTED_HASH" != "$CALCULATED_HASH" ]; then
144+
error "Checksum verification failed! The downloaded file may be corrupt."
145+
fi
146+
info "Checksum verified successfully. ✅"
147+
148+
info "Extracting and installing binary..."
149+
tar -xzf "$ARCHIVE_NAME"
150+
151+
mkdir -p "$INSTALL_DIR"
152+
install -m 755 "$BINARY_NAME" "${INSTALL_DIR}/${BINARY_NAME}"
153+
154+
info "${BINARY_NAME} has been successfully installed to ${INSTALL_DIR}/${BINARY_NAME}"
155+
info "You can now run '${BINARY_NAME}' from your terminal."
156+
}
157+
158+
do_uninstall() {
159+
local BINARY_PATH="${INSTALL_DIR}/${BINARY_NAME}"
160+
161+
if [ "$DRY_RUN" = "true" ]; then
162+
echo
163+
info "--- 💧 Dry Run Mode ---"
164+
info "No files will be removed."
165+
echo
166+
echo " Would remove binary from: ${BINARY_PATH}"
167+
echo
168+
return
169+
fi
170+
171+
info "Starting canary-gate uninstallation..."
172+
if [ "$(id -u)" -ne 0 ]; then
173+
error "This script must be run as root for uninstallation. Please use 'sudo'."
174+
fi
175+
176+
if [ -f "$BINARY_PATH" ]; then
177+
info "Removing ${BINARY_PATH}..."
178+
rm -f "$BINARY_PATH"
179+
info "${BINARY_NAME} has been successfully uninstalled."
180+
else
181+
info "${BINARY_NAME} is not installed in ${INSTALL_DIR}, nothing to do."
182+
fi
183+
}
184+
185+
#####################
186+
## Main
187+
#####################
188+
189+
ACTION="install"
190+
DRY_RUN="false"
191+
192+
# Parse arguments
193+
for arg in "$@"; do
194+
case $arg in
195+
uninstall | --uninstall)
196+
ACTION="uninstall"
197+
shift
198+
;;
199+
--dry-run)
200+
DRY_RUN="true"
201+
shift
202+
;;
203+
*)
204+
# Ignore unknown options
205+
;;
206+
esac
207+
done
208+
209+
if [ "$ACTION" = "install" ]; then
210+
do_install
211+
elif [ "$ACTION" = "uninstall" ]; then
212+
do_uninstall
213+
fi

0 commit comments

Comments
 (0)