Compare commits

...

7 Commits

Author SHA1 Message Date
bbd639c37d Merge branch 'main' into better-version-bump
Signed-off-by: Jens Langhammer <jens@goauthentik.io>

# Conflicts:
#	.bumpversion.cfg
#	authentik/__init__.py
#	docker-compose.yml
#	internal/constants/constants.go
2025-06-05 23:31:49 +02:00
5c131fec36 fully get rid of bumpversion
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-04 18:27:04 +02:00
a575de21bc update go version
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-04 18:24:55 +02:00
02275584a6 generate aws and fix compose
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-04 18:14:34 +02:00
27268d533c generate compose
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-04 18:08:27 +02:00
0dba4b61f5 use make and tools instead, less search&replace
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-04 18:08:27 +02:00
c4d4512818 get authentik version from importlib
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-04 18:08:27 +02:00
47 changed files with 275 additions and 214 deletions

View File

@ -1,36 +0,0 @@
[bumpversion]
current_version = 2025.6.0
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[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: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:web/src/common/constants.ts]
[bumpversion:file:lifecycle/aws/template.yaml]

View File

@ -1,13 +1,9 @@
"""Helper script to get the actual branch name, docker safe""" """Helper script to get the actual branch name, docker safe"""
import configparser
import os import os
from importlib.metadata import version as package_version
from json import dumps from json import dumps
from time import time from time import time
parser = configparser.ConfigParser()
parser.read(".bumpversion.cfg")
# Decide if we should push the image or not # Decide if we should push the image or not
should_push = True should_push = True
if len(os.environ.get("DOCKER_USERNAME", "")) < 1: if len(os.environ.get("DOCKER_USERNAME", "")) < 1:
@ -31,7 +27,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") sha = os.environ["GITHUB_SHA"] if not is_pull_request else os.getenv("PR_HEAD_SHA")
# 2042.1.0 or 2042.1.0-rc1 # 2042.1.0 or 2042.1.0-rc1
version = parser.get("bumpversion", "current_version") version = package_version("authentik")
# 2042.1 # 2042.1
version_family = ".".join(version.split("-", 1)[0].split(".")[:-1]) version_family = ".".join(version.split("-", 1)[0].split(".")[:-1])
prerelease = "-" in version prerelease = "-" in version

View File

@ -57,7 +57,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 i18n-extract: core-i18n-extract web-i18n-extract ## Extract strings that require translation into files to send to a translation service
aws-cfn: aws-cfn:
cd lifecycle/aws && npm run aws-cfn cd lifecycle/aws && npm i && npm run aws-cfn
run: ## Run the main authentik server process run: ## Run the main authentik server process
uv run ak server uv run ak server
@ -86,6 +86,15 @@ dev-create-db:
dev-reset: dev-drop-db dev-create-db migrate ## Drop and restore the Authentik PostgreSQL instance to a "fresh install" state. dev-reset: dev-drop-db dev-create-db migrate ## Drop and restore the Authentik PostgreSQL instance to a "fresh install" state.
bump:
uv version $(version)
$(MAKE) gen-build
$(MAKE) gen-compose
$(MAKE) 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 $(version) > ${PWD}/internal/constants/VERSION
######################### #########################
## API Schema ## API Schema
######################### #########################
@ -100,6 +109,9 @@ gen-build: ## Extract the schema from the database
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \ AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \
uv run ak spectacular --file schema.yml 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 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 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 npx prettier --write changelog.md

View File

@ -1,20 +1,28 @@
"""authentik root module""" """authentik root module"""
from functools import lru_cache
from importlib.metadata import version
from os import environ from os import environ
__version__ = "2025.6.0"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" 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""" """Get build hash"""
build_hash = environ.get(ENV_GIT_HASH_KEY, fallback if fallback else "") build_hash = environ.get(ENV_GIT_HASH_KEY, fallback if fallback else "")
return fallback if build_hash == "" and fallback else build_hash 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""" """Get full version, with build hash appended"""
version = __version__ version = authentik_version()
if (build_hash := get_build_hash()) != "": if (build_hash := authentik_build_hash()) != "":
return f"{version}+{build_hash}" return f"{version}+{build_hash}"
return version return version

View File

@ -16,7 +16,7 @@ from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView 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.core.api.utils import PassiveSerializer
from authentik.enterprise.license import LicenseKey from authentik.enterprise.license import LicenseKey
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
@ -78,7 +78,7 @@ class SystemInfoSerializer(PassiveSerializer):
"""Get versions""" """Get versions"""
return { return {
"architecture": platform.machine(), "architecture": platform.machine(),
"authentik_version": get_full_version(), "authentik_version": authentik_full_version(),
"environment": get_env(), "environment": get_env(),
"openssl_fips_enabled": ( "openssl_fips_enabled": (
backend._fips_enabled if LicenseKey.get_total().status().is_valid else None backend._fips_enabled if LicenseKey.get_total().status().is_valid else None

View File

@ -10,7 +10,7 @@ from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView 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.admin.tasks import VERSION_CACHE_KEY, VERSION_NULL, update_latest_version
from authentik.core.api.utils import PassiveSerializer from authentik.core.api.utils import PassiveSerializer
from authentik.outposts.models import Outpost from authentik.outposts.models import Outpost
@ -29,11 +29,11 @@ class VersionSerializer(PassiveSerializer):
def get_build_hash(self, _) -> str: def get_build_hash(self, _) -> str:
"""Get build hash, if version is not latest or released""" """Get build hash, if version is not latest or released"""
return get_build_hash() return authentik_build_hash()
def get_version_current(self, _) -> str: def get_version_current(self, _) -> str:
"""Get current version""" """Get current version"""
return __version__ return authentik_version()
def get_version_latest(self, _) -> str: def get_version_latest(self, _) -> str:
"""Get latest version from cache""" """Get latest version from cache"""
@ -42,7 +42,7 @@ class VersionSerializer(PassiveSerializer):
version_in_cache = cache.get(VERSION_CACHE_KEY) version_in_cache = cache.get(VERSION_CACHE_KEY)
if not version_in_cache: # pragma: no cover if not version_in_cache: # pragma: no cover
update_latest_version.delay() update_latest_version.delay()
return __version__ return authentik_version()
return version_in_cache return version_in_cache
def get_version_latest_valid(self, _) -> bool: def get_version_latest_valid(self, _) -> bool:

View File

@ -10,7 +10,7 @@ from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView 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.rbac.permissions import HasPermission
from authentik.root.celery import CELERY_APP from authentik.root.celery import CELERY_APP
@ -34,7 +34,7 @@ class WorkerView(APIView):
def get(self, request: Request) -> Response: def get(self, request: Request) -> Response:
"""Get currently connected worker count.""" """Get currently connected worker count."""
raw: list[dict[str, dict]] = CELERY_APP.control.ping(timeout=0.5) raw: list[dict[str, dict]] = CELERY_APP.control.ping(timeout=0.5)
our_version = parse(get_full_version()) our_version = parse(authentik_full_version())
response = [] response = []
for worker in raw: for worker in raw:
key = list(worker.keys())[0] key = list(worker.keys())[0]
@ -50,7 +50,7 @@ class WorkerView(APIView):
response.append( response.append(
{ {
"worker_id": f"authentik-debug@{gethostname()}", "worker_id": f"authentik-debug@{gethostname()}",
"version": get_full_version(), "version": authentik_full_version(),
"version_matching": True, "version_matching": True,
} }
) )

View File

@ -4,7 +4,7 @@ from django.dispatch import receiver
from packaging.version import parse from packaging.version import parse
from prometheus_client import Gauge from prometheus_client import Gauge
from authentik import get_full_version from authentik import authentik_full_version
from authentik.root.celery import CELERY_APP from authentik.root.celery import CELERY_APP
from authentik.root.monitoring import monitoring_set from authentik.root.monitoring import monitoring_set
@ -15,7 +15,7 @@ GAUGE_WORKERS = Gauge(
) )
_version = parse(get_full_version()) _version = parse(authentik_full_version())
@receiver(monitoring_set) @receiver(monitoring_set)

View File

@ -6,7 +6,7 @@ from packaging.version import parse
from requests import RequestException from requests import RequestException
from structlog.stdlib import get_logger 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.admin.apps import PROM_INFO
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
from authentik.events.system_tasks import SystemTask, TaskStatus, prefill_task from authentik.events.system_tasks import SystemTask, TaskStatus, prefill_task
@ -18,16 +18,16 @@ LOGGER = get_logger()
VERSION_NULL = "0.0.0" VERSION_NULL = "0.0.0"
VERSION_CACHE_KEY = "authentik_latest_version" VERSION_CACHE_KEY = "authentik_latest_version"
VERSION_CACHE_TIMEOUT = 8 * 60 * 60 # 8 hours VERSION_CACHE_TIMEOUT = 8 * 60 * 60 # 8 hours
LOCAL_VERSION = parse(__version__) LOCAL_VERSION = parse(authentik_version())
def _set_prom_info(): def _set_prom_info():
"""Set prometheus info for version""" """Set prometheus info for version"""
PROM_INFO.info( PROM_INFO.info(
{ {
"version": __version__, "version": authentik_version(),
"latest": cache.get(VERSION_CACHE_KEY, ""), "latest": cache.get(VERSION_CACHE_KEY, ""),
"build_hash": get_build_hash(), "build_hash": authentik_build_hash(),
} }
) )

View File

@ -5,7 +5,7 @@ from json import loads
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
from authentik import __version__ from authentik import authentik_version
from authentik.blueprints.tests import reconcile_app from authentik.blueprints.tests import reconcile_app
from authentik.core.models import Group, User from authentik.core.models import Group, User
from authentik.lib.generators import generate_id from authentik.lib.generators import generate_id
@ -27,7 +27,7 @@ class TestAdminAPI(TestCase):
response = self.client.get(reverse("authentik_api:admin_version")) response = self.client.get(reverse("authentik_api:admin_version"))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
body = loads(response.content) body = loads(response.content)
self.assertEqual(body["version_current"], __version__) self.assertEqual(body["version_current"], authentik_version())
def test_workers(self): def test_workers(self):
"""Test Workers API""" """Test Workers API"""

View File

@ -11,7 +11,7 @@ from rest_framework.relations import PrimaryKeyRelatedField
from rest_framework.serializers import Serializer from rest_framework.serializers import Serializer
from structlog.stdlib import get_logger 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.common import BlueprintEntryDesiredState
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT, is_model_allowed from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT, is_model_allowed
from authentik.blueprints.v1.meta.registry import BaseMetaModel, registry from authentik.blueprints.v1.meta.registry import BaseMetaModel, registry
@ -48,7 +48,7 @@ class Command(BaseCommand):
"$schema": "http://json-schema.org/draft-07/schema", "$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://goauthentik.io/blueprints/schema.json", "$id": "https://goauthentik.io/blueprints/schema.json",
"type": "object", "type": "object",
"title": f"authentik {__version__} Blueprint schema", "title": f"authentik {authentik_version()} Blueprint schema",
"required": ["version", "entries"], "required": ["version", "entries"],
"properties": { "properties": {
"version": { "version": {

View File

@ -6,7 +6,7 @@ from django.db.models import F, Q
from django.db.models import Value as V from django.db.models import Value as V
from django.http.request import HttpRequest from django.http.request import HttpRequest
from authentik import get_full_version from authentik import authentik_full_version
from authentik.brands.models import Brand from authentik.brands.models import Brand
from authentik.lib.sentry import get_http_meta from authentik.lib.sentry import get_http_meta
from authentik.tenants.models import Tenant from authentik.tenants.models import Tenant
@ -36,5 +36,5 @@ def context_processor(request: HttpRequest) -> dict[str, Any]:
"brand": brand, "brand": brand,
"footer_links": tenant.footer_links, "footer_links": tenant.footer_links,
"html_meta": {**get_http_meta()}, "html_meta": {**get_http_meta()},
"version": get_full_version(), "version": authentik_full_version(),
} }

View File

@ -11,7 +11,7 @@ from django.core.management.base import BaseCommand
from django.db.models import Model from django.db.models import Model
from django.db.models.signals import post_save, pre_delete 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.core.models import User
from authentik.events.middleware import should_log_model from authentik.events.middleware import should_log_model
from authentik.events.models import Event, EventAction 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: 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()} """ ### Node {platform.node()} | Arch {platform.machine()} | Python {platform.python_version()} """

View File

@ -3,7 +3,7 @@
from django import template from django import template
from django.templatetags.static import static as static_loader from django.templatetags.static import static as static_loader
from authentik import get_full_version from authentik import authentik_full_version
register = template.Library() register = template.Library()
@ -11,4 +11,4 @@ register = template.Library()
@register.simple_tag() @register.simple_tag()
def versioned_script(path: str) -> str: def versioned_script(path: str) -> str:
"""Wrapper around {% static %} tag that supports setting the version""" """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()))

View File

@ -10,7 +10,7 @@ from django.utils.translation import gettext as _
from django.views.generic.base import RedirectView, TemplateView from django.views.generic.base import RedirectView, TemplateView
from rest_framework.request import Request 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.admin.tasks import LOCAL_VERSION
from authentik.api.v3.config import ConfigView from authentik.api.v3.config import ConfigView
from authentik.brands.api import CurrentBrandSerializer from authentik.brands.api import CurrentBrandSerializer
@ -50,7 +50,7 @@ class InterfaceView(TemplateView):
kwargs["brand_json"] = dumps(CurrentBrandSerializer(self.request.brand).data) kwargs["brand_json"] = dumps(CurrentBrandSerializer(self.request.brand).data)
kwargs["version_family"] = f"{LOCAL_VERSION.major}.{LOCAL_VERSION.minor}" kwargs["version_family"] = f"{LOCAL_VERSION.major}.{LOCAL_VERSION.minor}"
kwargs["version_subdomain"] = f"version-{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["url_kwargs"] = self.kwargs
kwargs["base_url"] = self.request.build_absolute_uri(CONFIG.get("web.path", "/")) kwargs["base_url"] = self.request.build_absolute_uri(CONFIG.get("web.path", "/"))
kwargs["base_url_rel"] = CONFIG.get("web.path", "/") kwargs["base_url_rel"] = CONFIG.get("web.path", "/")

View File

@ -12,7 +12,7 @@ from cryptography.x509.oid import NameOID
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from authentik import __version__ from authentik import authentik_version
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
@ -85,7 +85,7 @@ class CertificateBuilder:
.issuer_name( .issuer_name(
x509.Name( x509.Name(
[ [
x509.NameAttribute(NameOID.COMMON_NAME, f"authentik {__version__}"), x509.NameAttribute(NameOID.COMMON_NAME, f"authentik {authentik_version()}"),
] ]
) )
) )

View File

@ -24,7 +24,7 @@ from requests import RequestException
from rest_framework.serializers import Serializer from rest_framework.serializers import Serializer
from structlog.stdlib import get_logger 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.models import Brand
from authentik.brands.utils import DEFAULT_BRAND from authentik.brands.utils import DEFAULT_BRAND
from authentik.core.middleware import ( from authentik.core.middleware import (
@ -473,7 +473,7 @@ class NotificationTransport(SerializerModel):
"title": notification.body, "title": notification.body,
"color": "#fd4b2d", "color": "#fd4b2d",
"fields": fields, "fields": fields,
"footer": f"authentik {get_full_version()}", "footer": f"authentik {authentik_full_version()}",
} }
], ],
} }

View File

@ -7,7 +7,7 @@ from django.core.mail.backends.locmem import EmailBackend
from django.test import TestCase from django.test import TestCase
from requests_mock import Mocker 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.core.tests.utils import create_test_admin_user
from authentik.events.models import ( from authentik.events.models import (
Event, Event,
@ -118,7 +118,7 @@ class TestEventTransports(TestCase):
{"short": True, "title": "Event user", "value": self.user.username}, {"short": True, "title": "Event user", "value": self.user.username},
{"title": "foo", "value": "bar,"}, {"title": "foo", "value": "bar,"},
], ],
"footer": f"authentik {get_full_version()}", "footer": f"authentik {authentik_full_version()}",
} }
], ],
}, },

View File

@ -10,7 +10,7 @@ from django.core.management.base import BaseCommand
from django.test import RequestFactory from django.test import RequestFactory
from structlog.stdlib import get_logger 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.core.tests.utils import create_test_admin_user
from authentik.flows.models import Flow from authentik.flows.models import Flow
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner 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_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) 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"Processes: {len(values)}")
print(f"\tMax: {total_max * 100}ms") print(f"\tMax: {total_max * 100}ms")
print(f"\tMin: {total_min * 100}ms") print(f"\tMin: {total_min * 100}ms")

View File

@ -31,7 +31,7 @@ from sentry_sdk.tracing import BAGGAGE_HEADER_NAME, SENTRY_TRACE_HEADER_NAME
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from websockets.exceptions import WebSocketException 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.config import CONFIG
from authentik.lib.utils.http import authentik_user_agent from authentik.lib.utils.http import authentik_user_agent
from authentik.lib.utils.reflection import get_env from authentik.lib.utils.reflection import get_env
@ -78,11 +78,11 @@ def sentry_init(**sentry_init_kwargs):
], ],
before_send=before_send, before_send=before_send,
traces_sampler=traces_sampler, traces_sampler=traces_sampler,
release=f"authentik@{__version__}", release=f"authentik@{authentik_version()}",
transport=SentryTransport, transport=SentryTransport,
**kwargs, **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.env", get_env())
set_tag("authentik.component", "backend") set_tag("authentik.component", "backend")

View File

@ -5,7 +5,7 @@ from uuid import uuid4
from requests.sessions import PreparedRequest, Session from requests.sessions import PreparedRequest, Session
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik import get_full_version from authentik import authentik_full_version
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
LOGGER = get_logger() LOGGER = get_logger()
@ -13,7 +13,7 @@ LOGGER = get_logger()
def authentik_user_agent() -> str: def authentik_user_agent() -> str:
"""Get a common user agent""" """Get a common user agent"""
return f"authentik@{get_full_version()}" return f"authentik@{authentik_full_version()}"
class TimeoutSession(Session): class TimeoutSession(Session):

View File

@ -13,7 +13,7 @@ from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet 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.providers import ProviderSerializer
from authentik.core.api.used_by import UsedByMixin from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import JSONDictField, ModelSerializer, PassiveSerializer from authentik.core.api.utils import JSONDictField, ModelSerializer, PassiveSerializer
@ -194,7 +194,7 @@ class OutpostViewSet(UsedByMixin, ModelViewSet):
"openssl_version": state.openssl_version, "openssl_version": state.openssl_version,
"fips_enabled": state.fips_enabled, "fips_enabled": state.fips_enabled,
"hostname": state.hostname, "hostname": state.hostname,
"build_hash_should": get_build_hash(), "build_hash_should": authentik_build_hash(),
} }
) )
return Response(OutpostHealthSerializer(states, many=True).data) return Response(OutpostHealthSerializer(states, many=True).data)

View File

@ -4,7 +4,7 @@ from dataclasses import dataclass
from structlog.stdlib import get_logger 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.events.logs import LogEvent, capture_logs
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
from authentik.lib.sentry import SentryIgnoredException from authentik.lib.sentry import SentryIgnoredException
@ -99,6 +99,6 @@ class BaseController:
image_name_template: str = CONFIG.get("outposts.container_image_base") image_name_template: str = CONFIG.get("outposts.container_image_base")
return image_name_template % { return image_name_template % {
"type": self.outpost.type, "type": self.outpost.type,
"version": __version__, "version": authentik_version(),
"build_hash": get_build_hash(), "build_hash": authentik_build_hash(),
} }

View File

@ -13,7 +13,7 @@ from paramiko.ssh_exception import SSHException
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from yaml import safe_dump from yaml import safe_dump
from authentik import __version__ from authentik import authentik_version
from authentik.outposts.apps import MANAGED_OUTPOST from authentik.outposts.apps import MANAGED_OUTPOST
from authentik.outposts.controllers.base import BaseClient, BaseController, ControllerException from authentik.outposts.controllers.base import BaseClient, BaseController, ControllerException
from authentik.outposts.docker_ssh import DockerInlineSSH, SSHManagedExternallyException from authentik.outposts.docker_ssh import DockerInlineSSH, SSHManagedExternallyException
@ -185,7 +185,7 @@ class DockerController(BaseController):
try: try:
self.client.images.pull(image) self.client.images.pull(image)
except DockerException: # pragma: no cover 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) self.client.images.pull(image)
return image return image

View File

@ -17,7 +17,7 @@ from requests import Response
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from urllib3.exceptions import HTTPError from urllib3.exceptions import HTTPError
from authentik import __version__ from authentik import authentik_version
from authentik.outposts.apps import MANAGED_OUTPOST from authentik.outposts.apps import MANAGED_OUTPOST
from authentik.outposts.controllers.base import ControllerException from authentik.outposts.controllers.base import ControllerException
from authentik.outposts.controllers.k8s.triggers import NeedsRecreate, NeedsUpdate from authentik.outposts.controllers.k8s.triggers import NeedsRecreate, NeedsUpdate
@ -29,8 +29,8 @@ T = TypeVar("T", V1Pod, V1Deployment)
def get_version() -> str: def get_version() -> str:
"""Wrapper for __version__ to make testing easier""" """Wrapper for authentik_version() to make testing easier"""
return __version__ return authentik_version()
class KubernetesObjectReconciler(Generic[T]): class KubernetesObjectReconciler(Generic[T]):

View File

@ -23,7 +23,7 @@ from kubernetes.client import (
V1SecurityContext, 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.base import FIELD_MANAGER
from authentik.outposts.controllers.k8s.base import KubernetesObjectReconciler from authentik.outposts.controllers.k8s.base import KubernetesObjectReconciler
from authentik.outposts.controllers.k8s.triggers import NeedsUpdate from authentik.outposts.controllers.k8s.triggers import NeedsUpdate
@ -94,7 +94,7 @@ class DeploymentReconciler(KubernetesObjectReconciler[V1Deployment]):
meta = self.get_object_meta(name=self.name) meta = self.get_object_meta(name=self.name)
image_name = self.controller.get_container_image() image_name = self.controller.get_container_image()
image_pull_secrets = self.outpost.config.kubernetes_image_pull_secrets image_pull_secrets = self.outpost.config.kubernetes_image_pull_secrets
version = get_full_version().replace("+", "-") version = authentik_full_version().replace("+", "-")
return V1Deployment( return V1Deployment(
metadata=meta, metadata=meta,
spec=V1DeploymentSpec( spec=V1DeploymentSpec(

View File

@ -19,7 +19,7 @@ from packaging.version import Version, parse
from rest_framework.serializers import Serializer from rest_framework.serializers import Serializer
from structlog.stdlib import get_logger 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.blueprints.models import ManagedModel
from authentik.brands.models import Brand from authentik.brands.models import Brand
from authentik.core.models import ( from authentik.core.models import (
@ -38,7 +38,7 @@ from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.errors import exception_to_string from authentik.lib.utils.errors import exception_to_string
from authentik.outposts.controllers.k8s.utils import get_namespace from authentik.outposts.controllers.k8s.utils import get_namespace
OUR_VERSION = parse(__version__) OUR_VERSION = parse(authentik_version())
OUTPOST_HELLO_INTERVAL = 10 OUTPOST_HELLO_INTERVAL = 10
LOGGER = get_logger() LOGGER = get_logger()
@ -451,7 +451,7 @@ class OutpostState:
"""Check if outpost version matches our version""" """Check if outpost version matches our version"""
if not self.version: if not self.version:
return False return False
if self.build_hash != get_build_hash(): if self.build_hash != authentik_build_hash():
return False return False
return parse(self.version) != OUR_VERSION return parse(self.version) != OUR_VERSION

View File

@ -8,7 +8,7 @@ from channels.testing import WebsocketCommunicator
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.test import TransactionTestCase 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.core.tests.utils import create_test_flow
from authentik.outposts.consumer import WebsocketMessage, WebsocketMessageInstruction from authentik.outposts.consumer import WebsocketMessage, WebsocketMessageInstruction
from authentik.outposts.models import Outpost, OutpostType from authentik.outposts.models import Outpost, OutpostType
@ -73,7 +73,7 @@ class TestOutpostWS(TransactionTestCase):
WebsocketMessage( WebsocketMessage(
instruction=WebsocketMessageInstruction.HELLO, instruction=WebsocketMessageInstruction.HELLO,
args={ args={
"version": __version__, "version": authentik_version(),
"buildHash": "foo", "buildHash": "foo",
"uuid": "123", "uuid": "123",
}, },

View File

@ -26,7 +26,7 @@ from structlog.contextvars import STRUCTLOG_KEY_PREFIX
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from tenant_schemas_celery.app import CeleryApp as TenantAwareCeleryApp from tenant_schemas_celery.app import CeleryApp as TenantAwareCeleryApp
from authentik import get_full_version from authentik import authentik_full_version
from authentik.lib.sentry import before_send from authentik.lib.sentry import before_send
from authentik.lib.utils.errors import exception_to_string from authentik.lib.utils.errors import exception_to_string
@ -158,7 +158,7 @@ class LivenessProbe(bootsteps.StartStopStep):
@inspect_command(default_timeout=0.2) @inspect_command(default_timeout=0.2)
def ping(state, **kwargs): def ping(state, **kwargs):
"""Ping worker(s).""" """Ping worker(s)."""
return {"ok": "pong", "version": get_full_version()} return {"ok": "pong", "version": authentik_full_version()}
CELERY_APP.config_from_object(settings.CELERY) CELERY_APP.config_from_object(settings.CELERY)

View File

@ -10,7 +10,7 @@ from celery.schedules import crontab
from sentry_sdk import set_tag from sentry_sdk import set_tag
from xmlsec import enable_debug_trace 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.config import CONFIG, django_db_config, redis_url
from authentik.lib.logging import get_logger_config, structlog_configure from authentik.lib.logging import get_logger_config, structlog_configure
from authentik.lib.sentry import sentry_init from authentik.lib.sentry import sentry_init
@ -137,7 +137,7 @@ GUARDIAN_MONKEY_PATCH_USER = False
SPECTACULAR_SETTINGS = { SPECTACULAR_SETTINGS = {
"TITLE": "authentik", "TITLE": "authentik",
"DESCRIPTION": "Making authentication simple.", "DESCRIPTION": "Making authentication simple.",
"VERSION": __version__, "VERSION": authentik_version(),
"COMPONENT_SPLIT_REQUEST": True, "COMPONENT_SPLIT_REQUEST": True,
"SCHEMA_PATH_PREFIX": "/api/v([0-9]+(beta)?)", "SCHEMA_PATH_PREFIX": "/api/v([0-9]+(beta)?)",
"SCHEMA_PATH_PREFIX_TRIM": True, "SCHEMA_PATH_PREFIX_TRIM": True,
@ -486,7 +486,7 @@ if DEBUG:
TENANT_APPS.append("authentik.core") TENANT_APPS.append("authentik.core")
CONFIG.log("info", "Booting authentik", version=__version__) CONFIG.log("info", "Booting authentik", version=authentik_version())
# Attempt to load enterprise app, if available # Attempt to load enterprise app, if available
try: try:

View File

@ -5,7 +5,7 @@ from ssl import OPENSSL_VERSION
import pytest import pytest
from cryptography.hazmat.backends.openssl.backend import backend 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 IS_CI = "CI" in environ
@ -22,7 +22,7 @@ def pytest_sessionstart(*_, **__):
def pytest_report_header(*_, **__): def pytest_report_header(*_, **__):
"""Add authentik version to pytest output""" """Add authentik version to pytest output"""
return [ return [
f"authentik version: {get_full_version()}", f"authentik version: {authentik_full_version()}",
f"OpenSSL version: {OPENSSL_VERSION}, FIPS: {backend._fips_enabled}", f"OpenSSL version: {OPENSSL_VERSION}, FIPS: {backend._fips_enabled}",
] ]

View File

@ -6,7 +6,7 @@ from django.http.response import Http404
from requests.exceptions import RequestException from requests.exceptions import RequestException
from structlog.stdlib import get_logger 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.core.sources.flow_manager import SourceFlowManager
from authentik.lib.utils.http import get_http_session from authentik.lib.utils.http import get_http_session
from authentik.sources.plex.models import PlexSource, UserPlexSourceConnection from authentik.sources.plex.models import PlexSource, UserPlexSourceConnection
@ -34,7 +34,7 @@ class PlexAuth:
"""Get common headers""" """Get common headers"""
return { return {
"X-Plex-Product": "authentik", "X-Plex-Product": "authentik",
"X-Plex-Version": __version__, "X-Plex-Version": authentik_version(),
"X-Plex-Device-Vendor": "goauthentik.io", "X-Plex-Device-Vendor": "goauthentik.io",
} }

View File

@ -45,7 +45,7 @@ var rootCmd = &cobra.Command{
AttachStacktrace: true, AttachStacktrace: true,
EnableTracing: true, EnableTracing: true,
TracesSampler: sentryutils.SamplerFunc(config.Get().ErrorReporting.SampleRate), 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, Environment: config.Get().ErrorReporting.Environment,
HTTPTransport: webutils.NewUserAgentTransport(constants.UserAgent(), http.DefaultTransport), HTTPTransport: webutils.NewUserAgentTransport(constants.UserAgent(), http.DefaultTransport),
IgnoreErrors: []string{ IgnoreErrors: []string{

View File

@ -1,90 +1,85 @@
---
services: services:
postgresql: postgresql:
image: docker.io/library/postgres:16-alpine env_file:
restart: unless-stopped - .env
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
environment: environment:
POSTGRES_DB: ${PG_DB:-authentik}
POSTGRES_PASSWORD: ${PG_PASS:?database password required} POSTGRES_PASSWORD: ${PG_PASS:?database password required}
POSTGRES_USER: ${PG_USER:-authentik} 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: healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
start_period: 20s
interval: 30s interval: 30s
retries: 5 retries: 5
timeout: 3s start_period: 20s
volumes: test:
- redis:/data - CMD-SHELL
server: - pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.0} timeout: 5s
image: docker.io/library/postgres:16-alpine
restart: unless-stopped 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: volumes:
- ./media:/media - database:/var/lib/postgresql/data
- ./custom-templates:/templates redis:
env_file: command: --save 60 1 --loglevel warning
- .env healthcheck:
ports: interval: 30s
- "${COMPOSE_PORT_HTTP:-9000}:9000" retries: 5
- "${COMPOSE_PORT_HTTPS:-9443}:9443" 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: depends_on:
postgresql: postgresql:
condition: service_healthy condition: service_healthy
redis: redis:
condition: service_healthy condition: service_healthy
worker: env_file:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.0} - .env
restart: unless-stopped
command: worker
environment: environment:
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
# `user: root` and the docker socket volume are optional. AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
# See more for the docker socket integration here: AUTHENTIK_REDIS__HOST: redis
# https://goauthentik.io/docs/outposts/integrations/docker AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
# Removing `user: root` also prevents the worker from fixing the permissions image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.0}
# on the mounted folders, so when removing this make sure the folders have the correct UID/GID ports:
# (1000:1000 by default) - ${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.0}
restart: unless-stopped
user: root user: root
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
- ./media:/media - ./media:/media
- ./certs:/certs - ./certs:/certs
- ./custom-templates:/templates - ./custom-templates:/templates
env_file:
- .env
depends_on:
postgresql:
condition: service_healthy
redis:
condition: service_healthy
volumes: volumes:
database: database:
driver: local driver: local

View File

@ -0,0 +1 @@
2025.4.1

View File

@ -1,10 +1,14 @@
package constants package constants
import ( import (
_ "embed"
"fmt" "fmt"
"os" "os"
) )
//go:embed VERSION
var version string
func BUILD(def string) string { func BUILD(def string) string {
build := os.Getenv("GIT_BUILD_HASH") build := os.Getenv("GIT_BUILD_HASH")
if build == "" { if build == "" {
@ -13,12 +17,15 @@ func BUILD(def string) string {
return build return build
} }
func VERSION() string {
return version
}
func FullVersion() string { func FullVersion() string {
ver := VERSION
if b := BUILD(""); b != "" { 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 { func UserAgentOutpost() string {
@ -32,5 +39,3 @@ func UserAgentIPC() string {
func UserAgent() string { func UserAgent() string {
return fmt.Sprintf("authentik@%s", FullVersion()) return fmt.Sprintf("authentik@%s", FullVersion())
} }
const VERSION = "2025.6.0"

View File

@ -198,7 +198,7 @@ func (a *APIController) OnRefresh() error {
func (a *APIController) getWebsocketPingArgs() map[string]interface{} { func (a *APIController) getWebsocketPingArgs() map[string]interface{} {
args := map[string]interface{}{ args := map[string]interface{}{
"version": constants.VERSION, "version": constants.VERSION(),
"buildHash": constants.BUILD(""), "buildHash": constants.BUILD(""),
"uuid": a.instanceUUID.String(), "uuid": a.instanceUUID.String(),
"golangVersion": runtime.Version(), "golangVersion": runtime.Version(),
@ -218,7 +218,7 @@ func (a *APIController) StartBackgroundTasks() error {
"outpost_name": a.Outpost.Name, "outpost_name": a.Outpost.Name,
"outpost_type": a.Server.Type(), "outpost_type": a.Server.Type(),
"uuid": a.instanceUUID.String(), "uuid": a.instanceUUID.String(),
"version": constants.VERSION, "version": constants.VERSION(),
"build": constants.BUILD(""), "build": constants.BUILD(""),
}).Set(1) }).Set(1)
go func() { go func() {

View File

@ -160,7 +160,7 @@ func (ac *APIController) startWSHandler() {
"outpost_name": ac.Outpost.Name, "outpost_name": ac.Outpost.Name,
"outpost_type": ac.Server.Type(), "outpost_type": ac.Server.Type(),
"uuid": ac.instanceUUID.String(), "uuid": ac.instanceUUID.String(),
"version": constants.VERSION, "version": constants.VERSION(),
"build": constants.BUILD(""), "build": constants.BUILD(""),
}).SetToCurrentTime() }).SetToCurrentTime()
} }
@ -222,7 +222,7 @@ func (ac *APIController) startIntervalUpdater() {
"outpost_name": ac.Outpost.Name, "outpost_name": ac.Outpost.Name,
"outpost_type": ac.Server.Type(), "outpost_type": ac.Server.Type(),
"uuid": ac.instanceUUID.String(), "uuid": ac.instanceUUID.String(),
"version": constants.VERSION, "version": constants.VERSION(),
"build": constants.BUILD(""), "build": constants.BUILD(""),
}).SetToCurrentTime() }).SetToCurrentTime()
} }

View File

@ -54,7 +54,7 @@ func doGlobalSetup(outpost api.Outpost, globalConfig *api.Config) {
Environment: globalConfig.ErrorReporting.Environment, Environment: globalConfig.ErrorReporting.Environment,
EnableTracing: true, EnableTracing: true,
TracesSampler: sentryutils.SamplerFunc(float64(globalConfig.ErrorReporting.TracesSampleRate)), 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), HTTPTransport: webutils.NewUserAgentTransport(constants.UserAgentOutpost(), http.DefaultTransport),
IgnoreErrors: []string{ IgnoreErrors: []string{
http.ErrAbortHandler.Error(), http.ErrAbortHandler.Error(),
@ -66,7 +66,7 @@ func doGlobalSetup(outpost api.Outpost, globalConfig *api.Config) {
} }
if !initialSetup { 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 initialSetup = true
} }
} }

View File

@ -107,7 +107,7 @@ func (ws *WebServer) staticHeaderMiddleware(h http.Handler) http.Handler {
etagHandler := etag.Handler(h, false) etagHandler := etag.Handler(h, false)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "public, no-transform") 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") w.Header().Set("Vary", "X-authentik-version, Etag")
etagHandler.ServeHTTP(w, r) etagHandler.ServeHTTP(w, r)
}) })

View File

@ -34,7 +34,7 @@ from aws_cdk import (
) )
from constructs import Construct from constructs import Construct
from authentik import __version__ from authentik import authentik_version as ak_version
class AuthentikStack(Stack): class AuthentikStack(Stack):
@ -88,7 +88,7 @@ class AuthentikStack(Stack):
self, self,
"AuthentikVersion", "AuthentikVersion",
type="String", type="String",
default=__version__, default=ak_version(),
description="authentik Docker image tag", description="authentik Docker image tag",
) )

View File

@ -11,7 +11,7 @@ from cryptography.hazmat.backends.openssl.backend import backend
from defusedxml import defuse_stdlib from defusedxml import defuse_stdlib
from prometheus_client.values import MultiProcessValue 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.config import CONFIG
from authentik.lib.debug import start_debug_server from authentik.lib.debug import start_debug_server
from authentik.lib.logging import get_logger_config from authentik.lib.logging import get_logger_config
@ -132,9 +132,9 @@ if not CONFIG.get_bool("disable_startup_analytics", False):
json={ json={
"domain": "authentik", "domain": "authentik",
"name": "pageview", "name": "pageview",
"referrer": get_full_version(), "referrer": authentik_full_version(),
"url": ( "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={ headers={

View File

@ -2,7 +2,7 @@
from lifecycle.migrate import BaseMigration from lifecycle.migrate import BaseMigration
from datetime import datetime from datetime import datetime
from authentik import __version__, get_build_hash from authentik import authentik_version, authentik_build_hash
class Migration(BaseMigration): class Migration(BaseMigration):
@ -14,7 +14,7 @@ class Migration(BaseMigration):
ORDER BY "timestamp" DESC ORDER BY "timestamp" DESC
LIMIT 1 LIMIT 1
""", """,
(__version__, get_build_hash()), (authentik_version(), authentik_build_hash()),
) )
return not bool(self.cur.rowcount) return not bool(self.cur.rowcount)
@ -24,7 +24,7 @@ class Migration(BaseMigration):
INSERT INTO authentik_version_history ("timestamp", version, build) INSERT INTO authentik_version_history ("timestamp", version, build)
VALUES (%s, %s, %s) VALUES (%s, %s, %s)
""", """,
(datetime.now(), __version__, get_build_hash()), (datetime.now(), authentik_version(), authentik_build_hash()),
) )
self.cur.execute( self.cur.execute(
""" """

View File

@ -79,7 +79,6 @@ dev = [
"aws-cdk-lib==2.188.0", "aws-cdk-lib==2.188.0",
"bandit==1.8.3", "bandit==1.8.3",
"black==25.1.0", "black==25.1.0",
"bump2version==1.0.1",
"channels[daphne]==4.2.2", "channels[daphne]==4.2.2",
"codespell==2.4.1", "codespell==2.4.1",
"colorama==0.4.6", "colorama==0.4.6",

View File

@ -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)

View File

@ -5,7 +5,7 @@ Generates a Semantic Versioning identifier, suffixed with a timestamp.
from time import time from time import time
from authentik import __version__ as package_version from authentik import authentik_version as package_version
""" """
See: https://semver.org/#spec-item-9 (Pre-release spec) See: https://semver.org/#spec-item-9 (Pre-release spec)

11
uv.lock generated
View File

@ -242,7 +242,6 @@ dev = [
{ name = "aws-cdk-lib" }, { name = "aws-cdk-lib" },
{ name = "bandit" }, { name = "bandit" },
{ name = "black" }, { name = "black" },
{ name = "bump2version" },
{ name = "channels", extra = ["daphne"] }, { name = "channels", extra = ["daphne"] },
{ name = "codespell" }, { name = "codespell" },
{ name = "colorama" }, { name = "colorama" },
@ -340,7 +339,6 @@ dev = [
{ name = "aws-cdk-lib", specifier = "==2.188.0" }, { name = "aws-cdk-lib", specifier = "==2.188.0" },
{ name = "bandit", specifier = "==1.8.3" }, { name = "bandit", specifier = "==1.8.3" },
{ name = "black", specifier = "==25.1.0" }, { name = "black", specifier = "==25.1.0" },
{ name = "bump2version", specifier = "==1.0.1" },
{ name = "channels", extras = ["daphne"], specifier = "==4.2.2" }, { name = "channels", extras = ["daphne"], specifier = "==4.2.2" },
{ name = "codespell", specifier = "==2.4.1" }, { name = "codespell", specifier = "==2.4.1" },
{ name = "colorama", specifier = "==0.4.6" }, { name = "colorama", specifier = "==0.4.6" },
@ -598,15 +596,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/14/7a/03482cd3c00008d9be646c8e1520611d6202ce447db725ac40b07f9b088a/botocore-1.38.29-py3-none-any.whl", hash = "sha256:4d623f54326eb66d1a633f0c1780992c80f3db317a91c9afe31d5c700290621e", size = 13588258, upload-time = "2025-06-03T19:22:45.14Z" }, { url = "https://files.pythonhosted.org/packages/14/7a/03482cd3c00008d9be646c8e1520611d6202ce447db725ac40b07f9b088a/botocore-1.38.29-py3-none-any.whl", hash = "sha256:4d623f54326eb66d1a633f0c1780992c80f3db317a91c9afe31d5c700290621e", size = 13588258, upload-time = "2025-06-03T19:22:45.14Z" },
] ]
[[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]] [[package]]
name = "cachetools" name = "cachetools"
version = "5.5.2" version = "5.5.2"