local dev

This commit is contained in:
Elio Bischof
2025-06-26 09:09:56 +02:00
parent 62b43ef0d4
commit f708e48cc7
16 changed files with 170 additions and 89 deletions

View File

@@ -20,7 +20,7 @@ jobs:
Therefore, we close this pull request automatically, but submitting your changes to the main repository is easy:
1. Fork and clone zitadel/zitadel
2. Create a new branch for your changes
3. Pull your changes into the new fork by running `make login-pull LOGIN_REMOTE_URL=<your-typescript-fork-org>/typescript LOGIN_REMOTE_BRANCH=<your-typescript-fork-branch>`.
3. Pull your changes into the new fork by running `make login_pull LOGIN_REMOTE_URL=<your-typescript-fork-org>/typescript LOGIN_REMOTE_BRANCH=<your-typescript-fork-branch>`.
4. Push your changes and open a pull request to zitadel/zitadel
`.trim();
await github.rest.issues.createComment({

View File

@@ -57,7 +57,7 @@ jobs:
${{ runner.os }}-login-run-caches-${{github.ref_name}}-${{ github.sha }}-
${{ runner.os }}-login-run-caches-${{github.ref_name}}-
${{ runner.os }}-login-run-caches-
- run: make login-quality
- run: make login_quality
env:
FORCE: ${{ github.event.inputs.force == 'true' }}
DOCKER_METADATA_OUTPUT_VERSION: ${{ github.event.inputs.ref-tag || env.DOCKER_METADATA_OUTPUT_VERSION || steps.meta.outputs.version }}

View File

@@ -24,7 +24,7 @@ Please consider the following guidelines when creating a pull request.
- The latest changes are always in `main`, so please make your pull request against that branch.
- pull requests should be raised for any change
- Pull requests need approval of a ZITADEL core engineer @zitadel/engineers before merging
- Pull requests need approval of a Zitadel core engineer @zitadel/engineers before merging
- We use ESLint/Prettier for linting/formatting, so please run `pnpm lint:fix` before committing to make resolving conflicts easier (VSCode users, check out [this ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and [this Prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) to fix lint and formatting issues in development)
- If you add new functionality, please provide the corresponding documentation as well and make it part of the pull request
@@ -37,7 +37,7 @@ pnpm install
# Generate gRPC stubs
pnpm generate
# Start a local development server
# Start a local development server for the login and manually configure apps/login/.env.local
pnpm dev
```
@@ -46,24 +46,56 @@ The application is now available at `http://localhost:3000`
Configure apps/login/.env.local to target the Zitadel instance of your choice.
The login app live-reloads on changes, so you can start developing right away.
### Developing Against Your Local ZITADEL Instance
### <a name="latest"></a>Developing Against A Local Latest Zitadel Release
The following command uses Docker to run a local ZITADEL instance and the login application in live-reloading dev mode.
The following command uses Docker to run a local Zitadel instance and the login application in live-reloading dev mode.
Additionally, it runs a Traefik reverse proxy that exposes the login with a self-signed certificate at https://127.0.0.1.sslip.io
127.0.0.1.sslip.io is a special domain that resolves to your localhost, so it's safe to allow your browser to proceed with loading the page.
```sh
# This command runs all dependencies and overwrites the file ./apps/login/.env.test.local.
pnpm test:acceptance:setup
# Install dependencies. Developing requires Node.js v20
pnpm install
# As soon as the setup container completed successfully, you are ready to run the login application in live-reloading dev mode
pnpm dev
# Generate gRPC stubs
pnpm generate
# Start a local development server and have apps/login/.env.test.local configured for you to target the local Zitadel instance.
pnpm dev:local
```
Log in at https://127.0.0.1.sslip.io/ui/v2/login/loginname and use the following credentials:
**Loginname**: *zitadel-admin@zitadel.127.0.0.1.sslip.io*
**Password**: *Password1!*.
The login app live-reloads on changes, so you can start developing right away.
### <a name="local"></a>Developing Against A Locally Compiled Zitadel
To develop against a locally compiled version of Zitadel, you need to build the Zitadel docker image first.
Clone the [Zitadel repository](https://github.com/zitadel/zitadel.git) and run the following command from its root:
```sh
# This compiles a Zitadel binary if it does not exist at ./zitadel already and copies it into a Docker image.
# If you want to recompile the binary, run `make compile` first
make login_dev
```
Open another terminal session at zitadel/zitadel/login and run the following commands to start the dev server.
```bash
# Install dependencies. Developing requires Node.js v20
pnpm install
# Start a local development server and have apps/login/.env.test.local configured for you to target the local Zitadel instance.
NODE_ENV=test pnpm dev
```
Log in at https://127.0.0.1.sslip.io/ui/v2/login/loginname and use the following credentials:
**Loginname**: *zitadel-admin@zitadel.127.0.0.1.sslip.io*
**Password**: *Password1!*.
The login app live-reloads on changes, so you can start developing right away.
### Quality Assurance
Use `make` commands to test the quality of your code against a production build without installing any dependencies besides Docker.
@@ -71,7 +103,7 @@ Using `make` commands, you can reproduce and debug the CI pipelines locally.
```sh
# Reproduce the whole CI pipeline in docker
make login-quality
make login_quality
# Show other options with make
make help
```
@@ -83,7 +115,7 @@ Use `pnpm` commands to run the tests in dev mode with live reloading and debuggi
Check the formatting and linting of the code in docker
```sh
make login-lint
make login_lint
```
Check the linting of the code using pnpm
@@ -105,7 +137,7 @@ pnpm format:fix
Run the tests in docker
```sh
make login-test-unit
make login_test_unit
```
Run unit tests with live-reloading
@@ -119,14 +151,20 @@ pnpm test:unit
Run the test in docker
```sh
make login-test-integration
make login_test_integration
```
Open the Cypress test suite to run the integration tests in interactive mode.
Alternatively, run a live-reloading development server with an interactive Cypress test suite.
First, set up your local test environment.
This runs a mock server in docker and the login application in dev mode with live-reloading enabled.
```sh
# Install dependencies. Developing requires Node.js v20
pnpm install
# Generate gRPC stubs
pnpm generate
# Start a local development server and use apps/login/.env.test to use the locally mocked Zitadel API.
pnpm test:integration:setup
```
@@ -141,3 +179,26 @@ Show more options with Cypress
```sh
pnpm test:integration help
```
#### Running Acceptance Tests
To run the tests in docker against the latest release of Zitadel, use the following command:
```sh
make login_test_acceptance
```
Alternatively, run can use a live-reloading development server with an interactive Playwright test suite.
Set up your local environment by running the commands either for [developing against a local latest Zitadel release](latest) or for [developing against a locally compiled Zitadel](compiled).
Now, in another terminal session, open the interactive Playwright acceptance test suite.
```sh
pnpm test:acceptance open
```
Show more options with Playwright
```sh
pnpm test:acceptance help
```

106
Makefile
View File

@@ -3,9 +3,9 @@ export CACHE_DIR ?= $(XDG_CACHE_HOME)/zitadel-make
LOGIN_DIR ?= ./
LOGIN_BAKE_CLI ?= docker buildx bake
LOGIN_BAKE_CLI_WITH_COMMON_ARGS := $(LOGIN_BAKE_CLI) --file $(LOGIN_DIR)docker-bake.hcl --file $(LOGIN_DIR)apps/login-test-acceptance/docker-compose.yaml
LOGIN_BAKE_CLI_WITH_ARGS := $(LOGIN_BAKE_CLI) --file $(LOGIN_DIR)docker-bake.hcl --file $(LOGIN_DIR)apps/login-test-acceptance/docker-compose.yaml
LOGIN_BAKE_CLI_ADDITIONAL_ARGS ?=
LOGIN_BAKE_CLI_WITH_COMMON_ARGS += $(LOGIN_BAKE_CLI_ADDITIONAL_ARGS)
LOGIN_BAKE_CLI_WITH_ARGS += $(LOGIN_BAKE_CLI_ADDITIONAL_ARGS)
export COMPOSE_BAKE=true
export UID := $(id -u)
@@ -26,11 +26,10 @@ export LOGIN_TEST_ACCEPTANCE_SAMLSP_TAG := login-test-acceptance-samlsp:${DOCKER
export LOGIN_TEST_ACCEPTANCE_SAMLIDP_TAG := login-test-acceptance-samlidp:${DOCKER_METADATA_OUTPUT_VERSION}
export POSTGRES_TAG := postgres:17.0-alpine3.19
export GOLANG_TAG := golang:1.24-alpine
export ZITADEL_TAG ?= ghcr.io/zitadel/zitadel:02617cf17fdde849378c1a6b5254bbfb2745b164
export ZITADEL_TAG ?= ghcr.io/zitadel/zitadel:latest
export CORE_MOCK_TAG := login-core-mock:${DOCKER_METADATA_OUTPUT_VERSION}
.PHONY: login-help
login-help:
login_help:
@echo "Makefile for the login service"
@echo "Available targets:"
@echo " login-help - Show this help message."
@@ -45,85 +44,92 @@ login-help:
@echo " clean-run-caches - Remove all run caches."
login-lint:
$(LOGIN_BAKE_CLI_WITH_COMMON_ARGS) login-lint
login_lint:
@echo "Running login linting and formatting checks"
$(LOGIN_BAKE_CLI_WITH_ARGS) login-lint
login-test-unit:
$(LOGIN_BAKE_CLI_WITH_COMMON_ARGS) login-test-unit
login_test_unit:
@echo "Running login unit tests"
$(LOGIN_BAKE_CLI_WITH_ARGS) login-test-unit
login-test-integration-build:
$(LOGIN_BAKE_CLI_WITH_COMMON_ARGS) core-mock login-test-integration login-standalone
login_test_integration_build:
@echo "Building login integration test environment with the local core mock image"
$(LOGIN_BAKE_CLI_WITH_ARGS) core-mock login-test-integration login-standalone
login-test-integration-dev: login-test-integration-cleanup
$(LOGIN_BAKE_CLI_WITH_COMMON_ARGS) core-mock && docker compose --file $(LOGIN_DIR)apps/login-test-integration/docker-compose.yaml run --service-ports --rm core-mock
login_test_integration_dev: login_test_integration_cleanup
@echo "Starting login integration test environment with the local core mock image"
$(LOGIN_BAKE_CLI_WITH_ARGS) core-mock && docker compose --file $(LOGIN_DIR)apps/login-test-integration/docker-compose.yaml run --service-ports --rm core-mock
login-test-integration-run: login-test-integration-cleanup
login_test_integration_run: login_test_integration_cleanup
@echo "Running login integration tests"
docker compose --file $(LOGIN_DIR)apps/login-test-integration/docker-compose.yaml run --rm integration
login-test-integration-cleanup:
login_test_integration_cleanup:
@echo "Cleaning up login integration test environment"
docker compose --file $(LOGIN_DIR)apps/login-test-integration/docker-compose.yaml down --volumes
.PHONY: login-test-integration
login-test-integration: login-test-integration-build
$(LOGIN_DIR)scripts/run_or_skip.sh login-test-integration-run \
login_test_integration: login_test_integration_build
$(LOGIN_DIR)scripts/run_or_skip.sh login_test_integration_run \
"$(LOGIN_TAG) \
$(CORE_MOCK_TAG) \
$(LOGIN_TEST_INTEGRATION_TAG)"
login-test-acceptance-build-bake:
$(LOGIN_BAKE_CLI_WITH_COMMON_ARGS) login-test-acceptance login-standalone
login_test_acceptance_build_bake:
@echo "Building login test acceptance images as defined in the docker-bake.hcl"
$(LOGIN_BAKE_CLI_WITH_ARGS) login-test-acceptance login-standalone
login-test-acceptance-build-compose:
$(LOGIN_BAKE_CLI_WITH_COMMON_ARGS) --load setup sink
login_test_acceptance_build_compose:
@echo "Building login test acceptance images as defined in the docker-compose.yaml"
$(LOGIN_BAKE_CLI_WITH_ARGS) --load setup sink
login-test-acceptance-build: login-test-acceptance-build-compose login-test-acceptance-build-bake
@:
# login_test_acceptance_build is overwritten by the login_dev target in zitadel/zitadel/Makefile
login_test_acceptance_build: login_test_acceptance_build_compose login_test_acceptance_build_bake
login-test-acceptance-env: login-test-acceptance-build-compose login-test-acceptance-cleanup
docker compose --file $(LOGIN_DIR)apps/login-test-acceptance/docker-compose.yaml run setup
login-test-acceptance-dev:
docker compose --file $(LOGIN_DIR)apps/login-test-acceptance/docker-compose.yaml up zitadel traefik sink
login-test-acceptance-run: login-test-acceptance-cleanup
login_test_acceptance_run: login_test_acceptance_cleanup
@echo "Running login test acceptance tests"
docker compose --file $(LOGIN_DIR)apps/login-test-acceptance/docker-compose.yaml --file $(LOGIN_DIR)apps/login-test-acceptance/docker-compose-ci.yaml run --rm --service-ports acceptance
login-test-acceptance-cleanup:
login_test_acceptance_cleanup:
@echo "Cleaning up login test acceptance environment"
docker compose --file $(LOGIN_DIR)apps/login-test-acceptance/docker-compose.yaml --file $(LOGIN_DIR)apps/login-test-acceptance/docker-compose-ci.yaml down --volumes
login-test-acceptance: login-test-acceptance-build
$(LOGIN_DIR)scripts/run_or_skip.sh login-test-acceptance-run \
login_test_acceptance: login_test_acceptance_build
$(LOGIN_DIR)scripts/run_or_skip.sh login_test_acceptance_run \
"$(LOGIN_TAG) \
$(ZITADEL_TAG) \
$(POSTGRES_TAG) \
$(GOLANG_TAG) \
$(LOGIN_TEST_ACCEPTANCE_TAG) \
$(LOGIN_TEST_ACCEPTANCE_SETUP_TAG) \
$(LOGIN_TEST_ACCEPTANCE_SINK_TAG) \
$(LOGIN_TEST_ACCEPTANCE_OIDCRP_TAG) \
$(LOGIN_TEST_ACCEPTANCE_SAMLSP_TAG)"
$(LOGIN_TEST_ACCEPTANCE_SINK_TAG)"
.PHONY: login-quality
login-quality: login-lint login-test-unit login-test-integration
@:
login_test_acceptance_setup_env: login_test_acceptance_build_compose login_test_acceptance_cleanup
@echo "Setting up the login test acceptance environment and writing the env.test.local file"
docker compose --file $(LOGIN_DIR)apps/login-test-acceptance/docker-compose.yaml run setup
.PHONY: login-standalone-build
login-standalone-build:
$(LOGIN_BAKE_CLI_WITH_COMMON_ARGS) --load login-standalone
login_test_acceptance_setup_dev:
@echo "Starting the login test acceptance environment with the local zitadel image"
docker compose --file $(LOGIN_DIR)apps/login-test-acceptance/docker-compose.yaml up --no-recreate zitadel traefik sink
login-standalone-build-tag:
login_quality: login_lint login_test_unit login_test_integration
@echo "Running login quality checks: lint, unit tests, integration tests"
login_standalone_build:
@echo "Building the login standalone docker image with tag: $(LOGIN_TAG)"
$(LOGIN_BAKE_CLI_WITH_ARGS) --load login-standalone
login_standalone_build_tag:
@echo -n "$(LOGIN_TAG)"
typescript-generate:
$(LOGIN_BAKE_CLI_WITH_COMMON_ARGS) typescript-proto-client-out
typescript_generate:
@echo "Generating TypeScript client and writing to local $(LOGIN_DIR)packages/zitadel-proto"
$(LOGIN_BAKE_CLI_WITH_ARGS) typescript-proto-client-out
.PHONY: clean-run-caches
clean-run-caches:
clean_run_caches:
@echo "Removing cache directory: $(CACHE_DIR)"
rm -rf "$(CACHE_DIR)"
.PHONY: show-run-caches
show-run-caches:
show_run_caches:
@echo "Showing run caches with docker image ids and exit codes in $(CACHE_DIR):"
@find "$(CACHE_DIR)" -type f 2>/dev/null | while read file; do \
echo "$$file: $$(cat $$file)"; \

View File

@@ -30,7 +30,6 @@ services:
condition: service_completed_successfully
acceptance:
user: "${UID:-1000}:${GID:-1000}"
image: "${LOGIN_TEST_ACCEPTANCE_TAG:-login-test-acceptance:local}"
container_name: acceptance
environment:
@@ -38,7 +37,9 @@ services:
- LOGIN_BASE_URL=https://traefik/ui/v2/login/
- NODE_TLS_REJECT_UNAUTHORIZED=0
volumes:
- ../login/.env.test.local:/builder/apps/login/.env.test.local
- ../login/.env.test.local:/build/apps/login/.env.test.local
- ./test-results:/build/apps/login-test-acceptance/test-results
- ./playwright-report:/build/apps/login-test-acceptance/playwright-report
ports:
- 9323:9323
ipc: "host"

View File

@@ -2,9 +2,8 @@ services:
zitadel:
user: "${UID:-1000}:${GID:-1000}"
image: "${ZITADEL_IMAGE:-ghcr.io/zitadel/zitadel:v3.3.0}"
image: "${ZITADEL_TAG:-ghcr.io/zitadel/zitadel:latest}"
container_name: acceptance-zitadel
pull_policy: always
command: 'start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --config /zitadel.yaml --steps /zitadel.yaml'
labels:
- "traefik.enable=true"

View File

@@ -2,9 +2,9 @@
"name": "login-test-acceptance",
"private": true,
"scripts": {
"test:acceptance": "pnpm exec playwright",
"test:acceptance:env": "cd ../.. && make login-test-acceptance-env",
"test:acceptance:setup": "cd ../.. && make login-test-acceptance-dev"
"test:acceptance": "dotenv -e ../login/.env.test.local pnpm exec playwright",
"test:acceptance:setup": "cd ../.. && make login_test_acceptance_setup_env && NODE_ENV=test pnpm exec turbo run test:acceptance:setup:dev",
"test:acceptance:setup:dev": "cd ../.. && make login_test_acceptance_setup_dev"
},
"devDependencies": {
"@faker-js/faker": "^9.7.0",

View File

@@ -1,11 +1,10 @@
{
"extends": ["//"],
"tasks": {
"test:acceptance:setup": {
"test:acceptance:setup:dev": {
"interactive": true,
"cache": false,
"persistent": true,
"with": ["@zitadel/login#dev"]
"persistent": true
}
}
}

View File

@@ -2,8 +2,8 @@
"name": "login-test-integration",
"private": true,
"scripts": {
"test:integration": "pnpm exec cypress",
"test:integration:setup": "cd ../.. && make login-test-integration-dev"
"test:integration": "dotenv -e ../login/.env.test pnpm exec cypress",
"test:integration:setup": "cd ../.. && make login_test_integration_dev"
},
"devDependencies": {
"@types/node": "^22.14.1",

View File

@@ -4,8 +4,7 @@
"test:integration:setup": {
"interactive": true,
"cache": false,
"persistent": true,
"with": ["@zitadel/login#dev"]
"persistent": true
}
}
}

View File

@@ -6,6 +6,14 @@ variable "DOCKERFILES_DIR" {
default = "dockerfiles/"
}
variable "UID" {
default = "1000"
}
variable "GID" {
default = "1000"
}
# typescript-proto-client is used to generate the client code for the login service.
# It is not login-prefixed, so it is easily extendable.
# To extend this bake-file.hcl, set the context of all login-prefixed targets to a different directory.
@@ -13,7 +21,6 @@ variable "DOCKERFILES_DIR" {
# The zitadel repository uses this to generate the client and the mock server from local proto files.
target "typescript-proto-client" {
dockerfile = "${DOCKERFILES_DIR}typescript-proto-client.Dockerfile"
target = "typescript-proto-client"
contexts = {
# We directly generate and download the client server-side with buf, so we don't need the proto files
login-pnpm = "target:login-pnpm"

View File

@@ -1,7 +1,7 @@
FROM node:20-bookworm AS login-pnpm
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable && corepack prepare pnpm@9.1.2 --activate && \
RUN corepack enable && COREPACK_ENABLE_DOWNLOAD_PROMPT=0 corepack prepare pnpm@9.1.2 --activate && \
apt-get update && apt-get install -y --no-install-recommends && \
rm -rf /var/lib/apt/lists/*
WORKDIR /build

View File

@@ -3,6 +3,7 @@
!apps/login
apps/login/.next
apps/login/dist
apps/login/.env*.local
!scripts/healthcheck.js
!packages/zitadel-tailwind-config

View File

@@ -2,7 +2,7 @@ FROM login-pnpm AS typescript-proto-client
COPY packages/zitadel-proto/package.json ./packages/zitadel-proto/
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile --workspace-root --filter zitadel-proto
COPY packages/zitadel-proto packages/zitadel-proto
COPY packages/zitadel-proto ./packages/zitadel-proto
RUN pnpm generate
FROM scratch AS typescript-proto-client-out
@@ -10,3 +10,5 @@ COPY --from=typescript-proto-client /build/packages/zitadel-proto/zitadel /zitad
COPY --from=typescript-proto-client /build/packages/zitadel-proto/google /google
COPY --from=typescript-proto-client /build/packages/zitadel-proto/protoc-gen-openapiv2 /protoc-gen-openapiv2
COPY --from=typescript-proto-client /build/packages/zitadel-proto/validate /validate
FROM typescript-proto-client

View File

@@ -13,12 +13,13 @@
"start": "pnpm exec turbo run start",
"start:built": "pnpm exec turbo run start:built",
"test:unit": "pnpm exec turbo run test:unit -- --passWithNoTests",
"test:integration:setup": "NODE_ENV=test dotenv -e ./apps/login-test-integration/.env pnpm exec turbo run test:integration:setup",
"test:integration": "cd apps/login-test-integration && dotenv -e ./.env pnpm test:integration",
"test:acceptance:setup": "pnpm exec turbo run test:acceptance:env && NODE_ENV=test pnpm exec turbo run test:acceptance:setup",
"test:acceptance": "cd apps/login-test-acceptance && dotenv -e ./env/.env pnpm test:acceptance",
"test:integration": "cd apps/login-test-integration && pnpm test:integration",
"test:integration:setup": "NODE_ENV=test pnpm exec turbo run test:integration:setup",
"test:acceptance": "cd apps/login-test-acceptance && pnpm test:acceptance",
"test:acceptance:setup": "cd apps/login-test-acceptance && pnpm test:acceptance:setup",
"test:watch": "pnpm exec turbo run test:watch",
"dev": "pnpm exec turbo run dev --no-cache --continue",
"dev:local": "pnpm test:acceptance:setup",
"lint": "pnpm exec turbo run lint",
"lint:fix": "pnpm exec turbo run lint:fix",
"clean": "pnpm exec turbo run clean && rm -rf node_modules",

View File

@@ -27,9 +27,14 @@
"start:built": {},
"test:unit": {},
"test:unit:standalone": {},
"test:integration:setup": {},
"test:acceptance:env": {},
"test:integration": {},
"test:integration:setup": {
"with": ["dev"]
},
"test:acceptance:setup": {},
"test:acceptance:setup:dev": {
"with": ["dev"]
},
"test:watch": {
"persistent": true
},