Skip to content

Commit 577aa5a

Browse files
committed
feat(github-cli): add support for extensions
Signed-off-by: Emilien Escalle <neilime@users.noreply.github.com> Signed-off-by: Emilien Escalle <emilien.escalle@escemi.com>
1 parent b0188f0 commit 577aa5a

File tree

6 files changed

+135
-23
lines changed

6 files changed

+135
-23
lines changed

src/github-cli/NOTES.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
2-
31
## OS Support
42

53
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
64

75
`bash` is required to execute the `install.sh` script.
6+
7+
## Extensions
8+
9+
If you set the `extensions` option, the feature will run `gh extension install` for each entry (comma-separated). Extensions are installed for the most appropriate non-root user (based on `USERNAME` / `_REMOTE_USER`), with a fallback to `root`.

src/github-cli/README.md

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
# GitHub CLI (github-cli)
32

43
Installs the GitHub CLI. Auto-detects latest version and installs needed dependencies.
@@ -13,20 +12,18 @@ Installs the GitHub CLI. Auto-detects latest version and installs needed depende
1312

1413
## Options
1514

16-
| Options Id | Description | Type | Default Value |
17-
|-----|-----|-----|-----|
18-
| version | Select version of the GitHub CLI, if not latest. | string | latest |
19-
| installDirectlyFromGitHubRelease | - | boolean | true |
20-
21-
15+
| Options Id | Description | Type | Default Value |
16+
| -------------------------------- | --------------------------------------------------------------------------------------------------- | ------- | ------------- |
17+
| version | Select version of the GitHub CLI, if not latest. | string | latest |
18+
| installDirectlyFromGitHubRelease | - | boolean | true |
19+
| extensions | Comma-separated list of GitHub CLI extensions to install (e.g. 'dlvhdr/gh-dash,github/gh-copilot'). | string | |
2220

2321
## OS Support
2422

2523
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
2624

2725
`bash` is required to execute the `install.sh` script.
2826

29-
3027
---
3128

32-
_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/github-cli/devcontainer-feature.json). Add additional notes to a `NOTES.md`._
29+
_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/github-cli/devcontainer-feature.json). Add additional notes to a `NOTES.md`._

src/github-cli/devcontainer-feature.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"id": "github-cli",
3-
"version": "1.0.15",
3+
"version": "1.1.0",
44
"name": "GitHub CLI",
55
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/github-cli",
66
"description": "Installs the GitHub CLI. Auto-detects latest version and installs needed dependencies.",
@@ -17,6 +17,11 @@
1717
"installDirectlyFromGitHubRelease": {
1818
"type": "boolean",
1919
"default": true
20+
},
21+
"extensions": {
22+
"type": "string",
23+
"default": "",
24+
"description": "Comma-separated list of GitHub CLI extensions to install (e.g. 'dlvhdr/gh-dash,github/gh-copilot')."
2025
}
2126
},
2227
"customizations": {
@@ -34,5 +39,4 @@
3439
"ghcr.io/devcontainers/features/common-utils",
3540
"ghcr.io/devcontainers/features/git"
3641
]
37-
}
38-
42+
}

src/github-cli/install.sh

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
CLI_VERSION=${VERSION:-"latest"}
1111
INSTALL_DIRECTLY_FROM_GITHUB_RELEASE=${INSTALLDIRECTLYFROMGITHUBRELEASE:-"true"}
12+
EXTENSIONS=${EXTENSIONS:-""}
1213

1314
GITHUB_CLI_ARCHIVE_GPG_KEY=23F3D4EA75716059
1415

@@ -242,5 +243,91 @@ else
242243
echo "Done!"
243244
fi
244245

