|
| 1 | +# DevContainer Configuration Guide |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +This project uses a DevContainer for consistent local development across all team members and CI/CD environments. |
| 6 | + |
| 7 | +- **Image Build**: `.devcontainer/Dockerfile` (based on `mcr.microsoft.com/devcontainers/dotnet:1-10.0-noble`) |
| 8 | +- **Features**: Node.js 24+ and GitHub CLI |
| 9 | +- **Post-create bootstrap**: Restores NuGet packages, runs frontend `npm ci`, and builds the solution |
| 10 | + |
| 11 | +## Git Credentials Setup |
| 12 | + |
| 13 | +I did this to avoid getting commits from the wrong user. This is optional for you. If you have the straight forward setup, you don't need to mount ~/.ssh as the Forwarding should work fine. This is just for the edge case of having multiple GitHub accounts and needing to use different SSH keys for each. |
| 14 | + |
| 15 | +The DevContainer is configured for SSH-based Git access using two GitHub accounts (personal and company). The host `~/.ssh` directory is mounted read-only at `/root/.ssh-host` and copied to `/root/.ssh` with correct permissions during container creation. |
| 16 | + |
| 17 | +### Why the copy step? |
| 18 | + |
| 19 | +SSH refuses to use a config file that is world-writable, which is common when `~/.ssh` is mounted from a Windows host filesystem. Mounting to a staging path and copying to `/root/.ssh` with `chmod 600`/`700` fixes this without modifying the host files. |
| 20 | + |
| 21 | +### Multi-account SSH config |
| 22 | + |
| 23 | +The host `~/.ssh/config` defines two named GitHub hosts: |
| 24 | + |
| 25 | +``` |
| 26 | +Host github.com-personal |
| 27 | + HostName github.com |
| 28 | + IdentityFile ~/.ssh/youruser_github |
| 29 | +
|
| 30 | +Host github.com |
| 31 | + HostName github.com |
| 32 | + IdentityFile ~/.ssh/yourcompany_github |
| 33 | +``` |
| 34 | + |
| 35 | +Use the host alias matching the account when cloning: |
| 36 | + |
| 37 | +```bash |
| 38 | +# Personal repos |
| 39 | +git clone git@github.com-personal:your-username/your-repo.git |
| 40 | + |
| 41 | +# Company repos |
| 42 | +git clone git@github.com:omnitech-org/your-repo.git |
| 43 | +``` |
| 44 | + |
| 45 | +Verify both accounts inside the container: |
| 46 | + |
| 47 | +```bash |
| 48 | +ssh -T git@github.com-personal |
| 49 | +ssh -T git@github.com |
| 50 | +``` |
| 51 | + |
| 52 | +### Prerequisites (one-time host setup) |
| 53 | + |
| 54 | +1. SSH keys for both accounts must exist in `~/.ssh` on the host machine |
| 55 | +2. Both public keys must be registered in the respective GitHub accounts ([GitHub SSH settings](https://github.com/settings/keys)) |
| 56 | +3. The `~/.ssh/config` file on the host must define the `github.com-personal` host alias as shown above |
| 57 | + |
| 58 | +## Environment Variables |
| 59 | + |
| 60 | +The container exports these environment variables: |
| 61 | + |
| 62 | +- `NODE_ENV=development` — Frontend runs in development mode (hot reload, source maps) |
| 63 | +- `PATH` — Includes `/usr/local/share/dotnet-tools` and `/root/.dotnet/tools` for CLI tools (CSharpier, Aspire, etc.) |
| 64 | + |
| 65 | +## Post-Create Setup |
| 66 | + |
| 67 | +The `postCreateCommand` runs automatically after container creation: |
| 68 | + |
| 69 | +1. `dotnet restore BikeTracking.slnx` |
| 70 | +2. `npm ci --prefix src/BikeTracking.Frontend` |
| 71 | +3. `dotnet build BikeTracking.slnx` |
| 72 | + |
| 73 | +SDK/tool installation (required .NET SDK, CSharpier, Aspire CLI) is baked into the image build in `.devcontainer/Dockerfile`, not installed at container start. |
| 74 | + |
| 75 | +**Output**: Terminal shows progress; container is ready when build succeeds. |
| 76 | + |
| 77 | +## Debugging & Troubleshooting |
| 78 | + |
| 79 | +### Git commands fail inside container |
| 80 | + |
| 81 | +**Symptom**: `git clone` returns "Permission denied" or authentication errors |
| 82 | + |
| 83 | +**Solution**: |
| 84 | +- Verify SSH key exists on host: `ls ~/.ssh/id_ed25519` (or `id_rsa`) |
| 85 | +- Verify key is added to GitHub: https://github.com/settings/keys |
| 86 | +- Inside container, test connectivity: `ssh -T git@github.com` |
| 87 | +- If still failing, use Git credential helper instead: `git config --global credential.helper store` |
| 88 | + |
| 89 | +### SSH mount not working (Windows) |
| 90 | + |
| 91 | +**Symptom**: `ssh -T git@github.com` returns "ssh: connect to host... refused" |
| 92 | + |
| 93 | +**Solution**: |
| 94 | +- Ensure OpenSSH server is running on Windows (Control Panel → Optional Features → OpenSSH Server) |
| 95 | +- Or use Git credential helper (`credential.helper store`) instead |
| 96 | +- SSH mounting is most reliable on macOS/Linux; Windows users should consider credential helper |
| 97 | + |
| 98 | +### Container build fails |
| 99 | + |
| 100 | +**Symptom**: "failed to solve with frontend dockerfile.v0" |
| 101 | + |
| 102 | +**Solution**: |
| 103 | +- Rebuild container: `Ctrl+Shift+P` → "Dev Containers: Rebuild Container" |
| 104 | +- Check Docker Desktop is running (Windows/macOS) |
| 105 | +- Check disk space (DevContainer images are ~2-3 GB) |
| 106 | + |
| 107 | +### Node modules or NuGet packages missing |
| 108 | + |
| 109 | +**Symptom**: `npm: command not found` or `dotnet: command not found` |
| 110 | + |
| 111 | +**Solution**: |
| 112 | +- Ensure `postCreateCommand` completed successfully (check terminal output) |
| 113 | +- Manually run: `npm ci --prefix src/BikeTracking.Frontend` |
| 114 | +- Manually run: `dotnet restore` from repo root |
| 115 | + |
| 116 | +## Security |
| 117 | + |
| 118 | +### SSH Keys |
| 119 | + |
| 120 | +- SSH keys are mounted **read-only** — cannot be modified inside container |
| 121 | +- Private keys never leave host machine |
| 122 | +- Pre-commit hooks prevent accidental credential commits |
| 123 | + |
| 124 | +### Git Credentials |
| 125 | + |
| 126 | +- Use SSH keys or GitHub CLI authentication (recommended) |
| 127 | +- Never commit credentials to repository |
| 128 | +- `.gitignore` prevents `git config --global --show-origin credential.helper` output |
| 129 | +- For CI/CD, use GitHub deploy keys or `GITHUB_TOKEN` environment variable |
| 130 | + |
| 131 | +Note the devcontainer.json setup with |
| 132 | + |
| 133 | +"remoteEnv": { |
| 134 | + "PATH": "${containerEnv:PATH}:/usr/local/share/dotnet-tools:/root/.dotnet/tools", |
| 135 | + "SSH_AUTH_SOCK": "/ssh-agent" |
| 136 | + }, |
| 137 | + "mounts": [ |
| 138 | + "source=${env:SSH_AUTH_SOCK},target=/ssh-agent,type=bind", |
| 139 | + "source=/mnt/c/Users/klogan/.ssh,target=/root/.ssh-host,type=bind,readonly" |
| 140 | + ] |
| 141 | + |
| 142 | +### Secrets Management |
| 143 | + |
| 144 | +- Local development: Use .NET User Secrets or environment variables |
| 145 | +- CI/CD: Use GitHub repository secrets or Azure Key Vault |
| 146 | +- DevContainer environment variables in `devcontainer.json` are for non-sensitive values only |
| 147 | + |
| 148 | +## Advanced Customization |
| 149 | + |
| 150 | +### Adding VS Code Extensions |
| 151 | + |
| 152 | +Edit `devcontainer.json`, add extension ID to `customizations.vscode.extensions`: |
| 153 | + |
| 154 | +```json |
| 155 | +"extensions": [ |
| 156 | + "GitHub.Copilot", |
| 157 | + "eamodio.gitlens" |
| 158 | +] |
| 159 | +``` |
| 160 | + |
| 161 | +Rebuild container: `Ctrl+Shift+P` → "Dev Containers: Rebuild Container" |
| 162 | + |
| 163 | +### Changing Node.js or .NET Version |
| 164 | + |
| 165 | +- Change **Node.js** in `devcontainer.json` features: |
| 166 | + |
| 167 | +```json |
| 168 | +"features": { |
| 169 | + "ghcr.io/devcontainers/features/node:1": { |
| 170 | + "version": "22" // Change to different Node version |
| 171 | + } |
| 172 | +} |
| 173 | +``` |
| 174 | + |
| 175 | +- Change **.NET SDK** in `.devcontainer/Dockerfile` by updating `REQUIRED_DOTNET_SDK_VERSION`, then rebuild the container. |
| 176 | + |
| 177 | +### Mounting Additional Volumes |
| 178 | + |
| 179 | +Edit `mounts` array to add more host directories (e.g., for shared data): |
| 180 | + |
| 181 | +```json |
| 182 | +"mounts": [ |
| 183 | + "source=${localEnv:HOME}${localEnv:USERPROFILE}/.ssh,target=/root/.ssh,readonly", |
| 184 | + "source=/path/on/host,target=/path/in/container" |
| 185 | +] |
| 186 | +``` |
| 187 | + |
| 188 | +## Resources |
| 189 | + |
| 190 | +- [Dev Containers Documentation](https://containers.dev/) |
| 191 | +- [GitHub SSH Keys Guide](https://docs.github.com/en/authentication/connecting-to-github-with-ssh) |
| 192 | +- [GitHub CLI Documentation](https://cli.github.com/) |
0 commit comments