WIP prepare skeleton for Nx migration

This commit is contained in:
Florian Forster
2025-08-06 09:39:57 -07:00
parent b6ebabb962
commit 8c7d8dcea8
54 changed files with 3611 additions and 2776 deletions

View File

@@ -1,20 +0,0 @@
FROM mcr.microsoft.com/devcontainers/typescript-node:20-bookworm
ENV SHELL=/bin/bash \
DEBIAN_FRONTEND=noninteractive \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8 \
CI=1 \
PNPM_HOME=/home/node/.local/share/pnpm \
PATH=/home/node/.local/share/pnpm:$PATH
RUN apt-get update && \
apt-get --no-install-recommends install -y \
# Cypress dependencies
libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2 libxtst6 xauth xvfb && \
apt-get clean && \
corepack enable && COREPACK_ENABLE_DOWNLOAD_PROMPT=0 corepack prepare pnpm@10.13.1 --activate
COPY --chown=node:node commands /commands
USER node

View File

@@ -1,2 +0,0 @@
*
!commands

View File

@@ -1,39 +0,0 @@
#!/bin/bash
if [ "$FAIL_COMMANDS_ON_ERRORS" == "true" ]; then
set -e
fi
echo
echo
echo
echo -e "THANKS FOR CONTRIBUTING TO ZITADEL 🚀"
echo
echo "Your dev container is configured for fixing login integration tests."
echo "The login is running in a separate container with the same configuration."
echo "It calls the mock-zitadel container which provides a mocked Zitadel gRPC API."
echo
echo "Also the test suite is configured correctly."
echo "For example, run a single test file:"
echo "pnpm cypress run --spec integration/integration/login.cy.ts"
echo
echo "You can also run the test interactively."
echo "However, this is only possible from outside the dev container."
echo "On your host machine, run:"
echo "cd apps/login"
echo "pnpm cypress open"
echo
echo "If you want to change the login code, you can replace the login container by a hot reloading dev server."
echo "docker stop login-integration"
echo "pnpm turbo dev"
echo "Navigate to the page you want to fix, for example:"
echo "http://localhost:3001/ui/v2/login/verify?userId=221394658884845598&code=abc"
echo "Change some code and reload the page for instant feedback."
echo
echo "When you are done, make sure all integration tests pass:"
echo "pnpm cypress run"
echo
if [ "$FAIL_COMMANDS_ON_ERRORS" != "true" ]; then
exit 0
fi

View File

@@ -1,18 +0,0 @@
#!/bin/bash
if [ "$FAIL_COMMANDS_ON_ERRORS" == "true" ]; then
echo "Running in fail-on-errors mode"
set -e
fi
pnpm install --frozen-lockfile \
--filter @zitadel/login \
--filter @zitadel/client \
--filter @zitadel/proto \
--filter zitadel-monorepo
pnpm cypress install
pnpm test:integration:login
if [ "$FAIL_COMMANDS_ON_ERRORS" != "true" ]; then
exit 0
fi

View File

@@ -1,30 +0,0 @@
#!/bin/bash
if [ "$FAIL_COMMANDS_ON_ERRORS" == "true" ]; then
set -e
fi
echo
echo
echo
echo -e "THANKS FOR CONTRIBUTING TO ZITADEL 🚀"
echo
echo "Your dev container is configured for fixing linting and unit tests."
echo "No other services are running alongside this container."
echo
echo "To fix all auto-fixable linting errors, run:"
echo "pnpm turbo lint:fix"
echo
echo "To watch console linting errors, run:"
echo "pnpm turbo watch lint --filter console"
echo
echo "To watch @zitadel/client unit test failures, run:"
echo "pnpm turbo watch test:unit --filter @zitadel/client"
echo
echo "To watch @zitadel/login relevant unit tests and linting failures, run:"
echo "pnpm turbo watch lint test:unit --filter @zitadel/login..."
echo
if [ "$FAIL_COMMANDS_ON_ERRORS" != "true" ]; then
exit 0
fi

View File

@@ -1,12 +0,0 @@
#!/bin/bash
if [ "$FAIL_COMMANDS_ON_ERRORS" == "true" ]; then
set -e
fi
pnpm install --frozen-lockfile --recursive
pnpm turbo lint test:unit
if [ "$FAIL_COMMANDS_ON_ERRORS" != "true" ]; then
exit 0
fi

View File

@@ -1,29 +0,0 @@
{
"$schema": "https://raw.githubusercontent.com/devcontainers/spec/refs/heads/main/schemas/devContainer.schema.json",
"name": "Base: Build and Run the Components you need",
"dockerComposeFile": "docker-compose.yaml",
"service": "devcontainer",
"runServices": [
"devContainer",
"db"
],
"workspaceFolder": "/workspaces",
"remoteEnv": {
"DISPLAY": ""
},
"forwardPorts": [
3000,
3001,
4200,
8080
],
"onCreateCommand": "pnpm install --frozen-lockfile --recursive --prefer-offline",
"features": {
"ghcr.io/devcontainers/features/go:1": {
"version": "1.24"
},
"ghcr.io/guiyomh/features/golangci-lint:0": {},
"ghcr.io/jungaretti/features/make:1": {},
"ghcr.io/devcontainers/features/docker-outside-of-docker": {}
}
}

View File

@@ -1,198 +0,0 @@
services:
devcontainer:
container_name: devcontainer
build:
context: ../base
volumes:
- ../../:/workspaces:cached
command: sleep infinity
working_dir: /workspaces
environment:
ZITADEL_DATABASE_POSTGRES_HOST: db
ZITADEL_EXTERNALSECURE: false
db:
container_name: db
image: postgres:17.0-alpine3.19
restart: unless-stopped
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
PGUSER: postgres
POSTGRES_PASSWORD: postgres
healthcheck:
test: [ "CMD-SHELL", "pg_isready" ]
interval: "10s"
timeout: "30s"
retries: 5
start_period: "20s"
ports:
- "5432:5432"
zitadel:
container_name: zitadel
image: "${ZITADEL_TAG:-ghcr.io/zitadel/zitadel:latest}"
command: 'start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --config /zitadel.yaml --steps /zitadel.yaml'
volumes:
- ../../apps/login/acceptance/pat:/pat:delegated
- ../../apps/login/acceptance/zitadel.yaml:/zitadel.yaml:cached
network_mode: service:devcontainer
healthcheck:
test:
- CMD
- /app/zitadel
- ready
- --config
- /zitadel.yaml
depends_on:
db:
condition: "service_healthy"
configure-login:
container_name: configure-login
restart: no
build:
context: ../../apps/login/acceptance/setup
dockerfile: ../go-command.Dockerfile
entrypoint: "./setup.sh"
network_mode: service:devcontainer
environment:
PAT_FILE: /pat/zitadel-admin-sa.pat
ZITADEL_API_URL: http://localhost:8080
WRITE_ENVIRONMENT_FILE: /login-env/.env.test.local
SINK_EMAIL_INTERNAL_URL: http://sink:3333/email
SINK_SMS_INTERNAL_URL: http://sink:3333/sms
SINK_NOTIFICATION_URL: http://sink:3333/notification
LOGIN_BASE_URL: http://localhost:3000/ui/v2/login/
ZITADEL_API_DOMAIN: localhost
ZITADEL_ADMIN_USER: zitadel-admin@zitadel.localhost
volumes:
- ../../apps/login/acceptance/pat:/pat:cached # Read the PAT file from zitadels setup
- ../../apps/login:/login-env:delegated # Write the environment variables file for the login
depends_on:
zitadel:
condition: "service_healthy"
login-acceptance:
container_name: login
image: "${LOGIN_TAG:-ghcr.io/zitadel/zitadel-login:latest}"
network_mode: service:devcontainer
volumes:
- ../../apps/login/.env.test.local:/env-files/.env:cached
depends_on:
configure-login:
condition: service_completed_successfully
mock-notifications:
container_name: mock-notifications
build:
context: ../../apps/login/acceptance/sink
dockerfile: ../go-command.Dockerfile
args:
- LOGIN_TEST_ACCEPTANCE_GOLANG_TAG=${LOGIN_TEST_ACCEPTANCE_GOLANG_TAG:-golang:1.24-alpine}
environment:
PORT: '3333'
command:
- -port
- '3333'
- -email
- '/email'
- -sms
- '/sms'
- -notification
- '/notification'
ports:
- "3333:3333"
depends_on:
configure-login:
condition: "service_completed_successfully"
mock-oidcrp:
container_name: mock-oidcrp
build:
context: ../../apps/login/acceptance/oidcrp
dockerfile: ../go-command.Dockerfile
args:
- LOGIN_TEST_ACCEPTANCE_GOLANG_TAG=${LOGIN_TEST_ACCEPTANCE_GOLANG_TAG:-golang:1.24-alpine}
network_mode: service:devcontainer
environment:
API_URL: 'http://localhost:8080'
API_DOMAIN: 'localhost'
PAT_FILE: '/pat/zitadel-admin-sa.pat'
LOGIN_URL: 'http://localhost:3000/ui/v2/login'
ISSUER: 'http://localhost:8000'
HOST: 'localhost'
PORT: '8000'
SCOPES: 'openid profile email'
volumes:
- ../../apps/login/acceptance/pat:/pat:cached
depends_on:
configure-login:
condition: "service_completed_successfully"
# mock-oidcop:
# container_name: mock-oidcop
# build:
# context: ../../apps/login/acceptance/idp/oidc
# dockerfile: ../../go-command.Dockerfile
# args:
# - LOGIN_TEST_ACCEPTANCE_GOLANG_TAG=${LOGIN_TEST_ACCEPTANCE_GOLANG_TAG:-golang:1.24-alpine}
# network_mode: service:devcontainer
# environment:
# API_URL: 'http://localhost:8080'
# API_DOMAIN: 'localhost'
# PAT_FILE: '/pat/zitadel-admin-sa.pat'
# SCHEMA: 'http'
# HOST: 'localhost'
# PORT: "8004"
# volumes:
# - "../apps/login/packages/acceptance/pat:/pat:cached"
# depends_on:
# configure-login:
# condition: "service_completed_successfully"
mock-samlsp:
container_name: mock-samlsp
build:
context: ../../apps/login/acceptance/samlsp
dockerfile: ../go-command.Dockerfile
args:
- LOGIN_TEST_ACCEPTANCE_GOLANG_TAG=${LOGIN_TEST_ACCEPTANCE_GOLANG_TAG:-golang:1.24-alpine}
network_mode: service:devcontainer
environment:
API_URL: 'http://localhost:8080'
API_DOMAIN: 'localhost'
PAT_FILE: '/pat/zitadel-admin-sa.pat'
LOGIN_URL: 'http://localhost:3000/ui/v2/login'
IDP_URL: 'http://localhost:8080/saml/v2/metadata'
HOST: 'http://localhost:8001'
PORT: '8001'
volumes:
- "../apps/login/packages/acceptance/pat:/pat:cached"
depends_on:
configure-login:
condition: "service_completed_successfully"
# mock-samlidp:
# container_name: mock-samlidp
# build:
# context: ../../apps/login/acceptance/idp/saml
# dockerfile: ../../go-command.Dockerfile
# args:
# - LOGIN_TEST_ACCEPTANCE_GOLANG_TAG=${LOGIN_TEST_ACCEPTANCE_GOLANG_TAG:-golang:1.24-alpine}
# network_mode: service:devcontainer
# environment:
# API_URL: 'http://localhost:8080'
# API_DOMAIN: 'localhost'
# PAT_FILE: '/pat/zitadel-admin-sa.pat'
# SCHEMA: 'http'
# HOST: 'localhost'
# PORT: "8003"
# volumes:
# - "../apps/login/packages/acceptance/pat:/pat"
# depends_on:
# configure-login:
# condition: "service_completed_successfully"
volumes:
postgres-data:

