Skip to content
Merged
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 @@ -17,6 +17,7 @@ jobs:
- microsoft-security-devops-cli
- prompty-dumpty
- copilot-persistence
- gitlab-ci-local
baseImage:
- debian:latest
- ubuntu:latest
Expand All @@ -40,6 +41,7 @@ jobs:
- microsoft-security-devops-cli
- prompty-dumpty
- copilot-persistence
- gitlab-ci-local
steps:
- uses: actions/checkout@v4

Expand Down
10 changes: 10 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,23 @@ 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

# 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
rm -rf .devcontainer/gitlab-ci-local
rm -f .devcontainer/*.tgz
echo "✓ Cleaned local features"
33 changes: 20 additions & 13 deletions src/copilot-persistence/README.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -10,24 +21,15 @@ 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-<timestamp>/` before the symlink is created.

## What Persists

- ✅ Chat history and sessions
- ✅ CLI configuration (model preferences, settings)
- ✅ 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:
Expand All @@ -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`._
25 changes: 25 additions & 0 deletions src/gitlab-ci-local/NOTES.md
Original file line number Diff line number Diff line change
@@ -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 <job-name>
```
49 changes: 49 additions & 0 deletions src/gitlab-ci-local/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@

# 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

## 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 <job-name>
```


---

_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`._
31 changes: 31 additions & 0 deletions src/gitlab-ci-local/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
86 changes: 86 additions & 0 deletions src/gitlab-ci-local/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/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'
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
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apt_get_update uses find /var/lib/apt/lists/* under set -e. On images where the glob doesn't match (or lists dir is empty), find can exit non-zero and abort the install script before apt-get update runs. Use a safer emptiness check that doesn't rely on a glob (e.g., find /var/lib/apt/lists -maxdepth 1 -type f | wc -l, ls -A /var/lib/apt/lists, or guard errors with 2>/dev/null || true).

Suggested change
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
if [ "$(find /var/lib/apt/lists -maxdepth 1 -type f 2>/dev/null | wc -l)" = "0" ]; then

Copilot uses AI. Check for mistakes.
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
Comment on lines +28 to +31
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feature uses apt-get without setting DEBIAN_FRONTEND=noninteractive. Other feature install scripts in this repo set it before apt operations (e.g., src/ohmyposh/install.sh:23, src/microsoft-security-devops-cli/install.sh:21) to avoid occasional interactive prompts during package configuration. Consider exporting DEBIAN_FRONTEND=noninteractive before apt-get update/install here as well.

Copilot uses AI. Check for mistakes.
}

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/"${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

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}" "${cli_filename}"
else
download_from_github "https://github.com/firecow/gitlab-ci-local/releases/download/${CLI_VERSION}/${cli_filename}" "${cli_filename}"
Comment on lines +41 to +65
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The install.sh script downloads and extracts a binary tarball from GitHub using wget and tar without any checksum, signature, or other integrity verification, and then installs it as root. If the firecow/gitlab-ci-local release or the download channel is compromised, an attacker could deliver a malicious binary that is installed and executed with full privileges in the devcontainer. To reduce supply chain risk, pin downloads to immutable release artifacts and verify them (e.g., with a published checksum or signature) before extraction and installation.

Copilot uses AI. Check for mistakes.
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"
14 changes: 14 additions & 0 deletions src/prompty-dumpty/NOTES.md
Original file line number Diff line number Diff line change
@@ -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/
1 change: 1 addition & 0 deletions src/prompty-dumpty/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`._
10 changes: 10 additions & 0 deletions test/gitlab-ci-local/scenarios.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"version": {
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"gitlab-ci-local": {
"version": "4.67.0"
}
}
}
}
25 changes: 25 additions & 0 deletions test/gitlab-ci-local/test.sh
Original file line number Diff line number Diff line change
@@ -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
15 changes: 15 additions & 0 deletions test/gitlab-ci-local/version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/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]\+'"

check "specific version 4.67.0 installed" bash -c "gitlab-ci-local --version 2>&1 | grep -q '4\.67\.0'"

reportResults