Skip to content

Commit c62d99e

Browse files
Copilotsebst
andauthored
Add getdnote.com feature (#177)
* Initial plan * feat: add getdnote.com feature Co-authored-by: sebst <592313+sebst@users.noreply.github.com> Agent-Logs-Url: https://github.com/devcontainer-community/devcontainer-features/sessions/bbb6036a-b86d-47c4-8e04-a0646ca34881 * fix: use ./dnote path to match archive member with leading ./ prefix Co-authored-by: sebst <592313+sebst@users.noreply.github.com> Agent-Logs-Url: https://github.com/devcontainer-community/devcontainer-features/sessions/3b754356-62d8-46bd-b065-0e04dad31499 --------- 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 d134f49 commit c62d99e

5 files changed

Lines changed: 213 additions & 0 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
| [fd](https://github.com/devcontainer-community/devcontainer-features/tree/main/src/fd) | `fd` — fast and user-friendly file finder | gh release | 1.0.0 |
3333
| [feature-installer](https://github.com/devcontainer-community/devcontainer-features/tree/main/src/feature-installer) | `feature-installer` — install devcontainer features at runtime | curl | 0.0.4 |
3434
| [fzf](https://github.com/devcontainer-community/devcontainer-features/tree/main/src/fzf) | `fzf` — general-purpose command-line fuzzy finder | gh release | 1.0.0 |
35+
| [getdnote.com](https://github.com/devcontainer-community/devcontainer-features/tree/main/src/getdnote.com) | `dnote` — simple command-line notebook for developers | gh release | 1.0.0 |
3536
| [github.com-cli](https://github.com/devcontainer-community/devcontainer-features/tree/main/src/github.com-cli) | `gh` — GitHub CLI | curl | 1.0.0 |
3637
| [helix-editor.com](https://github.com/devcontainer-community/devcontainer-features/tree/main/src/helix-editor.com) | `hx` — modal text editor with built-in LSP | gh release | 1.0.0 |
3738
| [hermes-agent.nousresearch.com](https://github.com/devcontainer-community/devcontainer-features/tree/main/src/hermes-agent.nousresearch.com) | `hermes` — self-improving AI agent by Nous Research | curl | 1.0.0 |

‎src/getdnote.com/NOTES.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# getdnote.com
2+
3+
## Project
4+
5+
- [dnote](https://github.com/dnote/dnote)
6+
7+
## Description
8+
9+
A simple, command-line notebook for developers. `dnote` lets you create and organise notes, search through them instantly, and sync across machines — all from the terminal.
10+
11+
## Installation Method
12+
13+
Downloaded as a pre-compiled binary from the [GitHub releases page](https://github.com/dnote/dnote/releases) and placed in `/usr/local/bin`.
14+
15+
## Other Notes
16+
17+
_No additional notes._
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "getdnote.com",
3+
"id": "getdnote.com",
4+
"version": "1.0.0",
5+
"description": "Install \"dnote\" binary",
6+
"documentationURL": "https://github.com/devcontainer-community/devcontainer-features/tree/main/src/getdnote.com",
7+
"options": {
8+
"version": {
9+
"type": "string",
10+
"default": "latest",
11+
"proposals": [
12+
"latest"
13+
],
14+
"description": "Version of \"dnote\" to install."
15+
}
16+
}
17+
}

‎src/getdnote.com/install.sh‎

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

‎test/getdnote.com/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 "dnote 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)