Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
]
}
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ generated/

**/.DS_Store
.idea
*.swp
*.swo
.vscode/
44 changes: 44 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -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: <model>` 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**.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
89 changes: 89 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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')