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
38 changes: 38 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Dev Container Features

## Testing

```bash
# Install devcontainers CLI
npm install -g @devcontainers/cli

# Test a specific feature against a specific image
devcontainer features test -f opencode -i debian:latest .

# Skip scenarios, run only autogenerated tests
devcontainer features test --skip-scenarios -f opencode .

# Skip autogenerated, run only scenario tests
devcontainer features test --skip-autogenerated --skip-duplicated -f opencode .
```

## Features

| Feature | Description |
|---------|-------------|
| `opencode` | Installs opencode CLI + fixes volume-mounted directory permissions |
| `agency-agents` | Installs msitarzewski/agency-agents for Cursor/Copilot |

## Feature Structure

Each feature lives in `src/<feature>/` with:
- `devcontainer-feature.json` - metadata + options
- `install.sh` - entrypoint script

## Options

Options from `devcontainer-feature.json` are exported as environment variables (uppercase, sanitized). Example: option `tool` becomes `$TOOL` in install.sh.

## Publishing

Features are published to GHCR. Release workflow runs on tag push (e.g., `feature_opencode_0.2.0`).
45 changes: 1 addition & 44 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,47 +107,4 @@ When these directories are volume-mounted into the container, they may be owned

### Shared Volumes Between Projects

If you share the same `~/.opencode` volume mounts across multiple projects, be aware that opencode's state and data will be shared as well. This can be useful for preserving context between projects, but may also cause unexpected behaviour if projects have conflicting configurations. Consider using separate volume mounts per project if isolation is needed.

## Repo and Feature Structure

Similar to the [`devcontainers/features`](https://github.com/devcontainers/features) repo, this repository has a `src` folder. Each Feature has its own sub-folder, containing at least a `devcontainer-feature.json` and an entrypoint script `install.sh`.

```
├── src
│ ├── agency-agents
│ │ ├── devcontainer-feature.json
│ │ └── install.sh
...
```

An [implementing tool](https://containers.dev/supporting#tools) will composite [the documented dev container properties](https://containers.dev/implementors/features/#devcontainer-feature-json-properties) from the feature's `devcontainer-feature.json` file, and execute in the `install.sh` entrypoint script in the container during build time. Implementing tools are also free to process attributes under the `customizations` property as desired.

### Options

All available options for a Feature should be declared in the `devcontainer-feature.json`. The syntax for the `options` property can be found in the [devcontainer Feature json properties reference](https://containers.dev/implementors/features/#devcontainer-feature-json-properties).

For example, the `agency-agents` feature exposes a `tool` string option. If no option is provided in a user's `devcontainer.json`, the value defaults to `auto`.

```jsonc
{
// ...
"options": {
"tool": {
"type": "string",
"default": "auto",
"description": "Tool name passed to ./scripts/install.sh --tool <tool>. Use 'auto' for --parallel auto-detection."
}
}
}
```

Options are exported as Feature-scoped environment variables. The option name is capitalised and sanitised according to [option resolution](https://containers.dev/implementors/features/#option-resolution).

```bash
#!/bin/sh

tool="${TOOL:-auto}"

...
```
If you share the same `~/.opencode` volume mounts across multiple projects, be aware that opencode's state and data will be shared as well. This can be useful for preserving context between projects, but may also cause unexpected behaviour if projects have conflicting configurations. Consider using separate volume mounts per project if isolation is needed.
2 changes: 1 addition & 1 deletion src/opencode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ The feature can be added without additional configuration. To customize the user
{
"image": "mcr.microsoft.com/devcontainers/base:trixie",
"features": {
"./features/opencode": {}
"ghcr.io/wcgomes/devcontainer-features/opencode:0": {}
},
"mounts": [
{
Expand Down
2 changes: 1 addition & 1 deletion src/opencode/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "opencode CLI",
"id": "opencode",
"version": "0.2.0",
"version": "0.2.1",
"description": "Installs the opencode AI coding agent CLI and ensures volume-mounted data directories are owned by the correct user.",
"documentationURL": "https://opencode.ai",
"options": {
Expand Down
26 changes: 25 additions & 1 deletion src/opencode/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,41 @@ su "$USERNAME" -c "curl -fsSL https://opencode.ai/install | bash -s -- --no-modi

ln -s "${USER_HOME}/.opencode/bin/opencode" /usr/local/bin/opencode

log "Criando diretórios para volumes persistentes..."

mkdir -p "${USER_HOME}/.config/opencode"
mkdir -p "${USER_HOME}/.local/share/opencode"
mkdir -p "${USER_HOME}/.local/state/opencode"

chown -R "${USERNAME}:${USERNAME}" \
"${USER_HOME}/.config/opencode" \
"${USER_HOME}/.local/share/opencode" \
"${USER_HOME}/.local/state/opencode"

cat > /usr/local/bin/opencode-fix-permissions <<EOF
#!/bin/bash
set -euo pipefail

for path in \
"${USER_HOME}/.config" \
"${USER_HOME}/.config/opencode" \
"${USER_HOME}/.local/share" \
"${USER_HOME}/.local/share/opencode" \
"${USER_HOME}/.local/state" \
"${USER_HOME}/.local/state/opencode"
do
if [ -e "\$path" ] && [ "\$(stat -c '%U' "\$path" 2>/dev/null)" != "${USERNAME}" ]; then
sudo chown ${USERNAME}:${USERNAME} "\$path" 2>/dev/null || true
fi
done

for path in \
"${USER_HOME}/.config/opencode" \
"${USER_HOME}/.local/share/opencode" \
"${USER_HOME}/.local/state/opencode"
do
if [ -e "\$path" ]; then
sudo chown -R ${USERNAME}:${USERNAME} "\$path"
sudo chown -R ${USERNAME}:${USERNAME} "\$path" 2>/dev/null || true
fi
done
EOF
Expand Down
Loading