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
49 changes: 44 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,33 @@ Published at: `ghcr.io/helpers4/devcontainer/<feature-name>`

## Features

### vite-plus

Complete Vite+ toolchain setup with VS Code extensions (Oxc, Vitest), optimized configuration, and optional global CLI tools. Perfect for modern web development with React, Vue, Svelte, and more.

**Key benefits:**
- Pre-configured Oxc formatter/linter (100x faster than ESLint)
- Vitest test explorer integration
- Smart defaults for Vite+ development
- Global Oxc CLI installation option
- Project setup helper command
- Supports all Vite-compatible frameworks

[📖 Documentation](./src/vite-plus/README.md)

### package-auto-install

Automatically detects and runs npm/yarn/pnpm install in non-interactive mode after container creation. Handles corepack setup for Node 24+ and intelligently detects the package manager from package.json or lockfiles.

**Key benefits:**
- Automatic package manager detection from package.json or lockfiles
- Corepack support for Node 24+ (auto-installs if needed)
- Non-interactive mode (CI=true) prevents prompts
- Smart command selection (npm ci, pnpm --frozen-lockfile, yarn --immutable)
- Eliminates need for manual postCreateCommand

[📖 Documentation](./src/package-auto-install/README.md)

### angular-dev

Angular-specific development environment with VS Code extensions and CLI autocompletion.
Expand Down Expand Up @@ -46,13 +73,14 @@ Installs git-absorb, a tool that automatically absorbs staged changes into their

### local-mounts

Mounts local Git, SSH, GPG, and npm configuration files into the devcontainer for seamless development authentication.
Mounts local Git, SSH, GPG, and npm configuration files into the devcontainer for seamless development authentication. Now with proper SSH agent forwarding support.

**Key benefits:**
- Git configuration available inside container
- SSH keys for Git operations and remote connections
- SSH keys and SSH agent forwarding configured automatically
- GPG keys for commit signing
- npm authentication for private registries
- Fixed SSH_AUTH_SOCK handling for devcontainer compatibility

[📖 Documentation](./src/local-mounts/README.md)

