Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
matrix:
features:
- cato
- rtk
baseImage:
- debian:latest
- ubuntu:latest
Expand All @@ -34,6 +35,7 @@ jobs:
matrix:
features:
- cato
- rtk
steps:
- uses: actions/checkout@v6

Expand Down
86 changes: 86 additions & 0 deletions .github/workflows/update-rtk-sha.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: "Update RTK SHA256 on Renovate PRs"

on:
pull_request:
branches:
- main
paths:
- "src/rtk/devcontainer-feature.json"

jobs:
update-sha:
# Only run on Renovate branches — avoids triggering on unrelated PRs that
# happen to touch devcontainer-feature.json (e.g. manual option changes).
if: startsWith(github.head_ref, 'renovate/')
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Checkout PR branch
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
# Use a token with write access so the subsequent push is accepted.
token: ${{ secrets.GITHUB_TOKEN }}

- name: Read RTK version from feature metadata
id: version
run: |
VERSION=$(jq -r '.options.version.default' src/rtk/devcontainer-feature.json)
echo "rtk_version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "RTK version from feature metadata: ${VERSION}"

- name: Download RTK tarballs and compute SHA256
id: sha
run: |
VERSION="${{ steps.version.outputs.rtk_version }}"
TMP=$(mktemp -d)

AMD64_FILE="rtk-x86_64-unknown-linux-musl.tar.gz"
ARM64_FILE="rtk-aarch64-unknown-linux-gnu.tar.gz"
BASE_URL="https://github.com/rtk-ai/rtk/releases/download/${VERSION}"

echo "Downloading ${AMD64_FILE}..."
curl -fsSL "${BASE_URL}/${AMD64_FILE}" -o "${TMP}/${AMD64_FILE}"

echo "Downloading ${ARM64_FILE}..."
curl -fsSL "${BASE_URL}/${ARM64_FILE}" -o "${TMP}/${ARM64_FILE}"

SHA_AMD64=$(sha256sum "${TMP}/${AMD64_FILE}" | awk '{print $1}')
SHA_ARM64=$(sha256sum "${TMP}/${ARM64_FILE}" | awk '{print $1}')

echo "sha_amd64=${SHA_AMD64}" >> "$GITHUB_OUTPUT"
echo "sha_arm64=${SHA_ARM64}" >> "$GITHUB_OUTPUT"
echo "amd64 SHA256: ${SHA_AMD64}"
echo "arm64 SHA256: ${SHA_ARM64}"

rm -rf "${TMP}"

- name: Patch SHA256 defaults in devcontainer-feature.json
run: |
SHA_AMD64="${{ steps.sha.outputs.sha_amd64 }}"
SHA_ARM64="${{ steps.sha.outputs.sha_arm64 }}"

jq \
--arg sha_amd64 "${SHA_AMD64}" \
--arg sha_arm64 "${SHA_ARM64}" \
'.options.sha256_amd64.default = $sha_amd64 | .options.sha256_arm64.default = $sha_arm64' \
src/rtk/devcontainer-feature.json > src/rtk/devcontainer-feature.json.tmp

mv src/rtk/devcontainer-feature.json.tmp src/rtk/devcontainer-feature.json
echo "Patched devcontainer-feature.json"
cat src/rtk/devcontainer-feature.json

- name: Commit and push updated SHA256 values
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

if git diff --quiet; then
echo "No SHA changes — already up to date"
else
git add src/rtk/devcontainer-feature.json
git commit -m "chore(rtk): update SHA256 for ${{ steps.version.outputs.rtk_version }}"
git push
fi
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@ src/
cato/
devcontainer-feature.json
install.sh
rtk/
devcontainer-feature.json
install.sh
test/
cato/
test.sh
scenarios.json
rtk/
test.sh
scenarios.json
```

## Development
Expand Down
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,36 @@ Operational expectations:

Bundled certificate lifecycle details, validity period, and SHA-256 fingerprint are documented in [src/cato/NOTES.md](src/cato/NOTES.md).

## Feature: rtk

The `rtk` feature installs the [RTK CLI](https://github.com/rtk-ai/rtk) — a proxy that reduces LLM token consumption by 60–90% on common dev commands. Single Rust binary, zero dependencies.

Published feature reference:

```json
"features": {
"ghcr.io/solarwinds/devcontainers/features/rtk:1": {}
}
```

### Options

| Option | Default | Description |
|--------|---------|-------------|
| `version` | `v0.40.0` | RTK release version to install. |
| `sha256_amd64` | *(see feature JSON)* | Expected SHA256 of the amd64 tarball. Leave empty to skip validation (not recommended). |
| `sha256_arm64` | *(see feature JSON)* | Expected SHA256 of the arm64 tarball. Leave empty to skip validation (not recommended). |

### Keeping RTK Up to Date

This repository uses [Renovate](https://docs.renovatebot.com/) to track new RTK releases. When a new version of `rtk-ai/rtk` is published on GitHub:

1. Renovate opens a PR that bumps the `version` default in `src/rtk/devcontainer-feature.json`.
2. The `.github/workflows/update-rtk-sha.yml` workflow triggers automatically on that PR branch, downloads the new release tarballs, computes their SHA256 checksums, and pushes a follow-up commit onto the Renovate PR branch that updates the `sha256_amd64` and `sha256_arm64` defaults.
3. Once CI passes, the PR can be merged as usual. No manual SHA calculation is required.

**Release**: After merging, bump `version` in `src/rtk/devcontainer-feature.json` and merge to `main` to publish an updated feature package to GHCR.

## Contributing

Development, testing, and release instructions are maintained in [CONTRIBUTING.md](CONTRIBUTING.md).
Expand Down
18 changes: 18 additions & 0 deletions renovate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"customManagers": [
{
"customType": "regex",
"description": "Update RTK version in src/rtk/devcontainer-feature.json. Matches the 'proposals' + 'default' pair in the version option block, which is unique in this file.",
"fileMatch": ["^src/rtk/devcontainer-feature\\.json$"],
"matchStrings": [
"\"proposals\":\\s*\\[\"(?<currentValue>v[\\d.]+)\"\\],\\s*\"default\":\\s*\"v[\\d.]+\""
],
"currentValueTemplate": "{{{currentValue}}}",
"depNameTemplate": "rtk-ai/rtk",
"datasourceTemplate": "github-releases",
"versioningTemplate": "semver"
}
]
}
25 changes: 25 additions & 0 deletions src/rtk/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"id": "rtk",
"version": "1.0.0",
"name": "RTK",
"description": "CLI proxy that reduces LLM token consumption by 60-90% on common dev commands. Single Rust binary, zero dependencies.",
"documentationURL": "https://github.com/solarwinds/devcontainers/tree/main/src/rtk",
"options": {
"version": {
"type": "string",
"proposals": ["v0.40.0"],
"default": "v0.40.0",
"description": "RTK release version to install (e.g. v0.40.0). renovate: datasource=github-releases depName=rtk-ai/rtk"
},
"sha256_amd64": {
"type": "string",
"default": "a75d210a445874106bc16da2b4efba01d36d297afa33ec134728f2d5f42ef5af",
"description": "Expected SHA256 of the amd64 tarball for the selected version. Leave empty to skip validation (not recommended)."
},
"sha256_arm64": {
"type": "string",
"default": "1d0087ad62a182c0833c2251ac678b5e05356418d91aa57305ac51a126c9b102",
"description": "Expected SHA256 of the arm64 tarball for the selected version. Leave empty to skip validation (not recommended)."
}
}
}
97 changes: 97 additions & 0 deletions src/rtk/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/bin/sh
set -eu

log() { echo "[rtk] $*"; }
die() { echo "[rtk] ERROR: $*" >&2; exit 1; }

RTK_VERSION="${VERSION:-"v0.40.0"}"
RTK_SHA256_AMD64="${SHA256_AMD64:-}"
RTK_SHA256_ARM64="${SHA256_ARM64:-}"

log "Activating feature 'rtk'"
log "Version: $RTK_VERSION"

# ── 1. Detect architecture ────────────────────────────────────────────────────

if command -v dpkg >/dev/null 2>&1; then
ARCH="$(dpkg --print-architecture)"
else
case "$(uname -m)" in
x86_64) ARCH="amd64" ;;
aarch64) ARCH="arm64" ;;
*) ARCH="$(uname -m)" ;;
esac
fi

case "${ARCH}" in
amd64)
RTK_FILE="rtk-x86_64-unknown-linux-musl.tar.gz"
EXPECTED_SHA="${RTK_SHA256_AMD64}"
;;
arm64)
RTK_FILE="rtk-aarch64-unknown-linux-gnu.tar.gz"
EXPECTED_SHA="${RTK_SHA256_ARM64}"
;;
*)
die "Unsupported architecture: ${ARCH}"
;;
esac

log "Architecture: ${ARCH}"
log "Tarball: ${RTK_FILE}"

# ── 2. Download ───────────────────────────────────────────────────────────────

RTK_URL="https://github.com/rtk-ai/rtk/releases/download/${RTK_VERSION}/${RTK_FILE}"
TMP_DIR="$(mktemp -d)"

log "Downloading ${RTK_URL}"
curl -fsSL "${RTK_URL}" -o "${TMP_DIR}/${RTK_FILE}"

# ── 3. SHA256 validation ──────────────────────────────────────────────────────

if [ -n "${EXPECTED_SHA}" ]; then
log "Validating SHA256..."
echo "${EXPECTED_SHA} ${TMP_DIR}/${RTK_FILE}" | sha256sum -c \
|| { rm -rf "${TMP_DIR}"; die "SHA256 mismatch for ${RTK_FILE}. The download may be corrupt or tampered. Update sha256_amd64/sha256_arm64 options if you changed the version."; }
log "SHA256 OK"
else
log "WARNING: sha256 option is empty — skipping checksum validation. Set sha256_amd64/sha256_arm64 in your feature options to enable it."
fi

# ── 4. Extract ────────────────────────────────────────────────────────────────

log "Extracting to /usr/local/bin..."
tar -xz -f "${TMP_DIR}/${RTK_FILE}" -C /usr/local/bin
chmod +x /usr/local/bin/rtk
rm -rf "${TMP_DIR}"

# ── 5. Verify binary ──────────────────────────────────────────────────────────

rtk --version >/dev/null 2>&1 || die "rtk binary not functional after install"
log "rtk installed: $(rtk --version 2>&1)"

# ── 6. Post-install configuration as the target user ─────────────────────────

TARGET_USER="${_REMOTE_USER:-}"

run_as_user() {
if [ -z "${TARGET_USER}" ] || [ "${TARGET_USER}" = "root" ] || [ "$(id -un)" = "${TARGET_USER}" ]; then
"$@"
elif command -v runuser >/dev/null 2>&1; then
runuser -l "${TARGET_USER}" -c "$*"
elif command -v su >/dev/null 2>&1; then
su - "${TARGET_USER}" -c "$*"
else
log "WARNING: cannot switch to user '${TARGET_USER}'; running as $(id -un)"
"$@"
fi
}

log "Disabling RTK telemetry..."
run_as_user rtk telemetry disable

log "Running rtk init..."
run_as_user rtk init -g --auto-patch

log "Feature setup finished"
9 changes: 9 additions & 0 deletions test/rtk/_scenario_assertions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh
set -eu

echo "[rtk-scenario] Verifying rtk installation"

command -v rtk
rtk --version

echo "[rtk-scenario] OK"
4 changes: 4 additions & 0 deletions test/rtk/base_jammy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
set -eu

"$(dirname "$0")/_scenario_assertions.sh"
4 changes: 4 additions & 0 deletions test/rtk/base_ubuntu.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
set -eu

"$(dirname "$0")/_scenario_assertions.sh"
16 changes: 16 additions & 0 deletions test/rtk/scenarios.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"base_jammy": {
"image": "mcr.microsoft.com/devcontainers/base:jammy",
"features": {
"rtk": {},
"ghcr.io/devcontainers/features/common-utils:2": {}
}
},
"base_ubuntu": {
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"rtk": {},
"ghcr.io/devcontainers/features/common-utils:2": {}
}
}
}
10 changes: 10 additions & 0 deletions test/rtk/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

set -e

source dev-container-features-test-lib

check "rtk installed" bash -c "command -v rtk"
check "rtk version runs" rtk --version

reportResults
Loading