View File

@@ -1,26 +0,0 @@
{
"$schema": "https://raw.githubusercontent.com/devcontainers/spec/refs/heads/main/schemas/devContainer.schema.json",
"name": "Login Integration",
"dockerComposeFile": [
"./docker-compose.yaml"
],
"service": "login-integration-dev",
"runServices": [
"login-integration"
],
"workspaceFolder": "/workspaces/apps/login",
"forwardPorts": [
22220,
22222,
3001
],
"remoteEnv": {
"FAIL_COMMANDS_ON_ERRORS": "${localEnv:FAIL_COMMANDS_ON_ERRORS}",
"DISPLAY": ""
},
"updateContentCommand": "/commands/login-integration.update-content.sh",
"postAttachCommand": "/commands/login-integration.post-attach.sh",
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker": {}
}
}

View File

@@ -1,35 +0,0 @@
services:
login-integration-dev:
extends:
file: ../base/docker-compose.yaml
service: devcontainer
container_name: login-integration-dev
env_file: ../../apps/login/.env.test
environment:
CORE_MOCK_STUBS_URL: http://localhost:22220/v1/stubs
LOGIN_BASE_URL: http://localhost:3001/ui/v2/login
CYPRESS_CACHE_FOLDER: /workspaces/.artifacts/cypress
network_mode: service:mock-zitadel
depends_on:
login-integration:
condition: service_healthy
login-integration:
container_name: login-integration
image: "${LOGIN_TAG:-ghcr.io/zitadel/zitadel-login:latest}"
build:
context: ../..
dockerfile: build/login/Dockerfile
env_file: ../../apps/login/.env.test
network_mode: service:mock-zitadel
mock-zitadel:
container_name: mock-zitadel
build:
context: ../../apps/login/integration/core-mock
additional_contexts:
- zitadel-protos=../../proto
ports:
- 22220:22220
- 22222:22222
- 3001:3001

View File

@@ -1,22 +0,0 @@
{
"$schema": "https://raw.githubusercontent.com/devcontainers/spec/refs/heads/main/schemas/devContainer.base.schema.json",
"image": "mcr.microsoft.com/devcontainers/typescript-node:20-bookworm",
"name": "Login Subtree Container - Use the Login As If You Would Have Forked the Mirror Repo",
"workspaceFolder": "/login",
"workspaceMount": "source=${localWorkspaceFolder}/apps/login,target=/login,type=bind,consistency=cached",
"mounts": [],
"forwardPorts": [
22220,
22222,
3000,
3001
],
"features": {
"ghcr.io/devcontainers/features/go:1": {
"version": "1.24"
},
"ghcr.io/guiyomh/features/golangci-lint:0": {},
"ghcr.io/jungaretti/features/make:1": {},
"ghcr.io/devcontainers/features/docker-outside-of-docker": {}
}
}

View File

@@ -1,20 +0,0 @@
{
"$schema": "https://raw.githubusercontent.com/devcontainers/spec/refs/heads/main/schemas/devContainer.schema.json",
"name": "Turbo Lint and Unit Tests",
"dockerComposeFile": [
"../base/docker-compose.yaml"
],
"service": "devcontainer",
"runServices": [
"devcontainer"
],
"workspaceFolder": "/workspaces",
"forwardPorts": [
3001
],
"remoteEnv": {
"FAIL_COMMANDS_ON_ERRORS": "${localEnv:FAIL_COMMANDS_ON_ERRORS}"
},
"updateContentCommand": "/commands/turbo-lint-unit.update-content.sh",
"postAttachCommand": "/commands/turbo-lint-unit.post-attach.sh"
}

View File

@@ -1,145 +0,0 @@
name: ZITADEL CI/CD
on:
push:
tags-ignore:
- "*"
branches:
- "main"
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: write
packages: write
issues: write
pull-requests: write
actions: write
jobs:
core:
uses: ./.github/workflows/core.yml
with:
node_version: "20"
buf_version: "latest"
console:
uses: ./.github/workflows/console.yml
with:
node_version: "20"
docs:
uses: ./.github/workflows/docs.yml
with:
node_version: "20"
buf_version: "latest"
version:
uses: ./.github/workflows/version.yml
with:
semantic_version: "23.0.7"
dry_run: true
compile:
needs: [core, console, version]
uses: ./.github/workflows/compile.yml
with:
core_cache_key: ${{ needs.core.outputs.cache_key }}
console_cache_key: ${{ needs.console.outputs.cache_key }}
core_cache_path: ${{ needs.core.outputs.cache_path }}
console_cache_path: ${{ needs.console.outputs.cache_path }}
version: ${{ needs.version.outputs.version }}
node_version: "20"
core-unit-test:
needs: core
uses: ./.github/workflows/core-unit-test.yml
with:
core_cache_key: ${{ needs.core.outputs.cache_key }}
core_cache_path: ${{ needs.core.outputs.cache_path }}
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
core-integration-test:
needs: core
uses: ./.github/workflows/core-integration-test.yml
with:
core_cache_key: ${{ needs.core.outputs.cache_key }}
core_cache_path: ${{ needs.core.outputs.cache_path }}
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
lint:
needs: [core, console]
uses: ./.github/workflows/lint.yml
with:
node_version: "18"
buf_version: "latest"
go_lint_version: "latest"
core_cache_key: ${{ needs.core.outputs.cache_key }}
core_cache_path: ${{ needs.core.outputs.cache_path }}
container:
needs: [compile]
uses: ./.github/workflows/container.yml
secrets: inherit
permissions:
packages: write
if: ${{ github.event_name == 'workflow_dispatch' }}
with:
build_image_name: "ghcr.io/zitadel/zitadel-build"
login-container:
uses: ./.github/workflows/login-container.yml
permissions:
packages: write
id-token: write
with:
login_build_image_name: "ghcr.io/zitadel/zitadel-login-build"
node_version: "20"
login-integration-test:
uses: ./.github/workflows/login-integration-test.yml
needs: [login-container]
with:
login_build_image: ${{ needs.login-container.outputs.login_build_image }}
e2e:
uses: ./.github/workflows/e2e.yml
needs: [compile]
release:
uses: ./.github/workflows/release.yml
permissions:
packages: write
contents: write
issues: write
pull-requests: write
needs:
[
version,
core-unit-test,
core-integration-test,
lint,
container,
login-container,
login-integration-test,
e2e,
]
if: ${{ github.event_name == 'workflow_dispatch' }}
secrets:
GCR_JSON_KEY_BASE64: ${{ secrets.GCR_JSON_KEY_BASE64 }}
APP_ID: ${{ secrets.APP_ID }}
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
with:
build_image_name: ${{ needs.container.outputs.build_image }}
semantic_version: "23.0.7"
image_name: "ghcr.io/zitadel/zitadel"
google_image_name: "europe-docker.pkg.dev/zitadel-common/zitadel-repo/zitadel"
build_image_name_login: ${{ needs.login-container.outputs.login_build_image }}
image_name_login: "ghcr.io/zitadel/zitadel-login"
google_image_name_login: "europe-docker.pkg.dev/zitadel-common/zitadel-repo/zitadel-login"

