Skip to content

Commit 294f24a

Browse files
authored
feat(btop): Add feature. (#2)
* feat(`btop`): Add feature. * ci: Add test for feature `btop`. * feat(`btop`): Add `bzip2` dependency.
1 parent 1d2944a commit 294f24a

File tree

7 files changed

+211
-0
lines changed

7 files changed

+211
-0
lines changed

.github/workflows/test.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ jobs:
1414
matrix:
1515
features:
1616
- atuin.sh
17+
- btop
1718
baseImage:
1819
- debian:latest
1920
- ubuntu:latest
@@ -34,6 +35,7 @@ jobs:
3435
matrix:
3536
features:
3637
- atuin.sh
38+
- btop
3739
steps:
3840
- uses: actions/checkout@v4
3941

src/atuin.sh/devcontainer-feature.json

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"id": "atuin.sh",
44
"version": "1.0.0",
55
"description": "Install \"atuin\" binary",
6+
"documentationURL": "https://github.com/devcontainer-community/devcontainer-features/tree/main/src/atuin.sh",
67
"options": {
78
"version": {
89
"type": "string",

src/btop/devcontainer-feature.json

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "btop",
3+
"id": "btop",
4+
"version": "1.0.0",
5+
"description": "Install \"btop\" binary",
6+
"documentationURL": "https://github.com/devcontainer-community/devcontainer-features/tree/main/src/btop",
7+
"options": {
8+
"version": {
9+
"type": "string",
10+
"default": "latest",
11+
"proposals": [
12+
"latest"
13+
],
14+
"description": "Version of \"btop\" to install."
15+
}
16+
}
17+
}

src/btop/install.sh

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#!/bin/bash
2+
set -o errexit
3+
set -o pipefail
4+
set -o noclobber
5+
set -o nounset
6+
set -o allexport
7+
readonly githubRepository='aristocratos/btop'
8+
readonly binaryName='btop'
9+
readonly versionArgument='--version'
10+
readonly downloadUrlTemplate='https://github.com/${githubRepository}/releases/download/v${version}/${name}-${architecture}-linux-musl.tbz'
11+
readonly binaryPathInArchiveTemplate='./btop/bin/${binaryName}'
12+
readonly binaryTargetFolder='/usr/local/bin'
13+
readonly name="${githubRepository##*/}"
14+
apt_get_update() {
15+
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
16+
echo "Running apt-get update..."
17+
apt-get update -y
18+
fi
19+
}
20+
apt_get_checkinstall() {
21+
if ! dpkg -s "$@" >/dev/null 2>&1; then
22+
apt_get_update
23+
DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends --no-install-suggests --option 'Debug::pkgProblemResolver=true' --option 'Debug::pkgAcquire::Worker=1' "$@"
24+
fi
25+
}
26+
apt_get_cleanup() {
27+
apt-get clean
28+
rm -rf /var/lib/apt/lists/*
29+
}
30+
check_curl_envsubst_file_tar_installed() {
31+
declare -a requiredAptPackagesMissing=()
32+
if ! [ -r '/etc/ssl/certs/ca-certificates.crt' ]; then
33+
requiredAptPackagesMissing+=('ca-certificates')
34+
fi
35+
if ! command -v curl >/dev/null 2>&1; then
36+
requiredAptPackagesMissing+=('curl')
37+
fi
38+
if ! command -v envsubst >/dev/null 2>&1; then
39+
requiredAptPackagesMissing+=('gettext-base')
40+
fi
41+
if ! command -v file >/dev/null 2>&1; then
42+
requiredAptPackagesMissing+=('file')
43+
fi
44+
if ! command -v tar >/dev/null 2>&1; then
45+
requiredAptPackagesMissing+=('tar')
46+
fi
47+
declare -i requiredAptPackagesMissingCount=${#requiredAptPackagesMissing[@]}
48+
if [ $requiredAptPackagesMissingCount -gt 0 ]; then
49+
apt_get_update
50+
apt_get_checkinstall "${requiredAptPackagesMissing[@]}"
51+
apt_get_cleanup
52+
fi
53+
}
54+
curl_check_url() {
55+
local url=$1
56+
local status_code
57+
status_code=$(curl -s -o /dev/null -w '%{http_code}' "$url")
58+
if [ "$status_code" -ne 200 ] && [ "$status_code" -ne 302 ]; then
59+
echo "Failed to download '$url'. Status code: $status_code."
60+
return 1
61+
fi
62+
}
63+
curl_download_stdout() {
64+
local url=$1
65+
curl \
66+
--silent \
67+
--location \
68+
--output '-' \
69+
--connect-timeout 5 \
70+
"$url"
71+
}
72+
curl_download_untarj() {
73+
local url=$1
74+
local strip=$2
75+
local target=$3
76+
local bin_path=$4
77+
curl_download_stdout "$url" | tar \
78+
-xj \
79+
-f '-' \
80+
--strip-components="$strip" \
81+
-C "$target" \
82+
"$bin_path"
83+
}
84+
debian_get_arch() {
85+
echo "$(dpkg --print-architecture)"
86+
}
87+
debian_get_target_arch() {
88+
case $(debian_get_arch) in
89+
amd64) echo 'x86_64' ;;
90+
arm64) echo 'aarch64' ;;
91+
armhf) echo 'armv7' ;;
92+
i386) echo 'i686' ;;
93+
*) echo 'unknown' ;;
94+
esac
95+
}
96+
echo_banner() {
97+
local text="$1"
98+
echo -e "\e[1m\e[97m\e[41m$text\e[0m"
99+
}
100+
github_list_releases() {
101+
if [ -z "$1" ]; then
102+
echo "Usage: list_github_releases <owner/repo>"
103+
return 1
104+
fi
105+
local repo="$1"
106+
local url="https://api.github.com/repos/$repo/releases"
107+
curl -s "$url" | grep -Po '"tag_name": "\K.*?(?=")' | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' | sed 's/^v//'
108+
}
109+
github_get_latest_release() {
110+
if [ -z "$1" ]; then
111+
echo "Usage: get_latest_github_release <owner/repo>"
112+
return 1
113+
fi
114+
github_list_releases "$1" | head -n 1
115+
}
116+
utils_check_version() {
117+
local version=$1
118+
if ! [[ "${version:-}" =~ ^(latest|[0-9]+\.[0-9]+\.[0-9]+)$ ]]; then
119+
printf >&2 '=== [ERROR] Option "version" (value: "%s") is not "latest" or valid semantic version format "X.Y.Z" !\n' \
120+
"$version"
121+
exit 1
122+
fi
123+
}
124+
install() {
125+
utils_check_version "$VERSION"
126+
check_curl_envsubst_file_tar_installed
127+
apt_get_checkinstall bzip2
128+
readonly architecture="$(debian_get_target_arch)"
129+
readonly binaryTargetPathTemplate='${binaryTargetFolder}/${binaryName}'
130+
if [ "$VERSION" == 'latest' ] || [ -z "$VERSION" ]; then
131+
VERSION=$(github_get_latest_release "$githubRepository")
132+
fi
133+
readonly version="${VERSION:?}"
134+
readonly downloadUrl="$(echo -n "$downloadUrlTemplate" | envsubst)"
135+
curl_check_url "$downloadUrl"
136+
readonly binaryPathInArchive="$(echo -n "$binaryPathInArchiveTemplate" | envsubst)"
137+
readonly stripComponents="$(echo -n "$binaryPathInArchive" | awk -F'/' '{print NF-1}')"
138+
readonly binaryTargetPath="$(echo -n "$binaryTargetPathTemplate" | envsubst)"
139+
curl_download_untarj "$downloadUrl" "$stripComponents" "$binaryTargetFolder" "$binaryPathInArchive"
140+
chmod 755 "$binaryTargetPath"
141+
apt_get_cleanup
142+
}
143+
echo_banner "devcontainer.community"
144+
echo "Installing $name..."
145+
install "$@"
146+
echo "(*) Done!"

test/btop/btop-debian.sh

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/bin/bash
2+
3+
4+
set -e
5+
6+
# Optional: Import test library bundled with the devcontainer CLI
7+
# See https://github.com/devcontainers/cli/blob/HEAD/docs/features/test.md#dev-container-features-test-lib
8+
# Provides the 'check' and 'reportResults' commands.
9+
source dev-container-features-test-lib
10+
11+
# Feature-specific tests
12+
# The 'check' command comes from the dev-container-features-test-lib. Syntax is...
13+
# check <LABEL> <cmd> [args...]
14+
check "execute command" bash -c "btop --version | grep 'btop'"
15+
16+
# Report results
17+
# If any of the checks above exited with a non-zero exit code, the test will fail.
18+
reportResults

test/btop/scenarios.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"btop-debian": {
3+
"image": "mcr.microsoft.com/devcontainers/base:debian",
4+
"features": {
5+
"btop": {
6+
}
7+
}
8+
}
9+
}

test/btop/test.sh

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/bin/bash
2+
3+
4+
set -e
5+
6+
# Optional: Import test library bundled with the devcontainer CLI
7+
# See https://github.com/devcontainers/cli/blob/HEAD/docs/features/test.md#dev-container-features-test-lib
8+
# Provides the 'check' and 'reportResults' commands.
9+
source dev-container-features-test-lib
10+
11+
# Feature-specific tests
12+
# The 'check' command comes from the dev-container-features-test-lib. Syntax is...
13+
# check <LABEL> <cmd> [args...]
14+
check "execute command" bash -c "btop --version | grep 'btop'"
15+
16+
# Report results
17+
# If any of the checks above exited with a non-zero exit code, the test will fail.
18+
reportResults

0 commit comments

Comments
 (0)