Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 33 additions & 8 deletions .github/actions/codeartifact-login/action.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
name: CodeArtifact login
description: |
Assume the production CodeArtifact role via OIDC, fetch an
authorisation token, and export uv credentials for the
`flagsmith-pypi-production` index.
Assume a CodeArtifact role via OIDC, fetch an authorisation token,
and export uv credentials for the named index.

inputs:
account-id:
description: AWS account hosting the CodeArtifact domain
required: false
default: "084060095745"
role-name:
description: Name of the IAM role to assume for CodeArtifact access
required: false
default: codeartifact-github-actions-production
domain:
description: CodeArtifact domain name
required: false
default: flagsmith-production
index-name:
description: |
Logical uv index name (matching `[[tool.uv.index]].name` in pyproject.toml).
The action exports UV_INDEX_<NAME>_USERNAME and UV_INDEX_<NAME>_PASSWORD
env vars, where NAME is the upper-snake-cased index name.
required: false
default: flagsmith-pypi-production

outputs:
token:
Expand All @@ -15,18 +35,23 @@ runs:
- name: Configure AWS credentials for CodeArtifact
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::084060095745:role/codeartifact-github-actions-production
role-to-assume: arn:aws:iam::${{ inputs.account-id }}:role/${{ inputs.role-name }}
aws-region: eu-west-2

- name: Fetch CodeArtifact authorisation token
id: codeartifact
shell: bash
env:
DOMAIN: ${{ inputs.domain }}
ACCOUNT_ID: ${{ inputs.account-id }}
INDEX_NAME: ${{ inputs.index-name }}
run: |
token=$(aws codeartifact get-authorization-token \
--domain flagsmith-production \
--domain-owner 084060095745 \
--domain "$DOMAIN" \
--domain-owner "$ACCOUNT_ID" \
--query authorizationToken --output text)
echo "::add-mask::$token"
echo "token=$token" >> "$GITHUB_OUTPUT"
echo "UV_INDEX_FLAGSMITH_PYPI_PRODUCTION_USERNAME=aws" >> "$GITHUB_ENV"
echo "UV_INDEX_FLAGSMITH_PYPI_PRODUCTION_PASSWORD=$token" >> "$GITHUB_ENV"
env_prefix="UV_INDEX_$(echo "$INDEX_NAME" | tr '[:lower:]-' '[:upper:]_')"
echo "${env_prefix}_USERNAME=aws" >> "$GITHUB_ENV"
echo "${env_prefix}_PASSWORD=$token" >> "$GITHUB_ENV"
12 changes: 11 additions & 1 deletion .github/workflows/.reusable-deploy-ecs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,19 @@ jobs:
type=ref,event=branch
type=ref,event=tag

- name: Authenticate with CodeArtifact
- name: Authenticate with CodeArtifact (production)
id: codeartifact
uses: ./.github/actions/codeartifact-login

- name: Authenticate with CodeArtifact (staging)
id: codeartifact-staging
uses: ./.github/actions/codeartifact-login
with:
account-id: "302456015006"
role-name: codeartifact-github-actions-staging
domain: flagsmith-staging
index-name: flagsmith-pypi-staging

- name: Build saas-api image
uses: depot/build-push-action@v1
with:
Expand All @@ -67,6 +76,7 @@ jobs:
secrets: |
github_private_cloud_token=${{ secrets.GH_PRIVATE_ACCESS_TOKEN }}
codeartifact_token=${{ steps.codeartifact.outputs.token }}
codeartifact_token_staging=${{ steps.codeartifact-staging.outputs.token }}
"sse_pgp_pkey=${{ secrets.SSE_PGP_PRIVATE_KEY }}"
push: true
tags: ${{ steps.meta.outputs.tags }}
Expand Down
13 changes: 12 additions & 1 deletion .github/workflows/.reusable-docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,21 @@ jobs:
${{ inputs.registry-url }}/flagsmith/${{ inputs.image-name }}
tags: ${{ inputs.tags }}

- name: Authenticate with CodeArtifact
- name: Authenticate with CodeArtifact (production)
if: inputs.codeartifact-login
id: codeartifact
uses: ./.github/actions/codeartifact-login

- name: Authenticate with CodeArtifact (staging)
if: inputs.codeartifact-login
id: codeartifact-staging
uses: ./.github/actions/codeartifact-login
with:
account-id: "302456015006"
role-name: codeartifact-github-actions-staging
domain: flagsmith-staging
index-name: flagsmith-pypi-staging

