Skip to content

Commit d0b4881

Browse files
committed
Refactor: Smplify install.sh to use gh-release pattern and standarize library_scripts.sh.
1 parent 3acf268 commit d0b4881

3 files changed

Lines changed: 182 additions & 131 deletions

File tree

src/bazel-compile-commands/devcontainer-feature.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
"version": "1.0.0",
44
"name": "bazel-compile-commands (via Github Releases)",
55
"documentationURL": "http://github.com/devcontainers-extra/features/tree/main/src/bazel-compile-commands",
6-
"description": "Generates compile_commands.json from Bazel builds. Installs the appropriate binary for the current platform and CPU architecture: a native .deb on Ubuntu Noble (24.04), a generic zip on other Linux distros, and the macOS universal binary on Darwin.",
6+
"description": "Generates compile_commands.json from Bazel builds.",
77
"options": {
88
"version": {
99
"default": "latest",
10-
"description": "Version to install. Accepts 'latest', a numeric version like '0.22.4', or the full release tag 'bazel-compile-commands-v0.22.4'.",
10+
"description": "Version to install. Accepts 'latest' or a numeric version like '0.22.4'.",
1111
"proposals": [
1212
"latest",
1313
"0.22.4"

src/bazel-compile-commands/install.sh

Lines changed: 24 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,29 @@
22

33
set -e
44

5-
source ./library_scripts.sh
6-
7-
BINARY_NAME="bazel-compile-commands"
8-
GITHUB_REPO="kiron1/bazel-compile-commands"
9-
VERSION="${VERSION:-"latest"}"
10-
11-
PLATFORM="$(detect_platform)"
12-
ARCH="$(detect_arch)"
13-
ensure_packages curl ca-certificates
14-
TAG="$(resolve_tag "${VERSION}" "${GITHUB_REPO}" "${BINARY_NAME}")"
15-
16-
# Extract the numeric version from the tag (e.g. "bazel-compile-commands-v0.22.4" → "0.22.4")
17-
FILE_VERSION="${TAG#${BINARY_NAME}-v}"
18-
19-
BASE_URL="https://github.com/${GITHUB_REPO}/releases/download/${TAG}"
20-
21-
echo "Installing ${BINARY_NAME} ${TAG} for ${PLATFORM}/${ARCH}..."
22-
23-
USE_DEB=false
24-
ZIP_ASSET=""
25-
26-
if [ "${PLATFORM}" = "linux" ]; then
27-
# On Ubuntu Noble (24.04), prefer the native .deb package; fall back to the generic .zip.
28-
if command -v apt-get >/dev/null 2>&1 && \
29-
[ -f /etc/os-release ] && \
30-
grep -qi "noble" /etc/os-release; then
31-
USE_DEB=true
32-
else
33-
ZIP_ASSET="linux_${ARCH}"
34-
fi
35-
elif [ "${PLATFORM}" = "macos" ]; then
36-
ZIP_ASSET="macos_universal"
37-
fi
38-
39-
if [ "${USE_DEB}" = true ]; then
40-
echo "Ubuntu Noble detected – installing via .deb package..."
41-
TMP_DEB=$(mktemp --suffix=.deb)
42-
curl -fsSL "${BASE_URL}/${BINARY_NAME}_${FILE_VERSION}-noble_${ARCH}.deb" -o "${TMP_DEB}"
43-
apt_get_update
44-
apt-get install -y "${TMP_DEB}"
45-
rm -f "${TMP_DEB}"
46-
apt_cleanup
47-
else
48-
echo "Installing via .zip (${ZIP_ASSET})..."
49-
install_via_zip "${BASE_URL}/${BINARY_NAME}_${FILE_VERSION}-${ZIP_ASSET}.zip" "${BINARY_NAME}"
5+
. ./library_scripts.sh
6+
7+
# nanolayer is a cli utility which keeps container layers as small as possible
8+
# source code: https://github.com/devcontainers-extra/nanolayer
9+
# `ensure_nanolayer` is a bash function that will find any existing nanolayer installations,
10+
# and if missing - will download a temporary copy that automatically get deleted at the end
11+
# of the script
12+
ensure_nanolayer nanolayer_location "v0.5.6"
13+
14+
# Upstream tags use a non-standard "bazel-compile-commands-v<ver>" format. Normalise
15+
# any plain version (e.g. "0.22.4" or "v0.22.4") so nanolayer can match it exactly.
16+
if [[ "${VERSION}" != "latest" && "${VERSION}" != bazel-compile-commands-* ]]; then
17+
VERSION="bazel-compile-commands-v${VERSION#v}"
5018
fi
5119

52-
echo "Done! ${BINARY_NAME} installed to /usr/local/bin/${BINARY_NAME}"
20+
$nanolayer_location \
21+
install \
22+
devcontainer-feature \
23+
"ghcr.io/devcontainers-extra/features/gh-release:1" \
24+
--option repo='kiron1/bazel-compile-commands' \
25+
--option binaryNames='bazel-compile-commands' \
26+
--option version="$VERSION" \
27+
--option assetRegex='linux_' \
28+
--option releaseTagRegex='bazel-compile-commands-v'
29+
30+
echo 'Done!'
Lines changed: 156 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,173 @@
11
#!/usr/bin/env bash
22

3-
# Adapted from https://github.com/devcontainers-community/features-bazel/blob/main/lib.sh
4-
# which itself traces back to the official devcontainers/features node install script.
3+
clean_download() {
4+
# The purpose of this function is to download a file with minimal impact on container layer size
5+
# this means if no valid downloader is found (curl or wget) then we install a downloader (currently wget) in a
6+
# temporary manner, and making sure to
7+
# 1. uninstall the downloader at the return of the function
8+
# 2. revert back any changes to the package installer database/cache (for example apt-get lists)
9+
# The above steps will minimize the leftovers being created while installing the downloader
10+
# Supported distros:
11+
# debian/ubuntu/alpine
512

6-
# Runs apt-get update only when the apt lists cache is empty, avoiding redundant network hits.
7-
apt_get_update() {
8-
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
9-
echo "Running apt-get update..."
13+
url=$1
14+
output_location=$2
15+
tempdir=$(mktemp -d)
16+
downloader_installed=""
17+
18+
function _apt_get_install() {
19+
tempdir=$1
20+
21+
# copy current state of apt list - in order to revert back later (minimize contianer layer size)
22+
cp -p -R /var/lib/apt/lists $tempdir
1023
apt-get update -y
11-
fi
12-
}
24+
apt-get -y install --no-install-recommends wget ca-certificates
25+
}
26+
27+
function _apt_get_cleanup() {
28+
tempdir=$1
29+
30+
echo "removing wget"
31+
apt-get -y purge wget --auto-remove
32+
33+
echo "revert back apt lists"
34+
rm -rf /var/lib/apt/lists/*
35+
rm -r /var/lib/apt/lists && mv $tempdir/lists /var/lib/apt/lists
36+
}
37+
38+
function _apk_install() {
39+
tempdir=$1
40+
# copy current state of apk cache - in order to revert back later (minimize contianer layer size)
41+
cp -p -R /var/cache/apk $tempdir
42+
43+
apk add --no-cache wget
44+
}
1345

14-
# Installs the given packages only if they are not already installed (checked via dpkg).
15-
# Debian/Ubuntu only; calls apt_get_update lazily rather than unconditionally.
16-
check_packages() {
17-
if ! dpkg -s "$@" > /dev/null 2>&1; then
18-
apt_get_update
19-
apt-get -y install --no-install-recommends "$@"
46+
function _apk_cleanup() {
47+
tempdir=$1
48+
49+
echo "removing wget"
50+
apk del wget
51+
}
52+
# try to use either wget or curl if one of them already installer
53+
if type curl >/dev/null 2>&1; then
54+
downloader=curl
55+
elif type wget >/dev/null 2>&1; then
56+
downloader=wget
57+
else
58+
downloader=""
2059
fi
21-
}
2260

23-
# Removes apt list files to keep the container layer small.
24-
apt_cleanup() {
25-
rm -rf /var/lib/apt/lists/*
26-
}
61+
# in case none of them is installed, install wget temporarly
62+
if [ -z $downloader ]; then
63+
if [ -x "/usr/bin/apt-get" ]; then
64+
_apt_get_install $tempdir
65+
elif [ -x "/sbin/apk" ]; then
66+
_apk_install $tempdir
67+
else
68+
echo "distro not supported"
69+
exit 1
70+
fi
71+
downloader="wget"
72+
downloader_installed="true"
73+
fi
2774

28-
# Ensures the given packages are installed, using apt-get on Debian/Ubuntu or apk on Alpine.
29-
ensure_packages() {
30-
if command -v apt-get >/dev/null 2>&1; then
31-
check_packages "$@"
32-
elif command -v apk >/dev/null 2>&1; then
33-
apk add --no-cache "$@"
75+
if [ $downloader = "wget" ]; then
76+
wget -q $url -O $output_location
3477
else
35-
echo "No supported package manager found to install: $*" >&2
36-
exit 1
78+
curl -sfL $url -o $output_location
3779
fi
38-
}
3980

40-
detect_platform() {
41-
local os
42-
os="$(uname -s)"
43-
case "${os}" in
44-
Linux) echo "linux" ;;
45-
Darwin) echo "macos" ;;
46-
*) echo "Unsupported platform: ${os}" >&2; exit 1 ;;
47-
esac
48-
}
81+
# NOTE: the cleanup procedure was not implemented using `trap X RETURN` only because
82+
# alpine lack bash, and RETURN is not a valid signal under sh shell
83+
if ! [ -z $downloader_installed ]; then
84+
if [ -x "/usr/bin/apt-get" ]; then
85+
_apt_get_cleanup $tempdir
86+
elif [ -x "/sbin/apk" ]; then
87+
_apk_cleanup $tempdir
88+
else
89+
echo "distro not supported"
90+
exit 1
91+
fi
92+
fi
4993

50-
detect_arch() {
51-
local arch
52-
arch="$(uname -m)"
53-
case "${arch}" in
54-
x86_64) echo "amd64" ;;
55-
aarch64 | arm64) echo "arm64" ;;
56-
*) echo "Unsupported architecture: ${arch}" >&2; exit 1 ;;
57-
esac
5894
}
5995

60-
# Resolves a user-provided version string to the exact GitHub release tag.
61-
# Accepted inputs: "latest", "0.22.4", "v0.22.4", or "bazel-compile-commands-v0.22.4"
62-
resolve_tag() {
63-
local version="$1"
64-
local repo="$2"
65-
local tag_prefix="$3" # e.g. "bazel-compile-commands"
66-
67-
if [ "${version}" = "latest" ]; then
68-
curl -fsSL "https://api.github.com/repos/${repo}/releases/latest" \
69-
| grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/'
70-
elif [[ "${version}" == ${tag_prefix}-* ]]; then
71-
echo "${version}"
72-
elif [[ "${version}" == v* ]]; then
73-
echo "${tag_prefix}-${version}"
74-
else
75-
echo "${tag_prefix}-v${version}"
96+
ensure_nanolayer() {
97+
# Ensure existance of the nanolayer cli program
98+
local variable_name=$1
99+
100+
local required_version=$2
101+
# normalize version
102+
if ! [[ $required_version == v* ]]; then
103+
required_version=v$required_version
76104
fi
77-
}
78105

79-
# Downloads a GitHub release zip asset, extracts a named binary, and places it in /usr/local/bin.
80-
#
81-
# Usage: install_via_zip <zip_url> <binary_name>
82-
#
83-
# zip_url - Direct URL to the .zip asset on GitHub Releases.
84-
# binary_name - Name of the binary inside the archive (e.g. "bazel-compile-commands").
85-
#
86-
# unzip is auto-installed if not already present, and the temporary download
87-
# directory is cleaned up on exit.
88-
install_via_zip() {
89-
local zip_url="$1"
90-
local binary_name="$2"
91-
92-
ensure_packages unzip
93-
94-
local tmp_dir
95-
tmp_dir=$(mktemp -d)
96-
curl -fsSL "${zip_url}" -o "${tmp_dir}/${binary_name}.zip"
97-
unzip -j "${tmp_dir}/${binary_name}.zip" -d "${tmp_dir}"
98-
install -m 0755 "${tmp_dir}/${binary_name}" /usr/local/bin/
99-
rm -rf "${tmp_dir}"
106+
local nanolayer_location=""
107+
108+
# If possible - try to use an already installed nanolayer
109+
if [[ -z "${NANOLAYER_FORCE_CLI_INSTALLATION}" ]]; then
110+
if [[ -z "${NANOLAYER_CLI_LOCATION}" ]]; then
111+
if type nanolayer >/dev/null 2>&1; then
112+
echo "Found a pre-existing nanolayer in PATH"
113+
nanolayer_location=nanolayer
114+
fi
115+
elif [ -f "${NANOLAYER_CLI_LOCATION}" ] && [ -x "${NANOLAYER_CLI_LOCATION}" ]; then
116+
nanolayer_location=${NANOLAYER_CLI_LOCATION}
117+
echo "Found a pre-existing nanolayer which were given in env variable: $nanolayer_location"
118+
fi
119+
120+
# make sure its of the required version
121+
if ! [[ -z "${nanolayer_location}" ]]; then
122+
local current_version
123+
current_version=$($nanolayer_location --version)
124+
if ! [[ $current_version == v* ]]; then
125+
current_version=v$current_version
126+
fi
127+
128+
if ! [ $current_version == $required_version ]; then
129+
echo "skipping usage of pre-existing nanolayer. (required version $required_version does not match existing version $current_version)"
130+
nanolayer_location=""
131+
fi
132+
fi
133+
134+
fi
135+
136+
# If not previuse installation found, download it temporarly and delete at the end of the script
137+
if [[ -z "${nanolayer_location}" ]]; then
138+
139+
if [ "$(uname -sm)" == "Linux x86_64" ] || [ "$(uname -sm)" == "Linux aarch64" ]; then
140+
tmp_dir=$(mktemp -d -t nanolayer-XXXXXXXXXX)
141+
142+
clean_up() {
143+
ARG=$?
144+
rm -rf $tmp_dir
145+
exit $ARG
146+
}
147+
trap clean_up EXIT
148+
149+
if [ -x "/sbin/apk" ]; then
150+
clib_type=musl
151+
else
152+
clib_type=gnu
153+
fi
154+
155+
tar_filename=nanolayer-"$(uname -m)"-unknown-linux-$clib_type.tgz
156+
157+
# clean download will minimize leftover in case a downloaderlike wget or curl need to be installed
158+
clean_download https://github.com/devcontainers-extra/nanolayer/releases/download/$required_version/$tar_filename $tmp_dir/$tar_filename
159+
160+
tar xfzv $tmp_dir/$tar_filename -C "$tmp_dir"
161+
chmod a+x $tmp_dir/nanolayer
162+
nanolayer_location=$tmp_dir/nanolayer
163+
164+
else
165+
echo "No binaries compiled for non-x86-linux architectures yet: $(uname -m)"
166+
exit 1
167+
fi
168+
fi
169+
170+
# Expose outside the resolved location
171+
declare -g ${variable_name}=$nanolayer_location
172+
100173
}

0 commit comments

Comments
 (0)