Skip to content

Commit 7e191c3

Browse files
aligneddevaligneddev
andauthored
Use Devcontainer to contain the AI Agents and make it easy to get started (#13)
* add devcontainer * github auth intructions with devcontainer * devcontainer working * devcontainer is working with a dockerfile * ssh working with my config file and SSH * documenation --------- Co-authored-by: aligneddev <aligneddev@github.com>
1 parent 44438b4 commit 7e191c3

9 files changed

Lines changed: 502 additions & 163 deletions

File tree

.devcontainer/DEVCONTAINER.md

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
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/)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# syntax=docker/dockerfile:1.7
2+
3+
FROM mcr.microsoft.com/devcontainers/dotnet:1-10.0-noble
4+
5+
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
6+
7+
ARG REQUIRED_DOTNET_SDK_VERSION=10.0.200
8+
9+
# Ensure the SDK version from global.json is available in the image.
10+
RUN if dotnet --list-sdks | grep -q "^${REQUIRED_DOTNET_SDK_VERSION}"; then \
11+
echo ".NET SDK ${REQUIRED_DOTNET_SDK_VERSION} already installed."; \
12+
else \
13+
echo "Installing .NET SDK ${REQUIRED_DOTNET_SDK_VERSION}..." && \
14+
curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh && \
15+
bash /tmp/dotnet-install.sh --version "${REQUIRED_DOTNET_SDK_VERSION}" --install-dir /usr/share/dotnet --no-path && \
16+
rm -f /tmp/dotnet-install.sh; \
17+
fi
18+
19+
# Install required CLI tools once at image build time.
20+
RUN mkdir -p /usr/local/share/dotnet-tools && \
21+
dotnet tool install csharpier --tool-path /usr/local/share/dotnet-tools
22+
23+
RUN curl -fsSL https://aspire.dev/install.sh | bash
24+
25+
ENV PATH="/usr/local/share/dotnet-tools:/root/.dotnet/tools:${PATH}"
26+

.devcontainer/devcontainer.json

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"name": "Bike Tracking - Full Stack",
3+
"build": {
4+
"dockerfile": "devcontainer.Dockerfile",
5+
"context": ".."
6+
},
7+
"features": {
8+
"ghcr.io/devcontainers/features/node:1": {
9+
"version": "24"
10+
},
11+
"ghcr.io/devcontainers/features/github-cli:1": {}
12+
},
13+
"containerEnv": {
14+
"NODE_ENV": "development"
15+
},
16+
"postCreateCommand": "mkdir -p /root/.ssh && cp /root/.ssh-host/* /root/.ssh/ && chmod 700 /root/.ssh && chmod 600 /root/.ssh/config /root/.ssh/aligneddev_github /root/.ssh/omnitech_github && chmod 644 /root/.ssh/*.pub /root/.ssh/known_hosts 2>/dev/null; dotnet dev-certs https --trust && dotnet restore BikeTracking.slnx && npm ci --prefix src/BikeTracking.Frontend && dotnet build BikeTracking.slnx",
17+
"customizations": {
18+
"vscode": {
19+
"extensions": [
20+
"ms-dotnettools.csharp",
21+
"ms-dotnettools.vscode-dotnet-runtime",
22+
"Ionide.Ionide-fsharp",
23+
"dbaeumer.vscode-eslint",
24+
"esbenp.prettier-vscode",
25+
"csharpier.csharpier-vscode",
26+
"ms-azuretools.vscode-docker",
27+
"ms-vscode.makefile-tools",
28+
"ms-vscode-remote.remote-containers",
29+
"ms-vscode-remote.remote-ssh",
30+
"microsoft-aspire.aspire-vscode",
31+
"ms-dotnettools.csdevkit",
32+
"eamodio.gitlens"
33+
],
34+
"settings": {
35+
"dotnet.defaultSolutionOrFolder": "${workspaceFolder}/BikeTracking.slnx",
36+
"[csharp]": {
37+
"editor.defaultFormatter": "csharpier.csharpier-vscode",
38+
"editor.formatOnSave": true
39+
},
40+
"[fsharp]": {
41+
"editor.defaultFormatter": "Ionide.Ionide-fsharp",
42+
"editor.formatOnSave": true
43+
},
44+
"[typescript]": {
45+
"editor.defaultFormatter": "esbenp.prettier-vscode",
46+
"editor.formatOnSave": true
47+
},
48+
"[json]": {
49+
"editor.defaultFormatter": "esbenp.prettier-vscode",
50+
"editor.formatOnSave": true
51+
},
52+
"eslint.validate": ["javascript", "typescript", "typescriptreact"]
53+
}
54+
}
55+
},
56+
"remoteUser": "root",
57+
"remoteEnv": {
58+
"PATH": "${containerEnv:PATH}:/usr/local/share/dotnet-tools:/root/.dotnet/tools",
59+
"SSH_AUTH_SOCK": "/ssh-agent"
60+
},
61+
"mounts": [
62+
"source=${env:SSH_AUTH_SOCK},target=/ssh-agent,type=bind",
63+
"source=/mnt/c/Users/klogan/.ssh,target=/root/.ssh-host,type=bind,readonly"
64+
]
65+
}

.github/dependabot.yml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
# To get started with Dependabot version updates, you'll need to specify which
2-
# package ecosystems to update and where the package manifests are located.
3-
# Please see the documentation for all configuration options:
4-
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5-
6-
version: 2
7-
updates:
8-
- package-ecosystem: "" # See documentation for possible values
9-
directory: "/" # Location of package manifests
10-
schedule:
11-
interval: "weekly"
1+
# To get started with Dependabot version updates, you'll need to specify which
2+
# package ecosystems to update and where the package manifests are located.
3+
# Please see the documentation for all configuration options:
4+
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5+
6+
version: 2
7+
updates:
8+
- package-ecosystem: "" # See documentation for possible values
9+
directory: "/" # Location of package manifests
10+
schedule:
11+
interval: "weekly"

0 commit comments

Comments
 (0)