- name: Build and push image
id: build
uses: depot/build-push-action@v1
Expand All @@ -126,6 +136,7 @@ jobs:
secrets: |
${{ secrets.secrets }}
${{ inputs.codeartifact-login && format('codeartifact_token={0}', steps.codeartifact.outputs.token) || '' }}
${{ inputs.codeartifact-login && format('codeartifact_token_staging={0}', steps.codeartifact-staging.outputs.token) || '' }}
target: ${{ inputs.target }}
build-args: |
CI_COMMIT_SHA=${{ github.sha }}
Expand Down
12 changes: 10 additions & 2 deletions .github/workflows/api-deploy-production-ecs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ jobs:
name: Push MCP Schema to Gram
runs-on: depot-ubuntu-latest
permissions:
id-token: write
contents: read
id-token: write # For CodeArtifact OIDC
defaults:
run:
working-directory: api
Expand All @@ -38,9 +38,17 @@ jobs:
with:
python-version: "3.12"

- name: Authenticate with CodeArtifact
- name: Authenticate with CodeArtifact (production)
uses: ./.github/actions/codeartifact-login

- name: Authenticate with CodeArtifact (staging)
uses: ./.github/actions/codeartifact-login
with:
account-id: "302456015006"
role-name: codeartifact-github-actions-staging
domain: flagsmith-staging
index-name: flagsmith-pypi-staging

- name: Install dependencies
run: |
echo "https://${{ secrets.GH_PRIVATE_ACCESS_TOKEN }}:@github.com" > ${HOME}/.git-credentials
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/api-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: API Pull Request

permissions:
contents: read # For actions/checkout
id-token: write # For Codecov OIDC
id-token: write # For Codecov and CodeArtifact OIDC

on:
pull_request:
Expand Down Expand Up @@ -47,6 +47,14 @@ jobs:
with:
python-version: ${{ matrix.python-version }}

- name: Authenticate with CodeArtifact (staging)
uses: ./.github/actions/codeartifact-login
with:
account-id: "302456015006"
role-name: codeartifact-github-actions-staging
domain: flagsmith-staging
index-name: flagsmith-pypi-staging

- name: Install Dependencies
run: make install-packages opts='--extra dev'

Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/api-run-makefile-target.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ on:
permissions:
contents: write
pull-requests: write
id-token: write # For CodeArtifact OIDC

defaults:
run:
Expand All @@ -38,6 +39,14 @@ jobs:
with:
python-version: 3.13

- name: Authenticate with CodeArtifact (staging)
uses: ./.github/actions/codeartifact-login
with:
account-id: "302456015006"
role-name: codeartifact-github-actions-staging
domain: flagsmith-staging
index-name: flagsmith-pypi-staging

- name: Install Dependencies
run: make install-packages opts='--extra dev'

Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/api-tests-with-private-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,17 @@ jobs:
- name: Install SAML Dependencies
run: sudo apt-get install -y xmlsec1

- name: Authenticate with CodeArtifact
- name: Authenticate with CodeArtifact (production)
uses: ./.github/actions/codeartifact-login

- name: Authenticate with CodeArtifact (staging)
uses: ./.github/actions/codeartifact-login
with:
account-id: "302456015006"
role-name: codeartifact-github-actions-staging
domain: flagsmith-staging
index-name: flagsmith-pypi-staging

- name: Install packages and Tests
shell: bash
run: |
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/update-flagsmith-environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ defaults:
run:
working-directory: api

permissions:
contents: read
id-token: write # For CodeArtifact OIDC

jobs:
update_server_defaults:
runs-on: depot-ubuntu-latest
Expand All @@ -25,6 +29,14 @@ jobs:
with:
python-version: 3.12

- name: Authenticate with CodeArtifact (staging)
uses: ./.github/actions/codeartifact-login
with:
account-id: "302456015006"
role-name: codeartifact-github-actions-staging
domain: flagsmith-staging
index-name: flagsmith-pypi-staging

- name: Install Dependencies
run: make install-packages

Expand Down
16 changes: 14 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@ ENV UV_PROJECT_ENVIRONMENT=/build/.venv \
UV_LINK_MODE=copy \
UV_NO_SYNC=1 \
UV_CACHE_DIR=/root/.cache/uv
RUN --mount=type=cache,target=/root/.cache/uv \
RUN --mount=type=secret,id=codeartifact_token_staging \
--mount=type=cache,target=/root/.cache/uv \
UV_INDEX_FLAGSMITH_PYPI_STAGING_USERNAME=aws \
UV_INDEX_FLAGSMITH_PYPI_STAGING_PASSWORD="$(cat /run/secrets/codeartifact_token_staging)" \
make install opts='--no-install-project'

