Skip to content

Latest commit

 

History

History
276 lines (227 loc) · 11.8 KB

File metadata and controls

276 lines (227 loc) · 11.8 KB

TaskFlow — Development Environment Setup

How to get the full stack running locally: .NET 10 API + EF Core + SQL Server + SignalR on the backend, Vue 3 + Vite + Pinia + TypeScript on the frontend, plus the quality tooling (SonarCloud, ESLint, tests). Everything here runs without any Azure resources — local equivalents are used for SQL and SignalR.


1. Prerequisites

Tool Version Purpose Install
.NET SDK 10.0 Backend build/run/test https://dotnet.microsoft.com/download
Node.js 20 LTS Frontend (Vite) https://nodejs.org (or nvm install 20)
Git latest Version control https://git-scm.com
Docker Desktop latest Local SQL Server (recommended) https://docker.com
EF Core tools matches SDK Migrations dotnet tool install -g dotnet-ef
Java (JRE/JDK) 17 Required by SonarScanner https://adoptium.net
Sonar scanner latest Local static analysis dotnet tool install -g dotnet-sonarscanner
Azure CLI (optional) latest Cloud deploy / KV / SQL https://aka.ms/azcli

Recommended IDE / editors

  • Backend: Visual Studio 2022 (17.12+), JetBrains Rider, or VS Code + C# Dev Kit.
  • Frontend: VS Code + Vue - Official (Volar), ESLint, Prettier extensions.
  • Shared: EditorConfig extension so formatting rules apply in every editor.

Verify the toolchain:

dotnet --version      # 10.0.x
node --version        # v20.x
npm --version
docker --version
dotnet ef --version

Pinned for reproducibility: the repo root includes global.json (locks the .NET SDK band) and .nvmrc (locks Node to 20). Run nvm use in the repo; the SDK is auto-selected by global.json. This keeps every machine — and CI — on the same versions.


2. Repository layout

taskflow/
├── backend/
│   ├── src/
│   │   ├── Domain/          # entities — no dependencies
│   │   ├── Application/     # use cases, DTOs, interfaces
│   │   ├── Infrastructure/  # EF Core, repos, SignalR, external services
│   │   └── Api/             # controllers + Program.cs (composition root)
│   └── tests/               # xUnit unit + integration tests
├── frontend/
│   └── src/
│       ├── api/             # axios client + endpoints
│       ├── components/      # reusable UI
│       ├── views/           # routed pages
│       ├── stores/          # Pinia stores
│       ├── router/          # Vue Router + guards
│       └── types/           # shared TS types
├── infra/                   # Bicep IaC
├── .github/                 # workflows, dependabot
└── docs/

3. Backend — .NET 10 API

3a. Local database (pick one)

Option A — SQL Server in Docker (recommended): simplest is docker compose up -d (see §6). Manual equivalent:

docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=Your_strong_Pass123" \
  -p 1433:1433 --name taskflow-sql \
  -v taskflow-sqldata:/var/opt/mssql \
  -d mcr.microsoft.com/mssql/server:2022-latest

Apple Silicon (M-series Macs): the image above has no native ARM64 build — use Azure SQL Edge instead (same env vars/port): swap the image for mcr.microsoft.com/azure-sql-edge:latest. The -v named volume persists data across container restarts.

Local connection string:

Server=localhost,1433;Database=taskflowdb;User Id=sa;Password=Your_strong_Pass123;TrustServerCertificate=True;

Option B — SQL Server LocalDB (Windows only):

Server=(localdb)\MSSQLLocalDB;Database=taskflowdb;Trusted_Connection=True;

Cloud uses managed identity (no password). Local uses SQL auth — that's expected. Never commit local credentials; use user-secrets below.

3b. Local secrets (never committed)

The API reads ConnectionStrings:Default, Jwt:SigningKey, and SignalR config. Store them with the .NET Secret Manager:

cd backend/src/Api
dotnet user-secrets init
dotnet user-secrets set "ConnectionStrings:Default" "Server=localhost,1433;Database=taskflowdb;User Id=sa;Password=Your_strong_Pass123;TrustServerCertificate=True;"
dotnet user-secrets set "Jwt:SigningKey" "$(openssl rand -base64 48)"

Windows PowerShell (no openssl):

dotnet user-secrets set "Jwt:SigningKey" ([Convert]::ToBase64String((1..48 | % { Get-Random -Max 256 })))

3c. SignalR locally

Production runs Azure SignalR in Default mode, which uses normal server-side Hubs — so local in-process SignalR mirrors production exactly. No emulator, /negotiate shim, or upstream webhooks needed.

  • Locally, keep Azure:SignalR:Enabled=false (an app-implemented flag) so the API uses in-process ASP.NET Core SignalR: services.AddSignalR();
  • In the cloud the same Hub code runs through Azure SignalR via services.AddSignalR().AddAzureSignalR(); — the only difference is the connection string. Your Hub classes and client code are identical in both environments.

This dev/prod parity is the whole reason the infra uses Default mode rather than Serverless.

3d. Apply migrations + run

cd backend
dotnet dev-certs https --trust        # one-time: trust the local HTTPS dev cert
dotnet restore
dotnet ef database update --project src/Infrastructure --startup-project src/Api
dotnet run --project src/Api
  • API: https://localhost:7xxx · Swagger UI: /swagger
  • /health — liveness, DB-free (this is what the keep-warm pinger hits, so it never resumes a paused DB).
  • /health/ready — readiness, checks SQL (returns unhealthy if the database is down). Don't collapse the two into one DB-free check, or an outage looks healthy.

3e. Add a migration

dotnet ef migrations add <Name> --project src/Infrastructure --startup-project src/Api

Follow the expand-contract rule (see devops-setup.md §4): additive/backward-compatible changes only in a release; drops come in a later release.