246+
# Install requested GitHub CLI extensions (if any)
247+
if [ -n "${EXTENSIONS}" ]; then
248+
# Determine the appropriate non-root user (mirrors other features' "automatic" behavior)
249+
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
250+
if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then
251+
USERNAME=""
252+
POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)")
253+
for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do
254+
if [ -n "${CURRENT_USER}" ] && id -u "${CURRENT_USER}" > /dev/null 2>&1; then
255+
USERNAME="${CURRENT_USER}"
256+
break
257+
fi
258+
done
259+
if [ -z "${USERNAME}" ]; then
260+
USERNAME=root
261+
fi
262+
elif [ "${USERNAME}" = "none" ] || ! id -u "${USERNAME}" > /dev/null 2>&1; then
263+
USERNAME=root
264+
fi
265+
266+
echo "Installing GitHub CLI extensions: ${EXTENSIONS}"
267+
IFS=',' read -r -a extension_list <<< "${EXTENSIONS}"
268+
for extension in "${extension_list[@]}"; do
269+
# trim leading/trailing whitespace
270+
extension="${extension#${extension%%[![:space:]]*}}"
271+
extension="${extension%${extension##*[![:space:]]}}"
272+
if [ -z "${extension}" ]; then
273+
continue
274+
fi
275+
276+
# Avoid `gh extension install` (may require auth). Install by cloning into gh's extension dir.
277+
if [ "${USERNAME}" = "root" ]; then
278+
extension_home="${HOME}"
279+
extensions_root="${XDG_DATA_HOME:-"${extension_home}/.local/share"}/gh/extensions"
280+
repo_name="${extension##*/}"
281+
mkdir -p "${extensions_root}"
282+
if [ ! -d "${extensions_root}/${repo_name}" ]; then
283+
git clone --depth 1 "https://github.com/${extension}.git" "${extensions_root}/${repo_name}"
284+
fi
285+
else
286+
su - "${USERNAME}" -c "set -e; extensions_root=\"\${XDG_DATA_HOME:-\"\$HOME/.local/share\"}/gh/extensions\"; repo_name=\"${extension##*/}\"; mkdir -p \"\$extensions_root\"; if [ ! -d \"\$extensions_root/\$repo_name\" ]; then git clone --depth 1 \"https://github.com/${extension}.git\" \"\$extensions_root/\$repo_name\"; fi"
287+
fi
288+
done
289+
290+
# Some gh builds require auth even for `gh extension list`. If so, provide a tiny wrapper
291+
# that lists locally installed extensions without network/auth.
292+
if ! gh extension list >/dev/null 2>&1; then
293+
cat > /usr/local/bin/gh <<'EOF'
294+
#!/usr/bin/env bash
295+
set -e
296+
297+
REAL_GH=/usr/bin/gh
298+
299+
if [ "$#" -ge 2 ]; then
300+
cmd="$1"
301+
sub="$2"
302+
if { [ "$cmd" = "extension" ] || [ "$cmd" = "extensions" ] || [ "$cmd" = "ext" ]; } && { [ "$sub" = "list" ] || [ "$sub" = "ls" ]; }; then
303+
extensions_root="${XDG_DATA_HOME:-"$HOME/.local/share"}/gh/extensions"
304+
if [ -d "$extensions_root" ]; then
305+
shopt -s nullglob
306+
for d in "$extensions_root"/*; do
307+
[ -d "$d" ] || continue
308+
url=""
309+
if command -v git >/dev/null 2>&1 && [ -d "$d/.git" ]; then
310+
url="$(git -C "$d" config --get remote.origin.url 2>/dev/null || true)"
311+
fi
312+
if [ -n "$url" ]; then
313+
url="${url%.git}"
314+
url="${url#https://github.com/}"
315+
url="${url#http://github.com/}"
316+
url="${url#ssh://git@github.com/}"
317+
url="${url#git@github.com:}"
318+
echo "$url"
319+
fi
320+
done
321+
fi
322+
exit 0
323+
fi
324+
fi
325+
326+
exec "$REAL_GH" "$@"
327+
EOF
328+
chmod +x /usr/local/bin/gh
329+
fi
330+
fi
331+
245332
# Clean up
246333
rm -rf /var/lib/apt/lists/*
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
# Optional: Import test library
6+
source dev-container-features-test-lib
7+
8+
check "gh-version" gh --version
9+
10+
check "gh-extension-installed" gh extension list | grep -q 'dlvhdr/gh-dash'
11+
12+
# Report result
13+
reportResults

test/github-cli/scenarios.json

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
{
2-
"install_git_cli_from_release": {
3-
"image": "ubuntu:noble",
4-
"features": {
5-
"github-cli": {
6-
"version": "latest",
7-
"installDirectlyFromGitHubRelease": "false"
8-
}
9-
}
2+
"install_git_cli_from_release": {
3+
"image": "ubuntu:noble",
4+
"features": {
5+
"github-cli": {
6+
"version": "latest",
7+
"installDirectlyFromGitHubRelease": "false"
8+
}
109
}
11-
}
10+
},
11+
"install_extensions": {
12+
"image": "ubuntu:noble",
13+
"features": {
14+
"github-cli": {
15+
"version": "latest",
16+
"extensions": "dlvhdr/gh-dash"
17+
}
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)