Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ on:

jobs:
test-autogenerated:
runs-on: ubuntu-latest
runs-on: ${{ matrix.runner }}
continue-on-error: true
strategy:
matrix:
include:
- runner: ubuntu-24.04
arch: amd64
- runner: ubuntu-24.04-arm
arch: arm64
features:
- databricks-cli
baseImage:
Expand All @@ -24,14 +29,19 @@ jobs:
- name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli

- name: "Generating tests for '${{ matrix.features }}' against '${{ matrix.baseImage }}'"
- name: "Generating tests for '${{ matrix.features }}' against '${{ matrix.baseImage }}' on '${{ matrix.arch }}'"
run: devcontainer features test --skip-scenarios -f ${{ matrix.features }} -i ${{ matrix.baseImage }} .

test-scenarios:
runs-on: ubuntu-latest
runs-on: ${{ matrix.runner }}
continue-on-error: true
strategy:
matrix:
include:
- runner: ubuntu-24.04
arch: amd64
- runner: ubuntu-24.04-arm
arch: arm64
features:
- databricks-cli
steps:
Expand All @@ -40,17 +50,24 @@ jobs:
- name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli

- name: "Generating tests for '${{ matrix.features }}' scenarios"
- name: "Generating tests for '${{ matrix.features }}' scenarios on '${{ matrix.arch }}'"
run: devcontainer features test -f ${{ matrix.features }} --skip-autogenerated --skip-duplicated .

test-global:
runs-on: ubuntu-latest
runs-on: ${{ matrix.runner }}
continue-on-error: true
strategy:
matrix:
include:
- runner: ubuntu-24.04
arch: amd64
- runner: ubuntu-24.04-arm
arch: arm64
steps:
- uses: actions/checkout@v4

- name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli

- name: "Testing global scenarios"
- name: "Testing global scenarios on '${{ matrix.arch }}'"
run: devcontainer features test --global-scenarios-only .
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@

### `databricks-cli`

Adding `databricks-cli` to the devcontainer will install the databricks-cli, defaulting to main, with the given version
Adding `databricks-cli` to the devcontainer installs the Databricks CLI. The feature requires `common-utils`, defaults to `main`, and resolves that to the latest GitHub release before downloading the matching Linux binary for the container architecture and verifying its checksum.

```jsonc
{
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"ghcr.io/devcontainers/features/common-utils:2": {},
"ghcr.io/mike-fi/devcontainer-features/databricks-cli:1": {
"version": "v0.241.0"
"version": "v0.294.0"
}
}
}
```
> __NOTE__: This feature currently requires curl to be installed, which is part of the common-utils feature. I'm working on a solution to make it installable cross-platform.
> __NOTE__: This feature depends on `ghcr.io/devcontainers/features/common-utils:2` and installs the Linux `amd64` and `arm64` release archives after verifying the published SHA-256 checksum.
6 changes: 3 additions & 3 deletions src/databricks-cli/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@

# Databricks CLI devcontainer feature

Installing the databricks-cli into your devcontainer as a feature, deminishing the need to write Dockerfiles or bash scripts.
Installing the Databricks CLI into your devcontainer as a feature, diminishing the need to write Dockerfiles or bash scripts. This feature depends on `ghcr.io/devcontainers/features/common-utils:2`.

## Example Usage

```json
"features": {
"ghcr.io/mike-fi/devcontainer-features/databricks-cli:1": {
"version": "v0.241.0"
"version": "v0.294.0"
}
}
```
Expand All @@ -17,4 +17,4 @@ Installing the databricks-cli into your devcontainer as a feature, deminishing t

| Options Id | Description | Type | Default Value |
|-----|-----|-----|-----|
| version | Select cli version | string | main |
| version | Select CLI version. `main` resolves to the latest GitHub release. | string | main |
14 changes: 7 additions & 7 deletions src/databricks-cli/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
{
"name": "Databricks CLI",
"id": "databricks-cli",
"version": "1.0.2",
"description": "Installing the databricks-cli into the container",
"version": "1.1.0",
"description": "Installing a specific version of databricks-cli into the container",
"options": {
"version": {
"type": "string",
"proposals": [
"v0.241.0"
"v0.294.0"
],
"default": "main",
"description": "CLI version to install"
"description": "Databricks CLI version to install. Use \"main\" to resolve the latest GitHub release."
}
},
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
]
"dependsOn": {
"ghcr.io/devcontainers/features/common-utils:2": {}
}
}
123 changes: 98 additions & 25 deletions src/databricks-cli/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,116 @@
# This script is licensed under the MIT License
# The software installed by this script is subject to its own license terms. For more information see NOTICE.md and the documentation provided by the software vendor.

set -e
set -eu

echo "Activating feature 'databricks-cli'"

VERSION=${VERSION:-"main"}
VERSION="${VERSION:-main}"
TARGET_BIN="/usr/local/bin/databricks"


# arch = "$(uname -m)"
# case $arch in
# x86_64) arch="amd64";;
# aarch64 | arm8*) arch="arm64";;
# aarch32 | armv7* | armvhf*) arch="arm61";;
# i?86) arch="386";;
# *) echo "('!) Architecture $arch unsupported"; exit 1;;
# esac