51
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: CI
on:
push:
branches:
- nx-for-ci
pull_request:
permissions:
actions: read
contents: read
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
filter: tree:0
fetch-depth: 0
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
run_install: false
# This enables task distribution via Nx Cloud
# Run this command as early as possible, before dependencies are installed
# Learn more at https://nx.dev/ci/reference/nx-cloud-cli#npx-nxcloud-startcirun
# Uncomment this line to enable task distribution
# - run: pnpm dlx nx start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="build"
# Cache node_modules
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- uses: nrwl/nx-set-shas@v4
# Prepend any command with "nx-cloud record --" to record its logs to Nx Cloud
# - run: pnpm exec nx-cloud record -- echo Hello World
#- run: pnpm exec nx affected -t lint test build
- run: pnpm exec nx run @zitadel/docs:build
# Nx Cloud recommends fixes for failures to help you get CI green faster. Learn more: https://nx.dev/ci/features/self-healing-ci
- run: pnpm exec nx fix-ci
if: always()

View File

@@ -1,65 +0,0 @@
name: "Code Scanning"
on:
push:
branches:
- 'main'
paths-ignore:
- 'docs/**'
pull_request:
# The branches below must be a subset of the branches above
branches:
- 'main'
paths-ignore:
- 'docs/**'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
CodeQL-Build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [go,javascript]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- if: matrix.language == 'go'
name: Install Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
# node to install sass for go
- if: matrix.language == 'go'
uses: actions/setup-node@v4
- if: matrix.language == 'go'
run: |
npm install -g sass
make core_build
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
# Override language selection by uncommenting this and choosing your languages
with:
languages: ${{ matrix.language }}
debug: true
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
# - name: Autobuild
# uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
# autobuild does not work anymore
# and to be able to compile without an actual console build, we just need a placeholder in the console dist folder
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3

View File

@@ -1,89 +0,0 @@
name: Compile
on:
workflow_call:
inputs:
core_cache_key:
required: true
type: string
core_cache_path:
required: true
type: string
console_cache_key:
required: true
type: string
console_cache_path:
required: true
type: string
version:
required: true
type: string
node_version:
required: true
type: string
jobs:
executable:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
goos: [linux, darwin, windows]
goarch: [amd64, arm64]
steps:
- uses: actions/checkout@v4
- uses: actions/cache/restore@v4
timeout-minutes: 1
name: restore console
with:
path: ${{ inputs.console_cache_path }}
key: ${{ inputs.console_cache_key }}
fail-on-cache-miss: true
- uses: actions/cache/restore@v4
timeout-minutes: 1
name: restore core
with:
path: ${{ inputs.core_cache_path }}
key: ${{ inputs.core_cache_key }}
fail-on-cache-miss: true
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: compile
timeout-minutes: 5
run: |
GOOS="${{matrix.goos}}" \
GOARCH="${{matrix.goarch}}" \
VERSION="${{ inputs.version }}" \
COMMIT_SHA="${{ github.sha }}" \
make compile_pipeline
- name: create folder
run: |
mkdir zitadel-${{ matrix.goos }}-${{ matrix.goarch }}
mv zitadel zitadel-${{ matrix.goos }}-${{ matrix.goarch }}/
cp LICENSE zitadel-${{ matrix.goos }}-${{ matrix.goarch }}/
cp README.md zitadel-${{ matrix.goos }}-${{ matrix.goarch }}/
tar -czvf zitadel-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz zitadel-${{ matrix.goos }}-${{ matrix.goarch }}
- uses: actions/upload-artifact@v4
with:
name: zitadel-${{ matrix.goos }}-${{ matrix.goarch }}
path: zitadel-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz
checksums:
runs-on: ubuntu-latest
needs: [executable]
steps:
- uses: actions/download-artifact@v4
with:
path: executables
pattern: 'zitadel-*-*'
- name: move files one folder up
run: mv */*.tar.gz . && find . -type d -empty -delete
working-directory: executables
- run: sha256sum * > checksums.txt
working-directory: executables
- uses: actions/upload-artifact@v4
with:
name: checksums.txt
path: executables/checksums.txt

View File

@@ -1,53 +0,0 @@
name: Build console
on:
workflow_call:
inputs:
node_version:
required: true
type: string
outputs:
cache_key:
value: ${{ jobs.build.outputs.cache_key }}
cache_path:
value: ${{ jobs.build.outputs.cache_path }}
env:
cache_path: console/dist/console
jobs:
build:
outputs:
cache_key: ${{ steps.cache.outputs.cache-primary-key }}
cache_path: ${{ env.cache_path }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache/restore@v4
timeout-minutes: 1
continue-on-error: true
id: cache
with:
key: console-${{ hashFiles('console', 'proto', '!console/dist') }}
restore-keys: |
console-
path: ${{ env.cache_path }}
- if: ${{ steps.cache.outputs.cache-hit != 'true' }}
uses: pnpm/action-setup@v4
- if: ${{ steps.cache.outputs.cache-hit != 'true' }}
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node_version }}
cache: "pnpm"
cache-dependency-path: pnpm-lock.yaml
- if: ${{ steps.cache.outputs.cache-hit != 'true' }}
name: Install dependencies
run: pnpm install --frozen-lockfile
- if: ${{ steps.cache.outputs.cache-hit != 'true' }}
name: Build console with Turbo
run: pnpm turbo build --filter=./console
- if: ${{ steps.cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v4
with:
path: ${{ env.cache_path }}
key: ${{ steps.cache.outputs.cache-primary-key }}

View File

@@ -1,173 +0,0 @@
name: Container
on:
workflow_call:
inputs:
build_image_name:
required: true
type: string
outputs:
build_image:
value: '${{ inputs.build_image_name }}:${{ github.sha }}'
permissions:
packages: write
env:
default_labels: |
org.opencontainers.image.documentation=https://zitadel.com/docs
org.opencontainers.image.vendor=CAOS AG
jobs:
build:
name: zitadel
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
arch: [amd64,arm64]
steps:
-
uses: actions/checkout@v4
-
name: Scratch meta
id: scratch-meta
uses: docker/metadata-action@v5
with:
images: ${{ inputs.build_image_name }}
labels: ${{ env.default_labels}}
tags: |
type=sha,prefix=,suffix=,format=long
-
name: Debug meta
id: debug-meta
uses: docker/metadata-action@v5
with:
images: ${{ inputs.build_image_name }}
labels: ${{ env.default_labels}}
tags: |
type=sha,prefix=,suffix=-debug,format=long
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Login to Docker registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
-
uses: actions/download-artifact@v4
with:
path: .artifacts
name: zitadel-linux-${{ matrix.arch }}
-
name: Unpack executable
run: |
tar -xvf .artifacts/zitadel-linux-${{ matrix.arch }}.tar.gz
mv zitadel-linux-${{ matrix.arch }}/zitadel ./zitadel
-
name: Debug
id: build-debug
uses: docker/build-push-action@v6
timeout-minutes: 3
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
file: build/zitadel/Dockerfile
target: artifact
platforms: linux/${{ matrix.arch }}
push: true
labels: ${{ steps.debug-meta.outputs.labels }}
outputs: type=image,name=${{ inputs.build_image_name }},push-by-digest=true,name-canonical=true,push=true
-
name: Scratch
id: build-scratch
uses: docker/build-push-action@v6
timeout-minutes: 3
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
file: build/zitadel/Dockerfile
target: final
platforms: linux/${{ matrix.arch }}
push: true
labels: ${{ steps.scratch-meta.outputs.labels }}
outputs: type=image,name=${{ inputs.build_image_name }},push-by-digest=true,name-canonical=true,push=true
-
name: Export debug digest
run: |
mkdir -p /tmp/digests/debug
digest="${{ steps.build-debug.outputs.digest }}"
touch "/tmp/digests/debug/${digest#sha256:}"
-
name: Export scratch digest
run: |
mkdir -p /tmp/digests/scratch
digest="${{ steps.build-scratch.outputs.digest }}"
touch "/tmp/digests/scratch/${digest#sha256:}"
-
name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ matrix.arch }}
path: /tmp/digests
if-no-files-found: error
retention-days: 1
merge:
runs-on: ubuntu-latest
needs:
- build
strategy:
fail-fast: false
matrix:
image: [scratch, debug]
include:
- image: scratch
suffix: ''
- image: debug
suffix: '-debug'
steps:
-
name: Download digests
uses: actions/download-artifact@v4
with:
pattern: digests-*
path: /tmp/digests
merge-multiple: true
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Login to Docker registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Docker meta
id: build-meta
uses: docker/metadata-action@v5
with:
images: '${{ inputs.build_image_name }}'
tags: |
type=sha,prefix=,suffix=${{ matrix.suffix }},format=long
-
name: Create build manifest list and push
working-directory: /tmp/digests/${{ matrix.image }}
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< '${{ steps.build-meta.outputs.json }}') \
$(printf '${{ inputs.build_image_name }}@sha256:%s ' *)
-
name: Inspect build image
run: |
docker buildx imagetools inspect ${{ inputs.build_image_name }}:${{ github.sha }}${{ matrix.suffix }}

View File

@@ -1,100 +0,0 @@
name: Integration test core
on:
workflow_call:
inputs:
core_cache_key:
required: true
type: string
core_cache_path:
required: true
type: string
secrets:
CODECOV_TOKEN:
required: true
jobs:
postgres:
runs-on:
group: zitadel-public
services:
postgres:
image: postgres
ports:
- 5432:5432
env:
POSTGRES_USER: zitadel
PGUSER: zitadel
POSTGRES_DB: zitadel
POSTGRES_HOST_AUTH_METHOD: trust
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
--health-start-period 10s
cache:
image: redis:latest
ports:
- 6379:6379
steps:
-
uses: actions/checkout@v4
-
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
-
uses: actions/cache/restore@v4
timeout-minutes: 1
name: restore core
id: restore-core
with:
path: ${{ inputs.core_cache_path }}
key: ${{ inputs.core_cache_key }}
fail-on-cache-miss: true
-
id: go-cache-path
name: set cache path
run: echo "GO_CACHE_PATH=$(go env GOCACHE)" >> $GITHUB_OUTPUT
-
uses: actions/cache/restore@v4
id: cache
timeout-minutes: 1
continue-on-error: true
name: restore previous results
with:
key: integration-test-postgres-${{ inputs.core_cache_key }}
restore-keys: |
integration-test-postgres-core-
path: ${{ steps.go-cache-path.outputs.GO_CACHE_PATH }}
-
name: test
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
env:
ZITADEL_MASTERKEY: MasterkeyNeedsToHave32Characters
run: make core_integration_test
-
name: upload server logs
if: always()
uses: actions/upload-artifact@v4
with:
name: integration-test-server-logs
path: |
tmp/zitadel.log
tmp/race.log.*
-
name: publish coverage
uses: codecov/codecov-action@v4.3.0
with:
file: profile.cov
name: core-integration-tests-postgres
flags: core-integration-tests-postgres
token: ${{ secrets.CODECOV_TOKEN }}
-
uses: actions/cache/save@v4
name: cache results
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
with:
key: integration-test-postgres-${{ inputs.core_cache_key }}
path: ${{ steps.go-cache-path.outputs.GO_CACHE_PATH }}

View File

@@ -1,72 +0,0 @@
name: Unit test core
on:
workflow_call:
inputs:
core_cache_key:
required: true
type: string
core_cache_path:
required: true
type: string
crdb_version:
required: false
type: string
secrets:
CODECOV_TOKEN:
required: true
jobs:
test:
runs-on: ubuntu-latest
steps:
-
uses: actions/checkout@v3
-
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
-
uses: actions/cache/restore@v4
timeout-minutes: 1
name: restore core
id: restore-core
with:
path: ${{ inputs.core_cache_path }}
key: ${{ inputs.core_cache_key }}
fail-on-cache-miss: true
-
id: go-cache-path
name: set cache path
run: echo "GO_CACHE_PATH=$(go env GOCACHE)" >> $GITHUB_OUTPUT
-
uses: actions/cache/restore@v4
id: cache
timeout-minutes: 1
continue-on-error: true
name: restore previous results
with:
key: unit-test-${{ inputs.core_cache_key }}
restore-keys: |
unit-test-core-
path: ${{ steps.go-cache-path.outputs.GO_CACHE_PATH }}
-
name: test
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
run: make core_unit_test
-
name: publish coverage
uses: codecov/codecov-action@v4.3.0
with:
file: profile.cov
name: core-unit-tests
flags: core-unit-tests
token: ${{ secrets.CODECOV_TOKEN }}
-
uses: actions/cache/save@v4
name: cache results
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
with:
key: unit-test-${{ inputs.core_cache_key }}
path: ${{ steps.go-cache-path.outputs.GO_CACHE_PATH }}

View File

@@ -1,80 +0,0 @@
name: Build core
on:
workflow_call:
inputs:
buf_version:
required: true
type: string
node_version:
required: true
type: string
outputs:
cache_key:
value: ${{ jobs.build.outputs.cache_key }}
cache_path:
value: ${{ jobs.build.outputs.cache_path }}
env:
cache_path: |
internal/statik/statik.go
internal/notification/statik/statik.go
internal/api/ui/login/static/resources/themes/zitadel/css/zitadel.css*
internal/api/ui/login/statik/statik.go
internal/api/assets/authz.go
internal/api/assets/router.go
openapi/v2
pkg/grpc/**/*.pb.*
pkg/grpc/**/*.connect.go
jobs:
build:
runs-on: ubuntu-latest
outputs:
cache_key: ${{ steps.cache.outputs.cache-primary-key }}
cache_path: ${{ env.cache_path }}
steps:
-
uses: actions/checkout@v4
-
uses: actions/cache/restore@v4
timeout-minutes: 1
continue-on-error: true
id: cache
with:
key: core-${{ hashFiles( 'go.*', 'openapi', 'cmd', 'pkg/grpc/**/*.go', 'proto', 'internal') }}
restore-keys: |
core-
path: ${{ env.cache_path }}
-
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
uses: bufbuild/buf-setup-action@v1
with:
github_token: ${{ github.token }}
version: ${{ inputs.buf_version }}
-
# node to install sass
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node_version }}
-
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
run: npm install -g sass
-
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
-
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
run: make core_build
-
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v4
with:
key: ${{ steps.cache.outputs.cache-primary-key }}
path: ${{ env.cache_path }}

