diff --git a/src/repos/devcontainer-feature.json b/src/repos/devcontainer-feature.json index 0cfb65c..717eab8 100755 --- a/src/repos/devcontainer-feature.json +++ b/src/repos/devcontainer-feature.json @@ -2,7 +2,7 @@ "name": "Automatically set up multi-repo projects", "id": "repos", "version": "3.1.0", - "description": "Installs the 'repos' CLI tool to manage multiple Git repositories. Optionally runs 'repos clone' when the container starts to clone repositories defined in repos.list.", + "description": "(DEPRECATED: Use the 'utils' feature instead) Installs the 'repos' CLI tool to manage multiple Git repositories. Optionally runs 'repos clone' when the container starts to clone repositories defined in repos.list.", "options": { "runOnStart": { "type": "boolean", @@ -10,5 +10,6 @@ "description": "Automatically run 'repos clone' when the container starts." } }, - "postStartCommand": "/usr/local/bin/repos-post-start" -} + "postStartCommand": "/usr/local/bin/repos-post-start", + "deprecated": true +} \ No newline at end of file diff --git a/src/utils/devcontainer-feature.json b/src/utils/devcontainer-feature.json new file mode 100644 index 0000000..318738f --- /dev/null +++ b/src/utils/devcontainer-feature.json @@ -0,0 +1,24 @@ +{ + "name": "MiguelRodo Utils", + "id": "utils", + "version": "1.0.0", + "description": "Installs Miguel Rodo's utilities like 'repos' and 'setupmjr'.", + "options": { + "installRepos": { + "type": "boolean", + "default": true, + "description": "Install the 'repos' utility." + }, + "installSetupmjr": { + "type": "boolean", + "default": true, + "description": "Install the 'setupmjr' utility." + }, + "runOnStart": { + "type": "boolean", + "default": false, + "description": "Automatically run 'repos clone' when the container starts (only applies if installRepos is true)." + } + }, + "postStartCommand": "/usr/local/bin/utils-post-start" +} \ No newline at end of file diff --git a/src/utils/install.sh b/src/utils/install.sh new file mode 100755 index 0000000..6c1939e --- /dev/null +++ b/src/utils/install.sh @@ -0,0 +1,163 @@ +#!/usr/bin/env bash +set -e + +# Must be root +[ "$(id -u)" -eq 0 ] || { echo "Please run as root (or via sudo)." >&2; exit 1; } + +# Options +INSTALL_REPOS=${INSTALLREPOS:-true} +INSTALL_SETUPMJR=${INSTALLSETUPMJR:-true} +RUN_ON_START=${RUNONSTART:-false} + +if [ "${INSTALL_REPOS}" = "false" ] && [ "${INSTALL_SETUPMJR}" = "false" ]; then + echo "Both installRepos and installSetupmjr are false. Nothing to do." + exit 0 +fi + +# ── Detect OS ──────────────────────────────────────────────────────────────── +detect_os() { + if [ -f /etc/os-release ]; then + . /etc/os-release + echo "$ID" + elif [ -f /etc/debian_version ]; then + echo "debian" + else + echo "unknown" + fi +} + +OS_ID=$(detect_os) +echo "Detected OS: $OS_ID" + +# ── Install dependencies ─────────────────────────────────────────────────── +echo "Installing dependencies based on package manager..." +if [ "$OS_ID" = "ubuntu" ] || [ "$OS_ID" = "debian" ]; then + apt-get update + apt-get install -y curl gnupg ca-certificates wget git +elif command -v apk >/dev/null 2>&1; then + apk add --no-cache bash curl git jq github-cli +elif command -v yum >/dev/null 2>&1; then + yum install -y bash curl git jq gh +elif command -v dnf >/dev/null 2>&1; then + dnf install -y bash curl git jq gh +elif command -v pacman >/dev/null 2>&1; then + pacman -Sy --noconfirm bash curl git jq github-cli +else + echo "Warning: Unknown package manager. Assuming dependencies are already installed." + echo "Required dependencies: bash, curl, git, jq, gh" +fi + +# ── Setup APT Repositories for Debian/Ubuntu (if needed) ─────────────────── +if [ "$OS_ID" = "ubuntu" ] || [ "$OS_ID" = "debian" ]; then + if [ "${INSTALL_REPOS}" = "true" ]; then + echo "Setting up Miguel Rodo APT repository..." + curl -fsSL https://miguelrodo.github.io/apt-miguelrodo/KEY.gpg \ + | gpg --dearmor -o /usr/share/keyrings/apt-miguelrodo.gpg + + echo "deb [signed-by=/usr/share/keyrings/apt-miguelrodo.gpg] https://miguelrodo.github.io/apt-miguelrodo stable main" \ + > /etc/apt/sources.list.d/apt-miguelrodo.list + + echo "Setting up GitHub CLI repository..." + mkdir -p -m 755 /etc/apt/keyrings + wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg | tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null + chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null + + apt-get update + fi +fi + +# ── Install repos ───────────────────────────────────────────────────────── +if [ "${INSTALL_REPOS}" = "true" ]; then + echo "Installing repos..." + if [ "$OS_ID" = "ubuntu" ] || [ "$OS_ID" = "debian" ]; then + echo "Using APT installation method for repos..." + apt-get install -y repos gh jq + else + echo "Using Local Release Installer for repos..." + MISSING_DEPS=() + if ! hash bash git curl jq gh 2>/dev/null; then + for dep in bash git curl jq gh; do + if ! command -v "$dep" >/dev/null 2>&1; then + MISSING_DEPS+=("$dep") + fi + done + fi + + if [ ${#MISSING_DEPS[@]} -gt 0 ]; then + echo "Error: Missing required dependencies: ${MISSING_DEPS[*]}" >&2 + echo "Please install them manually for your system." >&2 + exit 1 + fi + + TEMP_DIR=$(mktemp -d) + git clone https://github.com/MiguelRodo/repos.git "$TEMP_DIR/repos" + + cd "$TEMP_DIR/repos" + bash install-local.sh + cd - >/dev/null + + rm -rf "${TEMP_DIR:?}" + fi +fi + +# ── Install setupmjr ────────────────────────────────────────────────────── +if [ "${INSTALL_SETUPMJR}" = "true" ]; then + echo "Installing setupmjr..." + MISSING_DEPS=() + if ! hash bash git curl 2>/dev/null; then + for dep in bash git curl; do + if ! command -v "$dep" >/dev/null 2>&1; then + MISSING_DEPS+=("$dep") + fi + done + fi + + if [ ${#MISSING_DEPS[@]} -gt 0 ]; then + echo "Error: Missing required dependencies: ${MISSING_DEPS[*]}" >&2 + echo "Please install them manually for your system." >&2 + exit 1 + fi + + echo "Using Local Release Installer for setupmjr..." + TEMP_DIR=$(mktemp -d) + git clone https://github.com/MiguelRodo/setupmjr.git "$TEMP_DIR/setupmjr" + + cd "$TEMP_DIR/setupmjr" + bash install-local.sh + cd - >/dev/null + + rm -rf "${TEMP_DIR:?}" +fi + +# ── Cleanup Debian/Ubuntu ───────────────────────────────────────────────── +if [ "$OS_ID" = "ubuntu" ] || [ "$OS_ID" = "debian" ]; then + echo "Cleaning up APT..." + apt-get clean + rm -rf /var/lib/apt/lists/* +fi + +# ── Configure start script ─────────────────────────────────────────────────── +echo "Configuring post-start script..." +POST_START_SCRIPT="/usr/local/bin/utils-post-start" + +cat > "$POST_START_SCRIPT" << 'EOF' +#!/usr/bin/env bash +EOF + +if [ "${INSTALL_REPOS}" = "true" ] && [ "${RUN_ON_START}" = "true" ]; then + cat >> "$POST_START_SCRIPT" << 'EOF' +# Check if repos.list exists in the workspace +REPOS_LIST="${REPOS_LIST:-repos.list}" +if [ -f "$REPOS_LIST" ]; then + repos clone +else + echo "Info: No repos.list file found. Skipping repository setup." + echo "Create a repos.list file and run 'repos clone' to clone repositories." +fi +EOF +fi + +chmod +x "$POST_START_SCRIPT" + +echo "MiguelRodo Utils feature installation complete!" \ No newline at end of file diff --git a/test/_global/scenarios.json b/test/_global/scenarios.json index b3f928f..61d0ff8 100644 --- a/test/_global/scenarios.json +++ b/test/_global/scenarios.json @@ -2,13 +2,13 @@ "all": { "image": "mcr.microsoft.com/devcontainers/base:ubuntu", "features": { - "repos": {}, "apptainer": { "timezone": "America/New_York" }, "fit-sne": { "version": "1.2.1" - } + }, + "utils": {} } }, "renv-cache": { @@ -16,209 +16,209 @@ "features": { "renv-cache": { "restore": false - } + } } - }, - "renv-cache-jq-unit-test": { - "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + }, + "renv-cache-jq-unit-test": { + "image": "ghcr.io/rocker-org/devcontainer/r-ver:4.4", "features": { "renv-cache": { "restore": false - } + } } - }, - "renv-cache-restore": { + }, + "renv-cache-restore": { "image": "ghcr.io/rocker-org/devcontainer/r-ver:4.4", "features": { "renv-cache": { "restore": true, "debug": true - } + } } - }, - "renv-cache-pak": { + }, + "renv-cache-pak": { "image": "ghcr.io/rocker-org/devcontainer/r-ver:4.4", "features": { "renv-cache": { "restore": true, "usePak": true, "debug": true - } + } } - }, - "github-tokens": { + }, + "github-tokens": { "image": "mcr.microsoft.com/devcontainers/base:ubuntu", "features": { "github-tokens": {} } - }, - "github-tokens-elevate": { + }, + "github-tokens-elevate": { "image": "mcr.microsoft.com/devcontainers/base:ubuntu", "features": { "github-tokens": { "elevateGitHubToken": true, "overrideGitHubToken": false - } + } }, "remoteEnv": { "GITHUB_TOKEN": "gha_restricted_token", "GH_TOKEN": "ghp_permissive_token" } - }, - "github-tokens-functions": { + }, + "github-tokens-functions": { "image": "mcr.microsoft.com/devcontainers/base:ubuntu", "features": { "github-tokens": {} } - }, - "github-tokens-override": { + }, + "github-tokens-override": { "image": "mcr.microsoft.com/devcontainers/base:ubuntu", "features": { "github-tokens": { "elevateGitHubToken": false, "overrideGitHubToken": true - } + } }, "remoteEnv": { "GITHUB_TOKEN": "gha_restricted_token", "GITHUB_PAT": "ghp_pat_token" } - }, - "github-tokens-disabled": { + }, + "github-tokens-disabled": { "image": "mcr.microsoft.com/devcontainers/base:ubuntu", "features": { "github-tokens": { "elevateGitHubToken": false, "overrideGitHubToken": false - } + } }, "remoteEnv": { "GH_TOKEN": "ghp_permissive_token" } - }, - "github-tokens-bashrc-d": { + }, + "github-tokens-bashrc-d": { "image": "mcr.microsoft.com/devcontainers/base:ubuntu", "features": { "github-tokens": {} } - }, - "mermaid_default": { - "image": "mcr.microsoft.com/devcontainers/base:ubuntu", - "features": { - "mermaid": {} - } - }, - "mermaid_with_node20": { - "image": "mcr.microsoft.com/devcontainers/base:ubuntu", - "features": { - "mermaid": { - "nodeVersion": "20" - } - } - }, - "mermaid_custom_user": { - "image": "mcr.microsoft.com/devcontainers/base:ubuntu", - "features": { - "mermaid": { - "userName": "customuser" - } - } - }, - "repos_alpine": { - "image": "mcr.microsoft.com/devcontainers/base:alpine", - "features": { - "repos": {} - } - }, - "repos_debian": { - "image": "mcr.microsoft.com/devcontainers/base:debian", - "features": { - "repos": {} - } - }, - "apptainer_debian": { - "image": "mcr.microsoft.com/devcontainers/base:debian", - "features": { - "apptainer": { - "timezone": "America/New_York" - } - } - }, - "fit_sne_alpine": { - "image": "mcr.microsoft.com/devcontainers/base:alpine", - "features": { - "fit-sne": { - "version": "1.2.1" - } - } - }, - "fit_sne_debian": { - "image": "mcr.microsoft.com/devcontainers/base:debian", - "features": { - "fit-sne": { - "version": "1.2.1" - } - } - }, - "mermaid_debian": { - "image": "mcr.microsoft.com/devcontainers/base:debian", - "features": { - "mermaid": {} - } - }, - "mermaid_alpine": { - "image": "mcr.microsoft.com/devcontainers/base:alpine", - "features": { - "mermaid": {} - } - }, - "apptainer_fedora": { - "image": "fedora:latest", - "features": { - "apptainer": { - "timezone": "America/New_York" - } - } - }, - "fit_sne_fedora": { - "image": "fedora:latest", - "features": { - "fit-sne": { - "version": "1.2.1" - } - } - }, - "mermaid_fedora": { - "image": "fedora:latest", - "features": { - "mermaid": {} - } - }, - "cmdstan_default": { - "image": "mcr.microsoft.com/devcontainers/base:ubuntu", - "features": { - "cmdstan": {} - } - }, - "cmdstan_debian": { - "image": "mcr.microsoft.com/devcontainers/base:debian", - "features": { - "cmdstan": {} - } - }, - "cmdstan_python": { - "image": "mcr.microsoft.com/devcontainers/python:3", - "features": { - "cmdstan": {} - } - }, - "build_info_default": { - "image": "mcr.microsoft.com/devcontainers/base:ubuntu", - "features": { - "build-info": { - "version": "1.2.3", - "buildDate": "2023-10-27T10:00:00Z" - } + }, + "mermaid_default": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "mermaid": {} + } + }, + "mermaid_with_node20": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "mermaid": { + "nodeVersion": "20" + } + } + }, + "mermaid_custom_user": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "mermaid": { + "userName": "customuser" + } + } + }, + "apptainer_debian": { + "image": "mcr.microsoft.com/devcontainers/base:debian", + "features": { + "apptainer": { + "timezone": "America/New_York" + } + } + }, + "fit_sne_alpine": { + "image": "mcr.microsoft.com/devcontainers/base:alpine", + "features": { + "fit-sne": { + "version": "1.2.1" + } + } + }, + "fit_sne_debian": { + "image": "mcr.microsoft.com/devcontainers/base:debian", + "features": { + "fit-sne": { + "version": "1.2.1" + } + } + }, + "mermaid_debian": { + "image": "mcr.microsoft.com/devcontainers/base:debian", + "features": { + "mermaid": {} + } + }, + "mermaid_alpine": { + "image": "mcr.microsoft.com/devcontainers/base:alpine", + "features": { + "mermaid": {} + } + }, + "apptainer_fedora": { + "image": "fedora:latest", + "features": { + "apptainer": { + "timezone": "America/New_York" + } + } + }, + "fit_sne_fedora": { + "image": "fedora:latest", + "features": { + "fit-sne": { + "version": "1.2.1" + } + } + }, + "mermaid_fedora": { + "image": "fedora:latest", + "features": { + "mermaid": {} + } + }, + "cmdstan_default": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "cmdstan": {} + } + }, + "cmdstan_debian": { + "image": "mcr.microsoft.com/devcontainers/base:debian", + "features": { + "cmdstan": {} + } + }, + "cmdstan_python": { + "image": "mcr.microsoft.com/devcontainers/python:3", + "features": { + "cmdstan": {} + } + }, + "build_info_default": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "build-info": { + "version": "1.2.3", + "buildDate": "2023-10-27T10:00:00Z" + } + } + }, + "utils_alpine": { + "image": "mcr.microsoft.com/devcontainers/base:alpine", + "features": { + "utils": {} + } + }, + "utils_debian": { + "image": "mcr.microsoft.com/devcontainers/base:debian", + "features": { + "utils": {} + } } - } -} +} \ No newline at end of file diff --git a/test/utils/test.sh b/test/utils/test.sh new file mode 100755 index 0000000..e84c94e --- /dev/null +++ b/test/utils/test.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e +source dev-container-features-test-lib + +check "repos is installed and accessible" repos --help +check "setupmjr is installed and accessible" setupmjr --help +reportResults