From bed4cbc1d4550266ecc9f4330ece01a5abdecca1 Mon Sep 17 00:00:00 2001 From: ejacqu22 <2057151+etiennej70@users.noreply.github.com> Date: Thu, 24 Sep 2026 16:38:31 +0200 Subject: [PATCH 1/3] chore(config): add devcontainer setup --- .devcontainer/Dockerfile | 5 + .devcontainer/certs/.gitkeep | 3 + .devcontainer/devcontainer-lock.json | 19 +++ .devcontainer/devcontainer.json | 56 +++++++ .devcontainer/post-create.sh | 19 +++ .devcontainer/post-start.sh | 34 ++++ .devcontainer/scripts/dev-services.sh | 155 ++++++++++++++++++ .devcontainer/scripts/export-host-ca-certs.sh | 58 +++++++ .gitattributes | 5 + .github/dependabot.yml | 15 +- .github/workflows/devcontainer.yml | 41 +++++ .gitignore | 4 + docker-compose.yml | 7 +- docs/src/contributing/development-setup.md | 139 +++++++++++++++- src/main/resources/application-local.yml | 3 + 15 files changed, 559 insertions(+), 4 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/certs/.gitkeep create mode 100644 .devcontainer/devcontainer-lock.json create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/post-create.sh create mode 100644 .devcontainer/post-start.sh create mode 100644 .devcontainer/scripts/dev-services.sh create mode 100644 .devcontainer/scripts/export-host-ca-certs.sh create mode 100644 .gitattributes create mode 100644 .github/workflows/devcontainer.yml diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..c333a434 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,5 @@ +FROM mcr.microsoft.com/devcontainers/java:3-25-trixie@sha256:290d873937f3684458fc150a0ec968d6e54dc3d41c2374e3ea5a16c2689ce6d2 + +COPY certs/ /usr/local/share/ca-certificates/idp/ + +RUN update-ca-certificates diff --git a/.devcontainer/certs/.gitkeep b/.devcontainer/certs/.gitkeep new file mode 100644 index 00000000..c5666aa7 --- /dev/null +++ b/.devcontainer/certs/.gitkeep @@ -0,0 +1,3 @@ +# Keeps the optional certificate directory in the Docker build context. +# Populated automatically per-host by scripts/export-host-ca-certs.sh +# (initializeCommand); contents are gitignored and never committed. diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json new file mode 100644 index 00000000..d02eddda --- /dev/null +++ b/.devcontainer/devcontainer-lock.json @@ -0,0 +1,19 @@ +{ + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:4.1.1": { + "version": "4.1.1", + "resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:2b44bcb32e75d5a3c028479f7f957cf7d5e42d81761373025a8d501dd3748f88", + "integrity": "sha256:2b44bcb32e75d5a3c028479f7f957cf7d5e42d81761373025a8d501dd3748f88" + }, + "ghcr.io/devcontainers/features/github-cli:1.1.2": { + "version": "1.1.2", + "resolved": "ghcr.io/devcontainers/features/github-cli@sha256:7c409bf6316ffd85f04fd92fba85a744282639e603417dd4796409866bdb6805", + "integrity": "sha256:7c409bf6316ffd85f04fd92fba85a744282639e603417dd4796409866bdb6805" + }, + "ghcr.io/va-h/devcontainers-features/uv:1.1.4": { + "version": "1.1.4", + "resolved": "ghcr.io/va-h/devcontainers-features/uv@sha256:a15737142539d150ef4d358e2d6a7424a0a2f3dc43b29a3aa1b50162a8b11bc1", + "integrity": "sha256:a15737142539d150ef4d358e2d6a7424a0a2f3dc43b29a3aa1b50162a8b11bc1" + } + } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..cf0f1f1c --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,56 @@ +{ + "name": "Internal-Developer-Platform", + "initializeCommand": ["bash", ".devcontainer/scripts/export-host-ca-certs.sh"], + "build": { + "dockerfile": "Dockerfile", + "context": "." + }, + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:4.1.1": { + "moby": false, + "dockerDashComposeVersion": "v2" + }, + "ghcr.io/devcontainers/features/github-cli:1.1.2": {}, + "ghcr.io/va-h/devcontainers-features/uv:1.1.4": {} + }, + "remoteUser": "vscode", + "updateRemoteUserUID": true, + "containerEnv": { + "UV_NATIVE_TLS": "true" + }, + "hostRequirements": { + "cpus": 4, + "memory": "8gb" + }, + "mounts": [ + "source=idp-core-m2,target=/home/vscode/.m2,type=volume", + "source=idp-core-dind,target=/var/lib/docker,type=volume" + ], + "customizations": { + "vscode": { + "extensions": ["vscjava.vscode-java-pack"], + "settings": { + "java.autobuild.enabled": true + } + } + }, + "forwardPorts": [8084, 8000, 5437], + "portsAttributes": { + "8084": { + "label": "Spring Boot" + }, + "8000": { + "label": "Documentation", + "onAutoForward": "openPreview" + }, + "5437": { + "label": "PostgreSQL", + "onAutoForward": "silent" + } + }, + "otherPortsAttributes": { + "onAutoForward": "ignore" + }, + "postCreateCommand": "bash .devcontainer/post-create.sh", + "postStartCommand": "bash .devcontainer/post-start.sh" +} diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh new file mode 100644 index 00000000..db60cb1c --- /dev/null +++ b/.devcontainer/post-create.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +sudo apt-get update +sudo apt-get install --yes --no-install-recommends postgresql-client +sudo rm -rf /var/lib/apt/lists/* + +mkdir -p /home/vscode/.m2 +sudo chown -R vscode:vscode /home/vscode/.m2 + +uv sync --frozen --directory docs +uv tool install pre-commit +pre-commit install + +# Remove any stale target/ (e.g. from a prebuild on an older commit) so +# Maven's timestamp-based staleness check can't skip recompiling sources +# that were added/changed since it was generated. +./mvnw -B -ntp clean +./mvnw -B -ntp dependency:go-offline diff --git a/.devcontainer/post-start.sh b/.devcontainer/post-start.sh new file mode 100644 index 00000000..cadf9e6b --- /dev/null +++ b/.devcontainer/post-start.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$(realpath "$0")")/.." +services=(bash .devcontainer/scripts/dev-services.sh) +result=0 + +"${services[@]}" start docs || result=1 + +wait_for() { + for attempt in $(seq 1 30); do + if "$@" >/dev/null 2>&1; then + return 0 + fi + sleep 1 + done + return 1 +} + +if ! wait_for docker info; then + echo "Docker daemon did not become ready; app startup skipped" >&2 + result=1 +elif ! docker compose up -d postgres; then + echo "PostgreSQL container failed to start; app startup skipped" >&2 + result=1 +elif ! wait_for pg_isready -h localhost -p 5437 -U idpcore -d idpcore; then + echo "PostgreSQL did not become ready on localhost:5437; app startup skipped" >&2 + result=1 +else + "${services[@]}" start app || result=1 +fi + +"${services[@]}" status all || result=1 +exit "$result" diff --git a/.devcontainer/scripts/dev-services.sh b/.devcontainer/scripts/dev-services.sh new file mode 100644 index 00000000..4109445f --- /dev/null +++ b/.devcontainer/scripts/dev-services.sh @@ -0,0 +1,155 @@ +#!/usr/bin/env bash +# Manages the "docs" and "app" dev services (not PostgreSQL, which is owned by +# docker-compose). Both already watch/reload themselves (zensical serve, +# Spring DevTools) so this only handles the one-time detached launch, health +# gating, and teardown. Usage: +# dev-services.sh [docs|app|all] +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +root_dir="$(cd "$script_dir/../.." && pwd)" + +# Isolates state per checkout so multiple clones on the same host don't collide. +export IDP_STATE_DIR="/tmp/idp-dev-services-$(id -u)-$(printf '%s' "$root_dir" | sha256sum | cut -c1-12)" +mkdir -p -m 700 "$IDP_STATE_DIR" + +declare -A PORT=([docs]=8000 [app]=8084) +declare -A URL_PATH=([docs]=/ [app]=/actuator/health) +declare -A READY_TIMEOUT=([docs]=120 [app]=300) +declare -A COMMAND=( + [docs]="uv run --frozen --directory docs zensical serve --dev-addr 0.0.0.0:8000" + [app]="$root_dir/mvnw -B -ntp spring-boot:run -Dspring-boot.run.profiles=local" +) + +port_occupied() { + (exec 3<>"/dev/tcp/127.0.0.1/$1") 2>/dev/null +} + +# Spring Boot's default actuator health mapping is 200 only when UP (503 otherwise). +is_healthy() { + local name="$1" url code + url="http://127.0.0.1:${PORT[$name]}${URL_PATH[$name]}" + code="$(curl --noproxy '*' -sS -o /dev/null -w '%{http_code}' "$url" 2>/dev/null || true)" + [ "$code" = "200" ] +} + +# Each service is launched under job control so it gets its own process +# group (pgid == its pid): signalling "-$pid" reaches the whole group. +running_pid() { + local pid + pid="$(cat "$IDP_STATE_DIR/$1.pid" 2>/dev/null)" || return 1 + [ -n "$pid" ] && kill -0 "-$pid" 2>/dev/null && echo "$pid" +} + +svc_status() { + local name="$1" log="$IDP_STATE_DIR/$name.log" + local url="http://127.0.0.1:${PORT[$name]}${URL_PATH[$name]}" + if running_pid "$name" >/dev/null; then + if is_healthy "$name"; then + echo "$name: ready, $url; log: $log" + return 0 + fi + echo "$name: starting or unhealthy; log: $log" + return 1 + fi + if port_occupied "${PORT[$name]}"; then + echo "$name: port ${PORT[$name]} is externally owned; left untouched" + else + echo "$name: stopped; log: $log" + fi + return 1 +} + +svc_start() { + local name="$1" deadline + ( + flock -x 200 + if running_pid "$name" >/dev/null || port_occupied "${PORT[$name]}"; then + exit 0 + fi + rm -f "$IDP_STATE_DIR/$name.pid" + # Job control (not setsid, which forks a copy under bash job control and + # would make $! point at the wrong, short-lived PID) puts this in its own + # process group so it doesn't share ours. Close the lock fd on the + # backgrounded command itself: it outlives this subshell and would + # otherwise keep the flock held forever through its inherited copy. + set -m + bash -c "exec ${COMMAND[$name]}" >"$IDP_STATE_DIR/$name.log" 2>&1 200>&- & + echo "$!" >"$IDP_STATE_DIR/$name.pid" + disown + ) 200>"$IDP_STATE_DIR/$name.lock" + + if ! running_pid "$name" >/dev/null; then + if port_occupied "${PORT[$name]}"; then + echo "$name: port ${PORT[$name]} already occupied; skipping managed launch" + return 0 + fi + echo "$name: failed to launch; log: $IDP_STATE_DIR/$name.log" >&2 + return 1 + fi + + deadline=$((SECONDS + READY_TIMEOUT[$name])) + while [ "$SECONDS" -lt "$deadline" ]; do + is_healthy "$name" && { svc_status "$name"; return $?; } + running_pid "$name" >/dev/null || break + sleep 0.25 + done + echo "$name: failed to become ready; log: $IDP_STATE_DIR/$name.log" >&2 + tail -n 30 "$IDP_STATE_DIR/$name.log" >&2 2>/dev/null || true + svc_stop "$name" >/dev/null 2>&1 || true + return 1 +} + +svc_stop() { + local name="$1" pid + if pid="$(running_pid "$name")" && [ -n "$pid" ]; then + kill -TERM "-$pid" 2>/dev/null || true + for _ in $(seq 1 50); do + kill -0 "-$pid" 2>/dev/null || break + sleep 0.1 + done + kill -0 "-$pid" 2>/dev/null && kill -KILL "-$pid" 2>/dev/null || true + echo "$name: stopped managed process" + fi + rm -f "$IDP_STATE_DIR/$name.pid" + if port_occupied "${PORT[$name]}"; then + echo "$name: port ${PORT[$name]} still occupied; left untouched" + return 1 + fi + return 0 +} + +svc_logs() { + local name="$1" log="$IDP_STATE_DIR/$name.log" + echo "$name: $log" + [ -f "$log" ] && tail -n 80 "$log" + return 0 +} + +usage() { + echo "Usage: $(basename "$0") [docs|app|all]" >&2 + exit 2 +} + +action="${1:-}" +target="${2:-all}" +case "$action" in start | stop | restart | status | logs) ;; *) usage ;; esac +case "$target" in docs | app | all) ;; *) usage ;; esac + +names=(docs app) +[ "$target" != "all" ] && names=("$target") + +status=0 +for name in "${names[@]}"; do + case "$action" in + start) svc_start "$name" || status=1 ;; + stop) svc_stop "$name" || status=1 ;; + restart) + svc_stop "$name" || true + svc_start "$name" || status=1 + ;; + status) svc_status "$name" || status=1 ;; + logs) svc_logs "$name" || status=1 ;; + esac +done +exit "$status" diff --git a/.devcontainer/scripts/export-host-ca-certs.sh b/.devcontainer/scripts/export-host-ca-certs.sh new file mode 100644 index 00000000..2eeccfbe --- /dev/null +++ b/.devcontainer/scripts/export-host-ca-certs.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# Stages any corporate/proxy CA certificates trusted by the *host* machine into +# .devcontainer/certs/ so the container can trust the same TLS-inspecting +# proxy (Zscaler, Netskope, custom internal CA, ...) during the image build +# (docker-in-docker's github.com fetch, apt, etc.). +# +# Runs on the host via devcontainer.json's "initializeCommand", before every +# build. Never commits anything: .devcontainer/certs/* is gitignored, and this +# script wipes/repopulates it fresh on each run so it always reflects the +# current host, whoever/whatever machine that is. +set -uo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +certs_dir="$(cd "$script_dir/.." && pwd)/certs" + +mkdir -p "$certs_dir" +find "$certs_dir" -maxdepth 1 -type f ! -name '.gitkeep' -delete 2>/dev/null + +copy_source() { + local src="$1" + if [ -f "$src" ]; then + cp "$src" "$certs_dir/" 2>/dev/null + elif [ -d "$src" ]; then + find "$src" -maxdepth 2 -type f \( -name '*.crt' -o -name '*.pem' \) 2>/dev/null | + while IFS= read -r f; do cp "$f" "$certs_dir/" 2>/dev/null; done + fi +} + +# Explicit opt-in override: point at your own cert/bundle for anything the +# auto-detection below doesn't recognize. +if [ -n "${DEVCONTAINER_EXTRA_CA_CERTS:-}" ]; then + copy_source "$DEVCONTAINER_EXTRA_CA_CERTS" +fi + +# Best-effort auto-detection per host OS. Never fatal: an empty certs/ just +# means the build proceeds without extra trust, same as today. +case "$(uname -s 2>/dev/null)" in +Darwin) + # Dump the System keychain; duplicates of certs already in the image are harmless. + if command -v security >/dev/null 2>&1; then + security find-certificate -a -p /Library/Keychains/System.keychain 2>/dev/null | + awk -v dir="$certs_dir" ' + /-----BEGIN CERTIFICATE-----/ { n++; file = dir "/host-ca-" n ".pem" } + file { print > file } + ' + fi + ;; +Linux | MINGW* | MSYS* | CYGWIN*) + # Debian/Ubuntu and RHEL/Fedora both keep locally-added (i.e. corporate + # proxy installer) trust anchors separate from the default OS bundle here. + copy_source /usr/local/share/ca-certificates + copy_source /etc/pki/ca-trust/source/anchors + ;; +esac + +count=$(find "$certs_dir" -maxdepth 1 -type f ! -name '.gitkeep' 2>/dev/null | wc -l | tr -d ' ') +echo "[devcontainer] host CA export: ${count} certificate(s) staged in .devcontainer/certs (gitignored, build-context only)" +exit 0 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..68462c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +* text=auto eol=lf +*.sh text eol=lf +mvnw text eol=lf +*.cmd text eol=crlf +*.bat text eol=crlf diff --git a/.github/dependabot.yml b/.github/dependabot.yml index bb7b73ab..fb437bc2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -22,9 +22,22 @@ updates: # Docker dependencies - package-ecosystem: "docker" + directories: + - "/" + - "/.devcontainer" + schedule: + interval: "weekly" + day: "monday" + time: "05:00" + open-pull-requests-limit: 5 + pull-request-branch-name: + separator: "-" + + - package-ecosystem: "devcontainers" directory: "/" schedule: - interval: "daily" + interval: "weekly" + day: "monday" time: "05:00" open-pull-requests-limit: 5 pull-request-branch-name: diff --git a/.github/workflows/devcontainer.yml b/.github/workflows/devcontainer.yml new file mode 100644 index 00000000..d2ce93a1 --- /dev/null +++ b/.github/workflows/devcontainer.yml @@ -0,0 +1,41 @@ +--- +name: Dev Container + +on: + pull_request: + paths: + - '.devcontainer/**' + - '.gitattributes' + - 'docker-compose.yml' + - '.github/workflows/devcontainer.yml' + push: + branches: + - main + paths: + - '.devcontainer/**' + - '.gitattributes' + - 'docker-compose.yml' + - '.github/workflows/devcontainer.yml' + workflow_dispatch: + +jobs: + verify: + name: Build and verify dev container + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Build and verify dev container + uses: devcontainers/ci@513af61f4de4f75d37e4438f184ba4358f0fc1ca # v0.3.1900000450 + with: + runCmd: | + set -euo pipefail + trap 'bash .devcontainer/scripts/dev-services.sh stop all' EXIT + bash .devcontainer/post-start.sh + before=$(bash .devcontainer/scripts/dev-services.sh status all) + bash .devcontainer/scripts/dev-services.sh start all + after=$(bash .devcontainer/scripts/dev-services.sh status all) + [[ "$before" == "$after" ]] + bash .devcontainer/scripts/dev-services.sh stop all + ./mvnw -B -ntp verify diff --git a/.gitignore b/.gitignore index fee3e653..f879b115 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,10 @@ buildNumber.properties .env application-secret.* +# Local certificate authorities used by the dev container. +.devcontainer/certs/* +!.devcontainer/certs/.gitkeep + # BlueJ files *.ctxt diff --git a/docker-compose.yml b/docker-compose.yml index 13f77845..bbb95bd3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ services: postgres: - image: postgres:18 + image: postgres:18@sha256:86c951e05bf56c93d95d397747fb8820ac76cc3bedb78f43abd83eedbe3666ae environment: POSTGRES_USER: idpcore POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-idpcore_password} @@ -12,6 +12,11 @@ services: - ./scripts/init-extensions.sql:/docker-entrypoint-initdb.d/01-init-extensions.sql:ro networks: - postgres + healthcheck: + test: ["CMD-SHELL", "pg_isready -U idpcore -d idpcore"] + interval: 5s + timeout: 5s + retries: 12 restart: unless-stopped networks: diff --git a/docs/src/contributing/development-setup.md b/docs/src/contributing/development-setup.md index fdf2e9cb..f36e4f57 100644 --- a/docs/src/contributing/development-setup.md +++ b/docs/src/contributing/development-setup.md @@ -25,6 +25,141 @@ git --version pre-commit --version # If using pre-commit (recommended) ``` +## Development Container + +The repository includes a development container with Java 25, Maven, Docker, uv, +pre-commit, the GitHub command-line tool, and the PostgreSQL client. It starts the +PostgreSQL service, the documentation server, and Spring Boot automatically. + +### Supported Hosts + +| Host | Status | Requirement | +| ---- | ------ | ----------- | +| Debian with Docker Engine | Supported | Use a Docker daemon with root access | +| Windows with Docker Desktop | Supported | Keep the clone inside the WSL2 filesystem | +| WSL2 with Docker Engine | Supported | Use a Docker daemon with root access | +| macOS with Docker Desktop or OrbStack | Supported | Use a multi-architecture Docker runtime | +| Cloud development environment | Supported | Allocate at least 4 processors and 8 GB of memory | + +Docker rootless mode and other container management tools are not supported because the container uses +Docker-in-Docker for Testcontainers and the local Compose service. + +### Open the Container + +1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop/) + on Windows or macOS, or install Docker Engine on Debian or WSL2. +2. Install [VS Code](https://code.visualstudio.com/) and the + [development container extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers). +3. Open the repository in VS Code. +4. Run **development containers: reopen in container**. + +The first build downloads the pinned tool images and dependencies. Later +rebuilds reuse named volumes for the Maven and Docker caches. +The container sets `UV_NATIVE_TLS=true` so uv trusts the operating system's +certificate authorities, including local certificates for inspecting proxies. + +After startup, open for the API and + for the documentation site. PostgreSQL is available on +port `5437` with the local credentials used by the `local` Spring profile. +Check the **Ports** panel for the actual forwarded addresses: vs code can +choose another host port when the requested port is occupied. + +On Windows, clone the repository from a WSL2 terminal and open it from that +filesystem. Avoid `/mnt/c` because file sharing there significantly slows +Maven and file watching. + +### Watch and Reload + +Zensical runs with `serve`, which watches documentation sources and refreshes +the browser after changes. No separate watch command is required. + +Spring Boot starts with the `local` profile. Wait for VS Code to finish +importing the Maven project, then save your Java changes. Java auto-build +compiles them into `target/classes`, and DevTools restarts the app +after successful compilation. Resolve compilation errors in the **Problems** +panel before expecting a reload. Resource changes also trigger DevTools when +the build copies them to the class path. + +This workflow depends on VS Code Java auto-build. For terminal-only editing, +run `./mvnw compile` after changes. The separate `dev` profile retains its +existing `.reloadtrigger` requirement; the automatic launch does not use it. +Spring browser LiveReload is off for `local`; app restarts and +Zensical browser refresh remain enabled. + +### Service Controls + +Run these commands from the repository root: + +```bash +bash .devcontainer/scripts/dev-services.sh status all +bash .devcontainer/scripts/dev-services.sh logs app +bash .devcontainer/scripts/dev-services.sh stop app +bash .devcontainer/scripts/dev-services.sh start app +bash .devcontainer/scripts/dev-services.sh restart docs +``` + +Each command accepts `docs`, `app`, or `all`. The `all` option covers only docs +and Spring Boot; PostgreSQL remains under Docker Compose control. The `logs` +command prints recent output and the full log path. Use `tail -f` with that +path to follow output. Runtime state and logs live in a workspace-specific +directory under `/tmp`, outside the repository. + +Startup waits for HTTP readiness and reports failures with log output. The +first Spring Boot launch can take several minutes to compile. Docs startup +does not depend on database readiness. Repeated starts reuse managed services; +occupied ports belonging to other processes remain untouched. + +### Run Spring Boot Another Way + +Stop the managed app before launching it through VS Code **Run and Debug** or +your terminal: + +```bash +bash .devcontainer/scripts/dev-services.sh stop app +./mvnw spring-boot:run -Dspring-boot.run.profiles=local +``` + +Replace `local` with your required profile and supply its environment variables. +For VS Code, set `--spring.profiles.active=local` in your Java launch arguments, +or select your required profile instead. The container does not impose a +global Spring profile. + +The managed app stays stopped until you explicitly start it or restart the +container. Stop a manual launch with **Stop Debugging** or `Ctrl+C` before +running `start app` again. The helper never stops a manually launched process. + +### Troubleshoot Startup and Ports + +Use `status all` and `logs app` or `logs docs` to investigate startup failures. +For database failures, run `docker compose logs postgres`. Fix the reported +problem, then restart the affected service. Service commands are backed by +`supervisord`, which owns each process directly and never signals an +unrelated process. + +If a reload fails with unresolved generated mapper types after switching +branches or importing the project, stop the app, rebuild its generated +classes, then restart: + +```bash +bash .devcontainer/scripts/dev-services.sh stop app +./mvnw clean compile +bash .devcontainer/scripts/dev-services.sh start app +``` + +If IDE compilation errors persist, run **Java: clean Java Language Server +workspace** and wait for the Maven import to finish. + +Only ports `8000`, `8084`, and `5437` are automatically forwarded. Additional +listeners can belong to VS Code, Java tooling, or tests; a forwarded port does +not prove that an app is healthy. Run `ss -ltnp` to identify listeners +before stopping anything. Use **Stop Forwarding** to remove old entries from +the **Ports** panel, and forward debug ports manually when needed. + +After changing the container configuration, run **development containers: rebuild +container** to apply editor defaults and forwarding rules. Existing restored +forwarding entries might need removal once. Do not delete database volumes or +stop unrelated editor processes to clean up this list. + --- ## Clone Repository @@ -56,9 +191,9 @@ git remote add upstream https://github.com/Decathlon/internal-developer-platform ## Start the project -For your development setup, you can refer to the Getting started documentation here: [Getting Started](../getting-started/index.md) +For your development setup, you can refer to the getting started documentation here: [getting started](../getting-started/index.md) -Configure the application.yml security credentials and secrets locally using environment variables or a local, untracked configuration file. +Configure the app.yml security credentials and secrets locally using environment variables or a local, untracked configuration file. ### Pre-Commit Hooks (Optional) diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 49ef63b0..85ab5659 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -2,6 +2,9 @@ server: port: 8084 spring: + devtools: + livereload: + enabled: false datasource: url: jdbc:postgresql://localhost:5437/idpcore username: idpcore From 9a19097b5f609a904b902d5aeeb7b77e6142fa62 Mon Sep 17 00:00:00 2001 From: ejacqu22 <2057151+etiennej70@users.noreply.github.com> Date: Thu, 24 Sep 2026 17:00:34 +0200 Subject: [PATCH 2/3] fix(ci): fix GH actions permissions --- .github/workflows/devcontainer.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/devcontainer.yml b/.github/workflows/devcontainer.yml index d2ce93a1..f747b7a1 100644 --- a/.github/workflows/devcontainer.yml +++ b/.github/workflows/devcontainer.yml @@ -20,6 +20,8 @@ on: jobs: verify: + permissions: + contents: read name: Build and verify dev container runs-on: ubuntu-latest steps: From 89035f8c8b7dcc28604aa99b055c8dbbd4382461 Mon Sep 17 00:00:00 2001 From: Etienne JACQUOT <2057151+etiennej70@users.noreply.github.com> Date: Thu, 24 Sep 2026 17:18:25 +0200 Subject: [PATCH 3/3] Fix(review): apply suggestions Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Etienne JACQUOT <2057151+etiennej70@users.noreply.github.com> --- docs/src/contributing/development-setup.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/src/contributing/development-setup.md b/docs/src/contributing/development-setup.md index f36e4f57..f5492a3b 100644 --- a/docs/src/contributing/development-setup.md +++ b/docs/src/contributing/development-setup.md @@ -61,7 +61,7 @@ certificate authorities, including local certificates for inspecting proxies. After startup, open for the API and for the documentation site. PostgreSQL is available on port `5437` with the local credentials used by the `local` Spring profile. -Check the **Ports** panel for the actual forwarded addresses: vs code can +Check the **Ports** panel for the actual forwarded addresses: VS Code can choose another host port when the requested port is occupied. On Windows, clone the repository from a WSL2 terminal and open it from that @@ -132,9 +132,8 @@ running `start app` again. The helper never stops a manually launched process. Use `status all` and `logs app` or `logs docs` to investigate startup failures. For database failures, run `docker compose logs postgres`. Fix the reported -problem, then restart the affected service. Service commands are backed by -`supervisord`, which owns each process directly and never signals an -unrelated process. +problem, then restart the affected service. The helper launches and manages each +service in its own process group. If a reload fails with unresolved generated mapper types after switching branches or importing the project, stop the app, rebuild its generated @@ -193,7 +192,8 @@ git remote add upstream https://github.com/Decathlon/internal-developer-platform For your development setup, you can refer to the getting started documentation here: [getting started](../getting-started/index.md) -Configure the app.yml security credentials and secrets locally using environment variables or a local, untracked configuration file. +Configure the `application.yml` security credentials and secrets locally using +environment variables or a local, untracked configuration file. ### Pre-Commit Hooks (Optional)