View File

@@ -1,61 +0,0 @@
name: Build docs
on:
workflow_call:
inputs:
node_version:
required: true
type: string
buf_version:
required: true
type: string
outputs:
cache_key:
value: ${{ jobs.build.outputs.cache_key }}
cache_path:
value: ${{ jobs.build.outputs.cache_path }}
env:
cache_path: docs/build
jobs:
build:
outputs:
cache_key: ${{ steps.cache.outputs.cache-primary-key }}
cache_path: ${{ env.cache_path }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache/restore@v4
timeout-minutes: 1
continue-on-error: true
id: cache
with:
key: docs-${{ hashFiles('docs', 'proto', '!docs/build', '!docs/node_modules', '!docs/protoc-gen-connect-openapi') }}
restore-keys: |
docs-
path: ${{ env.cache_path }}
- if: ${{ steps.cache.outputs.cache-hit != 'true' }}
uses: bufbuild/buf-setup-action@v1
with:
github_token: ${{ github.token }}
version: ${{ inputs.buf_version }}
- if: ${{ steps.cache.outputs.cache-hit != 'true' }}
uses: pnpm/action-setup@v4
- if: ${{ steps.cache.outputs.cache-hit != 'true' }}
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node_version }}
cache: "pnpm"
cache-dependency-path: pnpm-lock.yaml
- if: ${{ steps.cache.outputs.cache-hit != 'true' }}
name: Install dependencies
run: pnpm install
- if: ${{ steps.cache.outputs.cache-hit != 'true' }}
name: Build docs with Turbo
run: pnpm turbo build --filter=./docs
- if: ${{ steps.cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v4
with:
path: ${{ env.cache_path }}
key: ${{ steps.cache.outputs.cache-primary-key }}

View File

@@ -1,63 +0,0 @@
name: "ZITADEL e2e Tests"
on:
workflow_call:
jobs:
test:
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
browser: [firefox, chrome]
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: .artifacts
name: zitadel-linux-amd64
- name: Unpack executable
run: |
tar -xvf .artifacts/zitadel-linux-amd64.tar.gz
mv zitadel-linux-amd64/zitadel ./zitadel
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"
cache-dependency-path: pnpm-lock.yaml
- name: Install dependencies
run: pnpm install
- name: Install Cypress binary
run: cd ./e2e && pnpm exec cypress install
- name: Start DB and ZITADEL
run: |
cd ./e2e
ZITADEL_IMAGE=zitadel:local docker compose up --detach --wait
- name: Cypress run
uses: cypress-io/github-action@v6
env:
CYPRESS_BASE_URL: http://localhost:8080/ui/console
CYPRESS_WEBHOOK_HANDLER_HOST: host.docker.internal
CYPRESS_DATABASE_CONNECTION_URL: "postgresql://root@localhost:26257/zitadel"
CYPRESS_BACKEND_URL: http://localhost:8080
with:
working-directory: e2e
browser: ${{ matrix.browser }}
config-file: cypress.config.ts
install: false
- uses: actions/upload-artifact@v4
if: always()
with:
name: production-tests-${{ matrix.browser }}
path: |
e2e/cypress/screenshots
e2e/cypress/videos
e2e/cypress/results
retention-days: 30

View File

@@ -1,93 +0,0 @@
name: Lint
on:
workflow_call:
inputs:
node_version:
required: true
type: string
buf_version:
required: true
type: string
go_lint_version:
required: true
type: string
core_cache_key:
required: true
type: string
core_cache_path:
required: true
type: string
jobs:
lint-skip:
name: lint skip
runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }}
steps:
- name: Lint skip
run: |
echo "Linting outside of pull requests is skipped"
api:
name: api
runs-on: ubuntu-latest
continue-on-error: true
if: ${{ github.event_name == 'pull_request' }}
steps:
- uses: actions/checkout@v4
- uses: bufbuild/buf-setup-action@v1
with:
version: ${{ inputs.buf_version }}
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: lint
uses: bufbuild/buf-lint-action@v1
- uses: bufbuild/buf-breaking-action@v1
with:
against: "https://github.com/${{ github.repository }}.git#branch=${{ github.base_ref }}"
turbo-lint-unit:
if: ${{ github.event_name == 'pull_request' }}
name: turbo-lint-unit
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Dev Container CLI
run: npm install -g @devcontainers/cli@0.80.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Lint and Unit Test All JavaScript Code
run: npm run devcontainer:lint-unit
- name: Fix Failures
if: failure()
run: |
echo "Reproduce this check locally:"
echo "npm run devcontainer:lint-unit"
echo "If you have pnpm installed, most linting errors can be fixed automatically:"
echo "pnpm turbo lint:fix"
echo "In other cases, you can open the dev container called \"Turbo Lint and Unit Tests\"."
echo "You will have the same environment as the pipeline check as well as some guidance on how to fix the errors."
core:
name: core
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }}
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- uses: actions/cache/restore@v4
timeout-minutes: 1
name: restore core
with:
path: ${{ inputs.core_cache_path }}
key: ${{ inputs.core_cache_key }}
fail-on-cache-miss: true
- uses: golangci/golangci-lint-action@v8
with:
version: ${{ inputs.go_lint_version }}
github-token: ${{ github.token }}
only-new-issues: true

