Skip to content

Commit 87d6f22

Browse files
Copilotsebst
andauthored
Add tree-sitter devcontainer feature (#225)
* Initial plan * Add tree-sitter devcontainer feature Agent-Logs-Url: https://github.com/devcontainer-community/devcontainer-features/sessions/4006bd65-790f-437b-8f8b-e264412ec356 Co-authored-by: sebst <592313+sebst@users.noreply.github.com> --------- 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 7cc99e0 commit 87d6f22

4 files changed

Lines changed: 184 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
| [sxyazi/yazi](https://github.com/devcontainer-community/devcontainer-features/tree/main/src/sxyazi-yazi) | `yazi` — blazing fast terminal file manager | gh release | 1.0.0 |
7474
| [tailscale.com](https://github.com/devcontainer-community/devcontainer-features/tree/main/src/tailscale.com) | `tailscale` — zero-config mesh VPN | curl | 1.0.0 |
7575
| [taskwarrior.org](https://github.com/devcontainer-community/devcontainer-features/tree/main/src/taskwarrior.org) | `task` — command-line task manager | apt | 1.0.0 |
76+
| [tree-sitter](https://github.com/devcontainer-community/devcontainer-features/tree/main/src/tree-sitter) | `tree-sitter` — incremental parsing toolkit for building syntax trees | gh release | 1.0.0 |
7677
| [turso.tech](https://github.com/devcontainer-community/devcontainer-features/tree/main/src/turso.tech) | `tursodb` — in-process SQL database compatible with SQLite | gh release | 1.0.0 |
7778
| [webinstall.dev](https://github.com/devcontainer-community/devcontainer-features/tree/main/src/webinstall.dev) | `webi` — install packages without sudo | curl | 1.0.1 |
7879
| [yakitrak/notesmd-cli](https://github.com/devcontainer-community/devcontainer-features/tree/main/src/yakitrak-notesmd-cli) | `notesmd-cli` — manage Obsidian vaults from the terminal | gh release | 1.0.0 |
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "tree-sitter",
3+
"id": "tree-sitter",
4+
"version": "1.0.0",
5+
"description": "Install \"tree-sitter\" binary",
6+
"documentationURL": "https://github.com/devcontainer-community/devcontainer-features/tree/main/src/tree-sitter",
7+
"options": {
8+
"version": {
9+
"type": "string",
10+
"default": "latest",
11+
"proposals": ["latest"],
12+
"description": "Version of \"tree-sitter\" to install."
13+
}
14+
}
15+
}

src/tree-sitter/install.sh

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='tree-sitter/tree-sitter'
8+
readonly binaryName='tree-sitter'
9+
readonly versionArgument='--version'
10+
readonly downloadUrlTemplate='https://github.com/${githubRepository}/releases/download/${releaseTag}/tree-sitter-cli-linux-${architecture}.zip'
11+
readonly binaryPathInArchiveTemplate='${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_unzip_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 unzip >/dev/null 2>&1; then
42+
requiredAptPackagesMissing+=('unzip')
43+
fi
44+
declare -i requiredAptPackagesMissingCount=${#requiredAptPackagesMissing[@]}
45+
if [ $requiredAptPackagesMissingCount -gt 0 ]; then
46+
apt_get_update
47+
apt_get_checkinstall "${requiredAptPackagesMissing[@]}"
48+
apt_get_cleanup
49+
fi
50+
}
51+
curl_check_url() {
52+
local url=$1
53+
local status_code
54+
status_code=$(curl -s -o /dev/null -w '%{http_code}' "$url")
55+
if [ "$status_code" -ne 200 ] && [ "$status_code" -ne 302 ]; then
56+
echo "Failed to download '$url'. Status code: $status_code."
57+
return 1
58+
fi
59+
}
60+
curl_download_unzip() {
61+
local url=$1
62+
local target=$2
63+
local bin_path=$3
64+
local tmpdir
65+
tmpdir=$(mktemp -d)
66+
curl \
67+
--silent \
68+
--location \
69+
--connect-timeout 5 \
70+
--output "$tmpdir/download.zip" \
71+
"$url"
72+
unzip -p "$tmpdir/download.zip" "$bin_path" > "$target/$binaryName"
73+
rm -rf "$tmpdir"
74+
}
75+
debian_get_arch() {
76+
echo "$(dpkg --print-architecture)"
77+
}
78+
debian_get_target_arch() {
79+
case $(debian_get_arch) in
80+
amd64) echo 'x64' ;;
81+
arm64) echo 'arm64' ;;
82+
armhf) echo 'arm' ;;
83+
i386) echo 'x86' ;;
84+
*) echo 'unknown' ;;
85+
esac
86+
}
87+
echo_banner() {
88+
local text="$1"
89+
echo -e "\e[1m\e[97m\e[41m$text\e[0m"
90+
}
91+
github_list_releases() {
92+
if [ -z "$1" ]; then
93+
echo "Usage: list_github_releases <owner/repo>"
94+
return 1
95+
fi
96+
local repo="$1"
97+
local url="https://api.github.com/repos/$repo/releases"
98+
curl -s "$url" | grep -Po '"tag_name": "\K.*?(?=")' | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' | sed 's/^v//'
99+
}
100+
github_get_latest_release() {
101+
if [ -z "$1" ]; then
102+
echo "Usage: get_latest_github_release <owner/repo>"
103+
return 1
104+
fi
105+
github_list_releases "$1" | head -n 1
106+
}
107+
github_get_tag_for_version() {
108+
if [ -z "$1" ] || [ -z "$2" ]; then
109+
echo "Usage: github_get_tag_for_version <owner/repo> <version>"
110+
return 1
111+
fi
112+
local repo="$1"
113+
local _version="$2"
114+
local url="https://api.github.com/repos/$repo/releases"
115+
local escaped_version
116+
escaped_version="$(printf '%s' "$_version" | sed 's/\./\\./g')"
117+
curl -s "$url" | grep -Po '"tag_name": "\K.*?(?=")' | grep -E "^v?${escaped_version}$" | head -n 1
118+
}
119+
utils_check_version() {
120+
local version=$1
121+
if ! [[ "${version:-}" =~ ^(latest|[0-9]+\.[0-9]+\.[0-9]+)$ ]]; then
122+
printf >&2 '=== [ERROR] Option "version" (value: "%s") is not "latest" or valid semantic version format "X.Y.Z" !\n' \
123+
"$version"
124+
exit 1
125+
fi
126+
}
127+
install() {
128+
utils_check_version "$VERSION"
129+
check_curl_envsubst_unzip_installed
130+
readonly architecture="$(debian_get_target_arch)"
131+
readonly binaryTargetPath="${binaryTargetFolder}/${binaryName}"
132+
if [ "$VERSION" == 'latest' ] || [ -z "$VERSION" ]; then
133+
VERSION=$(github_get_latest_release "$githubRepository")
134+
fi
135+
readonly version="${VERSION:?}"
136+
readonly releaseTag="$(github_get_tag_for_version "$githubRepository" "$version")"
137+
if [ -z "$releaseTag" ]; then
138+
printf >&2 '=== [ERROR] Could not find release tag for version "%s" in "%s"!\n' "$version" "$githubRepository"
139+
exit 1
140+
fi
141+
readonly downloadUrl="$(echo -n "$downloadUrlTemplate" | envsubst)"
142+
curl_check_url "$downloadUrl"
143+
readonly binaryPathInArchive="$(echo -n "$binaryPathInArchiveTemplate" | envsubst)"
144+
curl_download_unzip "$downloadUrl" "$binaryTargetFolder" "$binaryPathInArchive"
145+
chmod 755 "$binaryTargetPath"
146+
apt_get_cleanup
147+
}
148+
echo_banner "devcontainer.community"
149+
echo "Installing $name..."
150+
install "$@"
151+
echo "(*) Done!"

test/tree-sitter/test.sh

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

0 commit comments

Comments
 (0)