Skip to content

Commit e5f5fc5

Browse files
Copilotsebst
andauthored
Add yakitrak/notesmd-cli feature (#141)
* Initial plan * Add yakitrak/notesmd-cli devcontainer feature Co-authored-by: sebst <592313+sebst@users.noreply.github.com> Agent-Logs-Url: https://github.com/devcontainer-community/devcontainer-features/sessions/cbdf8e9f-6629-4431-8754-a4a634c1476e --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sebst <592313+sebst@users.noreply.github.com>
1 parent ae264a9 commit e5f5fc5

3 files changed

Lines changed: 186 additions & 0 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "yakitrak/notesmd-cli",
3+
"id": "yakitrak-notesmd-cli",
4+
"version": "1.0.0",
5+
"description": "Install \"notesmd-cli\" binary - Interact with Obsidian vaults from the terminal",
6+
"documentationURL": "https://github.com/devcontainer-community/devcontainer-features/tree/main/src/yakitrak-notesmd-cli",
7+
"options": {
8+
"version": {
9+
"type": "string",
10+
"default": "latest",
11+
"proposals": [
12+
"latest"
13+
],
14+
"description": "Version of \"notesmd-cli\" to install."
15+
}
16+
}
17+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
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='Yakitrak/notesmd-cli'
8+
readonly binaryName='notesmd-cli'
9+
readonly versionArgument='--version'
10+
readonly downloadUrlTemplate='https://github.com/${githubRepository}/releases/download/${releaseTag}/notesmd-cli_${version}_linux_${architecture}.tar.gz'
11+
readonly binaryTargetFolder='/usr/local/bin'
12+
readonly name="${githubRepository##*/}"
13+
apt_get_update() {
14+
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
15+
echo "Running apt-get update..."
16+
apt-get update -y
17+
fi
18+
}
19+
apt_get_checkinstall() {
20+
if ! dpkg -s "$@" >/dev/null 2>&1; then
21+
apt_get_update
22+
DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends --no-install-suggests --option 'Debug::pkgProblemResolver=true' --option 'Debug::pkgAcquire::Worker=1' "$@"
23+
fi
24+
}
25+
apt_get_cleanup() {
26+
apt-get clean
27+
rm -rf /var/lib/apt/lists/*
28+
}
29+
check_curl_envsubst_file_tar_installed() {
30+
declare -a requiredAptPackagesMissing=()
31+
if ! [ -r '/etc/ssl/certs/ca-certificates.crt' ]; then
32+
requiredAptPackagesMissing+=('ca-certificates')
33+
fi
34+
if ! command -v curl >/dev/null 2>&1; then
35+
requiredAptPackagesMissing+=('curl')
36+
fi
37+
if ! command -v envsubst >/dev/null 2>&1; then
38+
requiredAptPackagesMissing+=('gettext-base')
39+
fi
40+
if ! command -v file >/dev/null 2>&1; then
41+
requiredAptPackagesMissing+=('file')
42+
fi
43+
if ! command -v tar >/dev/null 2>&1; then
44+
requiredAptPackagesMissing+=('tar')
45+
fi
46+
declare -i requiredAptPackagesMissingCount=${#requiredAptPackagesMissing[@]}
47+
if [ $requiredAptPackagesMissingCount -gt 0 ]; then
48+
apt_get_update
49+
apt_get_checkinstall "${requiredAptPackagesMissing[@]}"
50+
apt_get_cleanup
51+
fi
52+
}
53+
curl_check_url() {
54+
local url=$1
55+
local status_code
56+
status_code=$(curl -s -o /dev/null -w '%{http_code}' "$url")
57+
if [ "$status_code" -ne 200 ] && [ "$status_code" -ne 302 ]; then
58+
echo "Failed to download '$url'. Status code: $status_code."
59+
return 1
60+
fi
61+
}
62+
curl_download_stdout() {
63+
local url=$1
64+
curl \
65+
--silent \
66+
--location \
67+
--output '-' \
68+
--connect-timeout 5 \
69+
"$url"
70+
}
71+
curl_download_untar() {
72+
local url=$1
73+
local strip=$2
74+
local target=$3
75+
local bin_path=$4
76+
curl_download_stdout "$url" | tar \
77+
-xz \
78+
-f '-' \
79+
--strip-components="$strip" \
80+
-C "$target" \
81+
"$bin_path"
82+
}
83+
debian_get_arch() {
84+
echo "$(dpkg --print-architecture)"
85+
}
86+
echo_banner() {
87+
local text="$1"
88+
echo -e "\e[1m\e[97m\e[41m$text\e[0m"
89+
}
90+
github_list_releases() {
91+
if [ -z "$1" ]; then
92+
echo "Usage: list_github_releases <owner/repo>"
93+
return 1
94+
fi
95+
local repo="$1"
96+
local url="https://api.github.com/repos/$repo/releases"
97+
curl -s "$url" | grep -Po '"tag_name": "\K.*?(?=")' | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' | sed 's/^v//'
98+
}
99+
github_get_latest_release() {
100+
if [ -z "$1" ]; then
101+
echo "Usage: get_latest_github_release <owner/repo>"
102+
return 1
103+
fi
104+
github_list_releases "$1" | head -n 1
105+
}
106+
github_get_tag_for_version() {
107+
if [ -z "$1" ] || [ -z "$2" ]; then
108+
echo "Usage: github_get_tag_for_version <owner/repo> <version>"
109+
return 1
110+
fi
111+
local repo="$1"
112+
local _version="$2"
113+
local url="https://api.github.com/repos/$repo/releases"
114+
local escaped_version
115+
escaped_version="$(printf '%s' "$_version" | sed 's/\./\\./g')"
116+
curl -s "$url" | grep -Po '"tag_name": "\K.*?(?=")' | grep -E "^v?${escaped_version}$" | head -n 1
117+
}
118+
utils_check_version() {
119+
local version=$1
120+
if ! [[ "${version:-}" =~ ^(latest|[0-9]+\.[0-9]+\.[0-9]+)$ ]]; then
121+
printf >&2 '=== [ERROR] Option "version" (value: "%s") is not "latest" or valid semantic version format "X.Y.Z" !\n' \
122+
"$version"
123+
exit 1
124+
fi
125+
}
126+
install() {
127+
utils_check_version "$VERSION"
128+
check_curl_envsubst_file_tar_installed
129+
readonly architecture="$(debian_get_arch)"
130+
readonly binaryTargetPathTemplate='${binaryTargetFolder}/${binaryName}'
131+
if [ "$VERSION" == 'latest' ] || [ -z "$VERSION" ]; then
132+
VERSION=$(github_get_latest_release "$githubRepository")
133+
fi
134+
readonly version="${VERSION:?}"
135+
readonly releaseTag="$(github_get_tag_for_version "$githubRepository" "$version")"
136+
if [ -z "$releaseTag" ]; then
137+
printf >&2 '=== [ERROR] Could not find release tag for version "%s" in "%s"!\n' "$version" "$githubRepository"
138+
exit 1
139+
fi
140+
readonly downloadUrl="$(echo -n "$downloadUrlTemplate" | envsubst)"
141+
curl_check_url "$downloadUrl"
142+
readonly binaryPathInArchive="${binaryName}"
143+
readonly stripComponents=0
144+
readonly binaryTargetPath="$(echo -n "$binaryTargetPathTemplate" | envsubst)"
145+
curl_download_untar "$downloadUrl" "$stripComponents" "$binaryTargetFolder" "$binaryPathInArchive"
146+
chmod 755 "$binaryTargetPath"
147+
}
148+
echo_banner "devcontainer.community"
149+
echo "Installing $name..."
150+
install "$@"
151+
echo "(*) Done!"

‎test/yakitrak-notesmd-cli/test.sh‎

Lines changed: 18 additions & 0 deletions
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 "notesmd-cli --version | grep 'notesmd-cli version'"
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)