View File

@@ -1,69 +0,0 @@
name: Login Container
on:
workflow_call:
inputs:
login_build_image_name:
description: 'The image repository name of the standalone login image'
type: string
required: true
node_version:
required: true
type: string
outputs:
login_build_image:
description: 'The full image tag of the standalone login image'
value: ${{ inputs.login_build_image_name }}:${{ github.sha }}
permissions:
packages: write
env:
default_labels: |
org.opencontainers.image.documentation=https://zitadel.com/docs
org.opencontainers.image.vendor=CAOS AG
org.opencontainers.image.licenses=MIT
jobs:
login-container:
name: Build Login Container
runs-on: ubuntu-latest
permissions:
packages: write
outputs:
login_build_image: ${{ steps.short-sha.outputs.login_build_image }}
steps:
- uses: actions/checkout@v4
- name: Login meta
id: login-meta
uses: docker/metadata-action@v5
with:
images: ${{ inputs.login_build_image_name }}
labels: ${{ env.default_labels}}
annotations: |
manifest:org.opencontainers.image.licenses=MIT
tags: |
type=sha,prefix=,format=long
- name: Login to Docker registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
id: setup-buildx
uses: docker/setup-buildx-action@v3
- name: Bake login multi-arch
uses: docker/bake-action@v6
env:
NODE_VERSION: ${{ inputs.node_version }}
with:
source: .
push: true
provenance: true
targets: login-standalone
files: |
./apps/login/docker-bake.hcl
${{ github.event_name == 'workflow_dispatch' && './apps/login/docker-bake-release.hcl' || '' }}
./docker-bake.hcl
cwd://${{ steps.login-meta.outputs.bake-file }}

View File

@@ -1,58 +0,0 @@
name: Integration test core
on:
workflow_call:
inputs:
login_build_image:
required: true
type: string
permissions:
packages: write
jobs:
login-integration-test:
name: login-integration-test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Dev Container CLI
run: npm install -g @devcontainers/cli@0.80.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Pull Login Build Image
run: docker compose --file .devcontainer/login-integration/docker-compose.yaml pull
env:
LOGIN_TAG: ${{ inputs.login_build_image }}
- name: Run Integration Tests against the Login and a Mocked Zitadel API
run: npm run devcontainer:integration:login
env:
LOGIN_TAG: ${{ inputs.login_build_image }}
DOCKER_BUILDKIT: 1
- name: Fix Failures
if: failure()
run: |
echo "Reproduce this check locally:"
echo "LOGIN_TAG=${{ inputs.login_build_image }} npm run devcontainer:integration:login"
echo "To fix the failures, open the dev container called \"Login Integration Tests\"."
echo "You will have the same environment as the pipeline check as well as some guidance on how to fix the errors."
- name: Show Compose Status
if: failure()
run: docker compose --file .devcontainer/base/docker-compose.yaml --file .devcontainer/login-integration-ci/docker-compose.yaml ps
- name: Print Config
if: failure()
run: COMPOSE_BAKE=1 docker compose --file .devcontainer/base/docker-compose.yaml --file .devcontainer/login-integration-ci/docker-compose.yaml config login-integration
env:
LOGIN_TAG: ${{ inputs.login_build_image }}
- name: Show Container Logs
if: failure()
run: docker compose --file .devcontainer/base/docker-compose.yaml --file .devcontainer/login-integration-ci/docker-compose.yaml logs --timestamps --no-color --tail 100 login-integration
- name: Inspect All Failed Containers
if: failure()
run: |
docker ps -a --filter "status=exited" --filter "status=created" --format "table {{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Image}}"
for container in $(docker ps -a --filter "status=exited" --filter "status=created" -q); do
echo "Inspecting container $container"
docker inspect $container || true
done

View File

@@ -1,219 +0,0 @@
name: Release
on:
workflow_call:
inputs:
semantic_version:
required: true
type: string
build_image_name:
required: true
type: string
image_name:
required: true
type: string
google_image_name:
required: true
type: string
build_image_name_login:
required: true
type: string
image_name_login:
required: true
type: string
google_image_name_login:
required: true
type: string
secrets:
GCR_JSON_KEY_BASE64:
description: 'base64 endcrypted key to connect to Google'
required: true
APP_ID:
description: 'GH App ID to request token for homebrew update'
required: true
APP_PRIVATE_KEY:
description: 'GH App Private Key to request token for homebrew update'
required: true
jobs:
version:
uses: ./.github/workflows/version.yml
with:
semantic_version: ${{ inputs.semantic_version }}
dry_run: false
# TODO: remove the publish job and publish releases directly with the @semantic-release/github plugin (remove draftRelease: true)
# as soon as it supports configuring the create release payload property make_latest to "legacy"
# https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#create-a-release--parameters
publish:
runs-on: ubuntu-22.04
needs: [ version ]
steps:
- id: get_release
uses: cardinalby/git-get-release-action@v1
with:
commitSha: ${{ github.sha }}
draft: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish Release
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.repos.updateRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: ${{ steps.get_release.outputs.id }},
draft: false,
make_latest: "legacy"
});
docker:
runs-on: ubuntu-22.04
needs: [ version ]
steps:
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Login to Docker registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Login to Google Artifact Registry
uses: docker/login-action@v3
with:
registry: europe-docker.pkg.dev
username: _json_key_base64
password: ${{ secrets.GCR_JSON_KEY_BASE64 }}
-
name: Publish ${{ needs.version.outputs.version }}
run: |
docker buildx imagetools create \
--tag ${{ inputs.image_name }}:${{ needs.version.outputs.version }} \
${{ inputs.build_image_name }}
docker buildx imagetools create \
--tag ${{ inputs.image_name }}:${{ needs.version.outputs.version }}-debug \
${{ inputs.build_image_name }}-debug
docker buildx imagetools create \
--tag ${{ inputs.google_image_name }}:${{ needs.version.outputs.version }} \
${{ inputs.build_image_name }}
docker buildx imagetools create \
--tag ${{ inputs.image_name_login }}:${{ needs.version.outputs.version }} \
${{ inputs.build_image_name_login }}
docker buildx imagetools create \
--tag ${{ inputs.google_image_name_login }}:${{ needs.version.outputs.version }} \
${{ inputs.build_image_name_login }}
-
name: Publish latest
if: ${{ github.ref_name == 'next' }}
run: |
docker buildx imagetools create \
--tag ${{ inputs.image_name }}:latest \
${{ inputs.build_image_name }}
docker buildx imagetools create \
--tag ${{ inputs.image_name }}:latest-debug \
${{ inputs.build_image_name }}-debug
docker buildx imagetools create \
--tag ${{ inputs.image_name_login }}:latest \
${{ inputs.build_image_name_login }}
homebrew-tap:
runs-on: ubuntu-22.04
needs: version
if: ${{ github.ref_name == 'next' }}
continue-on-error: true
steps:
- name: generate token
uses: tibdex/github-app-token@v2
id: generate-token
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Trigger Homebrew
env:
VERSION: ${{ needs.version.outputs.version }}
RUN_ID: ${{ github.run_id }}
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |
gh workflow -R zitadel/homebrew-tap run update.yml -f runId=${RUN_ID} -f version=${VERSION}
helm-chart:
runs-on: ubuntu-22.04
needs: version
if: ${{ github.ref_name == 'next' }}
continue-on-error: true
steps:
- name: generate token
uses: tibdex/github-app-token@v2
id: generate-token
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Trigger Chart Bump
env:
VERSION: ${{ needs.version.outputs.version }}
RUN_ID: ${{ github.run_id }}
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |
gh workflow -R zitadel/zitadel-charts run bump.yml
npm-packages:
runs-on: ubuntu-latest
needs: version
if: ${{ github.ref_name == 'next' }}
continue-on-error: true
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install dependencies
working-directory: login
run: pnpm install --frozen-lockfile
- name: Create Release Pull Request
uses: changesets/action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
version: ${{ needs.version.outputs.version }}
cwd: packages
createGithubReleases: false
login-repo:
runs-on: ubuntu-latest
needs: version
if: ${{ github.ref_name == 'next' }}
continue-on-error: true
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Push Subtree
run: make login_push LOGIN_REMOTE_BRANCH=mirror-zitadel-repo
- name: Create Pull Request
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'chore: mirror zitadel repo'
branch: mirror-zitadel-repo
title: 'chore: mirror zitadel repo'
body: 'This PR updates the login repository with the latest changes from the zitadel repository.'
base: main
reviewers: |
@peintnermax
@eliobischof

View File

@@ -1,52 +0,0 @@
name: Version
on:
workflow_call:
inputs:
semantic_version:
required: true
type: string
dry_run:
required: true
type: boolean
outputs:
version:
value: ${{ jobs.generate.outputs.version }}
published:
value: ${{jobs.generate.outputs.published }}
jobs:
generate:
runs-on: ubuntu-22.04
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
outputs:
version: ${{ steps.output.outputs.VERSION }}
published: ${{ steps.semantic.outputs.new_release_published }}
steps:
-
name: Source checkout
uses: actions/checkout@v4
-
uses: actions/download-artifact@v4
if: ${{ !inputs.dry_run }}
with:
path: .artifacts
pattern: "{checksums.txt,zitadel-*}"
-
name: Semantic Release
uses: cycjimmy/semantic-release-action@v4
id: semantic
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
dry_run: ${{ inputs.dry_run }}
semantic_version: ${{ inputs.semantic_version }}
extra_plugins: |
@semantic-release/exec@6.0.3
@semantic-release/github@10.0.2
-
name: output
id: output
run:
if [[ ! -z "${{ steps.semantic.outputs.new_release_version }}" ]]; then echo "VERSION=v${{ steps.semantic.outputs.new_release_version }}" >> "$GITHUB_OUTPUT"; else echo "VERSION=${{ github.sha }}" >> "$GITHUB_OUTPUT";fi

