feat(scaffold): install binaries from latest release in install script
This commit is contained in:
parent
a9378885f2
commit
973770ed78
2 changed files with 688 additions and 25 deletions
|
|
@ -357,6 +357,30 @@ BINARY_NAME="{{.BinaryName}}"
|
||||||
MODULE_PATH="{{.ModulePath}}"
|
MODULE_PATH="{{.ModulePath}}"
|
||||||
DEFAULT_PROFILE="{{.DefaultProfile}}"
|
DEFAULT_PROFILE="{{.DefaultProfile}}"
|
||||||
PROFILE_ENV="{{.ProfileEnv}}"
|
PROFILE_ENV="{{.ProfileEnv}}"
|
||||||
|
DEFAULT_RELEASE_DRIVER="{{.ReleaseDriver}}"
|
||||||
|
DEFAULT_RELEASE_BASE_URL="{{.ReleaseBaseURL}}"
|
||||||
|
DEFAULT_RELEASE_REPOSITORY="{{.ReleaseRepository}}"
|
||||||
|
DEFAULT_ASSET_NAME_TEMPLATE="{binary}-{os}-{arch}{ext}"
|
||||||
|
DEFAULT_CHECKSUM_ASSET_NAME="{asset}.sha256"
|
||||||
|
DEFAULT_CHECKSUM_REQUIRED="true"
|
||||||
|
DEFAULT_TOKEN_HEADER="Authorization"
|
||||||
|
DEFAULT_TOKEN_PREFIX="token"
|
||||||
|
DEFAULT_TOKEN_ENV_NAME="{{.ReleaseTokenEnv}}"
|
||||||
|
RELEASE_DRIVER="$DEFAULT_RELEASE_DRIVER"
|
||||||
|
RELEASE_BASE_URL="$DEFAULT_RELEASE_BASE_URL"
|
||||||
|
RELEASE_REPOSITORY="$DEFAULT_RELEASE_REPOSITORY"
|
||||||
|
LATEST_RELEASE_URL=""
|
||||||
|
ASSET_NAME_TEMPLATE="$DEFAULT_ASSET_NAME_TEMPLATE"
|
||||||
|
CHECKSUM_ASSET_NAME="$DEFAULT_CHECKSUM_ASSET_NAME"
|
||||||
|
CHECKSUM_REQUIRED="$DEFAULT_CHECKSUM_REQUIRED"
|
||||||
|
TOKEN_HEADER="$DEFAULT_TOKEN_HEADER"
|
||||||
|
TOKEN_PREFIX="$DEFAULT_TOKEN_PREFIX"
|
||||||
|
AUTH_HEADER_NAME=""
|
||||||
|
AUTH_HEADER_VALUE=""
|
||||||
|
TOKEN_ENV_NAMES=()
|
||||||
|
if [ -n "$DEFAULT_TOKEN_ENV_NAME" ]; then
|
||||||
|
TOKEN_ENV_NAMES=("$DEFAULT_TOKEN_ENV_NAME")
|
||||||
|
fi
|
||||||
PREFILL_SERVER_NAME=""
|
PREFILL_SERVER_NAME=""
|
||||||
PREFILL_PROFILE_VALUE=""
|
PREFILL_PROFILE_VALUE=""
|
||||||
PREFILL_COMMAND_PATH=""
|
PREFILL_COMMAND_PATH=""
|
||||||
|
|
@ -543,15 +567,546 @@ toml_escape() {
|
||||||
printf "%s" "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'
|
printf "%s" "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'
|
||||||
}
|
}
|
||||||
|
|
||||||
go_bin_dir() {
|
trim_whitespace() {
|
||||||
local gobin
|
printf "%s" "$1" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//'
|
||||||
gobin="$(go env GOBIN 2>/dev/null || true)"
|
}
|
||||||
if [ -n "$gobin" ]; then
|
|
||||||
printf "%s\n" "$gobin"
|
unquote_toml() {
|
||||||
|
local value
|
||||||
|
value="$(trim_whitespace "$1")"
|
||||||
|
if [ "${#value}" -ge 2 ]; then
|
||||||
|
case "$value" in
|
||||||
|
\"*\")
|
||||||
|
value="${value#\"}"
|
||||||
|
value="${value%\"}"
|
||||||
|
;;
|
||||||
|
\'*\')
|
||||||
|
value="${value#\'}"
|
||||||
|
value="${value%\'}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
printf "%s" "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize_bool() {
|
||||||
|
local value
|
||||||
|
value="$(printf "%s" "$1" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
value="$(trim_whitespace "$value")"
|
||||||
|
case "$value" in
|
||||||
|
true|1|yes|y)
|
||||||
|
printf "true"
|
||||||
|
;;
|
||||||
|
false|0|no|n)
|
||||||
|
printf "false"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf "%s" "$2"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
toml_read_update_value() {
|
||||||
|
local key="$1"
|
||||||
|
awk -v key="$key" '
|
||||||
|
BEGIN {
|
||||||
|
in_update = 0
|
||||||
|
}
|
||||||
|
{
|
||||||
|
line = $0
|
||||||
|
sub(/[[:space:]]*#.*/, "", line)
|
||||||
|
if (line ~ /^[[:space:]]*\[/) {
|
||||||
|
if (line ~ /^[[:space:]]*\[update\][[:space:]]*$/) {
|
||||||
|
in_update = 1
|
||||||
|
} else {
|
||||||
|
in_update = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (in_update == 1 && line ~ "^[[:space:]]*" key "[[:space:]]*=") {
|
||||||
|
sub("^[[:space:]]*" key "[[:space:]]*=[[:space:]]*", "", line)
|
||||||
|
print line
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' mcp.toml
|
||||||
|
}
|
||||||
|
|
||||||
|
toml_read_update_array() {
|
||||||
|
local key="$1"
|
||||||
|
local raw
|
||||||
|
raw="$(toml_read_update_value "$key")"
|
||||||
|
raw="$(trim_whitespace "$raw")"
|
||||||
|
if [ -z "$raw" ]; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
go env GOPATH 2>/dev/null | awk '{print $1 "/bin"}'
|
raw="${raw#\[}"
|
||||||
|
raw="${raw%\]}"
|
||||||
|
local items=()
|
||||||
|
local item=""
|
||||||
|
IFS=',' read -r -a items <<< "$raw"
|
||||||
|
for item in "${items[@]}"; do
|
||||||
|
item="$(unquote_toml "$item")"
|
||||||
|
item="$(trim_whitespace "$item")"
|
||||||
|
if [ -n "$item" ]; then
|
||||||
|
printf "%s\n" "$item"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize_release_config() {
|
||||||
|
RELEASE_DRIVER="$(printf "%s" "$RELEASE_DRIVER" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
RELEASE_DRIVER="$(trim_whitespace "$RELEASE_DRIVER")"
|
||||||
|
|
||||||
|
RELEASE_REPOSITORY="$(trim_whitespace "$RELEASE_REPOSITORY")"
|
||||||
|
RELEASE_REPOSITORY="${RELEASE_REPOSITORY#/}"
|
||||||
|
RELEASE_REPOSITORY="${RELEASE_REPOSITORY%/}"
|
||||||
|
|
||||||
|
RELEASE_BASE_URL="$(trim_whitespace "$RELEASE_BASE_URL")"
|
||||||
|
RELEASE_BASE_URL="${RELEASE_BASE_URL%/}"
|
||||||
|
|
||||||
|
LATEST_RELEASE_URL="$(trim_whitespace "$LATEST_RELEASE_URL")"
|
||||||
|
ASSET_NAME_TEMPLATE="$(trim_whitespace "$ASSET_NAME_TEMPLATE")"
|
||||||
|
CHECKSUM_ASSET_NAME="$(trim_whitespace "$CHECKSUM_ASSET_NAME")"
|
||||||
|
TOKEN_HEADER="$(trim_whitespace "$TOKEN_HEADER")"
|
||||||
|
TOKEN_PREFIX="$(trim_whitespace "$TOKEN_PREFIX")"
|
||||||
|
|
||||||
|
if [ -z "$ASSET_NAME_TEMPLATE" ]; then
|
||||||
|
ASSET_NAME_TEMPLATE="$DEFAULT_ASSET_NAME_TEMPLATE"
|
||||||
|
fi
|
||||||
|
if [ -z "$CHECKSUM_ASSET_NAME" ]; then
|
||||||
|
CHECKSUM_ASSET_NAME="$DEFAULT_CHECKSUM_ASSET_NAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local filtered_env_names=()
|
||||||
|
local env_name=""
|
||||||
|
for env_name in "${TOKEN_ENV_NAMES[@]}"; do
|
||||||
|
env_name="$(trim_whitespace "$env_name")"
|
||||||
|
if [ -n "$env_name" ]; then
|
||||||
|
filtered_env_names+=("$env_name")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
TOKEN_ENV_NAMES=("${filtered_env_names[@]}")
|
||||||
|
|
||||||
|
case "$RELEASE_DRIVER" in
|
||||||
|
gitea)
|
||||||
|
if [ -z "$TOKEN_HEADER" ]; then
|
||||||
|
TOKEN_HEADER="Authorization"
|
||||||
|
fi
|
||||||
|
if [ -z "$TOKEN_PREFIX" ]; then
|
||||||
|
TOKEN_PREFIX="token"
|
||||||
|
fi
|
||||||
|
if [ "${#TOKEN_ENV_NAMES[@]}" -eq 0 ]; then
|
||||||
|
TOKEN_ENV_NAMES=("GITEA_TOKEN")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
gitlab)
|
||||||
|
if [ -z "$RELEASE_BASE_URL" ]; then
|
||||||
|
RELEASE_BASE_URL="https://gitlab.com"
|
||||||
|
fi
|
||||||
|
if [ -z "$TOKEN_HEADER" ]; then
|
||||||
|
TOKEN_HEADER="PRIVATE-TOKEN"
|
||||||
|
fi
|
||||||
|
if [ "${#TOKEN_ENV_NAMES[@]}" -eq 0 ]; then
|
||||||
|
TOKEN_ENV_NAMES=("GITLAB_TOKEN" "GITLAB_PRIVATE_TOKEN")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
github)
|
||||||
|
if [ -z "$RELEASE_BASE_URL" ]; then
|
||||||
|
RELEASE_BASE_URL="https://api.github.com"
|
||||||
|
fi
|
||||||
|
if [ -z "$TOKEN_HEADER" ]; then
|
||||||
|
TOKEN_HEADER="Authorization"
|
||||||
|
fi
|
||||||
|
if [ -z "$TOKEN_PREFIX" ]; then
|
||||||
|
TOKEN_PREFIX="Bearer"
|
||||||
|
fi
|
||||||
|
if [ "${#TOKEN_ENV_NAMES[@]}" -eq 0 ]; then
|
||||||
|
TOKEN_ENV_NAMES=("GITHUB_TOKEN")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
load_release_config_from_manifest() {
|
||||||
|
if [ ! -f "mcp.toml" ]; then
|
||||||
|
ui_warn "mcp.toml introuvable: utilisation de la configuration release integree au script."
|
||||||
|
normalize_release_config
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local value
|
||||||
|
value="$(toml_read_update_value "driver")"
|
||||||
|
if [ -n "$value" ]; then
|
||||||
|
RELEASE_DRIVER="$(printf "%s" "$(unquote_toml "$value")" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
value="$(toml_read_update_value "repository")"
|
||||||
|
if [ -n "$value" ]; then
|
||||||
|
RELEASE_REPOSITORY="$(unquote_toml "$value")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
value="$(toml_read_update_value "base_url")"
|
||||||
|
if [ -n "$value" ]; then
|
||||||
|
RELEASE_BASE_URL="$(unquote_toml "$value")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
value="$(toml_read_update_value "latest_release_url")"
|
||||||
|
if [ -n "$value" ]; then
|
||||||
|
LATEST_RELEASE_URL="$(unquote_toml "$value")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
value="$(toml_read_update_value "asset_name_template")"
|
||||||
|
if [ -n "$value" ]; then
|
||||||
|
ASSET_NAME_TEMPLATE="$(unquote_toml "$value")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
value="$(toml_read_update_value "checksum_asset_name")"
|
||||||
|
if [ -n "$value" ]; then
|
||||||
|
CHECKSUM_ASSET_NAME="$(unquote_toml "$value")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
value="$(toml_read_update_value "checksum_required")"
|
||||||
|
if [ -n "$value" ]; then
|
||||||
|
CHECKSUM_REQUIRED="$(normalize_bool "$(unquote_toml "$value")" "$CHECKSUM_REQUIRED")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
value="$(toml_read_update_value "token_header")"
|
||||||
|
if [ -n "$value" ]; then
|
||||||
|
TOKEN_HEADER="$(unquote_toml "$value")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
value="$(toml_read_update_value "token_prefix")"
|
||||||
|
if [ -n "$value" ]; then
|
||||||
|
TOKEN_PREFIX="$(unquote_toml "$value")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local parsed_token_env_names=()
|
||||||
|
local parsed_env_name=""
|
||||||
|
while IFS= read -r parsed_env_name; do
|
||||||
|
if [ -n "$parsed_env_name" ]; then
|
||||||
|
parsed_token_env_names+=("$parsed_env_name")
|
||||||
|
fi
|
||||||
|
done < <(toml_read_update_array "token_env_names")
|
||||||
|
if [ "${#parsed_token_env_names[@]}" -gt 0 ]; then
|
||||||
|
TOKEN_ENV_NAMES=("${parsed_token_env_names[@]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
normalize_release_config
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_goos() {
|
||||||
|
case "$(uname -s)" in
|
||||||
|
Linux)
|
||||||
|
printf "linux"
|
||||||
|
;;
|
||||||
|
Darwin)
|
||||||
|
printf "darwin"
|
||||||
|
;;
|
||||||
|
FreeBSD)
|
||||||
|
printf "freebsd"
|
||||||
|
;;
|
||||||
|
OpenBSD)
|
||||||
|
printf "openbsd"
|
||||||
|
;;
|
||||||
|
NetBSD)
|
||||||
|
printf "netbsd"
|
||||||
|
;;
|
||||||
|
CYGWIN*|MINGW*|MSYS*)
|
||||||
|
printf "windows"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
ui_error "OS non supporte: $(uname -s)"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_goarch() {
|
||||||
|
case "$(uname -m)" in
|
||||||
|
x86_64|amd64)
|
||||||
|
printf "amd64"
|
||||||
|
;;
|
||||||
|
aarch64|arm64)
|
||||||
|
printf "arm64"
|
||||||
|
;;
|
||||||
|
armv7*|armv6*|armhf)
|
||||||
|
printf "arm"
|
||||||
|
;;
|
||||||
|
i386|i686)
|
||||||
|
printf "386"
|
||||||
|
;;
|
||||||
|
ppc64le)
|
||||||
|
printf "ppc64le"
|
||||||
|
;;
|
||||||
|
s390x)
|
||||||
|
printf "s390x"
|
||||||
|
;;
|
||||||
|
riscv64)
|
||||||
|
printf "riscv64"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
ui_error "Architecture non supportee: $(uname -m)"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_asset_name() {
|
||||||
|
local goos="$1"
|
||||||
|
local goarch="$2"
|
||||||
|
local ext=""
|
||||||
|
if [ "$goos" = "windows" ]; then
|
||||||
|
ext=".exe"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local template="$ASSET_NAME_TEMPLATE"
|
||||||
|
if [ -z "$template" ]; then
|
||||||
|
template="$DEFAULT_ASSET_NAME_TEMPLATE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local asset_name="$template"
|
||||||
|
asset_name="${asset_name//\{binary\}/$BINARY_NAME}"
|
||||||
|
asset_name="${asset_name//\{os\}/$goos}"
|
||||||
|
asset_name="${asset_name//\{arch\}/$goarch}"
|
||||||
|
asset_name="${asset_name//\{ext\}/$ext}"
|
||||||
|
asset_name="$(trim_whitespace "$asset_name")"
|
||||||
|
|
||||||
|
if [ -z "$asset_name" ]; then
|
||||||
|
ui_error "Le template d'asset resolve vers une valeur vide."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$asset_name" in
|
||||||
|
*/*|*\\*)
|
||||||
|
ui_error "Nom d'asset invalide (separateur de chemin): $asset_name"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
printf "%s" "$asset_name"
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_latest_release_url() {
|
||||||
|
if [ -n "$LATEST_RELEASE_URL" ]; then
|
||||||
|
printf "%s" "$LATEST_RELEASE_URL"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$RELEASE_DRIVER" ]; then
|
||||||
|
ui_error "Configuration release incomplete: definir [update].driver ou [update].latest_release_url."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$RELEASE_REPOSITORY" ]; then
|
||||||
|
ui_error "Configuration release incomplete: definir [update].repository."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$RELEASE_DRIVER" in
|
||||||
|
gitea)
|
||||||
|
if [ -z "$RELEASE_BASE_URL" ]; then
|
||||||
|
ui_error "Configuration release incomplete: [update].base_url requis pour driver gitea."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
printf "%s/api/v1/repos/%s/releases/latest" "$RELEASE_BASE_URL" "$RELEASE_REPOSITORY"
|
||||||
|
;;
|
||||||
|
gitlab)
|
||||||
|
local encoded_repository
|
||||||
|
encoded_repository="$(jq -nr --arg value "$RELEASE_REPOSITORY" '$value|@uri')"
|
||||||
|
printf "%s/api/v4/projects/%s/releases/permalink/latest" "$RELEASE_BASE_URL" "$encoded_repository"
|
||||||
|
;;
|
||||||
|
github)
|
||||||
|
printf "%s/repos/%s/releases/latest" "$RELEASE_BASE_URL" "$RELEASE_REPOSITORY"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
ui_error "Driver release non supporte: $RELEASE_DRIVER (attendu: gitea, gitlab ou github)."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_auth_header() {
|
||||||
|
AUTH_HEADER_NAME=""
|
||||||
|
AUTH_HEADER_VALUE=""
|
||||||
|
|
||||||
|
local token=""
|
||||||
|
local env_name
|
||||||
|
for env_name in "${TOKEN_ENV_NAMES[@]}"; do
|
||||||
|
if [ -z "$env_name" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [ -n "${!env_name:-}" ]; then
|
||||||
|
token="$(trim_whitespace "${!env_name}")"
|
||||||
|
if [ -n "$token" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$token" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local header_name
|
||||||
|
header_name="$(trim_whitespace "$TOKEN_HEADER")"
|
||||||
|
if [ -z "$header_name" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local prefix
|
||||||
|
prefix="$(trim_whitespace "$TOKEN_PREFIX")"
|
||||||
|
if [ -n "$prefix" ]; then
|
||||||
|
local lower_token
|
||||||
|
local lower_prefix
|
||||||
|
lower_token="$(printf "%s" "$token" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
lower_prefix="$(printf "%s" "$prefix" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
if [[ "$lower_token" != "$lower_prefix"* ]]; then
|
||||||
|
token="$prefix $token"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
AUTH_HEADER_NAME="$header_name"
|
||||||
|
AUTH_HEADER_VALUE="$token"
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_download() {
|
||||||
|
local url="$1"
|
||||||
|
local output_path="$2"
|
||||||
|
local mode="${3:-}"
|
||||||
|
local curl_args=(
|
||||||
|
-fsSL
|
||||||
|
-H "User-Agent: mcp install wizard"
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ "$mode" = "json" ]; then
|
||||||
|
curl_args+=(-H "Accept: application/json")
|
||||||
|
fi
|
||||||
|
if [ -n "$AUTH_HEADER_NAME" ] && [ -n "$AUTH_HEADER_VALUE" ]; then
|
||||||
|
curl_args+=(-H "${AUTH_HEADER_NAME}: ${AUTH_HEADER_VALUE}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl "${curl_args[@]}" "$url" -o "$output_path"
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_url_reference() {
|
||||||
|
local value="$1"
|
||||||
|
local base="$2"
|
||||||
|
|
||||||
|
if printf "%s" "$value" | grep -Eq '^https?://'; then
|
||||||
|
printf "%s" "$value"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local origin
|
||||||
|
origin="$(printf "%s" "$base" | sed -E 's#^(https?://[^/]+).*$#\1#')"
|
||||||
|
|
||||||
|
if [ -z "$origin" ]; then
|
||||||
|
printf "%s" "$value"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${value#/}" != "$value" ]; then
|
||||||
|
printf "%s%s" "$origin" "$value"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local base_no_query
|
||||||
|
base_no_query="$(printf "%s" "$base" | sed 's/[?#].*$//')"
|
||||||
|
local base_dir="${base_no_query%/*}"
|
||||||
|
printf "%s/%s" "$base_dir" "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_asset_url_from_release() {
|
||||||
|
local release_json_path="$1"
|
||||||
|
local asset_name="$2"
|
||||||
|
jq -r --arg asset "$asset_name" '
|
||||||
|
(
|
||||||
|
.assets.links[]? | select(.name == $asset) | (.direct_asset_url // .browser_download_url // .url // empty)
|
||||||
|
),
|
||||||
|
(
|
||||||
|
.assets[]? | select(.name == $asset) | (.direct_asset_url // .browser_download_url // .url // empty)
|
||||||
|
)
|
||||||
|
| select(. != null and . != "")
|
||||||
|
' "$release_json_path" | head -n 1
|
||||||
|
}
|
||||||
|
|
||||||
|
release_assets_preview() {
|
||||||
|
local release_json_path="$1"
|
||||||
|
jq -r '
|
||||||
|
[
|
||||||
|
(.assets.links[]?.name),
|
||||||
|
(.assets[]?.name)
|
||||||
|
]
|
||||||
|
| map(select(. != null and . != ""))
|
||||||
|
| unique
|
||||||
|
| .[:8]
|
||||||
|
| join(", ")
|
||||||
|
' "$release_json_path"
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_checksum_for_asset() {
|
||||||
|
local checksum_file="$1"
|
||||||
|
local asset_name="$2"
|
||||||
|
awk -v asset="$asset_name" '
|
||||||
|
function is_hex(value) {
|
||||||
|
return value ~ /^[0-9A-Fa-f]{64}$/
|
||||||
|
}
|
||||||
|
{
|
||||||
|
line = $0
|
||||||
|
sub(/#.*/, "", line)
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", line)
|
||||||
|
if (line == "") {
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
n = split(line, fields, /[[:space:]]+/)
|
||||||
|
if (n == 1 && is_hex(fields[1]) && fallback == "") {
|
||||||
|
fallback = fields[1]
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n >= 2 && is_hex(fields[1])) {
|
||||||
|
candidate = fields[2]
|
||||||
|
sub(/^\*/, "", candidate)
|
||||||
|
sub(/^\.\/+/, "", candidate)
|
||||||
|
if (candidate == asset) {
|
||||||
|
print fields[1]
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n >= 2 && is_hex(fields[n])) {
|
||||||
|
candidate = fields[1]
|
||||||
|
sub(/^\*/, "", candidate)
|
||||||
|
sub(/^\.\/+/, "", candidate)
|
||||||
|
if (candidate == asset) {
|
||||||
|
print fields[n]
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
if (fallback != "") {
|
||||||
|
print fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$checksum_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
sha256_file() {
|
||||||
|
local file_path="$1"
|
||||||
|
if command -v sha256sum >/dev/null 2>&1; then
|
||||||
|
sha256sum "$file_path" | awk '{print $1}'
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if command -v shasum >/dev/null 2>&1; then
|
||||||
|
shasum -a 256 "$file_path" | awk '{print $1}'
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
ui_error "Aucun outil SHA-256 detecte (sha256sum ou shasum requis)."
|
||||||
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve_binary_path() {
|
resolve_binary_path() {
|
||||||
|
|
@ -560,13 +1115,9 @@ resolve_binary_path() {
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v go >/dev/null 2>&1; then
|
if [ -x "$HOME/.local/bin/$BINARY_NAME" ]; then
|
||||||
local bin_dir
|
printf "%s\n" "$HOME/.local/bin/$BINARY_NAME"
|
||||||
bin_dir="$(go_bin_dir)"
|
return
|
||||||
if [ -n "$bin_dir" ] && [ -x "$bin_dir/$BINARY_NAME" ]; then
|
|
||||||
printf "%s\n" "$bin_dir/$BINARY_NAME"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf "%s\n" "$HOME/.local/bin/$BINARY_NAME"
|
printf "%s\n" "$HOME/.local/bin/$BINARY_NAME"
|
||||||
|
|
@ -583,10 +1134,18 @@ ensure_cli() {
|
||||||
}
|
}
|
||||||
|
|
||||||
install_binary() {
|
install_binary() {
|
||||||
|
ensure_cli "curl"
|
||||||
|
ensure_cli "jq"
|
||||||
|
|
||||||
|
load_release_config_from_manifest
|
||||||
|
resolve_auth_header
|
||||||
|
|
||||||
|
local target_path="$HOME/.local/bin/$BINARY_NAME"
|
||||||
if command -v "$BINARY_NAME" >/dev/null 2>&1; then
|
if command -v "$BINARY_NAME" >/dev/null 2>&1; then
|
||||||
ui_success "Binaire detecte: $(command -v "$BINARY_NAME")"
|
target_path="$(command -v "$BINARY_NAME")"
|
||||||
|
ui_success "Binaire detecte: $target_path"
|
||||||
local reinstall
|
local reinstall
|
||||||
reinstall="$(prompt "Reinstaller via go install ? (y/N)" "N")"
|
reinstall="$(prompt "Reinstaller depuis la derniere release ? (y/N)" "N")"
|
||||||
case "$reinstall" in
|
case "$reinstall" in
|
||||||
y|Y|yes|YES)
|
y|Y|yes|YES)
|
||||||
;;
|
;;
|
||||||
|
|
@ -596,19 +1155,118 @@ install_binary() {
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! command -v go >/dev/null 2>&1; then
|
if [ ! -w "$(dirname "$target_path")" ]; then
|
||||||
ui_error "Go n'est pas installe. Installe Go ou choisis une configuration manuelle."
|
ui_warn "Pas de droit d'ecriture dans $(dirname "$target_path"), installation dans $HOME/.local/bin."
|
||||||
|
target_path="$HOME/.local/bin/$BINARY_NAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local goos
|
||||||
|
goos="$(resolve_goos)"
|
||||||
|
local goarch
|
||||||
|
goarch="$(resolve_goarch)"
|
||||||
|
local asset_name
|
||||||
|
asset_name="$(resolve_asset_name "$goos" "$goarch")"
|
||||||
|
local release_url
|
||||||
|
release_url="$(resolve_latest_release_url)"
|
||||||
|
|
||||||
|
ui_info "Recherche de la derniere release pour $RELEASE_REPOSITORY ($goos/$goarch)..."
|
||||||
|
local release_json
|
||||||
|
release_json="$(mktemp)"
|
||||||
|
if ! curl_download "$release_url" "$release_json" "json"; then
|
||||||
|
rm -f "$release_json"
|
||||||
|
ui_error "Impossible de recuperer la release latest depuis $release_url."
|
||||||
|
if [ "${#TOKEN_ENV_NAMES[@]}" -gt 0 ]; then
|
||||||
|
ui_info "Si le repo est prive, configure un token via: ${TOKEN_ENV_NAMES[*]}"
|
||||||
|
fi
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ui_info "Installation du binaire via go install..."
|
local release_tag
|
||||||
go install "${MODULE_PATH}/cmd/${BINARY_NAME}@latest"
|
release_tag="$(jq -r '.tag_name // empty' "$release_json")"
|
||||||
|
if [ -z "$release_tag" ]; then
|
||||||
|
rm -f "$release_json"
|
||||||
|
ui_error "Reponse release invalide: tag_name manquant."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
local bin_dir
|
local asset_url
|
||||||
bin_dir="$(go_bin_dir)"
|
asset_url="$(resolve_asset_url_from_release "$release_json" "$asset_name")"
|
||||||
if [ -n "$bin_dir" ]; then
|
if [ -z "$asset_url" ]; then
|
||||||
ui_success "Binaire installe dans $bin_dir"
|
local preview
|
||||||
ui_info "Ajoute ce dossier au PATH si necessaire."
|
preview="$(release_assets_preview "$release_json")"
|
||||||
|
rm -f "$release_json"
|
||||||
|
ui_error "Aucun asset \"$asset_name\" dans la release $release_tag."
|
||||||
|
if [ -n "$preview" ]; then
|
||||||
|
ui_info "Assets disponibles: $preview"
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
asset_url="$(resolve_url_reference "$asset_url" "$release_url")"
|
||||||
|
|
||||||
|
ui_info "Telechargement de l'asset $asset_name ($release_tag)..."
|
||||||
|
local download_path
|
||||||
|
download_path="$(mktemp)"
|
||||||
|
if ! curl_download "$asset_url" "$download_path"; then
|
||||||
|
rm -f "$release_json" "$download_path"
|
||||||
|
ui_error "Echec du telechargement de l'asset: $asset_url"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local checksum_asset_name
|
||||||
|
checksum_asset_name="${CHECKSUM_ASSET_NAME//\{asset\}/$asset_name}"
|
||||||
|
if [ -z "$checksum_asset_name" ]; then
|
||||||
|
checksum_asset_name="${asset_name}.sha256"
|
||||||
|
fi
|
||||||
|
local checksum_url
|
||||||
|
checksum_url="$(resolve_asset_url_from_release "$release_json" "$checksum_asset_name")"
|
||||||
|
if [ -n "$checksum_url" ]; then
|
||||||
|
checksum_url="$(resolve_url_reference "$checksum_url" "$release_url")"
|
||||||
|
local checksum_path
|
||||||
|
checksum_path="$(mktemp)"
|
||||||
|
if ! curl_download "$checksum_url" "$checksum_path"; then
|
||||||
|
rm -f "$release_json" "$download_path" "$checksum_path"
|
||||||
|
ui_error "Echec du telechargement du checksum: $checksum_url"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local expected_checksum
|
||||||
|
expected_checksum="$(parse_checksum_for_asset "$checksum_path" "$asset_name")"
|
||||||
|
if [ -z "$expected_checksum" ]; then
|
||||||
|
rm -f "$release_json" "$download_path" "$checksum_path"
|
||||||
|
ui_error "Checksum introuvable dans $checksum_asset_name pour l'asset $asset_name."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local actual_checksum
|
||||||
|
actual_checksum="$(sha256_file "$download_path")"
|
||||||
|
local actual_checksum_lc
|
||||||
|
actual_checksum_lc="$(printf "%s" "$actual_checksum" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
local expected_checksum_lc
|
||||||
|
expected_checksum_lc="$(printf "%s" "$expected_checksum" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
if [ "$actual_checksum_lc" != "$expected_checksum_lc" ]; then
|
||||||
|
rm -f "$release_json" "$download_path" "$checksum_path"
|
||||||
|
ui_error "Checksum invalide pour $asset_name."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
rm -f "$checksum_path"
|
||||||
|
ui_success "Checksum verifie pour $asset_name."
|
||||||
|
else
|
||||||
|
if [ "$CHECKSUM_REQUIRED" = "true" ]; then
|
||||||
|
rm -f "$release_json" "$download_path"
|
||||||
|
ui_error "Checksum requis mais asset \"$checksum_asset_name\" introuvable."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
ui_warn "Checksum non disponible pour $asset_name (verification ignoree)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod +x "$download_path"
|
||||||
|
mkdir -p "$(dirname "$target_path")"
|
||||||
|
mv "$download_path" "$target_path"
|
||||||
|
rm -f "$release_json"
|
||||||
|
ui_success "Binaire installe: $target_path"
|
||||||
|
|
||||||
|
if ! printf ":%s:" "$PATH" | grep -Fq ":$(dirname "$target_path"):"; then
|
||||||
|
ui_info "Ajoute $(dirname "$target_path") au PATH si necessaire."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,12 @@ func TestGenerateCreatesRecommendedSkeleton(t *testing.T) {
|
||||||
for _, snippet := range []string{
|
for _, snippet := range []string{
|
||||||
"#!/usr/bin/env bash",
|
"#!/usr/bin/env bash",
|
||||||
`MODULE_PATH="example.com/acme/my-mcp"`,
|
`MODULE_PATH="example.com/acme/my-mcp"`,
|
||||||
`go install "${MODULE_PATH}/cmd/${BINARY_NAME}@latest"`,
|
`DEFAULT_RELEASE_REPOSITORY="org/my-mcp"`,
|
||||||
|
`load_release_config_from_manifest`,
|
||||||
|
`resolve_latest_release_url()`,
|
||||||
|
`curl_download "$release_url" "$release_json" "json"`,
|
||||||
|
`asset_name="$(resolve_asset_name "$goos" "$goarch")"`,
|
||||||
|
`Reinstaller depuis la derniere release ? (y/N)`,
|
||||||
"MCP Install Wizard",
|
"MCP Install Wizard",
|
||||||
`menu_select() {`,
|
`menu_select() {`,
|
||||||
`Utilise ↑/↓ puis Entrée.`,
|
`Utilise ↑/↓ puis Entrée.`,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue