diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e629ab3..779485a 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -57,6 +57,7 @@ jobs: - strix - colibri - gitai + - worktrunk baseImage: - debian:latest - ubuntu:latest diff --git a/README.md b/README.md index cb9efb6..2689dbc 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ This repository contains a _collection_ of Features. | strix | https://github.com/usestrix/strix | Open-source AI penetration testing tool to find and fix your app's vulnerabilities. | | colibri | https://github.com/JustVugg/colibri | Run GLM-5.2 (744B MoE) on a consumer machine — pure C, zero deps, experts streamed from disk. | | gitai | https://github.com/git-ai-project/git-ai | A Git extension for tracking AI-generated code through every commit, merge, rebase, cherry-pick, etc. | +| worktrunk | https://github.com/max-sixty/worktrunk | A CLI for Git worktree management, designed for parallel AI agent workflows. | @@ -809,3 +810,20 @@ Running `git-ai` inside the built container will print the version of the Git AI ```bash git-ai --version ``` + +### `worktrunk` + +Running `wt --version` inside the built container will print the installed Worktrunk version. + +```jsonc +{ + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/jsburckhardt/devcontainer-features/worktrunk:1": {} + } +} +``` + +```bash +wt --version +``` diff --git a/src/worktrunk/devcontainer-feature.json b/src/worktrunk/devcontainer-feature.json new file mode 100644 index 0000000..6c85a3c --- /dev/null +++ b/src/worktrunk/devcontainer-feature.json @@ -0,0 +1,13 @@ +{ + "name": "Worktrunk", + "id": "worktrunk", + "version": "1.0.0", + "description": "A CLI for Git worktree management, designed for parallel AI agent workflows.", + "options": { + "version": { + "type": "string", + "default": "latest", + "description": "Version to install from GitHub releases" + } + } +} diff --git a/src/worktrunk/install.sh b/src/worktrunk/install.sh new file mode 100755 index 0000000..24caec6 --- /dev/null +++ b/src/worktrunk/install.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Variables +REPO_OWNER="max-sixty" +REPO_NAME="worktrunk" +BINARY_NAME="wt" +WORKTRUNK_VERSION="${VERSION:-latest}" +GITHUB_API_REPO_URL="https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases" + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +# Clean up +rm -rf /var/lib/apt/lists/* + +# Checks if packages are installed and installs them if not +check_packages() { + if ! dpkg -s "$@" >/dev/null 2>&1; then + if [ -z "$(find /var/lib/apt/lists -mindepth 1 -print -quit 2>/dev/null)" ]; then + echo "Running apt-get update..." + apt-get update -y + fi + apt-get -y install --no-install-recommends "$@" + fi +} + +# Make sure release metadata can be queried and .tar.xz assets can be extracted. +check_packages curl jq ca-certificates tar xz-utils + +# Query the GitHub API, falling back to GitHub's latest-release redirect if needed. +get_latest_version() { + local version + local latest_url + + version="$(curl -fsSL "${GITHUB_API_REPO_URL}/latest" 2>/dev/null | jq -r '.tag_name // empty' 2>/dev/null || true)" + if [ -n "${version}" ]; then + printf '%s\n' "${version}" + return 0 + fi + + echo "GitHub API lookup failed; falling back to the latest-release redirect." >&2 + latest_url="$(curl -fsSLI -o /dev/null -w '%{url_effective}' \ + "https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/latest" 2>/dev/null || true)" + version="${latest_url##*/}" + if [ -z "${version}" ] || [ "${version}" = "latest" ]; then + echo "Unable to determine the latest Worktrunk version." >&2 + return 1 + fi + printf '%s\n' "${version}" +} + +if [ -z "${WORKTRUNK_VERSION}" ] || [ "${WORKTRUNK_VERSION}" = "latest" ]; then + WORKTRUNK_VERSION="$(get_latest_version)" + echo "No version provided or 'latest' specified, installing the latest version: ${WORKTRUNK_VERSION}" +else + echo "Installing Worktrunk version: ${WORKTRUNK_VERSION}" +fi + +case "$(uname -m)" in + x86_64 | amd64) + ARCH="x86_64" + ;; + aarch64 | arm64) + ARCH="aarch64" + ;; + i386 | i686) + ARCH="i686" + echo "Unsupported architecture: ${ARCH} (upstream does not publish i686 release assets)" >&2 + exit 1 + ;; + armv7l) + ARCH="armv7" + echo "Unsupported architecture: ${ARCH} (upstream does not publish armv7 release assets)" >&2 + exit 1 + ;; + *) + echo "Unsupported architecture: $(uname -m)" >&2 + exit 1 + ;; +esac + +case "$(uname -s | tr '[:upper:]' '[:lower:]')" in + linux) + TARGET="unknown-linux-musl" + ;; + darwin) + TARGET="apple-darwin" + ;; + *) + echo "Unsupported OS: $(uname -s)" >&2 + exit 1 + ;; +esac + +ASSET_NAME="worktrunk-${ARCH}-${TARGET}.tar.xz" +DOWNLOAD_URL="https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/download/${WORKTRUNK_VERSION}/${ASSET_NAME}" +TMP_DIR="$(mktemp -d)" +cleanup() { + rm -rf "${TMP_DIR}" + rm -rf /var/lib/apt/lists/* +} +trap cleanup EXIT + +echo "Downloading Worktrunk from ${DOWNLOAD_URL}" +curl -fsSL "${DOWNLOAD_URL}" -o "${TMP_DIR}/${ASSET_NAME}" + +echo "Extracting Worktrunk..." +tar -xJf "${TMP_DIR}/${ASSET_NAME}" -C "${TMP_DIR}" +EXTRACTED_DIR="${TMP_DIR}/worktrunk-${ARCH}-${TARGET}" +if [ ! -x "${EXTRACTED_DIR}/${BINARY_NAME}" ]; then + echo "Could not find ${BINARY_NAME} in ${ASSET_NAME}." >&2 + exit 1 +fi + +install -m 0755 "${EXTRACTED_DIR}/${BINARY_NAME}" "/usr/local/bin/${BINARY_NAME}" + +echo "Verifying installation..." +"${BINARY_NAME}" --version + +echo "Done!" diff --git a/test/_global/all-tools.sh b/test/_global/all-tools.sh index 726fa82..5534183 100755 --- a/test/_global/all-tools.sh +++ b/test/_global/all-tools.sh @@ -46,5 +46,6 @@ check "officecli" officecli --version check "strix" strix --version check "colibri" bash -c "command -v colibri" check "gitai" git-ai --version +check "worktrunk" wt --version reportResults diff --git a/test/_global/scenarios.json b/test/_global/scenarios.json index f1dd3ab..a23b4c5 100644 --- a/test/_global/scenarios.json +++ b/test/_global/scenarios.json @@ -47,7 +47,8 @@ "officecli": {}, "strix": {}, "colibri": {}, - "gitai": {} + "gitai": {}, + "worktrunk": {} } }, "flux-specific-version": { @@ -399,5 +400,13 @@ "version": "v1.6.21" } } + }, + "worktrunk-specific-version": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "worktrunk": { + "version": "v0.79.0" + } + } } } diff --git a/test/_global/worktrunk-specific-version.sh b/test/_global/worktrunk-specific-version.sh new file mode 100755 index 0000000..db07707 --- /dev/null +++ b/test/_global/worktrunk-specific-version.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -e +source dev-container-features-test-lib +check "worktrunk with specific version" /bin/bash -c "wt --version | grep '0.79.0'" +reportResults diff --git a/test/worktrunk/test.sh b/test/worktrunk/test.sh new file mode 100755 index 0000000..765cdda --- /dev/null +++ b/test/worktrunk/test.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -e +source dev-container-features-test-lib +check "worktrunk" wt --version +reportResults