5
.gitignore vendored
View File

@@ -97,3 +97,8 @@ load-test/output/*
# PNPM
.pnpm-store
.cursor/rules/nx-rules.mdc
.github/instructions/nx.instructions.md
# Nx
.nx

View File

@@ -1,10 +0,0 @@
module.exports = {
root: true,
// Use basic ESLint config since the login app has its own detailed config
extends: ["eslint:recommended"],
settings: {
next: {
rootDir: ["apps/*/"],
},
},
};

View File

@@ -1,2 +0,0 @@
screenshots
videos

View File

@@ -1 +0,0 @@
side-effects-cache=false

View File

@@ -1,18 +0,0 @@
FROM bufbuild/buf:1.54.0 AS dependencies
RUN buf export https://github.com/envoyproxy/protoc-gen-validate.git --path validate --output /proto && \
buf export https://github.com/grpc-ecosystem/grpc-gateway.git --path protoc-gen-openapiv2 --output /proto && \
buf export https://github.com/googleapis/googleapis.git --path google/api/annotations.proto --path google/api/http.proto --path google/api/field_behavior.proto --output /proto
FROM bufbuild/buf:1.54.0 AS zitadel-protos
RUN buf export https://github.com/zitadel/zitadel.git --path ./proto/zitadel --output /zitadel
FROM golang:1.20.5-alpine3.18 AS mock-zitadel
RUN go install github.com/eliobischof/grpc-mock/cmd/grpc-mock@01b09f60db1b501178af59bed03b2c22661df48c
COPY mocked-services.cfg .
COPY initial-stubs initial-stubs
COPY --from=dependencies /proto/ ./
COPY --from=zitadel-protos /zitadel/ ./zitadel/
ENTRYPOINT [ "sh", "-c", "grpc-mock -v 1 -proto $(tr '\n' ',' < ./mocked-services.cfg) -stub-dir ./initial-stubs -mock-addr :22222" ]

View File

@@ -1,66 +0,0 @@
[
{
"service": "zitadel.settings.v2.SettingsService",
"method": "GetBrandingSettings",
"out": {
"data": {}
}
},
{
"service": "zitadel.settings.v2.SettingsService",
"method": "GetSecuritySettings",
"out": {
"data": {}
}
},
{
"service": "zitadel.settings.v2.SettingsService",
"method": "GetLegalAndSupportSettings",
"out": {
"data": {
"settings": {
"tosLink": "http://whatever.com/help",
"privacyPolicyLink": "http://whatever.com/help",
"helpLink": "http://whatever.com/help"
}
}
}
},
{
"service": "zitadel.settings.v2.SettingsService",
"method": "GetActiveIdentityProviders",
"out": {
"data": {
"identityProviders": [
{
"id": "123",
"name": "Hubba bubba",
"type": 10
}
]
}
}
},
{
"service": "zitadel.settings.v2.SettingsService",
"method": "GetPasswordComplexitySettings",
"out": {
"data": {
"settings": {
"minLength": 8,
"requiresUppercase": true,
"requiresLowercase": true,
"requiresNumber": true,
"requiresSymbol": true
}
}
}
},
{
"service": "zitadel.settings.v2.SettingsService",
"method": "GetHostedLoginTranslation",
"out": {
"data": {}
}
}
]

View File

@@ -1,7 +0,0 @@
zitadel/user/v2/user_service.proto
zitadel/org/v2/org_service.proto
zitadel/session/v2/session_service.proto
zitadel/settings/v2/settings_service.proto
zitadel/management.proto
zitadel/auth.proto
zitadel/admin.proto

View File

@@ -1,110 +0,0 @@
import { stub } from "../support/e2e";
describe("verify invite", () => {
beforeEach(() => {
stub("zitadel.org.v2.OrganizationService", "ListOrganizations", {
data: {
details: {
totalResult: 1,
},
result: [{ id: "256088834543534543" }],
},
});
stub("zitadel.user.v2.UserService", "ListAuthenticationMethodTypes", {
data: {
authMethodTypes: [], // user with no auth methods was invited
},
});
stub("zitadel.user.v2.UserService", "GetUserByID", {
data: {
user: {
userId: "221394658884845598",
state: 1,
username: "john@example.com",
loginNames: ["john@example.com"],
preferredLoginName: "john@example.com",
human: {
userId: "221394658884845598",
state: 1,
username: "john@example.com",
loginNames: ["john@example.com"],
preferredLoginName: "john@example.com",
profile: {
givenName: "John",
familyName: "Doe",
avatarUrl: "https://example.com/avatar.jpg",
},
email: {
email: "john@example.com",
isVerified: false,
},
},
},
},
});
stub("zitadel.session.v2.SessionService", "CreateSession", {
data: {
details: {
sequence: 859,
changeDate: new Date("2024-04-04T09:40:55.577Z"),
resourceOwner: "220516472055706145",
},
sessionId: "221394658884845598",
sessionToken: "SDMc7DlYXPgwRJ-Tb5NlLqynysHjEae3csWsKzoZWLplRji0AYY3HgAkrUEBqtLCvOayLJPMd0ax4Q",
challenges: undefined,
},
});
stub("zitadel.session.v2.SessionService", "GetSession", {
data: {
session: {
id: "221394658884845598",
creationDate: new Date("2024-04-04T09:40:55.577Z"),
changeDate: new Date("2024-04-04T09:40:55.577Z"),
sequence: 859,
factors: {
user: {
id: "221394658884845598",
loginName: "john@example.com",
},
password: undefined,
webAuthN: undefined,
intent: undefined,
},
metadata: {},
},
},
});
stub("zitadel.settings.v2.SettingsService", "GetLoginSettings", {
data: {
settings: {
passkeysType: 1,
allowUsernamePassword: true,
},
},
});
});
it.only("shows authenticators after successful invite verification", () => {
stub("zitadel.user.v2.UserService", "VerifyInviteCode");
cy.visit("/verify?userId=221394658884845598&code=abc&invite=true");
cy.url().should("include", Cypress.config().baseUrl + "/authenticator/set");
});
it("shows an error if invite code validation failed", () => {
stub("zitadel.user.v2.UserService", "VerifyInviteCode", {
code: 3,
error: "error validating code",
});
// TODO: Avoid uncaught exception in application
cy.once("uncaught:exception", () => false);
cy.visit("/verify?userId=221394658884845598&code=abc&invite=true");
cy.contains("Could not verify invite", { timeout: 10_000 });
});
});

View File

@@ -1,172 +0,0 @@
import { stub } from "../support/e2e";
describe("login", () => {
beforeEach(() => {
stub("zitadel.org.v2.OrganizationService", "ListOrganizations", {
data: {
details: {
totalResult: 1,
},
result: [{ id: "256088834543534543" }],
},
});
stub("zitadel.session.v2.SessionService", "CreateSession", {
data: {
details: {
sequence: 859,
changeDate: new Date("2024-04-04T09:40:55.577Z"),
resourceOwner: "220516472055706145",
},
sessionId: "221394658884845598",
sessionToken: "SDMc7DlYXPgwRJ-Tb5NlLqynysHjEae3csWsKzoZWLplRji0AYY3HgAkrUEBqtLCvOayLJPMd0ax4Q",
challenges: undefined,
},
});
stub("zitadel.session.v2.SessionService", "GetSession", {
data: {
session: {
id: "221394658884845598",
creationDate: new Date("2024-04-04T09:40:55.577Z"),
changeDate: new Date("2024-04-04T09:40:55.577Z"),
sequence: 859,
factors: {
user: {
id: "221394658884845598",
loginName: "john@example.com",
},
password: undefined,
webAuthN: undefined,
intent: undefined,
},
metadata: {},
},
},
});
stub("zitadel.settings.v2.SettingsService", "GetLoginSettings", {
data: {
settings: {
passkeysType: 1,
allowUsernamePassword: true,
},
},
});
});
describe("password login", () => {
beforeEach(() => {
stub("zitadel.user.v2.UserService", "ListUsers", {
data: {
details: {
totalResult: 1,
},
result: [
{
userId: "221394658884845598",
state: 1,
username: "john@example.com",
loginNames: ["john@example.com"],
preferredLoginName: "john@example.com",
human: {
userId: "221394658884845598",
state: 1,
username: "john@example.com",
loginNames: ["john@example.com"],
preferredLoginName: "john@example.com",
profile: {
givenName: "John",
familyName: "Doe",
avatarUrl: "https://example.com/avatar.jpg",
},
email: {
email: "john@example.com",
isVerified: true,
},
},
},
],
},
});
stub("zitadel.user.v2.UserService", "ListAuthenticationMethodTypes", {
data: {
authMethodTypes: [1], // 1 for password authentication
},
});
});
it("should redirect a user with password authentication to /password", () => {
cy.visit("/loginname?loginName=john%40example.com&submit=true");
cy.url({ timeout: 5 * 60_000 }).should("include", Cypress.config().baseUrl + "/password");
});
describe("with passkey prompt", () => {
beforeEach(() => {
stub("zitadel.session.v2.SessionService", "SetSession", {
data: {
details: {
sequence: 859,
changeDate: "2023-07-04T07:58:20.126Z",
resourceOwner: "220516472055706145",
},
sessionToken: "SDMc7DlYXPgwRJ-Tb5NlLqynysHjEae3csWsKzoZWLplRji0AYY3HgAkrUEBqtLCvOayLJPMd0ax4Q",
challenges: undefined,
},
});
});
// it("should prompt a user to setup passwordless authentication if passkey is allowed in the login settings", () => {
// cy.visit("/loginname?loginName=john%40example.com&submit=true");
// cy.location("pathname", { timeout: 5 * 60_000 }).should("eq", "/password");
// cy.get('input[type="password"]').focus().type("MyStrongPassword!1");
// cy.get('button[type="submit"]').click();
// cy.location("pathname", { timeout: 10_000 }).should(
// "eq",
// "/passkey/set",
// );
// });
});
});
describe("passkey login", () => {
beforeEach(() => {
stub("zitadel.user.v2.UserService", "ListUsers", {
data: {
details: {
totalResult: 1,
},
result: [
{
userId: "221394658884845598",
state: 1,
username: "john@example.com",
loginNames: ["john@example.com"],
preferredLoginName: "john@example.com",
human: {
userId: "221394658884845598",
state: 1,
username: "john@example.com",
loginNames: ["john@example.com"],
preferredLoginName: "john@example.com",
profile: {
givenName: "John",
familyName: "Doe",
avatarUrl: "https://example.com/avatar.jpg",
},
email: {
email: "john@example.com",
isVerified: true,
},
},
},
],
},
});
stub("zitadel.user.v2.UserService", "ListAuthenticationMethodTypes", {
data: {
authMethodTypes: [2], // 2 for passwordless authentication
},
});
});
it("should redirect a user with passwordless authentication to /passkey", () => {
cy.visit("/loginname?loginName=john%40example.com&submit=true");
cy.url().should("include", Cypress.config().baseUrl + "/passkey");
});
});
});

