diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..8e44fb02 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,15 @@ +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write", + "hooks": [ + { + "type": "command", + "command": "FILE=\"$CLAUDE_FILE_PATH\"; case \"$FILE\" in *.yaml|*.yml) npx --yes dclint \"$FILE\" 2>/dev/null || true ;; *.sh) shellcheck \"$FILE\" 2>/dev/null || true ;; esac" + } + ] + } + ] + } +} diff --git a/.gitignore b/.gitignore index c891d088..7fd9e47d 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,6 @@ generated/ **/.DS_Store .idea +*.swp +*.swo +.vscode/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..91b80d09 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,44 @@ +# rhdh-local + +RHDH Local runs Red Hat Developer Hub on your machine with Docker or Podman Compose — a fast inner loop for catalog entries, TechDocs, templates, app config, and dynamic plugin development. It is a testing and development tool only: not a supported product, not for production, not suitable for teams. + +Run `make help` for the full list of targets. Commands default to Podman — pass `TOOL=docker` for Docker. + +## Key conventions + +- **Branching**: target `dev` for features, docs, and dependency updates; `release-x.y` for bug fixes on a supported release. Do not target `main` — maintainers update it by merge and cherry-pick. +- **Both container tools are supported**: compose changes and documented instructions must work with Podman and Docker, and CI runs the matrix on both. Docs present the pair as TechDocs content tabs (`=== "Podman"` / `=== "Docker"`). +- **Docs are part of the product**: everything under `docs/` is rendered as TechDocs through `mkdocs.yaml`, so a new page also needs a `nav` entry. Restart `rhdh` to preview edits (the mount is read-only, copied into a writable workdir at startup). +- **User configuration is never committed**: `.env`, `configs/**/*.override.yaml`, `configs/**/*.local.yaml`, and `compose.override.yaml` are git-ignored. Change the tracked `*.example.yaml` counterparts instead. +- **`*.example.yaml` files are CI test inputs**: CI copies them verbatim into the corresponding override/local files, so they must parse and work standalone — treat them as code, not documentation. +- **Images and behavior are tuned through environment variables** in `.env`/`default.env`, not by editing compose files. +- **`make effective-compose` output can contain secrets** (it interpolates `.env`) — never paste it into a PR or issue. +- **Lint only covers git-tracked files**. Developer-specific `*.local.yaml` overrides are deliberately excluded. +- **Shell scripts use `set -euo pipefail`** and are checked by ShellCheck in CI — new scripts must follow the same pattern. +- **CI is path-filtered**: lint and test jobs skip when no relevant files changed. If you add a new file type or top-level path, check whether the changed-files filter in the CI workflow covers it — "CI passed" may mean "CI didn't run." + +## Architecture + +- **Overlay model**: `compose.yaml` is the only base file. Every overlay must be named alongside it — `make … OVERLAYS=…` always passes `-f compose.yaml` first. A local `compose.override.yaml` only applies when `OVERLAYS` is empty. +- **`OVERLAYS` must be consistent across commands**: if you start with `make start OVERLAYS="db orchestrator"`, pass the same `OVERLAYS` to `stop`, `restart`, `logs`, `effective-compose`, `list-services`, and any other target — otherwise the command operates on the base config only and ignores the overlay services. +- **Two-stage startup**: `install-dynamic-plugins` runs `prepare-and-install-dynamic-plugins.sh` as root, selects the plugin config, and extracts plugins into the `dynamic-plugins-root` and `extensions-catalog` volumes. `rhdh` then runs `wait-for-plugins-and-start.sh` as uid 1001, blocks until the installer's lock file is gone, and execs the Backstage backend. +- **Config layering** (last wins): `app-config.yaml` → `app-config.dynamic-plugins.yaml` → `generated/app-config.patched.yaml` → `app-config.db.yaml` (when `WITH_POSTGRES=true`) → `app-config.local.yaml`. +- **Dynamic plugin configuration replaces, it does not merge**: the selected plugin config file replaces the default plugin list entirely, so start from a copy of the default before adding or removing entries. +- **Orchestrator plugins are a separate step from the overlay**: `make start OVERLAYS=orchestrator` only adds the compose services — the orchestrator's dynamic plugins must be appended to the override file separately (see `orchestrator/README.md`). +- **Tests need a running instance**: scripts in `tests/` assert against a live RHDH through guest auth, not unit tests. They need `yq`, `jq`, and `curl`. +- **Lightspeed configs are generated from upstream** by a scheduled workflow — fix those files upstream rather than hand-editing them. + +## Plugin development + +Plugin development is a first-class use case. RHDH Local loads dynamic plugins at boot, so a rebuilt plugin runs in a real RHDH UI and backend without publishing to a registry. The full walkthrough is in `docs/rhdh-local-guide/plugins-guide.md`; the key constraints: + +- Keep the plugin's `dynamicPlugins` entry in `configs/app-config/app-config.local.yaml`, not in the dynamic plugins override file. +- Files in `local-plugins/` must be readable by the container (uid 1001). +- Exported plugins persist in the `dynamic-plugins-root` volume — run `make clean` before a fresh frontend iteration. + +## PR conventions + +- Conventional Commits: `fix:`, `feat:`, `chore:`, `ci:`, `docs:`, etc. +- Agent-assisted commits include an `Assisted-by: ` footer. +- Always follow the PR template in `.github/PULL_REQUEST_TEMPLATE.md` — keep all sections and either tick each checklist item or say why it does not apply. +- Report issues to Jira project RHIDP with Component: **RHDH Local**. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..43c994c2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..b4177fda --- /dev/null +++ b/Makefile @@ -0,0 +1,89 @@ +# RHDH Local — common developer tasks. Run `make help` for the list. +# +# TOOL container tool to use, default podman (TOOL=docker to use Docker) +# OVERLAYS overlays applied on top of compose.yaml, space-separated. +# Accepts the named overlays below or a path to any compose file, +# e.g. make start OVERLAYS="db orchestrator" +# DCLINT_IMAGE dclint image used by `make lint`, pinned to a release tag +# SHELLCHECK_IMAGE shellcheck image used by `make lint`, pinned to a release tag +# SERVICE service to act on for `make logs`, default all services +# +TOOL ?= podman +OVERLAYS ?= +SERVICE ?= + +# Named overlays, so nobody has to remember compose file names. +OVERLAY_NAMES := db corporate-proxy dynamic-plugins-root orchestrator +OVERLAY_db := compose-with-db.yaml +OVERLAY_corporate-proxy := compose-with-corporate-proxy.yaml +OVERLAY_dynamic-plugins-root := compose-dynamic-plugins-root.yaml +OVERLAY_orchestrator := orchestrator/compose.yaml + +unknown_overlays := $(filter-out $(OVERLAY_NAMES) %.yaml,$(OVERLAYS)) +ifneq ($(strip $(unknown_overlays)),) +$(error Unknown OVERLAYS entry: $(unknown_overlays) (use: $(OVERLAY_NAMES), or a *.yaml path)) +endif + +OVERLAY_FILES := $(foreach overlay,$(OVERLAYS),$(if $(filter %.yaml,$(overlay)),$(overlay),$(OVERLAY_$(overlay)))) +# Compose only auto-detects compose.yaml and compose.override.yaml when no -f is +# given, so naming an overlay means naming the base file too. +COMPOSE_ARGS := $(if $(strip $(OVERLAYS)),-f compose.yaml $(addprefix -f ,$(OVERLAY_FILES))) +COMPOSE := $(strip $(TOOL) compose $(COMPOSE_ARGS)) + +DCLINT_IMAGE ?= docker.io/zavoloklom/dclint:3.1.0 +SHELLCHECK_IMAGE ?= docker.io/koalaman/shellcheck:v0.11.0 +LINT_MOUNT := -v "$(CURDIR)":/app:ro,z -w /app + +.PHONY: help start stop restart logs ps list-services effective-compose plugins-install down clean reset lint + +.DEFAULT_GOAL := help + +help: ## Show this help + @grep -hE '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) \ + | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-17s\033[0m %s\n", $$1, $$2}' + @printf '\n OVERLAYS: %s (or a path to any compose file)\n' "$(OVERLAY_NAMES)" + @printf ' e.g. make start OVERLAYS="db orchestrator"\n' + @printf ' e.g. make logs SERVICE=rhdh\n' + +start: ## Start RHDH Local + $(COMPOSE) up -d + +stop: ## Stop RHDH only, leaving the containers in place + $(COMPOSE) stop rhdh + +restart: ## Restart RHDH only, for configuration changes + $(COMPOSE) stop rhdh + $(COMPOSE) start rhdh + +logs: ## Follow logs for all services, or SERVICE=rhdh for one + $(COMPOSE) logs -f $(SERVICE) + +ps: ## Show all containers, including stopped ones + $(COMPOSE) ps -a + +list-services: ## List the services defined by the compose files + $(COMPOSE) config --services + +effective-compose: ## Render the effective Compose config + $(COMPOSE) config + +plugins-install: ## Reinstall dynamic plugins, then restart RHDH + $(COMPOSE) run install-dynamic-plugins + $(COMPOSE) stop rhdh + $(COMPOSE) start rhdh + +down: ## Stop and remove all containers and the network, keeping volumes + $(COMPOSE) down + +clean: ## Stop everything and remove volumes + $(COMPOSE) down --volumes + +reset: ## Remove containers, volumes, and every pulled image + $(COMPOSE) down --volumes --rmi all + -$(TOOL) rmi $(DCLINT_IMAGE) $(SHELLCHECK_IMAGE) 2>/dev/null + +lint: ## Lint the tracked compose files and shell scripts + $(TOOL) run --rm $(LINT_MOUNT) $(DCLINT_IMAGE) \ + $(shell git ls-files 'compose*.yaml' 'orchestrator/compose*.yaml') + $(TOOL) run --rm $(LINT_MOUNT) $(SHELLCHECK_IMAGE) \ + $(shell git ls-files '*.sh')