From ea47b502e1589d81ad82ead8b4b44e74b654c909 Mon Sep 17 00:00:00 2001 From: rosstaco Date: Thu, 19 Feb 2026 00:55:29 +0000 Subject: [PATCH 1/4] feat: add gitlab-ci-local feature with installation and testing scripts --- .github/workflows/test.yaml | 2 + justfile | 6 ++ src/gitlab-ci-local/README.md | 28 ++++++ src/gitlab-ci-local/devcontainer-feature.json | 31 +++++++ src/gitlab-ci-local/install.sh | 87 +++++++++++++++++++ test/gitlab-ci-local/scenarios.json | 10 +++ test/gitlab-ci-local/test.sh | 25 ++++++ test/gitlab-ci-local/version.sh | 13 +++ 8 files changed, 202 insertions(+) create mode 100644 src/gitlab-ci-local/README.md create mode 100644 src/gitlab-ci-local/devcontainer-feature.json create mode 100644 src/gitlab-ci-local/install.sh create mode 100644 test/gitlab-ci-local/scenarios.json create mode 100644 test/gitlab-ci-local/test.sh create mode 100644 test/gitlab-ci-local/version.sh diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f5896a5..9e51a47 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -17,6 +17,7 @@ jobs: - microsoft-security-devops-cli - prompty-dumpty - copilot-persistence + - gitlab-ci-local baseImage: - debian:latest - ubuntu:latest @@ -40,6 +41,7 @@ jobs: - microsoft-security-devops-cli - prompty-dumpty - copilot-persistence + - gitlab-ci-local steps: - uses: actions/checkout@v4 diff --git a/justfile b/justfile index d258399..bb373c7 100644 --- a/justfile +++ b/justfile @@ -17,13 +17,19 @@ build-ohmyposh: build-microsoft-security-devops-cli: just build-feature microsoft-security-devops-cli +# Build GitLab CI Local feature +build-gitlab-ci-local: + just build-feature gitlab-ci-local + # Build all features build-all: just build-ohmyposh just build-microsoft-security-devops-cli + just build-gitlab-ci-local # Clean copied features clean: rm -rf .devcontainer/ohmyposh + rm -rf .devcontainer/gitlab-ci-local rm -f .devcontainer/*.tgz echo "✓ Cleaned local features" diff --git a/src/gitlab-ci-local/README.md b/src/gitlab-ci-local/README.md new file mode 100644 index 0000000..6a94d6f --- /dev/null +++ b/src/gitlab-ci-local/README.md @@ -0,0 +1,28 @@ + +# GitLab CI Local (gitlab-ci-local) + +Installs gitlab-ci-local CLI for running GitLab CI/CD pipelines locally + +## Example Usage + +```json +"features": { + "ghcr.io/rosstaco/devcontainer-features/gitlab-ci-local:1": {} +} +``` + +## Options + +| Options Id | Description | Type | Default Value | +|-----|-----|-----|-----| +| version | Version of gitlab-ci-local to install. Use 'latest' or a specific version like '4.67.0' | string | latest | + +## About + +Tired of pushing to test your `.gitlab-ci.yml`? `gitlab-ci-local` lets you run GitLab CI/CD pipelines locally as a shell executor or docker executor. + +For more information, see: https://github.com/firecow/gitlab-ci-local + +--- + +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/rosstaco/devcontainer-features/blob/main/src/gitlab-ci-local/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/gitlab-ci-local/devcontainer-feature.json b/src/gitlab-ci-local/devcontainer-feature.json new file mode 100644 index 0000000..b092c65 --- /dev/null +++ b/src/gitlab-ci-local/devcontainer-feature.json @@ -0,0 +1,31 @@ +{ + "id": "gitlab-ci-local", + "version": "1.0.0", + "name": "GitLab CI Local", + "description": "Installs gitlab-ci-local CLI for running GitLab CI/CD pipelines locally", + "documentationURL": "https://github.com/rosstaco/devcontainer-features/tree/main/src/gitlab-ci-local", + "options": { + "version": { + "type": "string", + "proposals": [ + "latest" + ], + "default": "latest", + "description": "Version of gitlab-ci-local to install. Use 'latest' or a specific version like '4.67.0'" + } + }, + "customizations": { + "vscode": { + "settings": { + "github.copilot.chat.codeGeneration.instructions": [ + { + "text": "This dev container includes `gitlab-ci-local`, which is pre-installed and available on the `PATH`. Use it to run GitLab CI/CD pipelines locally." + } + ] + } + } + }, + "installsAfter": [ + "ghcr.io/devcontainers/features/common-utils" + ] +} diff --git a/src/gitlab-ci-local/install.sh b/src/gitlab-ci-local/install.sh new file mode 100644 index 0000000..350c729 --- /dev/null +++ b/src/gitlab-ci-local/install.sh @@ -0,0 +1,87 @@ +#!/bin/bash +set -e + +# GitLab CI Local installation script for devcontainer features +# https://github.com/firecow/gitlab-ci-local + +CLI_VERSION="${VERSION:-latest}" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +if [ "$(id -u)" -ne 0 ]; then + echo -e "${RED}Script must be run as root. Use sudo, su, or add \"USER root\" to your Dockerfile before running this script.${NC}" + exit 1 +fi + +apt_get_update() { + if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update -y + fi +} + +# Checks if packages are installed and installs them if not +check_packages() { + if ! dpkg -s "$@" > /dev/null 2>&1; then + apt_get_update + apt-get -y install --no-install-recommends "$@" + fi +} + +download_from_github() { + local release_url=$1 + echo "Downloading gitlab-ci-local from ${release_url}..." + + mkdir -p /tmp/gitlab-ci-local + pushd /tmp/gitlab-ci-local + wget --show-progress --progress=dot:giga "${release_url}" + tar -xzf /tmp/gitlab-ci-local/"${cli_filename}" + mv gitlab-ci-local /usr/local/bin/gitlab-ci-local + popd + rm -rf /tmp/gitlab-ci-local +} + +install_using_github() { + check_packages wget tar ca-certificates git + echo "Finished setting up dependencies" + + arch=$(dpkg --print-architecture) + if [ "${arch}" != "amd64" ] && [ "${arch}" != "arm64" ]; then + echo -e "${RED}Unsupported architecture: ${arch}${NC}" >&2 + echo -e "${RED}Only amd64 and arm64 are supported.${NC}" >&2 + exit 1 + fi + + cli_filename="gitlab-ci-local-linux-${arch}.tar.gz" + echo "Installing gitlab-ci-local for ${arch} architecture: ${cli_filename}" + + if [ "${CLI_VERSION}" = "latest" ]; then + download_from_github "https://github.com/firecow/gitlab-ci-local/releases/latest/download/${cli_filename}" + else + # Add leading v to version if it doesn't start with a digit (versions are plain numbers like 4.67.0) + download_from_github "https://github.com/firecow/gitlab-ci-local/releases/download/${CLI_VERSION}/${cli_filename}" + fi +} + +echo -e "${GREEN}Installing gitlab-ci-local...${NC}" + +install_using_github + +# Set executable permission +chmod +x /usr/local/bin/gitlab-ci-local + +# Verify installation +if ! command -v gitlab-ci-local &> /dev/null; then + echo -e "${RED}gitlab-ci-local installation failed - command not found in PATH${NC}" + exit 1 +fi + +INSTALLED_VERSION=$(gitlab-ci-local --version 2>&1 || true) +echo -e "${GREEN}gitlab-ci-local installed successfully: ${INSTALLED_VERSION}${NC}" +echo "" +echo "The 'gitlab-ci-local' command is now available." +echo "For more information: https://github.com/firecow/gitlab-ci-local" diff --git a/test/gitlab-ci-local/scenarios.json b/test/gitlab-ci-local/scenarios.json new file mode 100644 index 0000000..de2f389 --- /dev/null +++ b/test/gitlab-ci-local/scenarios.json @@ -0,0 +1,10 @@ +{ + "version": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "gitlab-ci-local": { + "version": "4.67.0" + } + } + } +} diff --git a/test/gitlab-ci-local/test.sh b/test/gitlab-ci-local/test.sh new file mode 100644 index 0000000..714993f --- /dev/null +++ b/test/gitlab-ci-local/test.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# This test file will be executed against an auto-generated devcontainer.json that +# includes the 'gitlab-ci-local' Feature with no options. +# +# Thus, the value of all options will fall back to the default value in the +# Feature's 'devcontainer-feature.json'. +# +# These scripts are run as 'root' by default. Although that can be changed +# with the '--remote-user' flag. + +set -e + +# Optional: Import test library bundled with the devcontainer CLI +source dev-container-features-test-lib + +# Feature-specific tests +check "gitlab-ci-local is executable" command -v gitlab-ci-local + +check "gitlab-ci-local binary exists" test -x /usr/local/bin/gitlab-ci-local + +check "gitlab-ci-local version command works" bash -c "gitlab-ci-local --version 2>&1 | grep -q '[0-9]\+\.[0-9]\+\.[0-9]\+'" + +# Report results +reportResults diff --git a/test/gitlab-ci-local/version.sh b/test/gitlab-ci-local/version.sh new file mode 100644 index 0000000..66aac4a --- /dev/null +++ b/test/gitlab-ci-local/version.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# This test verifies that a specific version can be installed + +set -e + +source dev-container-features-test-lib + +check "gitlab-ci-local is executable" command -v gitlab-ci-local + +check "gitlab-ci-local version command works" bash -c "gitlab-ci-local --version 2>&1 | grep -q '[0-9]\+\.[0-9]\+\.[0-9]\+'" + +reportResults From f6e5c7c757819df51b9d4998b3e4af12897b8e02 Mon Sep 17 00:00:00 2001 From: rosstaco Date: Thu, 26 Feb 2026 22:48:31 +0000 Subject: [PATCH 2/4] docs: add NOTES.md for gitlab-ci-local and prompty-dumpty features Move custom README content into NOTES.md files so it is preserved when READMEs are auto-generated from devcontainer-feature.json. Regenerate all feature READMEs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/copilot-persistence/README.md | 33 +++++++++++++++++++------------ src/gitlab-ci-local/NOTES.md | 25 +++++++++++++++++++++++ src/gitlab-ci-local/README.md | 21 ++++++++++++++++++++ src/prompty-dumpty/NOTES.md | 14 +++++++++++++ src/prompty-dumpty/README.md | 1 + 5 files changed, 81 insertions(+), 13 deletions(-) create mode 100644 src/gitlab-ci-local/NOTES.md create mode 100644 src/prompty-dumpty/NOTES.md diff --git a/src/copilot-persistence/README.md b/src/copilot-persistence/README.md index b17daed..e734777 100644 --- a/src/copilot-persistence/README.md +++ b/src/copilot-persistence/README.md @@ -1,6 +1,17 @@ -# Copilot CLI Persistence Feature -This devcontainer feature persists GitHub Copilot CLI settings and chat history across container rebuilds. +# Copilot CLI Persistence (copilot-persistence) + +Persists GitHub Copilot CLI settings and chat history across rebuilds + +## Example Usage + +```json +"features": { + "ghcr.io/rosstaco/devcontainer-features/copilot-persistence:1": {} +} +``` + + ## How It Works @@ -10,6 +21,8 @@ Inspired by the [shell-history pattern](https://github.com/stuartleeks/dev-conta 2. **Creates a symlink** from `~/.copilot` → `/copilot-data` 3. **Sets ownership** to the container user during installation (auto-detects from `$_REMOTE_USER`) +> **Note:** If `~/.copilot` already exists as a directory during installation, it is moved into the volume at `/copilot-data/migrated-/` before the symlink is created. + ## What Persists - ✅ Chat history and sessions @@ -17,17 +30,6 @@ Inspired by the [shell-history pattern](https://github.com/stuartleeks/dev-conta - ✅ Command history - ✅ Trusted folders -## Usage - -```json -{ - "features": { - "ghcr.io/devcontainers/features/copilot-cli:1": {}, - "ghcr.io/rosstaco/devcontainer-features/copilot-persistence:1": {} - } -} -``` - ## Troubleshooting View the volume data: @@ -39,3 +41,8 @@ Check the symlink: ```bash ls -la ~/.copilot ``` + + +--- + +_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/rosstaco/devcontainer-features/blob/main/src/copilot-persistence/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/gitlab-ci-local/NOTES.md b/src/gitlab-ci-local/NOTES.md new file mode 100644 index 0000000..ea4e6c8 --- /dev/null +++ b/src/gitlab-ci-local/NOTES.md @@ -0,0 +1,25 @@ +## About + +Tired of pushing to test your `.gitlab-ci.yml`? `gitlab-ci-local` lets you run GitLab CI/CD pipelines locally as a shell executor or docker executor. + +For more information, see: https://github.com/firecow/gitlab-ci-local + +## How It Works + +1. **Downloads the binary** from [firecow/gitlab-ci-local](https://github.com/firecow/gitlab-ci-local) GitHub releases +2. **Installs to** `/usr/local/bin/gitlab-ci-local` +3. **Supports** both `amd64` and `arm64` architectures + +## Usage + +Run a pipeline locally from a project with a `.gitlab-ci.yml`: + +```bash +gitlab-ci-local +``` + +Run a specific job: + +```bash +gitlab-ci-local --job +``` diff --git a/src/gitlab-ci-local/README.md b/src/gitlab-ci-local/README.md index 6a94d6f..b21eeb5 100644 --- a/src/gitlab-ci-local/README.md +++ b/src/gitlab-ci-local/README.md @@ -23,6 +23,27 @@ Tired of pushing to test your `.gitlab-ci.yml`? `gitlab-ci-local` lets you run G For more information, see: https://github.com/firecow/gitlab-ci-local +## How It Works + +1. **Downloads the binary** from [firecow/gitlab-ci-local](https://github.com/firecow/gitlab-ci-local) GitHub releases +2. **Installs to** `/usr/local/bin/gitlab-ci-local` +3. **Supports** both `amd64` and `arm64` architectures + +## Usage + +Run a pipeline locally from a project with a `.gitlab-ci.yml`: + +```bash +gitlab-ci-local +``` + +Run a specific job: + +```bash +gitlab-ci-local --job +``` + + --- _Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/rosstaco/devcontainer-features/blob/main/src/gitlab-ci-local/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/prompty-dumpty/NOTES.md b/src/prompty-dumpty/NOTES.md new file mode 100644 index 0000000..870cb42 --- /dev/null +++ b/src/prompty-dumpty/NOTES.md @@ -0,0 +1,14 @@ +## Usage + +After installation, the `dumpty` command will be available: + +```bash +dumpty --help +dumpty --version +``` + +## Notes + +This feature installs prompty-dumpty system-wide using pip3 with the `--break-system-packages` flag, which is appropriate for containerized development environments. Python 3 is assumed to be present in the base image. + +For more information about prompty-dumpty, visit: https://pypi.org/project/prompty-dumpty/ diff --git a/src/prompty-dumpty/README.md b/src/prompty-dumpty/README.md index 55dc1f9..6b3862a 100644 --- a/src/prompty-dumpty/README.md +++ b/src/prompty-dumpty/README.md @@ -32,6 +32,7 @@ This feature installs prompty-dumpty system-wide using pip3 with the `--break-sy For more information about prompty-dumpty, visit: https://pypi.org/project/prompty-dumpty/ + --- _Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/rosstaco/devcontainer-features/blob/main/src/prompty-dumpty/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ From e1cdd82be6ca8e31e1f060107a431828e80c638e Mon Sep 17 00:00:00 2001 From: rosstaco Date: Thu, 26 Feb 2026 22:48:38 +0000 Subject: [PATCH 3/4] fix(gitlab-ci-local): clean up install script and strengthen tests - Remove unused YELLOW color variable - Remove misleading comment about adding leading v to version - Pass cli_filename as parameter instead of leaking across functions - Make cli_filename local to install_using_github - Add version-specific assertion in scenario test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/gitlab-ci-local/install.sh | 11 +++++------ test/gitlab-ci-local/version.sh | 2 ++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/gitlab-ci-local/install.sh b/src/gitlab-ci-local/install.sh index 350c729..f65eb98 100644 --- a/src/gitlab-ci-local/install.sh +++ b/src/gitlab-ci-local/install.sh @@ -9,7 +9,6 @@ CLI_VERSION="${VERSION:-latest}" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' -YELLOW='\033[1;33m' NC='\033[0m' # No Color if [ "$(id -u)" -ne 0 ]; then @@ -34,12 +33,13 @@ check_packages() { download_from_github() { local release_url=$1 + local filename=$2 echo "Downloading gitlab-ci-local from ${release_url}..." mkdir -p /tmp/gitlab-ci-local pushd /tmp/gitlab-ci-local wget --show-progress --progress=dot:giga "${release_url}" - tar -xzf /tmp/gitlab-ci-local/"${cli_filename}" + tar -xzf /tmp/gitlab-ci-local/"${filename}" mv gitlab-ci-local /usr/local/bin/gitlab-ci-local popd rm -rf /tmp/gitlab-ci-local @@ -56,14 +56,13 @@ install_using_github() { exit 1 fi - cli_filename="gitlab-ci-local-linux-${arch}.tar.gz" + local cli_filename="gitlab-ci-local-linux-${arch}.tar.gz" echo "Installing gitlab-ci-local for ${arch} architecture: ${cli_filename}" if [ "${CLI_VERSION}" = "latest" ]; then - download_from_github "https://github.com/firecow/gitlab-ci-local/releases/latest/download/${cli_filename}" + download_from_github "https://github.com/firecow/gitlab-ci-local/releases/latest/download/${cli_filename}" "${cli_filename}" else - # Add leading v to version if it doesn't start with a digit (versions are plain numbers like 4.67.0) - download_from_github "https://github.com/firecow/gitlab-ci-local/releases/download/${CLI_VERSION}/${cli_filename}" + download_from_github "https://github.com/firecow/gitlab-ci-local/releases/download/${CLI_VERSION}/${cli_filename}" "${cli_filename}" fi } diff --git a/test/gitlab-ci-local/version.sh b/test/gitlab-ci-local/version.sh index 66aac4a..8f902e0 100644 --- a/test/gitlab-ci-local/version.sh +++ b/test/gitlab-ci-local/version.sh @@ -10,4 +10,6 @@ check "gitlab-ci-local is executable" command -v gitlab-ci-local check "gitlab-ci-local version command works" bash -c "gitlab-ci-local --version 2>&1 | grep -q '[0-9]\+\.[0-9]\+\.[0-9]\+'" +check "specific version 4.67.0 installed" bash -c "gitlab-ci-local --version 2>&1 | grep -q '4\.67\.0'" + reportResults From bd54ea60d026c0b58ea60fe8dc484b9a12cc00b6 Mon Sep 17 00:00:00 2001 From: rosstaco Date: Thu, 26 Feb 2026 22:48:43 +0000 Subject: [PATCH 4/4] chore: add generate-docs recipe to justfile Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- justfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/justfile b/justfile index bb373c7..1668f54 100644 --- a/justfile +++ b/justfile @@ -27,6 +27,10 @@ build-all: just build-microsoft-security-devops-cli just build-gitlab-ci-local +# Generate feature README.md files from devcontainer-feature.json and NOTES.md +generate-docs: + devcontainer features generate-docs -p src -n rosstaco/devcontainer-features --github-owner rosstaco --github-repo devcontainer-features + # Clean copied features clean: rm -rf .devcontainer/ohmyposh