View File

@@ -1,21 +0,0 @@
import { stub } from "../support/e2e";
const IDP_URL = "https://example.com/idp/url";
describe("register idps", () => {
beforeEach(() => {
stub("zitadel.user.v2.UserService", "StartIdentityProviderIntent", {
data: {
authUrl: IDP_URL,
},
});
});
it("should redirect the user to the correct url", () => {
cy.visit("/idp");
cy.get('button[e2e="google"]').click();
cy.origin(IDP_URL, { args: IDP_URL }, (url) => {
cy.location("href").should("eq", url);
});
});
});

View File

@@ -1,73 +0,0 @@
import { stub } from "../support/e2e";
describe("register", () => {
beforeEach(() => {
stub("zitadel.org.v2.OrganizationService", "ListOrganizations", {
data: {
details: {
totalResult: 1,
},
result: [{ id: "256088834543534543" }],
},
});
stub("zitadel.settings.v2.SettingsService", "GetLoginSettings", {
data: {
settings: {
passkeysType: 1,
allowRegister: true,
allowUsernamePassword: true,
defaultRedirectUri: "",
},
},
});
stub("zitadel.user.v2.UserService", "AddHumanUser", {
data: {
userId: "221394658884845598",
},
});
stub("zitadel.session.v2.SessionService", "CreateSession", {
data: {
details: {
sequence: 859,
changeDate: new Date("2024-04-04T09:40:55.577Z"),
resourceOwner: "220516472055706145",
},
sessionId: "221394658884845598",
sessionToken: "SDMc7DlYXPgwRJ-Tb5NlLqynysHjEae3csWsKzoZWLplRji0AYY3HgAkrUEBqtLCvOayLJPMd0ax4Q",
challenges: undefined,
},
});
stub("zitadel.session.v2.SessionService", "GetSession", {
data: {
session: {
id: "221394658884845598",
creationDate: new Date("2024-04-04T09:40:55.577Z"),
changeDate: new Date("2024-04-04T09:40:55.577Z"),
sequence: 859,
factors: {
user: {
id: "221394658884845598",
loginName: "john@example.com",
},
password: undefined,
webAuthN: undefined,
intent: undefined,
},
metadata: {},
},
},
});
});
it("should redirect a user who selects passwordless on register to /passkey/set", () => {
cy.visit("/register");
cy.get('input[data-testid="firstname-text-input"]').focus().type("John");
cy.get('input[data-testid="lastname-text-input"]').focus().type("Doe");
cy.get('input[data-testid="email-text-input"]').focus().type("john@example.com");
cy.get('input[type="checkbox"][value="privacypolicy"]').check();
cy.get('input[type="checkbox"][value="tos"]').check();
cy.get('button[type="submit"]').click();
cy.url().should("include", Cypress.config().baseUrl + "/passkey/set");
});
});

View File

@@ -1,95 +0,0 @@
import { stub } from "../support/e2e";
describe("verify email", () => {
beforeEach(() => {
stub("zitadel.org.v2.OrganizationService", "ListOrganizations", {
data: {
details: {
totalResult: 1,
},
result: [{ id: "256088834543534543" }],
},
});
stub("zitadel.user.v2.UserService", "ListAuthenticationMethodTypes", {
data: {
authMethodTypes: [1], // set one method such that we know that the user was not invited
},
});
stub("zitadel.user.v2.UserService", "SendEmailCode");
stub("zitadel.user.v2.UserService", "GetUserByID", {
data: {
user: {
userId: "221394658884845598",
state: 1,
username: "john@example.com",
loginNames: ["john@example.com"],
preferredLoginName: "john@example.com",
human: {
userId: "221394658884845598",
state: 1,
username: "john@example.com",
loginNames: ["john@example.com"],
preferredLoginName: "john@example.com",
profile: {
givenName: "John",
familyName: "Doe",
avatarUrl: "https://example.com/avatar.jpg",
},
email: {
email: "john@example.com",
isVerified: false, // email is not verified yet
},
},
},
},
});
stub("zitadel.session.v2.SessionService", "CreateSession", {
data: {
details: {
sequence: 859,
changeDate: new Date("2024-04-04T09:40:55.577Z"),
resourceOwner: "220516472055706145",
},
sessionId: "221394658884845598",
sessionToken: "SDMc7DlYXPgwRJ-Tb5NlLqynysHjEae3csWsKzoZWLplRji0AYY3HgAkrUEBqtLCvOayLJPMd0ax4Q",
challenges: undefined,
},
});
stub("zitadel.session.v2.SessionService", "GetSession", {
data: {
session: {
id: "221394658884845598",
creationDate: new Date("2024-04-04T09:40:55.577Z"),
changeDate: new Date("2024-04-04T09:40:55.577Z"),
sequence: 859,
factors: {
user: {
id: "221394658884845598",
loginName: "john@example.com",
},
password: undefined,
webAuthN: undefined,
intent: undefined,
},
metadata: {},
},
},
});
});
it("shows an error if email code validation failed", () => {
stub("zitadel.user.v2.UserService", "VerifyEmail", {
code: 3,
error: "error validating code",
});
// TODO: Avoid uncaught exception in application
cy.once("uncaught:exception", () => false);
cy.visit("/verify?userId=221394658884845598&code=abc");
cy.contains("Could not verify email");
});
});

View File

@@ -1,29 +0,0 @@
const url = Cypress.env("CORE_MOCK_STUBS_URL") || "http://localhost:22220/v1/stubs";
function removeStub(service: string, method: string) {
return cy.request({
url,
method: "DELETE",
qs: {
service,
method,
},
});
}
export function stub(service: string, method: string, out?: any) {
removeStub(service, method);
return cy.request({
url,
method: "POST",
body: {
stubs: [
{
service,
method,
out,
},
],
},
});
}

View File

@@ -1,8 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress", "node"]
},
"include": ["**/*.ts", "../cypress.config.ts"]
}

View File

@@ -1,55 +0,0 @@
FROM node:20-alpine AS runtime
FROM runtime AS pnpm-base
RUN apk add --no-cache libc6-compat
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable && corepack prepare pnpm@10.13.1 --activate
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm add -g turbo@2.5.5
FROM pnpm-base AS pruner
WORKDIR /prune
COPY . .
RUN pnpm turbo prune @zitadel/login @zitadel/client @zitadel/proto --docker
FROM pnpm-base AS installer
WORKDIR /install
COPY --from=pruner /prune/out/pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm fetch --frozen-lockfile
COPY --from=pruner /prune/out/json/ .
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile --ignore-scripts
FROM pnpm-base AS builder
WORKDIR /build
COPY --from=installer /install/ .
COPY --from=pruner /prune/out/full/ .
COPY proto ./proto
ENV CI=true
RUN --mount=type=cache,id=turbo,target=/build/.turbo/cache \
--mount=type=cache,id=next,target=/build/apps/login/.next/cache \
pnpm turbo build:login:standalone --cache-dir=/build/.turbo/cache
FROM scratch AS build-out
COPY /apps/login/public ./apps/login/public
COPY --from=builder /build/apps/login/.next/standalone ./
COPY --from=builder /build/apps/login/.next/static ./apps/login/.next/static
FROM runtime AS login-standalone
WORKDIR /runtime
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
# If /.env-file/.env is mounted into the container, its variables are made available to the server before it starts up.
RUN mkdir -p /.env-file && touch /.env-file/.env && chown -R nextjs:nodejs /.env-file
COPY --chown=nextjs:nodejs apps/login/scripts ./
COPY --chown=nextjs:nodejs --from=build-out . .
# Debug the final structure
USER nextjs
ENV HOSTNAME="0.0.0.0"
ENV PORT=3000
# TODO: Check healthy, not ready
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD ["/bin/sh", "-c", "node /runtime/healthcheck.js http://localhost:${PORT}/ui/v2/login/healthy"]
ENTRYPOINT ["/runtime/entrypoint.sh"]

