.PHONY: gen dev-reset all test web docs core-install i18n-extract gen-locales locales-normalize install bump gen-changelog integrations SHELL := /usr/bin/env bash .SHELLFLAGS += ${SHELLFLAGS} -e -o pipefail PWD = $(shell pwd) UID = $(shell id -u) GID = $(shell id -g) PY_SOURCES = authentik packages tests scripts lifecycle DOCKER_IMAGE ?= "authentik:test" UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Darwin) SED_INPLACE = /usr/bin/sed -i '' else SED_INPLACE = sed -i endif BREW_LDFLAGS := BREW_CPPFLAGS := BREW_PKG_CONFIG_PATH := CARGO := cargo UV := uv # For macOS users, add the libxml2 installed from brew libxmlsec1 to the build path # to prevent SAML-related tests from failing and ensure correct pip dependency compilation ifeq ($(UNAME_S),Darwin) # Only add for brew users who installed libxmlsec1 BREW_EXISTS := $(shell command -v brew 2> /dev/null) ifdef BREW_EXISTS LIBXML2_EXISTS := $(shell brew list libxml2 2> /dev/null) ifdef LIBXML2_EXISTS _xml_pref := $(shell brew --prefix libxml2) BREW_LDFLAGS += -L${_xml_pref}/lib BREW_CPPFLAGS += -I${_xml_pref}/include BREW_PKG_CONFIG_PATH = ${_xml_pref}/lib/pkgconfig:$(PKG_CONFIG_PATH) endif KRB5_EXISTS := $(shell brew list krb5 2> /dev/null) ifdef KRB5_EXISTS _krb5_pref := $(shell brew --prefix krb5) BREW_LDFLAGS += -L${_krb5_pref}/lib BREW_CPPFLAGS += -I${_krb5_pref}/include BREW_PKG_CONFIG_PATH = ${_krb5_pref}/lib/pkgconfig:$(PKG_CONFIG_PATH) endif UV := LDFLAGS="$(BREW_LDFLAGS)" CPPFLAGS="$(BREW_CPPFLAGS)" PKG_CONFIG_PATH="$(BREW_PKG_CONFIG_PATH)" uv endif endif NPM_VERSION := UV_EXISTS := $(shell command -v uv 2> /dev/null) ifdef UV_EXISTS NPM_VERSION := $(shell $(UV) run python -m scripts.generate_semver) else NPM_VERSION = $(shell python -m scripts.generate_semver) endif all: lint-fix lint gen web test ## Lint-fix, lint, generate the schema, generate the client libraries, lint the front end, run the python tests HELP_WIDTH := $(shell grep -h '^[a-z][^ ]*:.*\#\#' $(MAKEFILE_LIST) 2>/dev/null | \ cut -d':' -f1 | awk '{printf "%d\n", length}' | sort -rn | head -1) help: ## Show this help @echo "\nSpecify a command. The choices are:\n" @grep -Eh '^[0-9a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ awk 'BEGIN {FS = ":.*?## "}; {printf " \033[0;36m%-$(HELP_WIDTH)s \033[m %s\n", $$1, $$2}' | \ sort @echo "" go-test: ## Run the golang tests go test -timeout 0 -v -race -cover ./... rust-test: ## Run the Rust tests $(CARGO) nextest run --workspace test: ## Run the server tests and produce a coverage report (locally). Usage: make test [path] $(UV) run coverage run manage.py test --keepdb $(or $(filter-out $@ all,$(MAKECMDGOALS)),authentik) $(UV) run coverage combine $(UV) run coverage html $(UV) run coverage report lint-fix-rust: ## Format Rust sources (rustfmt) $(CARGO) +nightly fmt --all -- --config-path "${PWD}/.cargo/rustfmt.toml" lint-fix: lint-fix-rust ## Format and automatically fix Python (black, ruff) and Rust (rustfmt) sources $(UV) run black $(PY_SOURCES) $(UV) run ruff check --fix $(PY_SOURCES) lint-spellcheck: ## Reports spelling errors. npm run lint:spellcheck lint-catalogs: ## Reports pnpm catalog pins, and pnpm's own version pin, that drifted between workspaces. node ./scripts/node/lint-catalogs.ts lint-locales: ## Reports translation files and generated locale lists that drifted from locales.yaml. $(UV) run python scripts/locales.py check lint-check-types: ## Type-check the repository's Node.js scripts. pnpm run build:types lint: ci-lint-bandit ci-lint-mypy ci-lint-cargo-deny ci-lint-cargo-machete ## Check Python (bandit, mypy), Go (golangci) and Rust dependencies (cargo deny, machete) golangci-lint run -v --config .github/golangci.yml core-install: ifneq ($(BREW_EXISTS),) # Clear cache to ensure fresh compilation $(UV) cache clean # Force compilation from source for lxml and xmlsec with correct environment $(UV) sync --frozen --reinstall-package lxml --reinstall-package xmlsec --no-binary-package lxml --no-binary-package xmlsec else $(UV) sync --frozen endif migrate: ## Apply and check system and Django migrations $(UV) run python -m lifecycle.migrate i18n-extract: core-i18n-extract web-i18n-extract ## Extract strings that require translation into files to send to a translation service gen-locales: ## Regenerate lit-localize.json, the web locale loaders, and the Transifex language mapping from locales.yaml $(UV) run python scripts/locales.py gen locales-normalize: ## Move translation files that Transifex wrote under non-canonical names to their canonical paths $(UV) run python scripts/locales.py normalize aws-cfn: node-install ## Generate the AWS Cloudformation template pnpm --dir lifecycle/aws install $(UV) run pnpm --dir lifecycle/aws run aws-cfn run: ## Run the main authentik server and worker processes $(UV) run ak allinone run-watch: ## Run the authentik server and worker, with auto reloading watchexec --on-busy-update=restart --stop-signal=SIGINT --exts py,rs --no-meta --notify -- $(UV) run ak allinone core-i18n-extract: $(UV) run python -m manage makemessages \ --add-location file \ --no-obsolete \ --ignore web \ --ignore internal \ --ignore packages/client-ts \ --ignore website \ -l en install: node-install web-install core-install ## Install all required dependencies for `node`, `web` and `core` dev-drop-db: $(eval pg_user := $(shell $(UV) run python -m authentik.lib.config postgresql.user 2>/dev/null)) $(eval pg_host := $(shell $(UV) run python -m authentik.lib.config postgresql.host 2>/dev/null)) $(eval pg_name := $(shell $(UV) run python -m authentik.lib.config postgresql.name 2>/dev/null)) dropdb -U ${pg_user} -h ${pg_host} ${pg_name} || true # Also remove the test-db if it exists dropdb -U ${pg_user} -h ${pg_host} test_${pg_name} || true dev-create-db: $(eval pg_user := $(shell $(UV) run python -m authentik.lib.config postgresql.user 2>/dev/null)) $(eval pg_host := $(shell $(UV) run python -m authentik.lib.config postgresql.host 2>/dev/null)) $(eval pg_name := $(shell $(UV) run python -m authentik.lib.config postgresql.name 2>/dev/null)) createdb -U ${pg_user} -h ${pg_host} ${pg_name} dev-reset: dev-drop-db dev-create-db migrate ## Drop and restore the authentik PostgreSQL instance to a "fresh install" state. make-migrations: ## Create Django migrations for model changes (pgtrigger-aware) $(UV) run ak makemigrations update-test-mmdb: ## Update test GeoIP and ASN Databases curl \ -L \ -o ${PWD}/tests/geoip/GeoLite2-ASN-Test.mmdb \ https://raw.githubusercontent.com/maxmind/MaxMind-DB/refs/heads/main/test-data/GeoLite2-ASN-Test.mmdb curl \ -L \ -o ${PWD}/tests/geoip/GeoLite2-City-Test.mmdb \ https://raw.githubusercontent.com/maxmind/MaxMind-DB/refs/heads/main/test-data/GeoLite2-City-Test.mmdb bump: ## Bump authentik version. Usage: make bump version=20xx.xx.xx ifndef version $(error Usage: make bump version=20xx.xx.xx ) endif $(eval current_version := $(shell cat ${PWD}/internal/constants/VERSION)) $(SED_INPLACE) 's/^version = ".*"/version = "$(version)"/' ${PWD}/pyproject.toml $(SED_INPLACE) 's/^VERSION = ".*"/VERSION = "$(version)"/' ${PWD}/authentik/__init__.py $(SED_INPLACE) "s/version = \"${current_version}\"/version = \"$(version)\"/" ${PWD}/Cargo.toml ${PWD}/Cargo.lock $(MAKE) gen-build gen-compose aws-cfn $(SED_INPLACE) "s/\"${current_version}\"/\"$(version)\"/" ${PWD}/package.json ${PWD}/web/package.json echo -n $(version) > ${PWD}/internal/constants/VERSION ######################### ## API Schema ######################### gen-build: ## Generate schema.yml and blueprints/schema.json from Django model definitions AUTHENTIK_DEBUG=true \ AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \ $(UV) run python -m manage build_schema gen-compose: $(UV) run scripts/generate_compose.py gen-changelog: ## (Release) generate the changelog based from the commits since the last version # These are best-effort guesses based on commit messages $(eval last_version := $(shell git tag --list 'version/*' --sort 'version:refname' | grep -vE 'rc[0-9]+$$' | tail -1)) $(eval current_commit := $(shell git rev-parse HEAD)) git log --pretty=format:"- %s" $(shell git merge-base ${last_version} ${current_commit})...${current_commit} > merged_to_current git log --pretty=format:"- %s" $(shell git merge-base ${last_version} ${current_commit})...${last_version} > merged_to_last { grep -Eo 'cherry-pick (#[0-9]+)' merged_to_last || true; } | cut -d ' ' -f 2 | sed 's/.*/(&)$$/' > cherry_picked_to_last grep -vf cherry_picked_to_last merged_to_current | grep -vE '^- (ci:|website)' | sort > changelog.md rm merged_to_current rm merged_to_last rm cherry_picked_to_last pnpm exec oxfmt --write changelog.md gen-diff: ## (Release) generate the changelog diff between the current schema and the last version $(eval last_version := $(shell git tag --list 'version/*' --sort 'version:refname' | grep -vE 'rc[0-9]+$$' | tail -1)) git show ${last_version}:schema.yml > schema-old.yml docker compose -f scripts/compose.yml run --rm --user "${UID}:${GID}" diff \ --markdown \ /local/diff.md \ /local/schema-old.yml \ /local/schema.yml rm schema-old.yml $(SED_INPLACE) 's/{/\{/g' diff.md $(SED_INPLACE) 's/}/\}/g' diff.md pnpm exec oxfmt --write diff.md gen-client-go: ## Build and install the authentik API for Golang $(UV) run make -C "${PWD}/packages/client-go" build gen-client-rust: ## Build and install the authentik API for Rust $(UV) run make -C "${PWD}/packages/client-rust" build version=${NPM_VERSION} make lint-fix-rust gen-client-ts: ## Build and install the authentik API for Typescript into the authentik UI Application make -C "${PWD}/packages/client-ts" build pnpm --dir web install _gen-clients: gen-client-go gen-client-rust gen-client-ts gen-clients: ## Build and install API clients used by authentik $(MAKE) _gen-clients -j gen: gen-build gen-clients ## Build and install API schema and clients used by authentik gen-dev-config: ## Generate a local development config file $(UV) run scripts/generate_config.py ######################### ## Node.js ######################### # Lifecycle scripts are blocked by default in pnpm 10+ via # `pnpm-workspace.yaml#onlyBuiltDependencies`. Adding a package to that list # grants it arbitrary code execution at install — audit at review time. node-preinstall: ## Verify the active Node.js and pnpm versions match what's in package.json. node ./scripts/node/lint-runtime.ts node-install: node-preinstall ## Install the necessary libraries to build Node.js packages and build the shared lint configs pnpm install --frozen-lockfile pnpm run build:lint-config ######################### ## Web ######################### web-install: ## Install the necessary libraries to build the authentik front end pnpm --dir web install --frozen-lockfile web-build: node-install ## Build the authentik front end pnpm --dir web run build web: web-lint-fix web-lint web-check-compile ## Format and lint the front-end, run lit-analyzer and type-check web-check: ## Run front-end lint and format checks (does no modifications) pnpm --dir web lint:check pnpm --dir web format:check web-test-unit: ## Run web front-end tests once pnpm --dir web run test:unit web-test: ## Run tests for the authentik front end in watch mode pnpm --dir web run test web-test-e2e: ## Run front end Playwright end-to-end tests pnpm --dir web run test:e2e web-test-visual: ## Run visual regression tests for the front-end pnpm --dir web run test:visual web-watch: ## Build and watch the authentik front end for changes, updating automatically pnpm --dir web run watch web-storybook-watch: ## Build and run the storybook documentation server pnpm --dir web run storybook web-lint-fix: ## Format the front end source (oxfmt) pnpm --dir web run format web-lint: ## Lint the front end (oxfmt) and run lit-analyzer pnpm --dir web run lint pnpm --dir web run lit-analyse web-check-compile: ## Type-check the front end pnpm --dir web run tsc web-i18n-extract: pnpm --dir web run extract-locales ######################### ## Docs ######################### docs: docs-lint-fix docs-build ## Spellcheck, format, and build the docs website docs-install: node-install ## Install the necessary libraries to build the authentik documentation pnpm --dir website install --frozen-lockfile docs-lint-fix: lint-spellcheck ## Spellcheck and format the documentation site pnpm --dir website run lint pnpm --dir website run format docs-build: ## Build the docs website node ./scripts/node/lint-runtime.ts website pnpm --dir website run build docs-watch: ## Build and watch the topics documentation pnpm --dir website run start docs-test: ## Run the documentation website tests pnpm --dir website run test docs-check: ## Run documentation lint, typecheck, and format (does no modifications) pnpm --dir website run lint:check pnpm --dir website run build:types pnpm --dir website run format:check integrations: docs-lint-fix integrations-build ## Spellcheck, format, and build the integrations website integrations-build: ## Build the integrations website pnpm --dir website run build:integrations integrations-watch: ## Build and watch the Integrations documentation pnpm --dir website/integrations run start docs-api-build: ## Build the API reference site pnpm --dir website run build:api docs-api-watch: ## Build and watch the API documentation pnpm --dir website/api run generate pnpm --dir website/api run start docs-api-clean: ## Clean generated API documentation pnpm --dir website/api run clean ######################### ## Docker ######################### docker: ## Build a docker image of the current source tree DOCKER_BUILDKIT=1 docker build . -f lifecycle/container/Dockerfile --progress plain --tag ${DOCKER_IMAGE} test-docker: ## Build docker image and run full test suite in the container compose stack BUILD=true ${PWD}/scripts/test_docker.sh ######################### ## CI ######################### # These targets are use by GitHub actions to allow usage of matrix # which makes the YAML File a lot smaller ci--meta-debug: $(UV) run python -V || echo "No python installed" $(CARGO) --version || echo "No rust installed" node --version || echo "No node installed" ci-lint-mypy: ci--meta-debug $(UV) run mypy --strict $(PY_SOURCES) ci-lint-black: ci--meta-debug $(UV) run black --check $(PY_SOURCES) ci-lint-ruff: ci--meta-debug $(UV) run ruff check $(PY_SOURCES) ci-lint-spellcheck: ci--meta-debug npm run lint:spellcheck ci-lint-bandit: ci--meta-debug $(UV) run bandit -c pyproject.toml -r $(PY_SOURCES) -iii ci-lint-pending-migrations: ci--meta-debug $(UV) run python -m manage makemigrations --check ci-lint-cargo-deny: ci--meta-debug $(CARGO) deny --config "${PWD}/.cargo/deny.toml" --locked --workspace check ci-lint-cargo-machete: ci--meta-debug $(CARGO) machete ci-lint-rustfmt: ci--meta-debug $(CARGO) +nightly fmt --all --check -- --config-path "${PWD}/.cargo/rustfmt.toml" ci-lint-clippy: ci--meta-debug $(CARGO) clippy --workspace --all-targets -- -D warnings ci-lint-catalogs: ci--meta-debug node ./scripts/node/lint-catalogs.ts ci-lint-locales: ci--meta-debug $(UV) run python scripts/locales.py check ci-lint-oxlint-fixtures: ci--meta-debug pnpm --filter @goauthentik/oxlint-config run build pnpm --filter @goauthentik/oxlint-config run verify ci-lint-check-types: ci--meta-debug pnpm run build:types ci-test: ci--meta-debug $(UV) run coverage run manage.py test --keepdb --parallel auto authentik $(UV) run coverage combine $(UV) run coverage report $(UV) run coverage xml