# * build-python-private [build-python]
Expand All @@ -113,11 +116,14 @@ ARG RBAC_REVISION
ARG EXTRAS="--extra saml --extra auth-controller --extra ldap --extra workflows --extra licensing --extra release-pipelines --extra scim"
RUN --mount=type=secret,id=github_private_cloud_token \
--mount=type=secret,id=codeartifact_token \
--mount=type=secret,id=codeartifact_token_staging \
--mount=type=cache,target=/root/.cache/uv \
echo "https://$(cat /run/secrets/github_private_cloud_token):@github.com" > ${HOME}/.git-credentials && \
git config --global credential.helper store && \
UV_INDEX_FLAGSMITH_PYPI_PRODUCTION_USERNAME=aws \
UV_INDEX_FLAGSMITH_PYPI_PRODUCTION_PASSWORD="$(cat /run/secrets/codeartifact_token)" \
UV_INDEX_FLAGSMITH_PYPI_STAGING_USERNAME=aws \
UV_INDEX_FLAGSMITH_PYPI_STAGING_PASSWORD="$(cat /run/secrets/codeartifact_token_staging)" \
make install-packages opts="--no-install-project ${EXTRAS}" && \
make install-private-modules

Expand Down Expand Up @@ -166,7 +172,10 @@ FROM build-python AS api-test

COPY api /build/

RUN --mount=type=cache,target=/root/.cache/uv \
RUN --mount=type=secret,id=codeartifact_token_staging \
--mount=type=cache,target=/root/.cache/uv \
UV_INDEX_FLAGSMITH_PYPI_STAGING_USERNAME=aws \
UV_INDEX_FLAGSMITH_PYPI_STAGING_PASSWORD="$(cat /run/secrets/codeartifact_token_staging)" \
make install-packages opts='--extra dev'

CMD ["make", "test"]
Expand All @@ -177,9 +186,12 @@ FROM build-python-private AS api-private-test
COPY api /build/

RUN --mount=type=secret,id=codeartifact_token \
--mount=type=secret,id=codeartifact_token_staging \
--mount=type=cache,target=/root/.cache/uv \
UV_INDEX_FLAGSMITH_PYPI_PRODUCTION_USERNAME=aws \
UV_INDEX_FLAGSMITH_PYPI_PRODUCTION_PASSWORD="$(cat /run/secrets/codeartifact_token)" \
UV_INDEX_FLAGSMITH_PYPI_STAGING_USERNAME=aws \
UV_INDEX_FLAGSMITH_PYPI_STAGING_PASSWORD="$(cat /run/secrets/codeartifact_token_staging)" \
make install-packages opts='--extra dev --extra saml --extra auth-controller --extra ldap --extra workflows --extra licensing --extra release-pipelines --extra scim' && \
make integrate-private-tests && \
git config --global --unset credential.helper && \
Expand Down
8 changes: 6 additions & 2 deletions api/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@ OPENAPI_FORMAT_VERSION ?= 1.23.0