View File

@@ -1,38 +0,0 @@
*
!apps/login/constants
!apps/login/scripts
!apps/login/src
!apps/login/public
!apps/login/locales
!apps/login/next.config.mjs
!apps/login/next-env-vars.d.ts
!apps/login/next-env.d.ts
!apps/login/tailwind.config.mjs
!apps/login/postcss.config.cjs
!apps/login/tsconfig.json
!apps/login/package.json
!apps/login/turbo.json
!package.json
!pnpm-lock.yaml
!pnpm-workspace.yaml
!turbo.json
!packages/zitadel-proto/package.json
!packages/zitadel-proto/buf.gen.yaml
!packages/zitadel-proto/turbo.json
!packages/zitadel-client/package.json
!packages/zitadel-client/**/package.json
!packages/zitadel-client/src
!packages/zitadel-client/tsconfig.json
!packages/zitadel-client/tsup.config.ts
!packages/zitadel-client/turbo.json
!proto
**/*.md
**/node_modules
**/*.test.ts
**/*.test.tsx

View File

@@ -1,33 +0,0 @@
FROM --platform=$TARGETPLATFORM debian:latest as artifact
ENV ZITADEL_ARGS=
ARG TARGETPLATFORM
RUN apt-get update && apt-get install ca-certificates -y
COPY build/zitadel/entrypoint.sh /app/entrypoint.sh
COPY zitadel /app/zitadel
RUN useradd -s "" --home / zitadel && \
chown zitadel /app/zitadel && \
chmod +x /app/zitadel && \
chown zitadel /app/entrypoint.sh && \
chmod +x /app/entrypoint.sh
WORKDIR /app
ENV PATH="/app:${PATH}"
USER zitadel
ENTRYPOINT ["/app/entrypoint.sh"]
FROM --platform=$TARGETPLATFORM scratch as final
ARG TARGETPLATFORM
COPY --from=artifact /etc/passwd /etc/passwd
COPY --from=artifact /etc/ssl/certs /etc/ssl/certs
COPY --from=artifact /app/zitadel /app/zitadel
HEALTHCHECK NONE
EXPOSE 8080
USER zitadel
ENTRYPOINT ["/app/zitadel"]

View File

@@ -1,3 +0,0 @@
*
!build/zitadel/entrypoint.sh
!zitadel

View File

@@ -1,17 +0,0 @@
#!/bin/bash
case $@ in
sh*)
${@:3}
;;
bash*)
${@:5}
;;
*)
if [[ ! -z "$@" ]]
then
ZITADEL_ARGS="$@"
fi
/app/zitadel ${ZITADEL_ARGS}
;;
esac

9
docs/Dockerfile Normal file
View File

@@ -0,0 +1,9 @@
FROM nginx:1.29.0
RUN touch /var/run/nginx.pid && \
chown -R nginx:nginx /var/cache/nginx /var/run/nginx.pid
USER nginx
COPY --chown=nginx:nginx nginx.conf /etc/nginx/nginx.conf
COPY --chown=nginx:nginx build /usr/share/nginx/html/docs
EXPOSE 3003
ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"]
CMD ["-g", "daemon off;"]

69
docs/nginx.conf Normal file
View File

@@ -0,0 +1,69 @@
worker_processes auto;
pid /tmp/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging
access_log off;
error_log /dev/stderr warn;
# Performance
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 1000;
# Compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_min_length 256;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/x-javascript
application/json
application/xml
application/xml+rss
font/ttf
font/otf
image/svg+xml;
server {
listen 3003;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Docusarus Routing
location / {
try_files $uri $uri/ /index.html;
}
# Static Assets Caching
location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|eot|ttf|svg|map)$ {
expires 1y;
access_log off;
add_header Cache-Control "public, immutable";
}
# Optional: Explicit asset route
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
}

32
docs/project.json Normal file
View File

@@ -0,0 +1,32 @@
{
"name": "@zitadel/docs",
"$schema": "../node_modules/nx/schemas/project-schema.json",
"targets": {
"generate": {
"outputs": ["{projectRoot}/apis/resources/**"]
},
"start": {
"dependsOn": ["generate"]
},
"build": {
"outputs": ["{projectRoot}/build/**"],
"dependsOn": ["generate"]
},
"dev": {
"dependsOn": ["build"]
},
"docker:build": {
"dependsOn": [
"build"
]
},
"docker:run": {
"options": {
"args": [
"-p",
"3003:3003"
]
}
}
}
}

View File

@@ -1,45 +0,0 @@
{
"$schema": "https://turbo.build/schema.json",
"extends": ["//"],
"tasks": {
"generate": {
"dependsOn": ["^generate"],
"outputs": ["docs/api/**", "docs/self-hosting/manage/configure/*.yaml"],
"cache": true
},
"generate:grpc": {
"dependsOn": ["^generate"],
"outputs": ["docs/api/**"],
"cache": true
},
"generate:apidocs": {
"dependsOn": ["generate:grpc"],
"outputs": ["docs/api/**"],
"cache": true
},
"generate:configdocs": {
"outputs": ["docs/self-hosting/manage/configure/*.yaml"],
"cache": true
},
"build": {
"dependsOn": ["generate"],
"outputs": ["build/**"],
"cache": true
},
"dev": {
"dependsOn": ["generate"],
"cache": false,
"persistent": true
},
"start": {
"dependsOn": ["generate"],
"cache": false,
"persistent": true
},
"start:api": {
"dependsOn": ["generate"],
"cache": false,
"persistent": true
}
}
}

135
nx.json Normal file
View File

@@ -0,0 +1,135 @@
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"release": {
"projects": [
"@zitadel/login",
"@zitadel/console",
"@zitadel/api",
"@zitadel/docs"
],
"changelog": {
"workspaceChangelog": {
"createRelease": "github"
}
},
"projectsRelationship": "fixed",
"version": {
"conventionalCommits": true,
"fallbackCurrentVersionResolver": "disk"
},
"releaseTagPattern": "v{version}",
"docker": {
"skipVersionActions": true,
"registryUrl": "ghcr.io",
"groupPreVersionCommand": "echo BEFORE VERSIONING"
},
"dockerVersionScheme": {
"production": "{currentDate|YYMM.DD}.{shortCommitSha}",
"staging": "{currentDate|YYMM.DD}-staging.{shortCommitSha}"
}
},
"namedInputs": {
"proto": [
"{workspaceRoot}/proto/**"
],
"sharedGlobals": [
"{workspaceRoot}/**/.env.*local",
{
"env": "DEBUG"
},
{
"env": "VERCEL_URL"
},
{
"env": "EMAIL_VERIFICATION"
},
{
"env": "AUDIENCE"
},
{
"env": "SYSTEM_USER_ID"
},
{
"env": "SYSTEM_USER_PRIVATE_KEY"
},
{
"env": "ZITADEL_API_URL"
},
{
"env": "ZITADEL_SERVICE_USER_TOKEN"
},
{
"env": "NEXT_PUBLIC_BASE_PATH"
},
{
"env": "CUSTOM_REQUEST_HEADERS"
},
{
"env": "NODE_ENV"
},
{
"env": "PORT"
},
{
"env": "INKEEP_API_KEY"
},
{
"env": "DISPLAY"
},
{
"env": "CYPRESS_DISPLAY"
},
"{workspaceRoot}/.github/workflows/ci.yml"
],
"default": [
"{projectRoot}/**/*",
"sharedGlobals",
"proto"
]
},
"targetDefaults": {
"generate": {
"cache": true
},
"build": {
"cache": true
},
"quality": {
"cache": true
},
"start": {
"cache": true
},
"test:unit": {
"cache": true
},
"test:acceptance": {
"cache": true
},
"test:e2e": {
"cache": true
},
"lint": {
"cache": true
},
"lint:fix": {
"cache": true
},
"dev": {
"cache": false
},
"clean": {
"cache": false
}
},
"nxCloudId": "688a9021d48dce0efe0f119f",
"plugins": [
{
"plugin": "@nx/docker",
"options": {
"buildTarget": "docker:build",
"runTarget": "docker:run"
}
}
]
}

View File

@@ -1,15 +1,7 @@
{
"packageManager": "pnpm@10.13.1",
"packageManager": "pnpm@10.14.0",
"private": true,
"name": "zitadel-monorepo",
"scripts": {
"generate": "turbo run generate",
"changeset": "changeset",
"devcontainer:lint-unit": "FAIL_COMMANDS_ON_ERRORS=true devcontainer up --prebuild --config .devcontainer/turbo-lint-unit/devcontainer.json --workspace-folder .",
"devcontainer:integration:login": "FAIL_COMMANDS_ON_ERRORS=true devcontainer up --prebuild --config .devcontainer/login-integration/devcontainer.json --workspace-folder .",
"clean": "turbo run clean",
"clean:all": "pnpm run clean && rm -rf .turbo node_modules"
},
"name": "zitadel",
"pnpm": {
"overrides": {
"@typescript-eslint/parser": "^8.35.1",
@@ -18,9 +10,13 @@
}
},
"devDependencies": {
"@angular-devkit/core": "~20.1.0",
"@bufbuild/buf": "^1.55.1",
"@changesets/cli": "^2.29.5",
"@devcontainers/cli": "^0.80.0",
"sass": "^1.64.1",
"turbo": "2.5.5"
"@nx/angular": "21.4.0-beta.5",
"@nx/docker": "21.4.0-beta.5",
"nx": "21.3.11",
"sass": "1.64.1"
}
}

3418
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff