From bf843d2982fa9028d79b6f1274694d5d25bea077 Mon Sep 17 00:00:00 2001 From: Alexandre Balmes Date: Sat, 23 May 2026 19:55:28 +0200 Subject: [PATCH] feat(zpm): add ZPM devcontainer feature - `README.md`: add ZPM to table of contents, feature docs, and repo structure - `src/zpm/devcontainer-feature.json`: feature metadata with version option - `src/zpm/install.sh`: install script with arch detection and SHA256 verification - `test/zpm/scenarios.json`: latest and specific version test scenarios - `test/zpm/test.sh`: base assertions for PATH and version command - `test/zpm/install_zpm_latest.sh`: scenario script for latest version - `test/zpm/install_zpm_specific_version.sh`: scenario script verifying v0.3.0 --- README.md | 44 +++++++++++++ src/zpm/devcontainer-feature.json | 18 ++++++ src/zpm/install.sh | 81 ++++++++++++++++++++++++ test/zpm/install_zpm_latest.sh | 6 ++ test/zpm/install_zpm_specific_version.sh | 23 +++++++ test/zpm/scenarios.json | 16 +++++ test/zpm/test.sh | 21 ++++++ 7 files changed, 209 insertions(+) create mode 100644 src/zpm/devcontainer-feature.json create mode 100755 src/zpm/install.sh create mode 100755 test/zpm/install_zpm_latest.sh create mode 100755 test/zpm/install_zpm_specific_version.sh create mode 100644 test/zpm/scenarios.json create mode 100755 test/zpm/test.sh diff --git a/README.md b/README.md index 2b1a870..e2fdf70 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ A collection of [Dev Container Features](https://containers.dev/implementors/fea - [Mistral Vibe](#mistral-vibe) - [Tree-sitter](#tree-sitter) - [RTK](#rtk) + - [ZPM](#zpm) - [AWF CLI](#awf-cli) - [Repository Structure](#repository-structure) - [Local Testing](#local-testing) @@ -236,6 +237,43 @@ RTK supports Linux containers on both `x86_64` (amd64) and `aarch64` (arm64) arc The feature runs `rtk init --global` during installation, setting up the global configuration for the container user. +### ZPM + +Installs [ZPM](https://github.com/awf-project/zpm), a Zig-based MCP server exposing a Trealla Prolog logic engine over STDIO for deterministic logical reasoning. Provides 29 MCP tools for knowledge management, logical querying, and truth maintenance. + +```jsonc +// devcontainer.json +{ + "features": { + "ghcr.io/awf-project/devcontainer-features/zpm:1": {} + } +} +``` + +#### Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `version` | string | `latest` | Version to install: `latest` or a specific version (e.g. `v0.3.0`) | + +#### Examples + +Pin a specific version: + +```jsonc +{ + "features": { + "ghcr.io/awf-project/devcontainer-features/zpm:1": { + "version": "v0.3.0" + } + } +} +``` + +#### Architecture Support + +ZPM supports Linux containers on both `x86_64` (amd64) and `aarch64` (arm64) architectures. The feature automatically detects the container architecture and downloads the appropriate binary. SHA256 checksums are verified on every install. + ### AWF CLI > **Private package** — This feature requires access to the [awf-project/cli](https://github.com/awf-project/cli) private repository via the `gh` CLI. Only the latest version is available (active development). @@ -289,6 +327,9 @@ src/ tree-sitter/ # Tree-sitter feature devcontainer-feature.json install.sh + zpm/ # ZPM feature + devcontainer-feature.json + install.sh test/ flutter/ # Flutter tests scenarios.json @@ -305,6 +346,9 @@ test/ tree-sitter/ # Tree-sitter tests scenarios.json test.sh + zpm/ # ZPM tests + scenarios.json + test.sh .github/ workflows/ release.yml # CI/CD: test on PR, publish on tag diff --git a/src/zpm/devcontainer-feature.json b/src/zpm/devcontainer-feature.json new file mode 100644 index 0000000..57d3c4d --- /dev/null +++ b/src/zpm/devcontainer-feature.json @@ -0,0 +1,18 @@ +{ + "id": "zpm", + "version": "1.0.0", + "name": "ZPM", + "description": "Installs ZPM, a Zig-based MCP server exposing Trealla Prolog for deterministic logical reasoning.", + "documentationURL": "https://github.com/awf-project/zpm", + "licenseURL": "https://github.com/awf-project/zpm/blob/main/LICENSE", + "options": { + "version": { + "type": "string", + "default": "latest", + "description": "ZPM version to install (e.g., 'v0.3.0'). Use 'latest' for the most recent release." + } + }, + "installsAfter": [ + "ghcr.io/devcontainers/features/common-utils" + ] +} diff --git a/src/zpm/install.sh b/src/zpm/install.sh new file mode 100755 index 0000000..a88bd1f --- /dev/null +++ b/src/zpm/install.sh @@ -0,0 +1,81 @@ +#!/bin/bash +set -e + +# --- Options injected by devcontainer feature engine --- +VERSION="${VERSION:-latest}" + +echo "==> ZPM feature: version=${VERSION}" + +# Ensure curl is available +if ! command -v curl &>/dev/null || ! command -v sha256sum &>/dev/null; then + echo "==> Installing missing dependencies..." + apt-get update -y + apt-get install -y --no-install-recommends curl ca-certificates coreutils + apt-get clean && rm -rf /var/lib/apt/lists/* +fi + +# Detect architecture +ARCH=$(uname -m) +case "$ARCH" in + x86_64|amd64) ARCH_SUFFIX="x86_64" ;; + aarch64|arm64) ARCH_SUFFIX="arm64" ;; + *) + echo "ERROR: unsupported architecture: $ARCH" + exit 1 + ;; +esac +echo "==> Detected architecture: ${ARCH} (${ARCH_SUFFIX})" + +# Resolve version +if [ "$VERSION" = "latest" ]; then + VERSION=$(curl -fsSL "https://api.github.com/repos/awf-project/zpm/releases/latest" \ + | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/') + if [ -z "$VERSION" ]; then + echo "ERROR: failed to resolve latest ZPM version from GitHub API" + exit 1 + fi + echo "==> Resolved latest version: ${VERSION}" +fi + +# Ensure version has 'v' prefix (GitHub tags use v0.3.0 format) +case "$VERSION" in + v*) ;; + *) VERSION="v${VERSION}" ;; +esac + +# Download binary and checksums +TMP_DIR=$(mktemp -d) +trap 'rm -rf "$TMP_DIR"' EXIT + +ARTIFACT="zpm-linux-${ARCH_SUFFIX}" +DOWNLOAD_URL="https://github.com/awf-project/zpm/releases/download/${VERSION}/${ARTIFACT}" +CHECKSUMS_URL="https://github.com/awf-project/zpm/releases/download/${VERSION}/SHA256SUMS" + +echo "==> Downloading ${DOWNLOAD_URL}..." +curl -fsSL "$DOWNLOAD_URL" -o "${TMP_DIR}/${ARTIFACT}" + +echo "==> Downloading SHA256SUMS..." +curl -fsSL "$CHECKSUMS_URL" -o "${TMP_DIR}/SHA256SUMS" + +# Verify SHA256 checksum +echo "==> Verifying checksum..." +EXPECTED_SUM=$(grep " ${ARTIFACT}$" "${TMP_DIR}/SHA256SUMS" | awk '{print $1}') +if [ -z "$EXPECTED_SUM" ]; then + echo "ERROR: artifact ${ARTIFACT} not found in SHA256SUMS" + exit 1 +fi + +ACTUAL_SUM=$(sha256sum "${TMP_DIR}/${ARTIFACT}" | awk '{print $1}') +if [ "$EXPECTED_SUM" != "$ACTUAL_SUM" ]; then + echo "ERROR: SHA256 checksum mismatch" + echo " expected: ${EXPECTED_SUM}" + echo " actual: ${ACTUAL_SUM}" + exit 1 +fi +echo "==> Checksum verified" + +# Install binary +install -m 0755 "${TMP_DIR}/${ARTIFACT}" /usr/local/bin/zpm + +echo "==> ZPM $(zpm version 2>&1) installed at $(command -v zpm)" +echo "==> ZPM feature install complete" diff --git a/test/zpm/install_zpm_latest.sh b/test/zpm/install_zpm_latest.sh new file mode 100755 index 0000000..1e00752 --- /dev/null +++ b/test/zpm/install_zpm_latest.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -e + +# Scenario: install latest ZPM + +source "$(dirname "$0")/test.sh" diff --git a/test/zpm/install_zpm_specific_version.sh b/test/zpm/install_zpm_specific_version.sh new file mode 100755 index 0000000..4e61edc --- /dev/null +++ b/test/zpm/install_zpm_specific_version.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -e + +# Scenario: install a specific ZPM version (v0.3.0) + +echo "==> Testing ZPM specific version scenario..." + +# zpm binary must be on PATH +if ! command -v zpm &>/dev/null; then + echo "FAIL: zpm not found on PATH" + exit 1 +fi +echo "PASS: zpm on PATH ($(command -v zpm))" + +# Verify the exact version matches +ZPM_VERSION=$(zpm version 2>&1) +if [[ "$ZPM_VERSION" != *"0.3.0"* ]]; then + echo "FAIL: expected version 0.3.0, got: ${ZPM_VERSION}" + exit 1 +fi +echo "PASS: zpm version matches 0.3.0 => ${ZPM_VERSION}" + +echo "==> ZPM specific version tests passed" diff --git a/test/zpm/scenarios.json b/test/zpm/scenarios.json new file mode 100644 index 0000000..b33660c --- /dev/null +++ b/test/zpm/scenarios.json @@ -0,0 +1,16 @@ +{ + "install_zpm_latest": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "zpm": {} + } + }, + "install_zpm_specific_version": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "zpm": { + "version": "v0.3.0" + } + } + } +} diff --git a/test/zpm/test.sh b/test/zpm/test.sh new file mode 100755 index 0000000..5d4a76e --- /dev/null +++ b/test/zpm/test.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -e + +echo "==> Testing ZPM feature..." + +# zpm binary must be on PATH +if ! command -v zpm &>/dev/null; then + echo "FAIL: zpm not found on PATH" + exit 1 +fi +echo "PASS: zpm on PATH ($(command -v zpm))" + +# zpm --version must succeed and return a version string +ZPM_VERSION=$(zpm version 2>&1) +if [ -z "$ZPM_VERSION" ]; then + echo "FAIL: zpm --version returned empty output" + exit 1 +fi +echo "PASS: zpm --version => ${ZPM_VERSION}" + +echo "==> All ZPM feature tests passed"