From a38239509b876c4e77708104a2b6f8ef54fb1abc Mon Sep 17 00:00:00 2001 From: "Jens L." Date: Tue, 12 Aug 2025 14:50:12 +0100 Subject: [PATCH] root: Better version bump (#14905) Co-authored-by: Marc 'risson' Schmitt --- .bumpversion.cfg | 36 -- .dockerignore | 1 + .../actions/docker-push-variables/action.yml | 6 +- .../docker-push-variables/push_vars.py | 7 +- .github/actions/setup/action.yml | 12 + Makefile | 20 +- authentik/__init__.py | 18 +- authentik/admin/api/system.py | 4 +- authentik/admin/api/version.py | 10 +- authentik/admin/tasks.py | 8 +- authentik/admin/tests/test_api.py | 4 +- .../commands/make_blueprint_schema.py | 4 +- authentik/brands/utils.py | 4 +- authentik/core/management/commands/shell.py | 4 +- authentik/core/templatetags/authentik_core.py | 4 +- authentik/core/views/interface.py | 4 +- authentik/crypto/builder.py | 4 +- authentik/events/models.py | 4 +- authentik/events/tests/test_transports.py | 4 +- .../flows/management/commands/benchmark.py | 4 +- authentik/lib/sentry.py | 6 +- authentik/lib/utils/http.py | 4 +- authentik/outposts/api/outposts.py | 4 +- authentik/outposts/controllers/base.py | 6 +- authentik/outposts/controllers/docker.py | 4 +- authentik/outposts/controllers/k8s/base.py | 6 +- .../outposts/controllers/k8s/deployment.py | 4 +- authentik/outposts/models.py | 6 +- authentik/outposts/tests/test_ws.py | 4 +- authentik/root/settings.py | 6 +- authentik/root/test_plugin.py | 4 +- authentik/sources/plex/plex.py | 4 +- authentik/tasks/api/workers.py | 4 +- authentik/tasks/middleware.py | 8 +- authentik/tasks/signals.py | 4 +- cmd/server/server.go | 2 +- docker-compose.yml | 127 ++++--- internal/constants/VERSION | 1 + internal/constants/constants.go | 15 +- internal/outpost/ak/api.go | 4 +- internal/outpost/ak/api_event.go | 4 +- internal/outpost/ak/global.go | 4 +- internal/web/static.go | 2 +- lifecycle/aws/app.py | 4 +- lifecycle/gunicorn.conf.py | 6 +- .../version_history_update.py | 6 +- pyproject.toml | 317 +++++++++--------- scripts/generate_docker_compose.py | 92 +++++ scripts/generate_semver.py | 4 +- tests/e2e/test_provider_ldap.py | 12 - tests/e2e/test_provider_proxy.py | 18 - tests/e2e/test_provider_proxy_forward.py | 11 - tests/e2e/test_provider_radius.py | 9 - tests/e2e/utils.py | 4 +- uv.lock | 11 - web/package-lock.json | 4 +- web/package.json | 2 +- web/packages/core/version/node.js | 2 +- 58 files changed, 465 insertions(+), 432 deletions(-) delete mode 100644 .bumpversion.cfg create mode 100644 internal/constants/VERSION create mode 100644 scripts/generate_docker_compose.py diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index 4cdd70a004..0000000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,36 +0,0 @@ -[bumpversion] -current_version = 2025.6.4 -tag = True -commit = True -parse = (?P\d+)\.(?P\d+)\.(?P\d+)(?:-(?P[a-zA-Z-]+)(?P[1-9]\\d*))? -serialize = - {major}.{minor}.{patch}-{rc_t}{rc_n} - {major}.{minor}.{patch} -message = release: {new_version} -tag_name = version/{new_version} - -[bumpversion:part:rc_t] -values = - rc - final -optional_value = final - -[bumpversion:file:pyproject.toml] - -[bumpversion:file:uv.lock] - -[bumpversion:file:package.json] - -[bumpversion:file:package-lock.json] - -[bumpversion:file:docker-compose.yml] - -[bumpversion:file:schema.yml] - -[bumpversion:file:blueprints/schema.json] - -[bumpversion:file:authentik/__init__.py] - -[bumpversion:file:internal/constants/constants.go] - -[bumpversion:file:lifecycle/aws/template.yaml] diff --git a/.dockerignore b/.dockerignore index 29cbbac9d1..b91760ac52 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,6 @@ htmlcov *.env.yml +node_modules **/node_modules dist/** build/** diff --git a/.github/actions/docker-push-variables/action.yml b/.github/actions/docker-push-variables/action.yml index 2b1fc9ce26..661e91c396 100644 --- a/.github/actions/docker-push-variables/action.yml +++ b/.github/actions/docker-push-variables/action.yml @@ -54,6 +54,10 @@ outputs: runs: using: "composite" steps: + - name: Setup authentik env + uses: ./.github/actions/setup + with: + dependencies: "python" - name: Generate config id: ev shell: bash @@ -64,4 +68,4 @@ runs: PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} REF: ${{ github.ref }} run: | - python3 ${{ github.action_path }}/push_vars.py + uv run python3 ${{ github.action_path }}/push_vars.py diff --git a/.github/actions/docker-push-variables/push_vars.py b/.github/actions/docker-push-variables/push_vars.py index b11dfd4161..3ab37b84a1 100644 --- a/.github/actions/docker-push-variables/push_vars.py +++ b/.github/actions/docker-push-variables/push_vars.py @@ -1,13 +1,10 @@ """Helper script to get the actual branch name, docker safe""" -import configparser import os +from importlib.metadata import version as package_version from json import dumps from time import time -parser = configparser.ConfigParser() -parser.read(".bumpversion.cfg") - # Decide if we should push the image or not should_push = True if len(os.environ.get("DOCKER_USERNAME", "")) < 1: @@ -31,7 +28,7 @@ is_release = "dev" not in image_names[0] sha = os.environ["GITHUB_SHA"] if not is_pull_request else os.getenv("PR_HEAD_SHA") # 2042.1.0 or 2042.1.0-rc1 -version = parser.get("bumpversion", "current_version") +version = package_version("authentik") # 2042.1 version_family = ".".join(version.split("-", 1)[0].split(".")[:-1]) prerelease = "-" in version diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 3f3a44a34c..000250a697 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -2,6 +2,9 @@ name: "Setup authentik testing environment" description: "Setup authentik testing environment" inputs: + dependencies: + description: "List of dependencies to setup" + default: "system,python,node,go,runtime" postgresql_version: description: "Optional postgresql image tag" default: "16" @@ -10,42 +13,51 @@ runs: using: "composite" steps: - name: Install apt deps + if: ${{ contains(inputs.dependencies, 'system') || contains(inputs.dependencies, 'python') }} shell: bash run: | sudo apt-get update sudo apt-get install --no-install-recommends -y libpq-dev openssl libxmlsec1-dev pkg-config gettext libkrb5-dev krb5-kdc krb5-user krb5-admin-server - name: Install uv + if: ${{ contains(inputs.dependencies, 'python') }} uses: astral-sh/setup-uv@v5 with: enable-cache: true - name: Setup python + if: ${{ contains(inputs.dependencies, 'python') }} uses: actions/setup-python@v5 with: python-version-file: "pyproject.toml" - name: Install Python deps + if: ${{ contains(inputs.dependencies, 'python') }} shell: bash run: uv sync --all-extras --dev --frozen - name: Setup node + if: ${{ contains(inputs.dependencies, 'node') }} uses: actions/setup-node@v4 with: node-version-file: web/package.json cache: "npm" cache-dependency-path: web/package-lock.json - name: Setup go + if: ${{ contains(inputs.dependencies, 'go') }} uses: actions/setup-go@v5 with: go-version-file: "go.mod" - name: Setup docker cache + if: ${{ contains(inputs.dependencies, 'runtime') }} uses: AndreKurait/docker-cache@0fe76702a40db986d9663c24954fc14c6a6031b7 with: key: docker-images-${{ runner.os }}-${{ hashFiles('.github/actions/setup/docker-compose.yml', 'Makefile') }}-${{ inputs.postgresql_version }} - name: Setup dependencies + if: ${{ contains(inputs.dependencies, 'runtime') }} shell: bash run: | export PSQL_TAG=${{ inputs.postgresql_version }} docker compose -f .github/actions/setup/docker-compose.yml up -d cd web && npm ci - name: Generate config + if: ${{ contains(inputs.dependencies, 'python') }} shell: uv run python {0} run: | from authentik.lib.generators import generate_id diff --git a/Makefile b/Makefile index f78e2bc496..21bdea841f 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ GEN_API_GO = gen-go-api pg_user := $(shell uv run python -m authentik.lib.config postgresql.user 2>/dev/null) pg_host := $(shell uv run python -m authentik.lib.config postgresql.host 2>/dev/null) pg_name := $(shell uv run python -m authentik.lib.config postgresql.name 2>/dev/null) +redis_db := $(shell uv run python -m authentik.lib.config redis.db 2>/dev/null) all: lint-fix lint test gen web ## Lint, build, and test everything @@ -57,7 +58,7 @@ migrate: ## Run the Authentik Django server's migrations i18n-extract: core-i18n-extract web-i18n-extract ## Extract strings that require translation into files to send to a translation service aws-cfn: - cd lifecycle/aws && npm run aws-cfn + cd lifecycle/aws && npm i && npm run aws-cfn run-server: ## Run the main authentik server process uv run ak server @@ -79,10 +80,10 @@ core-i18n-extract: install: node-install docs-install core-install ## Install all requires dependencies for `node`, `docs` and `core` dev-drop-db: - dropdb -U ${pg_user} -h ${pg_host} ${pg_name} + dropdb -U ${pg_user} -h ${pg_host} ${pg_name} || true # Also remove the test-db if it exists dropdb -U ${pg_user} -h ${pg_host} test_${pg_name} || true - redis-cli -n 0 flushall + redis-cli -n ${redis_db} flushall dev-create-db: createdb -U ${pg_user} -h ${pg_host} ${pg_name} @@ -93,6 +94,16 @@ update-test-mmdb: ## Update test GeoIP and ASN Databases curl -L https://raw.githubusercontent.com/maxmind/MaxMind-DB/refs/heads/main/test-data/GeoLite2-ASN-Test.mmdb -o ${PWD}/tests/GeoLite2-ASN-Test.mmdb curl -L https://raw.githubusercontent.com/maxmind/MaxMind-DB/refs/heads/main/test-data/GeoLite2-City-Test.mmdb -o ${PWD}/tests/GeoLite2-City-Test.mmdb +bump: ## Bump authentik version. Usage: make bump version=20xx.xx.xx + ifndef version + $(error Usage: make bump version=20xx.xx.xx ) + endif + uv version $(version) + $(MAKE) gen-build gen-compose aws-cfn + npm version --no-git-tag-version --allow-same-version $(version) + cd ${PWD}/web && npm version --no-git-tag-version --allow-same-version $(version) + echo -n $(version) > ${PWD}/internal/constants/VERSION + ######################### ## API Schema ######################### @@ -107,6 +118,9 @@ gen-build: ## Extract the schema from the database AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \ uv run ak spectacular --file schema.yml +gen-compose: + uv run scripts/generate_docker_compose.py + gen-changelog: ## (Release) generate the changelog based from the commits since the last tag git log --pretty=format:" - %s" $(shell git describe --tags $(shell git rev-list --tags --max-count=1))...$(shell git branch --show-current) | sort > changelog.md npx prettier --write changelog.md diff --git a/authentik/__init__.py b/authentik/__init__.py index c19b9a59a6..cd5b4224e0 100644 --- a/authentik/__init__.py +++ b/authentik/__init__.py @@ -1,20 +1,28 @@ """authentik root module""" +from functools import lru_cache +from importlib.metadata import version from os import environ -__version__ = "2025.6.4" ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" -def get_build_hash(fallback: str | None = None) -> str: +@lru_cache +def authentik_version() -> str: + return version("authentik") + + +@lru_cache +def authentik_build_hash(fallback: str | None = None) -> str: """Get build hash""" build_hash = environ.get(ENV_GIT_HASH_KEY, fallback if fallback else "") return fallback if build_hash == "" and fallback else build_hash -def get_full_version() -> str: +@lru_cache +def authentik_full_version() -> str: """Get full version, with build hash appended""" - version = __version__ - if (build_hash := get_build_hash()) != "": + version = authentik_version() + if (build_hash := authentik_build_hash()) != "": return f"{version}+{build_hash}" return version diff --git a/authentik/admin/api/system.py b/authentik/admin/api/system.py index 95a997c531..28b0b86f46 100644 --- a/authentik/admin/api/system.py +++ b/authentik/admin/api/system.py @@ -16,7 +16,7 @@ from rest_framework.request import Request from rest_framework.response import Response from rest_framework.views import APIView -from authentik import get_full_version +from authentik import authentik_full_version from authentik.core.api.utils import PassiveSerializer from authentik.enterprise.license import LicenseKey from authentik.lib.config import CONFIG @@ -78,7 +78,7 @@ class SystemInfoSerializer(PassiveSerializer): """Get versions""" return { "architecture": platform.machine(), - "authentik_version": get_full_version(), + "authentik_version": authentik_full_version(), "environment": get_env(), "openssl_fips_enabled": ( backend._fips_enabled if LicenseKey.get_total().status().is_valid else None diff --git a/authentik/admin/api/version.py b/authentik/admin/api/version.py index f7eecdd53e..94a673a59d 100644 --- a/authentik/admin/api/version.py +++ b/authentik/admin/api/version.py @@ -10,7 +10,7 @@ from rest_framework.request import Request from rest_framework.response import Response from rest_framework.views import APIView -from authentik import __version__, get_build_hash +from authentik import authentik_build_hash, authentik_version from authentik.admin.tasks import VERSION_CACHE_KEY, VERSION_NULL, update_latest_version from authentik.core.api.utils import PassiveSerializer from authentik.outposts.models import Outpost @@ -29,20 +29,20 @@ class VersionSerializer(PassiveSerializer): def get_build_hash(self, _) -> str: """Get build hash, if version is not latest or released""" - return get_build_hash() + return authentik_build_hash() def get_version_current(self, _) -> str: """Get current version""" - return __version__ + return authentik_version() def get_version_latest(self, _) -> str: """Get latest version from cache""" if get_current_tenant().schema_name == get_public_schema_name(): - return __version__ + return authentik_version() version_in_cache = cache.get(VERSION_CACHE_KEY) if not version_in_cache: # pragma: no cover update_latest_version.send() - return __version__ + return authentik_version() return version_in_cache def get_version_latest_valid(self, _) -> bool: diff --git a/authentik/admin/tasks.py b/authentik/admin/tasks.py index 68fac20234..33bb0db12b 100644 --- a/authentik/admin/tasks.py +++ b/authentik/admin/tasks.py @@ -8,7 +8,7 @@ from packaging.version import parse from requests import RequestException from structlog.stdlib import get_logger -from authentik import __version__, get_build_hash +from authentik import authentik_build_hash, authentik_version from authentik.admin.apps import PROM_INFO from authentik.events.models import Event, EventAction from authentik.lib.config import CONFIG @@ -19,16 +19,16 @@ LOGGER = get_logger() VERSION_NULL = "0.0.0" VERSION_CACHE_KEY = "authentik_latest_version" VERSION_CACHE_TIMEOUT = 8 * 60 * 60 # 8 hours -LOCAL_VERSION = parse(__version__) +LOCAL_VERSION = parse(authentik_version()) def _set_prom_info(): """Set prometheus info for version""" PROM_INFO.info( { - "version": __version__, + "version": authentik_version(), "latest": cache.get(VERSION_CACHE_KEY, ""), - "build_hash": get_build_hash(), + "build_hash": authentik_build_hash(), } ) diff --git a/authentik/admin/tests/test_api.py b/authentik/admin/tests/test_api.py index 1f38ee8d15..8dc0ad54bd 100644 --- a/authentik/admin/tests/test_api.py +++ b/authentik/admin/tests/test_api.py @@ -5,7 +5,7 @@ from json import loads from django.test import TestCase from django.urls import reverse -from authentik import __version__ +from authentik import authentik_version from authentik.blueprints.tests import reconcile_app from authentik.core.models import Group, User from authentik.lib.generators import generate_id @@ -27,7 +27,7 @@ class TestAdminAPI(TestCase): response = self.client.get(reverse("authentik_api:admin_version")) self.assertEqual(response.status_code, 200) body = loads(response.content) - self.assertEqual(body["version_current"], __version__) + self.assertEqual(body["version_current"], authentik_version()) def test_apps(self): """Test apps API""" diff --git a/authentik/blueprints/management/commands/make_blueprint_schema.py b/authentik/blueprints/management/commands/make_blueprint_schema.py index d50f51c168..8772f3a115 100644 --- a/authentik/blueprints/management/commands/make_blueprint_schema.py +++ b/authentik/blueprints/management/commands/make_blueprint_schema.py @@ -11,7 +11,7 @@ from rest_framework.relations import PrimaryKeyRelatedField from rest_framework.serializers import Serializer from structlog.stdlib import get_logger -from authentik import __version__ +from authentik import authentik_version from authentik.blueprints.v1.common import BlueprintEntryDesiredState from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT, is_model_allowed from authentik.blueprints.v1.meta.registry import BaseMetaModel, registry @@ -48,7 +48,7 @@ class Command(BaseCommand): "$schema": "http://json-schema.org/draft-07/schema", "$id": "https://goauthentik.io/blueprints/schema.json", "type": "object", - "title": f"authentik {__version__} Blueprint schema", + "title": f"authentik {authentik_version()} Blueprint schema", "required": ["version", "entries"], "properties": { "version": { diff --git a/authentik/brands/utils.py b/authentik/brands/utils.py index db6309c7ff..fd3f318a3c 100644 --- a/authentik/brands/utils.py +++ b/authentik/brands/utils.py @@ -9,7 +9,7 @@ from django.http.request import HttpRequest from django.utils.html import _json_script_escapes from django.utils.safestring import mark_safe -from authentik import get_full_version +from authentik import authentik_full_version from authentik.brands.models import Brand from authentik.lib.sentry import get_http_meta from authentik.tenants.models import Tenant @@ -44,5 +44,5 @@ def context_processor(request: HttpRequest) -> dict[str, Any]: "brand_css": brand_css, "footer_links": tenant.footer_links, "html_meta": {**get_http_meta()}, - "version": get_full_version(), + "version": authentik_full_version(), } diff --git a/authentik/core/management/commands/shell.py b/authentik/core/management/commands/shell.py index 8d2791b8b4..fcddde53b2 100644 --- a/authentik/core/management/commands/shell.py +++ b/authentik/core/management/commands/shell.py @@ -11,7 +11,7 @@ from django.core.management.base import BaseCommand from django.db.models import Model from django.db.models.signals import post_save, pre_delete -from authentik import get_full_version +from authentik import authentik_full_version from authentik.core.models import User from authentik.events.middleware import should_log_model from authentik.events.models import Event, EventAction @@ -19,7 +19,7 @@ from authentik.events.utils import model_to_dict def get_banner_text(shell_type="shell") -> str: - return f"""### authentik {shell_type} ({get_full_version()}) + return f"""### authentik {shell_type} ({authentik_full_version()}) ### Node {platform.node()} | Arch {platform.machine()} | Python {platform.python_version()} """ diff --git a/authentik/core/templatetags/authentik_core.py b/authentik/core/templatetags/authentik_core.py index 44ac3a4ffc..9e4d9d104e 100644 --- a/authentik/core/templatetags/authentik_core.py +++ b/authentik/core/templatetags/authentik_core.py @@ -3,7 +3,7 @@ from django import template from django.templatetags.static import static as static_loader -from authentik import get_full_version +from authentik import authentik_full_version register = template.Library() @@ -11,4 +11,4 @@ register = template.Library() @register.simple_tag() def versioned_script(path: str) -> str: """Wrapper around {% static %} tag that supports setting the version""" - return static_loader(path.replace("%v", get_full_version())) + return static_loader(path.replace("%v", authentik_full_version())) diff --git a/authentik/core/views/interface.py b/authentik/core/views/interface.py index a87de24651..1184b89e3f 100644 --- a/authentik/core/views/interface.py +++ b/authentik/core/views/interface.py @@ -10,7 +10,7 @@ from django.utils.translation import gettext as _ from django.views.generic.base import RedirectView, TemplateView from rest_framework.request import Request -from authentik import get_build_hash +from authentik import authentik_build_hash from authentik.admin.tasks import LOCAL_VERSION from authentik.api.v3.config import ConfigView from authentik.brands.api import CurrentBrandSerializer @@ -52,7 +52,7 @@ class InterfaceView(TemplateView): kwargs["brand_json"] = dumps(brand.data) kwargs["version_family"] = f"{LOCAL_VERSION.major}.{LOCAL_VERSION.minor}" kwargs["version_subdomain"] = f"version-{LOCAL_VERSION.major}-{LOCAL_VERSION.minor}" - kwargs["build"] = get_build_hash() + kwargs["build"] = authentik_build_hash() kwargs["url_kwargs"] = self.kwargs kwargs["base_url"] = self.request.build_absolute_uri(CONFIG.get("web.path", "/")) kwargs["base_url_rel"] = CONFIG.get("web.path", "/") diff --git a/authentik/crypto/builder.py b/authentik/crypto/builder.py index 5201f9d2ce..acac14e5b9 100644 --- a/authentik/crypto/builder.py +++ b/authentik/crypto/builder.py @@ -12,7 +12,7 @@ from cryptography.x509.oid import NameOID from django.db import models from django.utils.translation import gettext_lazy as _ -from authentik import __version__ +from authentik import authentik_version from authentik.crypto.models import CertificateKeyPair @@ -85,7 +85,7 @@ class CertificateBuilder: .issuer_name( x509.Name( [ - x509.NameAttribute(NameOID.COMMON_NAME, f"authentik {__version__}"), + x509.NameAttribute(NameOID.COMMON_NAME, f"authentik {authentik_version()}"), ] ) ) diff --git a/authentik/events/models.py b/authentik/events/models.py index 8b593b2262..64606d4847 100644 --- a/authentik/events/models.py +++ b/authentik/events/models.py @@ -18,7 +18,7 @@ from requests import RequestException from rest_framework.serializers import Serializer from structlog.stdlib import get_logger -from authentik import get_full_version +from authentik import authentik_full_version from authentik.brands.models import Brand from authentik.brands.utils import DEFAULT_BRAND from authentik.core.middleware import ( @@ -434,7 +434,7 @@ class NotificationTransport(TasksModel, SerializerModel): "title": notification.body, "color": "#fd4b2d", "fields": fields, - "footer": f"authentik {get_full_version()}", + "footer": f"authentik {authentik_full_version()}", } ], } diff --git a/authentik/events/tests/test_transports.py b/authentik/events/tests/test_transports.py index f4be8cc429..b1d1c5b279 100644 --- a/authentik/events/tests/test_transports.py +++ b/authentik/events/tests/test_transports.py @@ -7,7 +7,7 @@ from django.core.mail.backends.locmem import EmailBackend from django.test import TestCase from requests_mock import Mocker -from authentik import get_full_version +from authentik import authentik_full_version from authentik.core.tests.utils import create_test_admin_user from authentik.events.models import ( Event, @@ -118,7 +118,7 @@ class TestEventTransports(TestCase): {"short": True, "title": "Event user", "value": self.user.username}, {"title": "foo", "value": "bar,"}, ], - "footer": f"authentik {get_full_version()}", + "footer": f"authentik {authentik_full_version()}", } ], }, diff --git a/authentik/flows/management/commands/benchmark.py b/authentik/flows/management/commands/benchmark.py index cab0f105e3..ed4a6053ef 100644 --- a/authentik/flows/management/commands/benchmark.py +++ b/authentik/flows/management/commands/benchmark.py @@ -10,7 +10,7 @@ from django.core.management.base import BaseCommand from django.test import RequestFactory from structlog.stdlib import get_logger -from authentik import __version__ +from authentik import authentik_version from authentik.core.tests.utils import create_test_admin_user from authentik.flows.models import Flow from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner @@ -99,7 +99,7 @@ class Command(BaseCommand): total_min: int = min(min(inner) for inner in values) total_avg = sum(sum(inner) for inner in values) / sum(len(inner) for inner in values) - print(f"Version: {__version__}") + print(f"Version: {authentik_version()}") print(f"Processes: {len(values)}") print(f"\tMax: {total_max * 100}ms") print(f"\tMin: {total_min * 100}ms") diff --git a/authentik/lib/sentry.py b/authentik/lib/sentry.py index 919666da3a..9b406d1ab9 100644 --- a/authentik/lib/sentry.py +++ b/authentik/lib/sentry.py @@ -30,7 +30,7 @@ from sentry_sdk.tracing import BAGGAGE_HEADER_NAME, SENTRY_TRACE_HEADER_NAME from structlog.stdlib import get_logger from websockets.exceptions import WebSocketException -from authentik import __version__, get_build_hash +from authentik import authentik_build_hash, authentik_version from authentik.lib.config import CONFIG from authentik.lib.utils.http import authentik_user_agent from authentik.lib.utils.reflection import get_env @@ -117,11 +117,11 @@ def sentry_init(**sentry_init_kwargs): ], before_send=before_send, traces_sampler=traces_sampler, - release=f"authentik@{__version__}", + release=f"authentik@{authentik_version()}", transport=SentryTransport, **kwargs, ) - set_tag("authentik.build_hash", get_build_hash("tagged")) + set_tag("authentik.build_hash", authentik_build_hash("tagged")) set_tag("authentik.env", get_env()) set_tag("authentik.component", "backend") diff --git a/authentik/lib/utils/http.py b/authentik/lib/utils/http.py index 81ff775423..4e330b1333 100644 --- a/authentik/lib/utils/http.py +++ b/authentik/lib/utils/http.py @@ -5,7 +5,7 @@ from uuid import uuid4 from requests.sessions import PreparedRequest, Session from structlog.stdlib import get_logger -from authentik import get_full_version +from authentik import authentik_full_version from authentik.lib.config import CONFIG LOGGER = get_logger() @@ -13,7 +13,7 @@ LOGGER = get_logger() def authentik_user_agent() -> str: """Get a common user agent""" - return f"authentik@{get_full_version()}" + return f"authentik@{authentik_full_version()}" class TimeoutSession(Session): diff --git a/authentik/outposts/api/outposts.py b/authentik/outposts/api/outposts.py index b4723ce3a6..318c3e3ff4 100644 --- a/authentik/outposts/api/outposts.py +++ b/authentik/outposts/api/outposts.py @@ -13,7 +13,7 @@ from rest_framework.request import Request from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet -from authentik import get_build_hash +from authentik import authentik_build_hash from authentik.core.api.providers import ProviderSerializer from authentik.core.api.used_by import UsedByMixin from authentik.core.api.utils import JSONDictField, ModelSerializer, PassiveSerializer @@ -194,7 +194,7 @@ class OutpostViewSet(UsedByMixin, ModelViewSet): "openssl_version": state.openssl_version, "fips_enabled": state.fips_enabled, "hostname": state.hostname, - "build_hash_should": get_build_hash(), + "build_hash_should": authentik_build_hash(), } ) return Response(OutpostHealthSerializer(states, many=True).data) diff --git a/authentik/outposts/controllers/base.py b/authentik/outposts/controllers/base.py index a7321e01cc..03b5afaaf0 100644 --- a/authentik/outposts/controllers/base.py +++ b/authentik/outposts/controllers/base.py @@ -4,7 +4,7 @@ from dataclasses import dataclass from structlog.stdlib import get_logger -from authentik import __version__, get_build_hash +from authentik import authentik_build_hash, authentik_version from authentik.events.logs import LogEvent, capture_logs from authentik.lib.config import CONFIG from authentik.lib.sentry import SentryIgnoredException @@ -99,6 +99,6 @@ class BaseController: image_name_template: str = CONFIG.get("outposts.container_image_base") return image_name_template % { "type": self.outpost.type, - "version": __version__, - "build_hash": get_build_hash(), + "version": authentik_version(), + "build_hash": authentik_build_hash(), } diff --git a/authentik/outposts/controllers/docker.py b/authentik/outposts/controllers/docker.py index 16dd01fdce..c3e3255b4b 100644 --- a/authentik/outposts/controllers/docker.py +++ b/authentik/outposts/controllers/docker.py @@ -13,7 +13,7 @@ from paramiko.ssh_exception import SSHException from structlog.stdlib import get_logger from yaml import safe_dump -from authentik import __version__ +from authentik import authentik_version from authentik.outposts.apps import MANAGED_OUTPOST from authentik.outposts.controllers.base import BaseClient, BaseController, ControllerException from authentik.outposts.docker_ssh import DockerInlineSSH, SSHManagedExternallyException @@ -185,7 +185,7 @@ class DockerController(BaseController): try: self.client.images.pull(image) except DockerException: # pragma: no cover - image = f"ghcr.io/goauthentik/{self.outpost.type}:{__version__}" + image = f"ghcr.io/goauthentik/{self.outpost.type}:{authentik_version()}" self.client.images.pull(image) return image diff --git a/authentik/outposts/controllers/k8s/base.py b/authentik/outposts/controllers/k8s/base.py index 40c54a4c9c..a33ba64442 100644 --- a/authentik/outposts/controllers/k8s/base.py +++ b/authentik/outposts/controllers/k8s/base.py @@ -17,7 +17,7 @@ from requests import Response from structlog.stdlib import get_logger from urllib3.exceptions import HTTPError -from authentik import __version__ +from authentik import authentik_version from authentik.outposts.apps import MANAGED_OUTPOST from authentik.outposts.controllers.base import ControllerException from authentik.outposts.controllers.k8s.triggers import NeedsRecreate, NeedsUpdate @@ -29,8 +29,8 @@ T = TypeVar("T", V1Pod, V1Deployment) def get_version() -> str: - """Wrapper for __version__ to make testing easier""" - return __version__ + """Wrapper for authentik_version() to make testing easier""" + return authentik_version() class KubernetesObjectReconciler(Generic[T]): diff --git a/authentik/outposts/controllers/k8s/deployment.py b/authentik/outposts/controllers/k8s/deployment.py index 83dd4a9e6a..2b0d7db8b3 100644 --- a/authentik/outposts/controllers/k8s/deployment.py +++ b/authentik/outposts/controllers/k8s/deployment.py @@ -23,7 +23,7 @@ from kubernetes.client import ( V1SecurityContext, ) -from authentik import get_full_version +from authentik import authentik_full_version from authentik.outposts.controllers.base import FIELD_MANAGER from authentik.outposts.controllers.k8s.base import KubernetesObjectReconciler from authentik.outposts.controllers.k8s.triggers import NeedsUpdate @@ -94,7 +94,7 @@ class DeploymentReconciler(KubernetesObjectReconciler[V1Deployment]): meta = self.get_object_meta(name=self.name) image_name = self.controller.get_container_image() image_pull_secrets = self.outpost.config.kubernetes_image_pull_secrets - version = get_full_version().replace("+", "-") + version = authentik_full_version().replace("+", "-") return V1Deployment( metadata=meta, spec=V1DeploymentSpec( diff --git a/authentik/outposts/models.py b/authentik/outposts/models.py index 6bafa61f4e..6a66613c32 100644 --- a/authentik/outposts/models.py +++ b/authentik/outposts/models.py @@ -19,7 +19,7 @@ from packaging.version import Version, parse from rest_framework.serializers import Serializer from structlog.stdlib import get_logger -from authentik import __version__, get_build_hash +from authentik import authentik_build_hash, authentik_version from authentik.blueprints.models import ManagedModel from authentik.brands.models import Brand from authentik.core.models import ( @@ -40,7 +40,7 @@ from authentik.outposts.controllers.k8s.utils import get_namespace from authentik.tasks.schedules.common import ScheduleSpec from authentik.tasks.schedules.models import ScheduledModel -OUR_VERSION = parse(__version__) +OUR_VERSION = parse(authentik_version()) OUTPOST_HELLO_INTERVAL = 10 LOGGER = get_logger() @@ -481,7 +481,7 @@ class OutpostState: """Check if outpost version matches our version""" if not self.version: return False - if self.build_hash != get_build_hash(): + if self.build_hash != authentik_build_hash(): return False return parse(self.version) != OUR_VERSION diff --git a/authentik/outposts/tests/test_ws.py b/authentik/outposts/tests/test_ws.py index b15e415c7c..a0211c8c06 100644 --- a/authentik/outposts/tests/test_ws.py +++ b/authentik/outposts/tests/test_ws.py @@ -6,7 +6,7 @@ from channels.routing import URLRouter from channels.testing import WebsocketCommunicator from django.test import TransactionTestCase -from authentik import __version__ +from authentik import authentik_version from authentik.core.tests.utils import create_test_flow from authentik.outposts.consumer import WebsocketMessage, WebsocketMessageInstruction from authentik.outposts.models import Outpost, OutpostType @@ -65,7 +65,7 @@ class TestOutpostWS(TransactionTestCase): WebsocketMessage( instruction=WebsocketMessageInstruction.HELLO, args={ - "version": __version__, + "version": authentik_version(), "buildHash": "foo", "uuid": "123", }, diff --git a/authentik/root/settings.py b/authentik/root/settings.py index 2a16c854d8..15b6878193 100644 --- a/authentik/root/settings.py +++ b/authentik/root/settings.py @@ -10,7 +10,7 @@ import orjson from sentry_sdk import set_tag from xmlsec import enable_debug_trace -from authentik import __version__ +from authentik import authentik_version from authentik.lib.config import CONFIG, django_db_config, redis_url from authentik.lib.logging import get_logger_config, structlog_configure from authentik.lib.sentry import sentry_init @@ -144,7 +144,7 @@ GUARDIAN_MONKEY_PATCH_USER = False SPECTACULAR_SETTINGS = { "TITLE": "authentik", "DESCRIPTION": "Making authentication simple.", - "VERSION": __version__, + "VERSION": authentik_version(), "COMPONENT_SPLIT_REQUEST": True, "SCHEMA_PATH_PREFIX": "/api/v([0-9]+(beta)?)", "SCHEMA_PATH_PREFIX_TRIM": True, @@ -567,7 +567,7 @@ if DEBUG: enable_debug_trace(True) -CONFIG.log("info", "Booting authentik", version=__version__) +CONFIG.log("info", "Booting authentik", version=authentik_version()) # Load subapps's settings _filter_and_update(SHARED_APPS + TENANT_APPS) diff --git a/authentik/root/test_plugin.py b/authentik/root/test_plugin.py index 15837e4633..caab7803a8 100644 --- a/authentik/root/test_plugin.py +++ b/authentik/root/test_plugin.py @@ -5,7 +5,7 @@ from ssl import OPENSSL_VERSION import pytest from cryptography.hazmat.backends.openssl.backend import backend -from authentik import get_full_version +from authentik import authentik_full_version IS_CI = "CI" in environ @@ -22,7 +22,7 @@ def pytest_sessionstart(*_, **__): def pytest_report_header(*_, **__): """Add authentik version to pytest output""" return [ - f"authentik version: {get_full_version()}", + f"authentik version: {authentik_full_version()}", f"OpenSSL version: {OPENSSL_VERSION}, FIPS: {backend._fips_enabled}", ] diff --git a/authentik/sources/plex/plex.py b/authentik/sources/plex/plex.py index 8a9e98632b..d31ca06f9b 100644 --- a/authentik/sources/plex/plex.py +++ b/authentik/sources/plex/plex.py @@ -6,7 +6,7 @@ from django.http.response import Http404 from requests.exceptions import RequestException from structlog.stdlib import get_logger -from authentik import __version__ +from authentik import authentik_version from authentik.core.sources.flow_manager import SourceFlowManager from authentik.lib.utils.http import get_http_session from authentik.sources.plex.models import ( @@ -38,7 +38,7 @@ class PlexAuth: """Get common headers""" return { "X-Plex-Product": "authentik", - "X-Plex-Version": __version__, + "X-Plex-Version": authentik_version(), "X-Plex-Device-Vendor": "goauthentik.io", } diff --git a/authentik/tasks/api/workers.py b/authentik/tasks/api/workers.py index d6a855e3f5..4952870560 100644 --- a/authentik/tasks/api/workers.py +++ b/authentik/tasks/api/workers.py @@ -7,7 +7,7 @@ from rest_framework.request import Request from rest_framework.response import Response from rest_framework.views import APIView -from authentik import get_full_version +from authentik import authentik_full_version from authentik.rbac.permissions import HasPermission from authentik.tasks.models import WorkerStatus @@ -30,7 +30,7 @@ class WorkerView(APIView): ) def get(self, request: Request) -> Response: response = [] - our_version = parse(get_full_version()) + our_version = parse(authentik_full_version()) for status in WorkerStatus.objects.filter(last_seen__gt=now() - timedelta(minutes=2)): lock_id = f"goauthentik.io/worker/status/{status.pk}" with pglock.advisory(lock_id, timeout=0, side_effect=pglock.Return) as acquired: diff --git a/authentik/tasks/middleware.py b/authentik/tasks/middleware.py index 70a4138de7..730c2bd4cc 100644 --- a/authentik/tasks/middleware.py +++ b/authentik/tasks/middleware.py @@ -7,7 +7,9 @@ import pglock from django.db import OperationalError, connections from django.utils.timezone import now from django_dramatiq_postgres.middleware import HTTPServer -from django_dramatiq_postgres.middleware import MetricsMiddleware as BaseMetricsMiddleware +from django_dramatiq_postgres.middleware import ( + MetricsMiddleware as BaseMetricsMiddleware, +) from django_redis import get_redis_connection from dramatiq.broker import Broker from dramatiq.message import Message @@ -15,7 +17,7 @@ from dramatiq.middleware import Middleware from redis.exceptions import RedisError from structlog.stdlib import get_logger -from authentik import get_full_version +from authentik import authentik_full_version from authentik.events.models import Event, EventAction from authentik.lib.sentry import should_ignore_exception from authentik.tasks.models import Task, TaskStatus, WorkerStatus @@ -208,7 +210,7 @@ class WorkerStatusMiddleware(Middleware): def run(): status = WorkerStatus.objects.create( hostname=socket.gethostname(), - version=get_full_version(), + version=authentik_full_version(), ) lock_id = f"goauthentik.io/worker/status/{status.pk}" with pglock.advisory(lock_id, side_effect=pglock.Raise): diff --git a/authentik/tasks/signals.py b/authentik/tasks/signals.py index 226a6e447b..7854b4b2a4 100644 --- a/authentik/tasks/signals.py +++ b/authentik/tasks/signals.py @@ -6,7 +6,7 @@ from django.utils.timezone import now, timedelta from packaging.version import parse from prometheus_client import Gauge -from authentik import get_full_version +from authentik import authentik_full_version from authentik.root.monitoring import monitoring_set from authentik.tasks.models import WorkerStatus @@ -22,7 +22,7 @@ GAUGE_WORKERS = Gauge( ) -_version = parse(get_full_version()) +_version = parse(authentik_full_version()) @receiver(monitoring_set) diff --git a/cmd/server/server.go b/cmd/server/server.go index 85556ed304..ea5ce45ecd 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -36,7 +36,7 @@ var rootCmd = &cobra.Command{ AttachStacktrace: true, EnableTracing: true, TracesSampler: sentryutils.SamplerFunc(config.Get().ErrorReporting.SampleRate), - Release: fmt.Sprintf("authentik@%s", constants.VERSION), + Release: fmt.Sprintf("authentik@%s", constants.VERSION()), Environment: config.Get().ErrorReporting.Environment, HTTPTransport: webutils.NewUserAgentTransport(constants.UserAgent(), http.DefaultTransport), IgnoreErrors: []string{ diff --git a/docker-compose.yml b/docker-compose.yml index 0a0966a419..09d36eef1d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,90 +1,85 @@ ---- - services: postgresql: - image: docker.io/library/postgres:16-alpine - restart: unless-stopped - healthcheck: - test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"] - start_period: 20s - interval: 30s - retries: 5 - timeout: 5s - volumes: - - database:/var/lib/postgresql/data + env_file: + - .env environment: + POSTGRES_DB: ${PG_DB:-authentik} POSTGRES_PASSWORD: ${PG_PASS:?database password required} POSTGRES_USER: ${PG_USER:-authentik} - POSTGRES_DB: ${PG_DB:-authentik} - env_file: - - .env - redis: - image: docker.io/library/redis:alpine - command: --save 60 1 --loglevel warning - restart: unless-stopped healthcheck: - test: ["CMD-SHELL", "redis-cli ping | grep PONG"] - start_period: 20s interval: 30s retries: 5 - timeout: 3s - volumes: - - redis:/data - server: - image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.4} + start_period: 20s + test: + - CMD-SHELL + - pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER} + timeout: 5s + image: docker.io/library/postgres:16-alpine restart: unless-stopped - command: server - environment: - AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required} - AUTHENTIK_REDIS__HOST: redis - AUTHENTIK_POSTGRESQL__HOST: postgresql - AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} - AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} - AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} volumes: - - ./media:/media - - ./custom-templates:/templates - env_file: - - .env - ports: - - "${COMPOSE_PORT_HTTP:-9000}:9000" - - "${COMPOSE_PORT_HTTPS:-9443}:9443" + - database:/var/lib/postgresql/data + redis: + command: --save 60 1 --loglevel warning + healthcheck: + interval: 30s + retries: 5 + start_period: 20s + test: + - CMD-SHELL + - redis-cli ping | grep PONG + timeout: 3s + image: docker.io/library/redis:alpine + restart: unless-stopped + volumes: + - redis:/data + server: + command: server depends_on: postgresql: condition: service_healthy redis: condition: service_healthy - worker: - image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.4} - restart: unless-stopped - command: worker + env_file: + - .env environment: - AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required} - AUTHENTIK_REDIS__HOST: redis AUTHENTIK_POSTGRESQL__HOST: postgresql - AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} - # `user: root` and the docker socket volume are optional. - # See more for the docker socket integration here: - # https://goauthentik.io/docs/outposts/integrations/docker - # Removing `user: root` also prevents the worker from fixing the permissions - # on the mounted folders, so when removing this make sure the folders have the correct UID/GID - # (1000:1000 by default) + AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} + AUTHENTIK_REDIS__HOST: redis + AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required} + image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.4} + ports: + - ${COMPOSE_PORT_HTTP:-9000}:9000 + - ${COMPOSE_PORT_HTTPS:-9443}:9443 + restart: unless-stopped + volumes: + - ./media:/media + - ./custom-templates:/templates + worker: + command: worker + depends_on: + postgresql: + condition: service_healthy + redis: + condition: service_healthy + env_file: + - .env + environment: + AUTHENTIK_POSTGRESQL__HOST: postgresql + AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} + AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} + AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} + AUTHENTIK_REDIS__HOST: redis + AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required} + image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.4} + restart: unless-stopped user: root volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ./media:/media - - ./certs:/certs - - ./custom-templates:/templates - env_file: - - .env - depends_on: - postgresql: - condition: service_healthy - redis: - condition: service_healthy - + - /var/run/docker.sock:/var/run/docker.sock + - ./media:/media + - ./certs:/certs + - ./custom-templates:/templates volumes: database: driver: local diff --git a/internal/constants/VERSION b/internal/constants/VERSION new file mode 100644 index 0000000000..f1a89f3a05 --- /dev/null +++ b/internal/constants/VERSION @@ -0,0 +1 @@ +2025.6.4 \ No newline at end of file diff --git a/internal/constants/constants.go b/internal/constants/constants.go index d50a9258d1..10b99374ed 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -1,10 +1,14 @@ package constants import ( + _ "embed" "fmt" "os" ) +//go:embed VERSION +var version string + func BUILD(def string) string { build := os.Getenv("GIT_BUILD_HASH") if build == "" { @@ -13,12 +17,15 @@ func BUILD(def string) string { return build } +func VERSION() string { + return version +} + func FullVersion() string { - ver := VERSION if b := BUILD(""); b != "" { - return fmt.Sprintf("%s+%s", ver, b) + return fmt.Sprintf("%s+%s", version, b) } - return ver + return version } func UserAgentOutpost() string { @@ -32,5 +39,3 @@ func UserAgentIPC() string { func UserAgent() string { return fmt.Sprintf("authentik@%s", FullVersion()) } - -const VERSION = "2025.6.4" diff --git a/internal/outpost/ak/api.go b/internal/outpost/ak/api.go index 85d202032c..cc755c3cc9 100644 --- a/internal/outpost/ak/api.go +++ b/internal/outpost/ak/api.go @@ -198,7 +198,7 @@ func (a *APIController) OnRefresh() error { func (a *APIController) getEventPingArgs() map[string]interface{} { args := map[string]interface{}{ - "version": constants.VERSION, + "version": constants.VERSION(), "buildHash": constants.BUILD(""), "uuid": a.instanceUUID.String(), "golangVersion": runtime.Version(), @@ -218,7 +218,7 @@ func (a *APIController) StartBackgroundTasks() error { "outpost_name": a.Outpost.Name, "outpost_type": a.Server.Type(), "uuid": a.instanceUUID.String(), - "version": constants.VERSION, + "version": constants.VERSION(), "build": constants.BUILD(""), }).Set(1) go func() { diff --git a/internal/outpost/ak/api_event.go b/internal/outpost/ak/api_event.go index ba05c74614..4d14b032f6 100644 --- a/internal/outpost/ak/api_event.go +++ b/internal/outpost/ak/api_event.go @@ -163,7 +163,7 @@ func (ac *APIController) startEventHandler() { "outpost_name": ac.Outpost.Name, "outpost_type": ac.Server.Type(), "uuid": ac.instanceUUID.String(), - "version": constants.VERSION, + "version": constants.VERSION(), "build": constants.BUILD(""), }).SetToCurrentTime() } @@ -228,7 +228,7 @@ func (ac *APIController) startIntervalUpdater() { "outpost_name": ac.Outpost.Name, "outpost_type": ac.Server.Type(), "uuid": ac.instanceUUID.String(), - "version": constants.VERSION, + "version": constants.VERSION(), "build": constants.BUILD(""), }).SetToCurrentTime() } diff --git a/internal/outpost/ak/global.go b/internal/outpost/ak/global.go index 231f36d211..46b483afe5 100644 --- a/internal/outpost/ak/global.go +++ b/internal/outpost/ak/global.go @@ -53,7 +53,7 @@ func doGlobalSetup(outpost api.Outpost, globalConfig *api.Config) { Environment: globalConfig.ErrorReporting.Environment, EnableTracing: true, TracesSampler: sentryutils.SamplerFunc(float64(globalConfig.ErrorReporting.TracesSampleRate)), - Release: fmt.Sprintf("authentik@%s", constants.VERSION), + Release: fmt.Sprintf("authentik@%s", constants.VERSION()), HTTPTransport: webutils.NewUserAgentTransport(constants.UserAgentOutpost(), http.DefaultTransport), IgnoreErrors: []string{ http.ErrAbortHandler.Error(), @@ -66,7 +66,7 @@ func doGlobalSetup(outpost api.Outpost, globalConfig *api.Config) { } if !initialSetup { - l.WithField("hash", constants.BUILD("tagged")).WithField("version", constants.VERSION).Info("Starting authentik outpost") + l.WithField("hash", constants.BUILD("tagged")).WithField("version", constants.VERSION()).Info("Starting authentik outpost") initialSetup = true } } diff --git a/internal/web/static.go b/internal/web/static.go index 25cdcaaed5..9715359821 100644 --- a/internal/web/static.go +++ b/internal/web/static.go @@ -98,7 +98,7 @@ func (ws *WebServer) staticHeaderMiddleware(h http.Handler) http.Handler { etagHandler := etag.Handler(h, false) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Cache-Control", "public, no-transform") - w.Header().Set("X-authentik-version", constants.VERSION) + w.Header().Set("X-authentik-version", constants.VERSION()) w.Header().Set("Vary", "X-authentik-version, Etag") etagHandler.ServeHTTP(w, r) }) diff --git a/lifecycle/aws/app.py b/lifecycle/aws/app.py index 6f67e5c0b4..6a8d087e64 100755 --- a/lifecycle/aws/app.py +++ b/lifecycle/aws/app.py @@ -34,7 +34,7 @@ from aws_cdk import ( ) from constructs import Construct -from authentik import __version__ +from authentik import authentik_version as ak_version class AuthentikStack(Stack): @@ -88,7 +88,7 @@ class AuthentikStack(Stack): self, "AuthentikVersion", type="String", - default=__version__, + default=ak_version(), description="authentik Docker image tag", ) diff --git a/lifecycle/gunicorn.conf.py b/lifecycle/gunicorn.conf.py index 4381684d61..284f001a60 100644 --- a/lifecycle/gunicorn.conf.py +++ b/lifecycle/gunicorn.conf.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING from prometheus_client.values import MultiProcessValue -from authentik import get_full_version +from authentik import authentik_full_version from authentik.lib.config import CONFIG from authentik.lib.debug import start_debug_server from authentik.lib.logging import get_logger_config @@ -127,9 +127,9 @@ if not CONFIG.get_bool("disable_startup_analytics", False): json={ "domain": "authentik", "name": "pageview", - "referrer": get_full_version(), + "referrer": authentik_full_version(), "url": ( - f"http://localhost/{env}?utm_source={get_full_version()}&utm_medium={env}" + f"http://localhost/{env}?utm_source={authentik_full_version()}&utm_medium={env}" ), }, headers={ diff --git a/lifecycle/system_migrations/version_history_update.py b/lifecycle/system_migrations/version_history_update.py index 1fb38eabb8..bfa9803fe1 100644 --- a/lifecycle/system_migrations/version_history_update.py +++ b/lifecycle/system_migrations/version_history_update.py @@ -2,7 +2,7 @@ from lifecycle.migrate import BaseMigration from datetime import datetime -from authentik import __version__, get_build_hash +from authentik import authentik_version, authentik_build_hash class Migration(BaseMigration): @@ -14,7 +14,7 @@ class Migration(BaseMigration): ORDER BY "timestamp" DESC LIMIT 1 """, - (__version__, get_build_hash()), + (authentik_version(), authentik_build_hash()), ) return not bool(self.cur.rowcount) @@ -24,7 +24,7 @@ class Migration(BaseMigration): INSERT INTO authentik_version_history ("timestamp", version, build) VALUES (%s, %s, %s) """, - (datetime.now(), __version__, get_build_hash()), + (datetime.now(), authentik_version(), authentik_build_hash()), ) self.cur.execute( """ diff --git a/pyproject.toml b/pyproject.toml index f8c40e8972..d20a0713ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,112 +5,111 @@ description = "" authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }] requires-python = "==3.13.*" dependencies = [ - "argon2-cffi==25.1.0", - "channels==4.3.0", - "channels-redis==4.3.0", - "cryptography==45.0.5", - "dacite==1.9.2", - "deepmerge==2.0", - "defusedxml==0.7.1", - "django==5.1.11", - "django-countries==7.6.1", - "django-cte==2.0.0", - "django-dramatiq-postgres", - "django-filter==25.1", - "django-guardian==3.0.3", - "django-model-utils==5.0.0", - "django-pglock==1.7.2", - "django-pgtrigger==4.15.2", - "django-prometheus==2.4.1", - "django-redis==6.0.0", - "django-storages[s3]==1.14.6", - "django-tenants==3.8.0", - "djangoql==0.18.1", - "djangorestframework-guardian==0.4.0", - "djangorestframework==3.16.0", - "docker==7.1.0", - "drf-orjson-renderer==1.7.3", - "drf-spectacular==0.28.0", - "dumb-init==1.2.5.post1", - "duo-client==5.5.0", - "fido2==2.0.0", - "geoip2==5.1.0", - "geopy==2.4.1", - "google-api-python-client==2.177.0", - "gssapi==1.9.0", - "gunicorn==23.0.0", - "jsonpatch==1.33", - "jwcrypto==1.5.6", - "kubernetes==33.1.0", - "ldap3==2.9.1", - "lxml==6.0.0", - "msgraph-sdk==1.39.0", - "opencontainers==0.0.15", - "packaging==25.0", - "paramiko==3.5.1", - "psycopg[c,pool]==3.2.9", - "pydantic==2.11.7", - "pydantic-scim==0.0.8", - "pyjwt==2.10.1", - "pyrad==2.4", - "python-kadmin-rs==0.6.1", - "pyyaml==6.0.2", - "requests-oauthlib==2.0.0", - "scim2-filter-parser==0.7.0", - "sentry-sdk==2.33.2", - "service-identity==24.2.0", - "setproctitle==1.3.6", - "structlog==25.4.0", - "swagger-spec-validator==3.0.4", - "twilio==9.7.0", - "ua-parser==1.0.1", - "unidecode==1.4.0", - "urllib3<3", - "uvicorn[standard]==0.35.0", - "watchdog==6.0.0", - "webauthn==2.6.0", - "wsproto==1.2.0", - "xmlsec==1.3.16", - "zxcvbn==4.5.0", + "argon2-cffi==25.1.0", + "channels==4.3.0", + "channels-redis==4.3.0", + "cryptography==45.0.5", + "dacite==1.9.2", + "deepmerge==2.0", + "defusedxml==0.7.1", + "django==5.1.11", + "django-countries==7.6.1", + "django-cte==2.0.0", + "django-dramatiq-postgres", + "django-filter==25.1", + "django-guardian==3.0.3", + "django-model-utils==5.0.0", + "django-pglock==1.7.2", + "django-pgtrigger==4.15.2", + "django-prometheus==2.4.1", + "django-redis==6.0.0", + "django-storages[s3]==1.14.6", + "django-tenants==3.8.0", + "djangoql==0.18.1", + "djangorestframework-guardian==0.4.0", + "djangorestframework==3.16.0", + "docker==7.1.0", + "drf-orjson-renderer==1.7.3", + "drf-spectacular==0.28.0", + "dumb-init==1.2.5.post1", + "duo-client==5.5.0", + "fido2==2.0.0", + "geoip2==5.1.0", + "geopy==2.4.1", + "google-api-python-client==2.177.0", + "gssapi==1.9.0", + "gunicorn==23.0.0", + "jsonpatch==1.33", + "jwcrypto==1.5.6", + "kubernetes==33.1.0", + "ldap3==2.9.1", + "lxml==6.0.0", + "msgraph-sdk==1.39.0", + "opencontainers==0.0.15", + "packaging==25.0", + "paramiko==3.5.1", + "psycopg[c,pool]==3.2.9", + "pydantic==2.11.7", + "pydantic-scim==0.0.8", + "pyjwt==2.10.1", + "pyrad==2.4", + "python-kadmin-rs==0.6.1", + "pyyaml==6.0.2", + "requests-oauthlib==2.0.0", + "scim2-filter-parser==0.7.0", + "sentry-sdk==2.33.2", + "service-identity==24.2.0", + "setproctitle==1.3.6", + "structlog==25.4.0", + "swagger-spec-validator==3.0.4", + "twilio==9.7.0", + "ua-parser==1.0.1", + "unidecode==1.4.0", + "urllib3<3", + "uvicorn[standard]==0.35.0", + "watchdog==6.0.0", + "webauthn==2.6.0", + "wsproto==1.2.0", + "xmlsec==1.3.16", + "zxcvbn==4.5.0", ] [dependency-groups] dev = [ - "aws-cdk-lib==2.188.0", - "bandit==1.8.3", - "black==25.1.0", - "bump2version==1.0.1", - "channels[daphne]==4.3.0", - "codespell==2.4.1", - "colorama==0.4.6", - "constructs==10.4.2", - "coverage[toml]==7.8.0", - "debugpy==1.8.14", - "drf-jsonschema-serializer==3.0.0", - "freezegun==1.5.1", - "importlib-metadata==8.6.1", - "k5test==0.10.4", - "pdoc==15.0.3", - "pytest==8.3.5", - "pytest-django==4.11.1", - "pytest-github-actions-annotate-failures==0.3.0", - "pytest-randomly==3.16.0", - "pytest-timeout==2.4.0", - "requests-mock==1.12.1", - "ruff==0.11.9", - "selenium==4.32.0", + "aws-cdk-lib==2.188.0", + "bandit==1.8.3", + "black==25.1.0", + "channels[daphne]==4.3.0", + "codespell==2.4.1", + "colorama==0.4.6", + "constructs==10.4.2", + "coverage[toml]==7.8.0", + "debugpy==1.8.14", + "drf-jsonschema-serializer==3.0.0", + "freezegun==1.5.1", + "importlib-metadata==8.6.1", + "k5test==0.10.4", + "pdoc==15.0.3", + "pytest==8.3.5", + "pytest-django==4.11.1", + "pytest-github-actions-annotate-failures==0.3.0", + "pytest-randomly==3.16.0", + "pytest-timeout==2.4.0", + "requests-mock==1.12.1", + "ruff==0.11.9", + "selenium==4.32.0", ] [tool.uv] no-binary-package = [ - # This differs from the no-binary packages in the Dockerfile. This is due to the fact - # that these packages are built from source for different reasons than cryptography and kadmin. - # These packages are built from source to link against the libxml2 on the system which is - # required for functionality and to stay up-to-date on both libraries. - # The other packages specified in the dockerfile are compiled from source to link against the - # correct FIPS OpenSSL libraries - "lxml", - "xmlsec", + # This differs from the no-binary packages in the Dockerfile. This is due to the fact + # that these packages are built from source for different reasons than cryptography and kadmin. + # These packages are built from source to link against the libxml2 on the system which is + # required for functionality and to stay up-to-date on both libraries. + # The other packages specified in the dockerfile are compiled from source to link against the + # correct FIPS OpenSSL libraries + "lxml", + "xmlsec", ] [tool.uv.sources] @@ -133,29 +132,29 @@ exclude_dirs = ["**/node_modules/**"] [tool.codespell] skip = [ - "**/node_modules", - "**/package-lock.json", - "schema.yml", - "unittest.xml", - "./blueprints/schema.json", - "go.sum", - "locale", - "**/web/src/locales", - "**/dist", # Distributed build output - "**/storybook-static", - "**/web/xliff", - "**/out", # TypeScript type-checking output - "./web/custom-elements.json", # TypeScript custom element definitions - "./website/build", # TODO: Remove this after moving website to docs - "./website/**/build", # TODO: Remove this after moving website to docs - "./docs/build", # Docusaurus Topic docs build output - "./docs/**/build", # Docusaurus workspaces output - "*.api.mdx", # Generated API docs - "./gen-ts-api", - "./gen-py-api", - "./gen-go-api", - "./htmlcov", - "./media", + "**/node_modules", + "**/package-lock.json", + "schema.yml", + "unittest.xml", + "./blueprints/schema.json", + "go.sum", + "locale", + "**/web/src/locales", + "**/dist", # Distributed build output + "**/storybook-static", + "**/web/xliff", + "**/out", # TypeScript type-checking output + "./web/custom-elements.json", # TypeScript custom element definitions + "./website/build", # TODO: Remove this after moving website to docs + "./website/**/build", # TODO: Remove this after moving website to docs + "./docs/build", # Docusaurus Topic docs build output + "./docs/**/build", # Docusaurus workspaces output + "*.api.mdx", # Generated API docs + "./gen-ts-api", + "./gen-py-api", + "./gen-go-api", + "./htmlcov", + "./media", ] dictionary = ".github/codespell-dictionary.txt,-" ignore-words = ".github/codespell-words.txt" @@ -172,23 +171,23 @@ exclude = ["**/migrations/**", "**/node_modules/**"] [tool.ruff.lint] select = [ - # pycodestyle - "E", - # Pyflakes - "F", - # isort - "I", - # pyupgrade - "UP", - # flake8-bugbear - "B", - # django - "DJ", - # pylint - "PL", + # pycodestyle + "E", + # Pyflakes + "F", + # isort + "I", + # pyupgrade + "UP", + # flake8-bugbear + "B", + # django + "DJ", + # pylint + "PL", ] ignore = [ - "DJ001", # Avoid using `null=True` on string-based fields, + "DJ001", # Avoid using `null=True` on string-based fields, ] [tool.ruff.lint.pylint] @@ -200,14 +199,14 @@ max-returns = 10 source = ["authentik"] relative_files = true omit = [ - "*/asgi.py", - "manage.py", - "*/migrations/*", - "*/management/commands/*", - "*/apps.py", - # TODO: Remove this after moving website to docs - "website/", - "docs/", + "*/asgi.py", + "manage.py", + "*/migrations/*", + "*/management/commands/*", + "*/apps.py", + # TODO: Remove this after moving website to docs + "website/", + "docs/", ] [tool.coverage.report] @@ -215,19 +214,19 @@ sort = "Cover" skip_covered = true precision = 2 exclude_lines = [ - "pragma: no cover", - # Don't complain about missing debug-only code: - "def __unicode__", - "def __str__", - "def __repr__", - "if self.debug", - "if TYPE_CHECKING", - # Don't complain if tests don't hit defensive assertion code: - "raise AssertionError", - "raise NotImplementedError", - # Don't complain if non-runnable code isn't run: - "if 0:", - "if __name__ == .__main__.:", + "pragma: no cover", + # Don't complain about missing debug-only code: + "def __unicode__", + "def __str__", + "def __repr__", + "if self.debug", + "if TYPE_CHECKING", + # Don't complain if tests don't hit defensive assertion code: + "raise AssertionError", + "raise NotImplementedError", + # Don't complain if non-runnable code isn't run: + "if 0:", + "if __name__ == .__main__.:", ] show_missing = true @@ -237,6 +236,6 @@ python_files = ["tests.py", "test_*.py", "*_tests.py"] junit_family = "xunit2" addopts = "-p authentik.root.test_plugin --junitxml=unittest.xml -vv --full-trace --doctest-modules --import-mode=importlib --ignore=authentik/tasks/setup.py" filterwarnings = [ - "ignore:defusedxml.lxml is no longer supported and will be removed in a future release.:DeprecationWarning", - "ignore:SelectableGroups dict interface is deprecated. Use select.:DeprecationWarning", + "ignore:defusedxml.lxml is no longer supported and will be removed in a future release.:DeprecationWarning", + "ignore:SelectableGroups dict interface is deprecated. Use select.:DeprecationWarning", ] diff --git a/scripts/generate_docker_compose.py b/scripts/generate_docker_compose.py new file mode 100644 index 0000000000..7fd1fd7d49 --- /dev/null +++ b/scripts/generate_docker_compose.py @@ -0,0 +1,92 @@ +from yaml import safe_dump + +from authentik import authentik_version + +authentik_image = ( + f"${{AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}}:${{AUTHENTIK_TAG:-{authentik_version()}}}" +) + +base = { + "services": { + "postgresql": { + "env_file": [".env"], + "environment": { + "POSTGRES_DB": "${PG_DB:-authentik}", + "POSTGRES_PASSWORD": "${PG_PASS:?database " "password " "required}", + "POSTGRES_USER": "${PG_USER:-authentik}", + }, + "healthcheck": { + "interval": "30s", + "retries": 5, + "start_period": "20s", + "test": ["CMD-SHELL", "pg_isready -d " "$${POSTGRES_DB} -U " "$${POSTGRES_USER}"], + "timeout": "5s", + }, + "image": "docker.io/library/postgres:16-alpine", + "restart": "unless-stopped", + "volumes": ["database:/var/lib/postgresql/data"], + }, + "redis": { + "command": "--save 60 1 --loglevel warning", + "healthcheck": { + "interval": "30s", + "retries": 5, + "start_period": "20s", + "test": ["CMD-SHELL", "redis-cli ping | grep PONG"], + "timeout": "3s", + }, + "image": "docker.io/library/redis:alpine", + "restart": "unless-stopped", + "volumes": ["redis:/data"], + }, + "server": { + "command": "server", + "depends_on": { + "postgresql": {"condition": "service_healthy"}, + "redis": {"condition": "service_healthy"}, + }, + "env_file": [".env"], + "environment": { + "AUTHENTIK_POSTGRESQL__HOST": "postgresql", + "AUTHENTIK_POSTGRESQL__NAME": "${PG_DB:-authentik}", + "AUTHENTIK_POSTGRESQL__PASSWORD": "${PG_PASS}", + "AUTHENTIK_POSTGRESQL__USER": "${PG_USER:-authentik}", + "AUTHENTIK_REDIS__HOST": "redis", + "AUTHENTIK_SECRET_KEY": "${AUTHENTIK_SECRET_KEY:?secret " "key " "required}", + }, + "image": authentik_image, + "ports": ["${COMPOSE_PORT_HTTP:-9000}:9000", "${COMPOSE_PORT_HTTPS:-9443}:9443"], + "restart": "unless-stopped", + "volumes": ["./media:/media", "./custom-templates:/templates"], + }, + "worker": { + "command": "worker", + "depends_on": { + "postgresql": {"condition": "service_healthy"}, + "redis": {"condition": "service_healthy"}, + }, + "env_file": [".env"], + "environment": { + "AUTHENTIK_POSTGRESQL__HOST": "postgresql", + "AUTHENTIK_POSTGRESQL__NAME": "${PG_DB:-authentik}", + "AUTHENTIK_POSTGRESQL__PASSWORD": "${PG_PASS}", + "AUTHENTIK_POSTGRESQL__USER": "${PG_USER:-authentik}", + "AUTHENTIK_REDIS__HOST": "redis", + "AUTHENTIK_SECRET_KEY": "${AUTHENTIK_SECRET_KEY:?secret " "key " "required}", + }, + "image": authentik_image, + "restart": "unless-stopped", + "user": "root", + "volumes": [ + "/var/run/docker.sock:/var/run/docker.sock", + "./media:/media", + "./certs:/certs", + "./custom-templates:/templates", + ], + }, + }, + "volumes": {"database": {"driver": "local"}, "redis": {"driver": "local"}}, +} + +with open("docker-compose.yml", "w") as _compose: + safe_dump(base, _compose) diff --git a/scripts/generate_semver.py b/scripts/generate_semver.py index 7156662b2a..4d2c769127 100755 --- a/scripts/generate_semver.py +++ b/scripts/generate_semver.py @@ -5,11 +5,11 @@ Generates a Semantic Versioning identifier, suffixed with a timestamp. from time import time -from authentik import __version__ as package_version +from authentik import authentik_version """ See: https://semver.org/#spec-item-9 (Pre-release spec) """ pre_release_timestamp = int(time()) -print(f"{package_version}-{pre_release_timestamp}") +print(f"{authentik_version()}-{pre_release_timestamp}") diff --git a/tests/e2e/test_provider_ldap.py b/tests/e2e/test_provider_ldap.py index 4eb163797c..fac8342755 100644 --- a/tests/e2e/test_provider_ldap.py +++ b/tests/e2e/test_provider_ldap.py @@ -1,7 +1,6 @@ """LDAP and Outpost e2e tests""" from dataclasses import asdict -from time import sleep from guardian.shortcuts import assign_perm from ldap3 import ALL, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, SUBTREE, Connection, Server @@ -56,17 +55,6 @@ class TestProviderLDAP(SeleniumTestCase): outpost.providers.add(ldap) self.start_ldap(outpost) - - # Wait until outpost healthcheck succeeds - healthcheck_retries = 0 - while healthcheck_retries < 50: # noqa: PLR2004 - if len(outpost.state) > 0: - state = outpost.state[0] - if state.last_seen: - break - healthcheck_retries += 1 - sleep(0.5) - sleep(5) return outpost @retry() diff --git a/tests/e2e/test_provider_proxy.py b/tests/e2e/test_provider_proxy.py index 66d25ce8d7..186cb48f87 100644 --- a/tests/e2e/test_provider_proxy.py +++ b/tests/e2e/test_provider_proxy.py @@ -88,15 +88,6 @@ class TestProviderProxy(SeleniumTestCase): self.start_proxy(outpost) - # Wait until outpost healthcheck succeeds - healthcheck_retries = 0 - while healthcheck_retries < 50: # noqa: PLR2004 - if len(outpost.state) > 0: - state = outpost.state[0] - if state.last_seen: - break - healthcheck_retries += 1 - sleep(0.5) sleep(5) self.driver.get("http://localhost:9000/api") @@ -170,15 +161,6 @@ class TestProviderProxy(SeleniumTestCase): self.start_proxy(outpost) - # Wait until outpost healthcheck succeeds - healthcheck_retries = 0 - while healthcheck_retries < 50: # noqa: PLR2004 - if len(outpost.state) > 0: - state = outpost.state[0] - if state.last_seen: - break - healthcheck_retries += 1 - sleep(0.5) sleep(5) self.driver.get("http://localhost:9000/api") diff --git a/tests/e2e/test_provider_proxy_forward.py b/tests/e2e/test_provider_proxy_forward.py index ab48779662..757454d5a4 100644 --- a/tests/e2e/test_provider_proxy_forward.py +++ b/tests/e2e/test_provider_proxy_forward.py @@ -77,17 +77,6 @@ class TestProviderProxyForward(SeleniumTestCase): self.start_outpost(outpost) - # Wait until outpost healthcheck succeeds - healthcheck_retries = 0 - while healthcheck_retries < 50: # noqa: PLR2004 - if len(outpost.state) > 0: - state = outpost.state[0] - if state.last_seen: - break - healthcheck_retries += 1 - sleep(0.5) - sleep(5) - @retry() def test_traefik(self): """Test traefik""" diff --git a/tests/e2e/test_provider_radius.py b/tests/e2e/test_provider_radius.py index 9a22c9a423..39034837d2 100644 --- a/tests/e2e/test_provider_radius.py +++ b/tests/e2e/test_provider_radius.py @@ -51,15 +51,6 @@ class TestProviderRadius(SeleniumTestCase): self.start_radius(outpost) - # Wait until outpost healthcheck succeeds - healthcheck_retries = 0 - while healthcheck_retries < 50: # noqa: PLR2004 - if len(outpost.state) > 0: - state = outpost.state[0] - if state.last_seen: - break - healthcheck_retries += 1 - sleep(0.5) sleep(5) return outpost diff --git a/tests/e2e/utils.py b/tests/e2e/utils.py index 5b2fcfc2f4..587dd2bd2d 100644 --- a/tests/e2e/utils.py +++ b/tests/e2e/utils.py @@ -54,7 +54,7 @@ def get_local_ip() -> str: class DockerTestCase(TestCase): """Mixin for dealing with containers""" - max_healthcheck_attempts = 30 + max_healthcheck_attempts = 45 __client: DockerClient __network: Network @@ -88,7 +88,7 @@ class DockerTestCase(TestCase): sleep(1) attempt += 1 if attempt >= self.max_healthcheck_attempts: - self.failureException("Container failed to start") + raise self.failureException("Container failed to start") def get_container_image(self, base: str) -> str: """Try to pull docker image based on git branch, fallback to main if not found.""" diff --git a/uv.lock b/uv.lock index 1fc41a5cbe..e3c0cab611 100644 --- a/uv.lock +++ b/uv.lock @@ -236,7 +236,6 @@ dev = [ { name = "aws-cdk-lib" }, { name = "bandit" }, { name = "black" }, - { name = "bump2version" }, { name = "channels", extra = ["daphne"] }, { name = "codespell" }, { name = "colorama" }, @@ -334,7 +333,6 @@ dev = [ { name = "aws-cdk-lib", specifier = "==2.188.0" }, { name = "bandit", specifier = "==1.8.3" }, { name = "black", specifier = "==25.1.0" }, - { name = "bump2version", specifier = "==1.0.1" }, { name = "channels", extras = ["daphne"], specifier = "==4.3.0" }, { name = "codespell", specifier = "==2.4.1" }, { name = "colorama", specifier = "==0.4.6" }, @@ -583,15 +581,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/16/56/dd25fb9e47060e8f7e353208678fefb65d1b06704ea30983cad8bdd81370/botocore-1.40.2-py3-none-any.whl", hash = "sha256:a31e6269af05498f8dc1c7f2b3f34448a0f16c79a8601c0389ecddab51b2c2ab", size = 13944886, upload-time = "2025-08-04T19:31:37.027Z" }, ] -[[package]] -name = "bump2version" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/2a/688aca6eeebfe8941235be53f4da780c6edee05dbbea5d7abaa3aab6fad2/bump2version-1.0.1.tar.gz", hash = "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6", size = 36236, upload-time = "2020-10-07T18:38:40.119Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/e3/fa60c47d7c344533142eb3af0b73234ef8ea3fb2da742ab976b947e717df/bump2version-1.0.1-py2.py3-none-any.whl", hash = "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410", size = 22030, upload-time = "2020-10-07T18:38:38.148Z" }, -] - [[package]] name = "cachetools" version = "5.5.2" diff --git a/web/package-lock.json b/web/package-lock.json index ea88233918..57c94b20fe 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "@goauthentik/web", - "version": "0.0.0", + "version": "2025.6.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@goauthentik/web", - "version": "0.0.0", + "version": "2025.6.4", "license": "MIT", "workspaces": [ "./packages/*" diff --git a/web/package.json b/web/package.json index c9dff8117a..e2f97097ee 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "@goauthentik/web", - "version": "0.0.0", + "version": "2025.6.4", "license": "MIT", "private": true, "scripts": { diff --git a/web/packages/core/version/node.js b/web/packages/core/version/node.js index 9f717759f1..31ff0871c9 100644 --- a/web/packages/core/version/node.js +++ b/web/packages/core/version/node.js @@ -41,7 +41,7 @@ export function readGitBuildHash() { /** * Reads the build identifier for the current environment. * - * This must match the behavior defined in authentik's server-side `get_full_version` function. + * This must match the behavior defined in authentik's server-side `authentik_full_version` function. * * @runtime node * @see {@link "authentik\_\_init\_\_.py"}