.PHONY: codeartifact-login
codeartifact-login:
@token=$$(aws codeartifact get-authorization-token \
@prod_token=$$(aws codeartifact get-authorization-token \
--domain flagsmith-production --domain-owner 084060095745 \
--region eu-west-2 \
--query authorizationToken --output text) && \
printf 'UV_INDEX_FLAGSMITH_PYPI_PRODUCTION_USERNAME=aws\nUV_INDEX_FLAGSMITH_PYPI_PRODUCTION_PASSWORD=%s\n' "$$token" > .env-codeartifact
staging_token=$$(aws codeartifact get-authorization-token \
--domain flagsmith-staging --domain-owner 302456015006 \
--region eu-west-2 \
--query authorizationToken --output text) && \
printf 'UV_INDEX_FLAGSMITH_PYPI_PRODUCTION_USERNAME=aws\nUV_INDEX_FLAGSMITH_PYPI_PRODUCTION_PASSWORD=%s\nUV_INDEX_FLAGSMITH_PYPI_STAGING_USERNAME=aws\nUV_INDEX_FLAGSMITH_PYPI_STAGING_PASSWORD=%s\n' "$$prod_token" "$$staging_token" > .env-codeartifact

.PHONY: install-pip
install-pip:
Expand Down
15 changes: 15 additions & 0 deletions api/app/settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
"features.workflows.core",
"features.release_pipelines.core",
"segments",
"segment_membership",
"app",
"e2etests",
"simple_history",
Expand Down Expand Up @@ -1439,3 +1440,17 @@
PYLON_IDENTITY_VERIFICATION_SECRET = env.str("PYLON_IDENTITY_VERIFICATION_SECRET", None)

OSIC_UPDATE_BATCH_SIZE = env.int("OSIC_UPDATE_BATCH_SIZE", default=500)

# --- ClickHouse (segment membership inspection) ------------------------------
# All-None CLICKHOUSE_HOST disables the segment_membership backfill and refresh
# tasks. When set, the api/segments/membership tasks open a clickhouse-connect
# client and run against this account. See
# docs/deployment/observability/segment-membership.md for the operational shape.
CLICKHOUSE_HOST = env.str("CLICKHOUSE_HOST", default=None)
CLICKHOUSE_PORT = env.int("CLICKHOUSE_PORT", default=8443)
CLICKHOUSE_USER = env.str("CLICKHOUSE_USER", default="default")
CLICKHOUSE_PASSWORD = env.str("CLICKHOUSE_PASSWORD", default="")
CLICKHOUSE_DATABASE = env.str("CLICKHOUSE_DATABASE", default="default")
# ClickHouse Cloud uses HTTPS on 8443; OSS deployments typically run HTTP on
# 8123. Set CLICKHOUSE_SECURE=1 for HTTPS.
CLICKHOUSE_SECURE = env.bool("CLICKHOUSE_SECURE", default=True)
14 changes: 13 additions & 1 deletion api/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ dependencies = [
"drf-writable-nested>=0.6.2,<0.7.0",
"django-filter>=2.4.0,<2.5.0",
"flagsmith-flag-engine>=10.1.0,<11.0.0",
"flagsmith-sql-flag-engine==0.1.0a2",
"clickhouse-connect>=0.15,<1.0",
"boto3>=1.35.95,<1.36.0",
"slack-sdk>=3.9.0,<3.10.0",
"asgiref>=3.8.1,<3.9.0",
Expand Down Expand Up @@ -71,7 +73,7 @@ dependencies = [
"hubspot-api-client>=12.0.0,<13.0.0",
"djangorestframework-dataclasses>=1.3.1,<2.0.0",
"pyotp>=2.9.0,<3.0.0",
"flagsmith-common[common-core,flagsmith-schemas,task-processor]>=3.9.0,<4",
"flagsmith-common[common-core,flagsmith-schemas,task-processor]>=3.9.1,<4",
"django-stubs>=5.1.3,<6.0.0",
"tzdata>=2024.1,<2025.0.0",
"djangorestframework-simplejwt>=5.5.1,<6.0.0",
Expand Down Expand Up @@ -185,6 +187,12 @@ flagsmith-ldap = { git = "https://github.com/flagsmith/flagsmith-ldap", tag = "v
workflows-logic = { git = "https://github.com/flagsmith/flagsmith-workflows", tag = "v3.4.0" }
licensing = { git = "https://github.com/flagsmith/licensing", tag = "v0.3.0" }
flagsmith-private = { index = "flagsmith-pypi-production" }
flagsmith-sql-flag-engine = { index = "flagsmith-pypi-staging" }

[[tool.uv.index]]
name = "flagsmith-pypi-staging"
url = "https://flagsmith-staging-302456015006.d.codeartifact.eu-west-2.amazonaws.com/pypi/flagsmith-pypi-staging/simple/"
explicit = true

[tool.uv]
required-version = "==0.8.14"
Expand Down Expand Up @@ -547,6 +555,10 @@ ignore_missing_imports = true
module = ["openfeature_flagsmith.*"]
ignore_missing_imports = true

[[tool.mypy.overrides]]
module = ["clickhouse_connect.*"]
ignore_missing_imports = true

[[tool.mypy.overrides]]
module = ["scim.*"]
ignore_missing_imports = true
Expand Down
Empty file.
6 changes: 6 additions & 0 deletions api/segment_membership/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from core.apps import BaseAppConfig


class SegmentMembershipConfig(BaseAppConfig):
name = "segment_membership"
default = True
Loading
Loading