3f. Backend tests

dotnet test backend
# with coverage (same as CI)
dotnet test backend --collect:"XPlat Code Coverage;Format=opencover"

4. Frontend — Vue 3 + Vite

4a. Environment variables

Vite exposes only vars prefixed VITE_. Create frontend/.env.local (git-ignored):

VITE_API_BASE_URL=https://localhost:7xxx
VITE_SIGNALR_HUB_URL=https://localhost:7xxx/hubs/board

⚠️ VITE_ variables are NOT secret. Vite inlines them into the client bundle shipped to the browser, so anyone can read them. Use them only for public config (URLs, feature flags). Never put a JWT key, API secret, or connection string in a VITE_ var.

Commit a frontend/.env.example with the same keys and placeholder values.

4b. Install + run

cd frontend
npm ci                # clean install from package-lock
npm run dev           # Vite dev server, hot reload → http://localhost:5173

4c. Stack & key libraries

  • Vite — dev server + build tool.
  • Vue 3 with <script setup> (Composition API).
  • Pinia — state (auth store, board store). Devtools-friendly.
  • Vue Router — routes + navigation guards (redirect unauthenticated).
  • Axiossrc/api/client.ts with an interceptor attaching the JWT and handling 401s.
  • @microsoft/signalr — real-time client; connects to VITE_SIGNALR_HUB_URL.
  • TypeScriptvue-tsc for type-checking.
  • Styling: Tailwind / PrimeVue / Vuetify (pick one, stay consistent).

4d. Scripts (package.json)

npm run dev        # start dev server
npm run build      # type-check + production build → dist/
npm run preview    # serve the production build locally
npm run lint       # ESLint
npm run format     # Prettier
npm run test       # Vitest (unit/component tests)

4e. Talking to the API in dev (CORS)

The API must allow the Vite origin. In Program.cs (Development), configure CORS in code:

builder.Services.AddCors(o => o.AddPolicy("dev", p => p
    .WithOrigins("http://localhost:5173")
    .AllowAnyHeader().AllowAnyMethod()
    .AllowCredentials()));   // required for SignalR

CORS is owned in code, not at the Azure App Service level (see devops-setup.md §6).

Alternative — skip CORS entirely in dev with a Vite proxy. The browser then talks same-origin to the Vite server, which forwards to the API (also avoids the self-signed-cert prompt):

// vite.config.ts
export default defineConfig({
  server: {
    proxy: {
      '/api':  { target: 'https://localhost:7xxx', changeOrigin: true, secure: false },
      '/hubs': { target: 'https://localhost:7xxx', changeOrigin: true, secure: false, ws: true }
    }
  }
})

5. Quality tooling

5a. SonarCloud — run analysis locally

Mirrors the CI quality gate. Needs Java 17 + a Sonar token.

export SONAR_TOKEN=<your-token>
dotnet sonarscanner begin \
  /k:"<project-key>" /o:"<org>" \
  /d:sonar.host.url="https://sonarcloud.io" \
  /d:sonar.token="$SONAR_TOKEN" \
  /d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml"
dotnet build backend -c Release
dotnet test backend --collect:"XPlat Code Coverage;Format=opencover"
dotnet sonarscanner end /d:sonar.token="$SONAR_TOKEN"

CI runs the same with sonar.qualitygate.wait=true, so a failing gate fails the build.

5b. Linting & formatting

  • Backend: .editorconfig at the repo root drives C# style; dotnet format to auto-fix.
  • Frontend: ESLint (eslint.config.js) + Prettier. Run npm run lint before pushing.
  • (Optional) Husky + lint-staged pre-commit hook to run dotnet format and npm run lint on staged files.

5c. CodeQL & Dependabot

These run in CI only (GitHub-hosted) — nothing to install locally. See .github/workflows/codeql.yml and .github/dependabot.yml.


6. Run the whole stack

Golden path — one command for dependencies (uses docker-compose.yml at the repo root):

docker compose up -d        # starts SQL Server with a persistent named volume

Then run the apps on the host:

cd backend  && dotnet run --project src/Api     # terminal 1  (or: dotnet watch)
cd frontend && npm run dev                       # terminal 2

Open http://localhost:5173. SignalR runs in-process locally (Default-mode parity with prod), so there's nothing extra to start.

Stop dependencies with docker compose down (add -v to also wipe the DB volume).


7. Environment variables reference

Backend (user-secrets in dev / App Settings + Key Vault in cloud)

Key Dev value Cloud value
ConnectionStrings:Default local SQL (sa) Authentication=Active Directory Managed Identity
Jwt:SigningKey user-secret Key Vault reference
Azure:SignalR:Enabled false (or emulator) true
Azure:SignalR:ConnectionString emulator string AuthType=azure.msi
APPLICATIONINSIGHTS_CONNECTION_STRING (empty) from Bicep

Frontend (.env.local)

Key Example
VITE_API_BASE_URL https://localhost:7xxx
VITE_SIGNALR_HUB_URL https://localhost:7xxx/hubs/board

8. Troubleshooting

Symptom Fix
dotnet ef not found dotnet tool install -g dotnet-ef; ensure ~/.dotnet/tools is on PATH
Cannot connect to SQL in Docker container running? docker ps; password meets complexity rules; port 1433 free
HTTPS dev cert warnings dotnet dev-certs https --trust
CORS error in browser API origin must list http://localhost:5173 with AllowCredentials
SignalR won't connect locally use in-process mode (Azure:SignalR:Enabled=false); check the hub route + JWT on the connection
Vite env var is undefined must be prefixed VITE_; restart dev server after editing .env.local
Sonar scanner fails to start Java 17 installed and on PATH
npm run build type errors run npm run lint and vue-tsc --noEmit to locate them