Expand All @@ -63,6 +91,8 @@ Features from this repository are available via GitHub Container Registry. Refer
```json
{
"features": {
"ghcr.io/helpers4/devcontainer/vite-plus:1": {},
"ghcr.io/helpers4/devcontainer/package-auto-install:1": {},
"ghcr.io/helpers4/devcontainer/angular-dev:1": {},
"ghcr.io/helpers4/devcontainer/shell-history-per-project:1": {},
"ghcr.io/helpers4/devcontainer/git-absorb:1": {},
Expand All @@ -75,6 +105,8 @@ Features from this repository are available via GitHub Container Registry. Refer

| Feature | Description | Documentation |
|---------|-------------|---------------|
| [vite-plus](./src/vite-plus) | Complete Vite+ toolchain with Oxc, Vitest, and VS Code integration | [README](./src/vite-plus/README.md) |
| [package-auto-install](./src/package-auto-install) | Automatic package installation with corepack support for Node 24+ | [README](./src/package-auto-install/README.md) |
| [angular-dev](./src/angular-dev) | Angular development environment with extensions and CLI autocompletion | [README](./src/angular-dev/README.md) |
| [shell-history-per-project](./src/shell-history-per-project) | Per-project shell history persistence with multi-shell auto-detection | [README](./src/shell-history-per-project/README.md) |
| [git-absorb](./src/git-absorb) | Automatic absorption of staged changes into logical commits | [README](./src/git-absorb/README.md) |
Expand All @@ -87,8 +119,10 @@ This repository follows the [DevContainer Features specification](https://contai
### Repository Structure

```
.
├── src/
.package-auto-install/
│ │ ├── devcontainer-feature.json
│ │ ├── install.sh
│ │ └── README.md
│ ├── angular-dev/
│ │ ├── devcontainer-feature.json
│ │ ├── install.sh
Expand All @@ -106,6 +140,10 @@ This repository follows the [DevContainer Features specification](https://contai
│ ├── install.sh
│ └── README.md
├── test/
│ ├── package-auto-install/
│ │ └── test.sh── install.sh
│ └── README.md
├── test/
│ ├── angular-dev/
│ │ └── test.sh
│ ├── git-absorb/
Expand All @@ -114,7 +152,8 @@ This repository follows the [DevContainer Features specification](https://contai
│ │ └── test.sh
│ └── shell-history-per-project/
│ └── test.sh
└── README.md
└── README.mdpackage-auto-install
devcontainer features test --features
```

### Testing
Expand Down
212 changes: 212 additions & 0 deletions src/package-auto-install/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# Automatic Package Installation (package-auto-install)

Automatically detects and runs npm/yarn/pnpm install in non-interactive mode after container creation.

## Features

- **Automatic detection**: Detects package manager based on lockfile (pnpm-lock.yaml, yarn.lock, package-lock.json)
- **Corepack support**: Automatically installs and enables corepack if `packageManager` field is found in package.json (required for Node 24+)
- **Non-interactive mode**: Sets `CI=true` to avoid prompts (e.g., pnpm won't ask to delete node_modules)
- **Smart command selection**: Uses `npm ci`, `pnpm install --frozen-lockfile`, or `yarn install --immutable` when lockfiles exist
- **Flexible configuration**: Override package manager, command, and working directory
- **Skip if exists**: Optionally skip installation if node_modules already exists

## Usage

### Basic Usage

Add this feature to your `devcontainer.json`:

```json
{
"features": {
"ghcr.io/helpers4/devcontainer/package-auto-install:1": {}
}
}
```

This will:
1. Detect the package manager from lockfile
2. Run the appropriate install command automatically
3. Set `CI=true` to prevent interactive prompts

### Remove Manual postCreateCommand

If you have this in your devcontainer.json, you can now remove it:

```json
{
"postCreateCommand": "CI=true pnpm install" // ❌ Not needed anymore
}
```

## Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `command` | string | `auto` | Installation command: `install`, `ci`, or `auto` to detect |
| `packageManager` | string | `auto` | Package manager: `npm`, `yarn`, `pnpm`, or `auto` to detect |
| `workingDirectory` | string | `/workspaces/${localWorkspaceFolderBasename}` | Directory where to run install |
| `skipIfNodeModulesExists` | boolean | `false` | Skip if node_modules exists |
| `additionalArgs` | string | `""` | Additional arguments for install command |

## Examples

### Force specific package manager

```json
{
"features": {
"ghcr.io/helpers4/devcontainer/package-auto-install:1": {
"packageManager": "pnpm"
}
}
}
```

### Use npm ci explicitly

```json
{
"features": {
"ghcr.io/helpers4/devcontainer/package-auto-install:1": {
"packageManager": "npm",
"command": "ci"
}
}
}
```

### Skip if node_modules exists (useful for rebuilds)

```json
{
"features": {
"ghcr.io/helpers4/devcontainer/package-auto-install:1": {
"skipIfNodeModulesExists": true
}
}
}
```

### Pass additional arguments

```json
{
"features": {
"ghcr.io/helpers4/devcontainer/package-auto-install:1": {
"additionalArgs": "--ignore-scripts"
}
}
}
```

### Custom working directory

```json
{
"features": {
"ghcr.io/helpers4/devcontainer/package-auto-install:1": {
"workingDirectory": "/workspace/frontend"
}
}
}
```

## How It Works
Corepack Support (Node 24+)

If your `package.json` contains a `packageManager` field (e.g., `"packageManager": "pnpm@9.0.0"`):

1. The feature checks if corepack is available
2. If not, it installs corepack globally with `npm install -g corepack`
3. Enables corepack with `corepack enable`
4. Corepack then automatically installs and uses the exact package manager version specified

This is particularly important for Node 24+ where corepack is no longer included by default.

###
### Package Manager Detection

The feature detects the package manager in this order:

1. **From `packageManager` field in package.json** (highest priority)
- Example: `"packageManager": "pnpm@9.0.0"` → uses **pnpm**
- This is the most reliable and modern approach
2. **From lockfiles**:
- `pnpm-lock.yaml` → uses **pnpm**
- `yarn.lock` → uses **yarn**
- `package-lock.json` → uses **npm**
3. **Default**: Falls back to **npm** if nothing is detected

### Command Selection (when `command: "auto"`)

For each package manager:

- **npm**: Uses `npm ci` if package-lock.json exists, otherwise `npm install`
- **pnpm**: Uses `pnpm install --frozen-lockfile` if pnpm-lock.yaml exists, otherwise `pnpm install`
- **yarn**:
- Yarn 2+: Uses `yarn install --immutable` if yarn.lock exists
- Yarn 1.x: Uses `yarn install --frozen-lockfile` if yarn.lock exists

### CI Mode

The feature sets `CI=true` environment variable, which:

- **pnpm**: Automatically removes old node_modules without prompting
- **npm**: Enables strict mode in `npm ci`
- **yarn**: Enables immutable installs

## Execution Order

The feature uses `installsAfter` to ensure it runs after:
- `ghcr.io/devcontainers/features/common-utils`
- `ghcr.io/devcontainers/features/node`

The actual package installation runs in `postCreateCommand`, which is the last lifecycle hook after all features are installed.

## Troubleshooting

### Installation not running

Check that:
- `package.json` exists in the working directory
- The package manager is installed (use node feature or appropriate base image)

### Wrong package manager detected

Force the package manager explicitly:

```json
{
"features": {
"ghcr.io/helpers4/devcontainer/package-auto-install:1": {
"packageManager": "pnpm"
}
}
}
```

### Installation fails

Check the container logs during creation. The feature will show the exact command being run and any errors.

### Need to debug

You can manually run the installation script:

```bash
/usr/local/bin/devcontainer-package-install
```

## Future Enhancements

Planned features:
- Support for monorepos (multiple package.json locations)
- Custom post-install scripts
- Conditional installation based on file changes
- Cache optimization

## License

AGPL-3.0 - See LICENSE file for details
56 changes: 56 additions & 0 deletions src/package-auto-install/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"id": "package-auto-install",
"version": "1.0.0",
"name": "Automatic Package Installation",
"description": "Automatically detects and runs npm/yarn/pnpm install in non-interactive mode after container creation.",
"documentationURL": "https://github.com/helpers4/devcontainer/tree/main/src/package-auto-install",
"options": {
"command": {
"type": "string",
"enum": [
"install",
"ci",
"auto"
],
"default": "auto",
"description": "Installation command to use: 'install', 'ci' (npm ci/pnpm install --frozen-lockfile), or 'auto' to detect"
},
"packageManager": {
"type": "string",
"enum": [
"auto",
"npm",
"yarn",
"pnpm"
],
"default": "auto",
"description": "Package manager to use. 'auto' detects based on lockfile"
},
"workingDirectory": {
"type": "string",
"default": "/workspaces/${localWorkspaceFolderBasename}",
"description": "Directory where to run the install command"
},
"skipIfNodeModulesExists": {
"type": "boolean",
"default": false,
"description": "Skip installation if node_modules directory already exists"
},
"additionalArgs": {
"type": "string",
"default": "",
"description": "Additional arguments to pass to the install command"
}
},
"postCreateCommand": "/usr/local/bin/devcontainer-package-install",
"containerEnv": {
"CI": "true"
},
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils",
"ghcr.io/devcontainers/features/node",
"ghcr.io/helpers4/devcontainer/angular-dev",
"ghcr.io/helpers4/devcontainer/vite-plus",
"ghcr.io/helpers4/devcontainer/local-mounts"
]
}
Loading