# The 'install.sh' entrypoint script is always executed as the root user.
#
# These following environment variables are passed in by the dev container CLI.
# These may be useful in instances where the context of the final
# remoteUser or containerUser is useful.
# For more details, see https://containers.dev/implementors/features#user-env-var
echo "The effective dev container remoteUser is '$_REMOTE_USER'"
echo "The effective dev container remoteUser's home directory is '$_REMOTE_USER_HOME'"

echo "The effective dev container containerUser is '$_CONTAINER_USER'"
echo "The effective dev container containerUser's home directory is '$_CONTAINER_USER_HOME'"

echo "Installing Databricks CLI version: $VERSION"
require_command() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "Error: required command '$1' is not installed."
exit 1
fi
}

checksum_verify() {
checksum_file="$1"
archive_name="$2"
archive_checksum_file="$TMP_DIR/${archive_name}.sha256"

if ! grep "[ *]${archive_name}\$" "$TMP_DIR/$checksum_file" >"$archive_checksum_file"; then
echo "Error: checksum entry for '$archive_name' was not found in '$checksum_file'."
exit 1
fi

if command -v sha256sum >/dev/null 2>&1; then
(cd "$TMP_DIR" && sha256sum -c "$(basename "$archive_checksum_file")" >/dev/null)
return
fi

if command -v shasum >/dev/null 2>&1; then
expected_hash="$(awk '{print $1}' "$archive_checksum_file")"
actual_hash="$(shasum -a 256 "$TMP_DIR/$archive_name" | awk '{print $1}')"
[ "$expected_hash" = "$actual_hash" ]
return
fi

echo "Error: neither 'sha256sum' nor 'shasum' is available for checksum verification."
exit 1
}

resolve_version() {
requested_version="$1"

if [ "$requested_version" = "main" ] || [ "$requested_version" = "latest" ]; then
latest_release_url="$(curl -fsSL -o /dev/null -w '%{url_effective}' https://github.com/databricks/cli/releases/latest)"
resolved_version="${latest_release_url##*/}"
else
resolved_version="$requested_version"
fi

case "$resolved_version" in
v*) printf '%s\n' "$resolved_version" ;;
*) printf 'v%s\n' "$resolved_version" ;;
esac
}

detect_arch() {
raw_arch="$(uname -m)"
case "$raw_arch" in
x86_64|amd64) printf 'amd64\n' ;;
aarch64|arm64) printf 'arm64\n' ;;
*)
echo "Error: unsupported architecture '$raw_arch'. Supported architectures: amd64, arm64."
exit 1
;;
esac
}

require_command curl
require_command tar
require_command install

resolved_version="$(resolve_version "$VERSION")"
version_number="${resolved_version#v}"
arch="$(detect_arch)"

archive_name="databricks_cli_${version_number}_linux_${arch}.tar.gz"
checksum_name="databricks_cli_${version_number}_SHA256SUMS_unix"
release_base_url="https://github.com/databricks/cli/releases/download/${resolved_version}"

echo "Installing Databricks CLI version: ${resolved_version} for linux/${arch}"

TMP_DIR="$(mktemp -d)"
STAGED_BIN=""
cleanup() {
if [ -n "$STAGED_BIN" ] && [ -e "$STAGED_BIN" ]; then
rm -f "$STAGED_BIN"
fi
rm -rf "$TMP_DIR"
}
trap cleanup EXIT INT HUP TERM

curl -fsSLo "$TMP_DIR/$archive_name" "${release_base_url}/${archive_name}"
curl -fsSLo "$TMP_DIR/$checksum_name" "${release_base_url}/${checksum_name}"

# assert curl is available
if ! command -v curl >/dev/null 2>&1; then
echo "Error: curl is not installed. Please install curl and try again."
if ! checksum_verify "$checksum_name" "$archive_name"; then
echo "Error: checksum verification failed for '$archive_name'."
exit 1
fi

curl -fsSL https://raw.githubusercontent.com/databricks/setup-cli/$VERSION/install.sh | sh
tar -xzf "$TMP_DIR/$archive_name" -C "$TMP_DIR"
STAGED_BIN="$(mktemp "${TARGET_BIN}.tmp.XXXXXX")"
install -m 0755 "$TMP_DIR/databricks" "$STAGED_BIN"
mv -f "$STAGED_BIN" "$TARGET_BIN"
STAGED_BIN=""

echo "Databricks CLI installed successfully"
chmod +x /usr/local/bin/databricks
echo "Databricks CLI installed successfully: $("$TARGET_BIN" -v)"
2 changes: 1 addition & 1 deletion test/databricks-cli/scenarios.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"test_version": {
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"databricks": {
"databricks-cli": {
}
}
}
Expand Down
38 changes: 38 additions & 0 deletions test/databricks-cli/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/sh

set -eu

check() {
description="$1"
shift

if "$@"; then
echo "PASS: ${description}"
else
echo "FAIL: ${description}"
exit 1
fi
}

check_output_contains() {
description="$1"
expected="$2"
shift 2

output="$("$@")"
case "$output" in
*"$expected"*)
echo "PASS: ${description}"
;;
*)
echo "FAIL: ${description}"
echo "Output was: ${output}"
exit 1
;;
esac
}

check "curl is available via common-utils" command -v curl
check "databricks is installed" command -v databricks
check "databricks binary is executable" test -x /usr/local/bin/databricks
check_output_contains "databricks reports a version" "Databricks CLI" databricks -v
Loading