Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
89cb131
Enforce mutation grants for governed namespaces
Aug 28, 2026
010832b
Protect governed boundary lifecycle from deployers
Aug 28, 2026
efbb81f
Enable declarative custom metadata schemas for repo-backed namespaces…
shangyian Aug 28, 2026
eadbada
Seed test fixtures once per module instead of once per test (#2469)
shangyian Aug 28, 2026
a5b9905
Clone one database per module for the function type tests (#2472)
shangyian Aug 29, 2026
13ec030
Bump version to v0.0.224 (#2471)
shangyian Aug 29, 2026
c6adbce
Measure test coverage on one Python version only (#2474)
shangyian Aug 30, 2026
db8a600
Forward the deployment fields the client was dropping (#2475)
shangyian Aug 30, 2026
fd95a13
Plan the preagg fixtures once into a template database (#2473)
shangyian Aug 30, 2026
94f2ec0
Bump version to v0.0.225 (#2476)
shangyian Aug 30, 2026
3bbcc39
Drop the unused owner field from the metadata schema registry (#2477)
shangyian Aug 30, 2026
bc1cea3
add display_name to semantic metadata (#2470)
joellubi Aug 31, 2026
7199eb2
fix: standard types in _generate_sql (#2479)
betodealmeida Sep 1, 2026
c4e2c14
Bump version to v0.0.226 (#2478)
shangyian Sep 1, 2026
ee4a64e
Document the custom metadata schema registry (#2487)
shangyian Sep 1, 2026
7e416ba
feat(ui): restore the delete button on the node details page (#2486)
robinld Sep 1, 2026
54a3037
Allow role-qualified dimension attributes in DJ SQL projections (#2485)
shangyian Sep 2, 2026
24ccf09
Bump version to v0.0.227 (#2491)
shangyian Sep 2, 2026
6e0eb9e
Propagate upstream changes into downstream cubes (#2424)
shangyian Sep 2, 2026
5b4104e
Bump version to v0.0.228 (#2495)
shangyian Sep 2, 2026
e9217f5
feat: annotate sqlglot ast (#2484)
betodealmeida Sep 2, 2026
cf2a7b9
Thread branch-deploy signal into cube materialization scheduling (#2499)
shangyian Sep 2, 2026
308a051
Surface which revision each listed materialization belongs to (#2497)
shangyian Sep 2, 2026
f99567b
Bump version to v0.0.229 (#2500)
shangyian Sep 2, 2026
bf62778
[DJ Semantic Fingerprint 1] Add semantic node fingerprints (#2482)
philipfweiss Sep 3, 2026
ea3bf69
Single-Pass Column Pruning (#2496)
shangyian Sep 3, 2026
e093f59
Derive deployed versions from committed state, not the plan snapshot …
shangyian Sep 4, 2026
2ad71c4
Stop re-deploying metrics whose required dimensions are qualified (#2…
shangyian Sep 4, 2026
a6e99a6
Stop a cube minting a version on every deploy (#2504)
shangyian Sep 4, 2026
d98ba9e
Bump version to v0.0.230 (#2506)
shangyian Sep 4, 2026
d5b130e
Fix column pruning for when table aliases are used in subqueries and …
shangyian Sep 4, 2026
9f5dce5
Bump version to v0.0.231 (#2510)
shangyian Sep 4, 2026
d7c84e7
Always export required dimensions in fully-qualified `node.column` fo…
shangyian Sep 4, 2026
1744dcd
Bump version to v0.0.232 (#2512)
shangyian Sep 4, 2026
c7e3355
[DJ Semantic Fingerprint 2] Evaluate fingerprints across deployment g…
philipfweiss Sep 4, 2026
e247e43
Preload required-dimension nodes into dependency_nodes on deploy (#2513)
shangyian Sep 4, 2026
0174e2c
Bump version to v0.0.233 (#2514)
shangyian Sep 4, 2026
5c29917
Check a base-db marker instead of connecting to the template (#2515)
shangyian Sep 5, 2026
4de3167
Round-trip a reference link's role through to_spec() (#2516)
shangyian Sep 5, 2026
ad920a8
Bump version to v0.0.234 (#2518)
shangyian Sep 5, 2026
f01383b
Fix deployment round-trip gaps (#2519)
shangyian Sep 5, 2026
5eb2f17
Bump version to v0.0.235 (#2520)
shangyian Sep 5, 2026
b056aad
Fail deployment when a declared column doesn't match any query output…
shangyian Sep 5, 2026
d857838
Reject unstorable join links instead of 500ing (#2524)
shangyian Sep 5, 2026
0819844
Drain in-flight /mcp requests on shutdown (#2521)
shangyian Sep 5, 2026
a9c7ecc
Support `--format json` on `dj push` (#2523)
shangyian Sep 5, 2026
847821e
Bump version to v0.0.236 (#2525)
shangyian Sep 5, 2026
08b6fd7
feat: endpoint for dimensional values (#2498)
betodealmeida Sep 8, 2026
afd0c41
Bump version to v0.0.237 (#2526)
shangyian Sep 8, 2026
9a6e7f9
Bump version to v0.0.238 (#2527)
shangyian Sep 8, 2026
bf1cffd
Add swr cache control option (#2481)
emahannan Sep 8, 2026
9781ca2
Bump version to v0.0.239 (#2528)
shangyian Sep 8, 2026
2af640b
Deployment build order should take into account dimension links (#2531)
shangyian Sep 9, 2026
609fc4e
Bump version to v0.0.240 (#2533)
shangyian Sep 9, 2026
c1c4dbd
Report cube column mismatch warning as a structured DeploymentResult …
shangyian Sep 11, 2026
ad52af6
Add `DELETE /tags/{name}/ endpoint` (#2536)
shangyian Sep 11, 2026
e6c3bb2
Bump version to v0.0.241 (#2537)
shangyian Sep 11, 2026
0bf6438
[DJ Semantic Fingerprint 3] Add semantic fingerprints to deployment i…
philipfweiss Sep 11, 2026
eb42c5d
Bump version to v0.0.242 (#2538)
philipfweiss Sep 11, 2026
1ca2cfe
Bump version to v0.0.243 (#2539)
shangyian Sep 11, 2026
4b39f98
Distinguish a pre-existing failure from a new one (#2540)
shangyian Sep 13, 2026
a7f6325
Bump version to v0.0.244 (#2541)
shangyian Sep 13, 2026
364bae8
Merge remote-tracking branch 'origin/rbac-governed-boundary-enforceme…
Sep 14, 2026
6037c7e
Require management grants before deleting governed descendants
Sep 14, 2026
d875982
Match namespace prefixes literally during hard deletion
Sep 14, 2026
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
217 changes: 212 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
djqs: ${{ steps.filter.outputs.djqs }}
djrs: ${{ steps.filter.outputs.djrs }}
ui: ${{ steps.filter.outputs.ui }}
workflow: ${{ steps.filter.outputs.workflow }}

steps:
- uses: actions/checkout@v4
Expand All @@ -46,12 +47,33 @@ jobs:
ui:
- datajunction-ui/**
- '!datajunction-ui/package.json'
workflow:
- .github/workflows/test.yml
predicate-quantifier: every

build:
needs: changes
runs-on: ubuntu-latest

# One Postgres for the whole job. Without it each xdist worker starts its
# own container and rebuilds the same template, N times over, all at
# startup. Services cannot be conditional, so the non-server legs get one
# too; it costs a couple of seconds to start and nothing to ignore.
services:
postgres:
image: postgres:latest
env:
POSTGRES_USER: dj
POSTGRES_PASSWORD: dj
POSTGRES_DB: dj
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U dj"
--health-interval 10s
--health-timeout 5s
--health-retries 5

strategy:
fail-fast: false
matrix:
Expand All @@ -71,11 +93,15 @@ jobs:
run: uv python install ${{ matrix.python-version }}

- name: Run Tests
# Editing this workflow has to exercise this workflow. Without the
# `workflow` clause a change to test.yml matches no filter, every test
# step is skipped, and the PR goes green having run nothing -- so a
# broken workflow reaches main and surfaces on somebody else's PR.
if: |
(matrix.library == 'client' && (needs.changes.outputs.client == 'true' || needs.changes.outputs.server == 'true')) ||
(matrix.library == 'server' && needs.changes.outputs.server == 'true') ||
(matrix.library == 'djqs' && needs.changes.outputs.djqs == 'true') ||
(matrix.library == 'djrs' && needs.changes.outputs.djrs == 'true')
(matrix.library == 'client' && (needs.changes.outputs.client == 'true' || needs.changes.outputs.server == 'true' || needs.changes.outputs.workflow == 'true')) ||
(matrix.library == 'server' && (needs.changes.outputs.server == 'true' || needs.changes.outputs.workflow == 'true')) ||
(matrix.library == 'djqs' && (needs.changes.outputs.djqs == 'true' || needs.changes.outputs.workflow == 'true')) ||
(matrix.library == 'djrs' && (needs.changes.outputs.djrs == 'true' || needs.changes.outputs.workflow == 'true'))
run: |
echo "Testing ${{ matrix.library }} ..."
export TEST_DIR=${{ matrix.library == 'server' && './datajunction-server' || matrix.library == 'client' && './datajunction-clients/python' || matrix.library == 'djqs' && './datajunction-query' || matrix.library == 'djrs' && './datajunction-reflection'}}
Expand All @@ -94,9 +120,190 @@ jobs:
# Run linters
uv run pre-commit run --all-files

# Build the template databases once for the whole job. Each xdist
# worker then clones them (~90ms) instead of building its own.
if [ "${{ matrix.library }}" = "server" ]; then
PG="postgresql://dj:dj@localhost:5432"
PGX="postgresql+psycopg://dj:dj@localhost:5432"
export DJ_TEST_POSTGRES_URL="$PGX/dj"

# The suite creates this role for containers it starts itself; on a
# server it does not own, it cannot.
psql "$PG/dj" -c "CREATE ROLE readonly_user WITH LOGIN PASSWORD 'readonly'"

psql "$PG/dj" -c "CREATE DATABASE template_all_examples"
uv run python tests/helpers/populate_template.py "$PGX/template_all_examples"

psql "$PG/dj" -c "CREATE DATABASE template_preaggs TEMPLATE template_all_examples"
uv run python tests/helpers/populate_preaggs_template.py "$PGX/template_preaggs"

psql "$PG/dj" -c "CREATE DATABASE template_dimension_links"
uv run python tests/helpers/populate_template.py \
"$PGX/template_dimension_links" COMPLEX_DIMENSION_LINK
fi

# Run tests
export MODULE=${{ matrix.library == 'server' && 'datajunction_server' || matrix.library == 'client' && 'datajunction' || matrix.library == 'djqs' && 'djqs' || matrix.library == 'djrs' && 'datajunction_reflection'}}
uv run pytest ${{ (matrix.library == 'server' || matrix.library == 'client') && '-n auto --dist=loadscope' || '' }} --cov-fail-under=100 --cov=$MODULE --cov-report term-missing -vv tests/ --doctest-modules $MODULE --without-integration --without-slow-integration --ignore=datajunction_server/alembic/env.py

# Only the server skips coverage here. It is the expensive one --
# 28.8min instrumented against ~15min not -- so the `coverage-shard`
# matrix below measures it in parallel pieces and the `coverage` job
# enforces its threshold.
#
# The other three are instrumented in place. Their suites are small
# enough that it costs the gate nothing, and they have nowhere else
# to be measured, because the shards are server-only. An earlier
# version of this workflow moved coverage off every leg at once,
# which silently took client, djqs and djrs from a 100% gate to no
# gate at all.
COV_ARGS=""
if [ "${{ matrix.library }}" != "server" ]; then
COV_ARGS="--cov-fail-under=100 --cov-report=term-missing --cov=$MODULE"
fi

uv run pytest ${{ (matrix.library == 'server' || matrix.library == 'client') && '-n auto --dist=loadscope' || '' }} $COV_ARGS -vv tests/ --doctest-modules $MODULE --without-integration --without-slow-integration --ignore=datajunction_server/alembic/env.py

# Coverage roughly doubles the suite, so measure it across shards in parallel
# rather than serially inside a build leg. Each shard only produces data; the
# `coverage` job below combines them and enforces the threshold.
coverage-shard:
needs: changes
runs-on: ubuntu-latest

# Same shared Postgres as the build job. This matters more here: without it
# every one of the five shards starts its own containers and rebuilds every
# template, so the duplicated startup cost is paid five times over.
services:
postgres:
image: postgres:latest
env:
POSTGRES_USER: dj
POSTGRES_PASSWORD: dj
POSTGRES_DB: dj
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U dj"
--health-interval 10s
--health-timeout 5s
--health-retries 5
if: needs.changes.outputs.server == 'true' || needs.changes.outputs.workflow == 'true'

strategy:
fail-fast: false
matrix:
# The last shard is a catch-all: `tests/` minus the others. Listing
# every directory explicitly means a newly added one belongs to no
# shard, and the only symptom is combined coverage quietly dropping
# below the threshold.
include:
- shard: 1
paths: "tests/api"
- shard: 2
paths: "tests/construction"
- shard: 3
paths: "tests/sql"
- shard: 4
paths: "tests/internal"
# Also carries --doctest-modules. The build legs pass it, and it is
# what imports every module in the package -- without it the
# import-time lines of modules no test imports directly (models/
# table.py, __about__.py, a couple of __init__.py) are never
# executed, and the combined total lands just under the threshold.
- shard: 5
paths: >-
tests
--ignore=tests/api
--ignore=tests/construction
--ignore=tests/sql
--ignore=tests/internal
--doctest-modules datajunction_server
--ignore=datajunction_server/alembic/env.py

steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
version: "latest"
enable-cache: true

- name: Set up Python
run: uv python install 3.13

- name: Run shard ${{ matrix.shard }}
working-directory: ./datajunction-server
# Write to a name coverage's own erase() will not match. On startup
# pytest-cov erases every `.coverage.*` file in the directory, not just
# its own, so a fragment named that way can be destroyed by another
# shard sharing the workspace -- or, worse on a reused runner, a stale
# one can survive and skew the combined total.
env:
COVERAGE_FILE: .cov-shard${{ matrix.shard }}
run: |
uv sync --group test --python 3.13

PG="postgresql://dj:dj@localhost:5432"
PGX="postgresql+psycopg://dj:dj@localhost:5432"
export DJ_TEST_POSTGRES_URL="$PGX/dj"

# The suite creates this role for containers it starts itself; on a
# server it does not own, it cannot.
psql "$PG/dj" -c "CREATE ROLE readonly_user WITH LOGIN PASSWORD 'readonly'"

psql "$PG/dj" -c "CREATE DATABASE template_all_examples"
uv run python tests/helpers/populate_template.py "$PGX/template_all_examples"

psql "$PG/dj" -c "CREATE DATABASE template_preaggs TEMPLATE template_all_examples"
uv run python tests/helpers/populate_preaggs_template.py "$PGX/template_preaggs"

psql "$PG/dj" -c "CREATE DATABASE template_dimension_links"
uv run python tests/helpers/populate_template.py \
"$PGX/template_dimension_links" COMPLEX_DIMENSION_LINK

uv run pytest -n auto --dist=loadscope \
--cov=datajunction_server --cov-report= \
-q ${{ matrix.paths }} \
--without-integration --without-slow-integration

- uses: actions/upload-artifact@v4
with:
name: coverage-shard-${{ matrix.shard }}
path: datajunction-server/.cov-shard${{ matrix.shard }}
include-hidden-files: true
retention-days: 1

# This is the job branch protection should require; the shards only produce
# data and enforce nothing on their own.
coverage:
needs: coverage-shard
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
version: "latest"
enable-cache: true

- name: Set up Python
run: uv python install 3.13

- uses: actions/download-artifact@v4
with:
pattern: coverage-shard-*
merge-multiple: true
path: datajunction-server

- name: Combine and enforce
working-directory: ./datajunction-server
run: |
uv sync --group test --python 3.13
ls -la .cov-shard* || { echo "no coverage fragments downloaded"; exit 1; }
uv run coverage combine .cov-shard*
uv run coverage report --show-missing --fail-under=100

build-javascript:
runs-on: ubuntu-latest
Expand Down
4 changes: 2 additions & 2 deletions datajunction-clients/javascript/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion datajunction-clients/javascript/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "datajunction",
"version": "0.0.223",
"version": "0.0.244",
"description": "A Javascript client for interacting with a DataJunction server",
"module": "src/index.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion datajunction-clients/python/datajunction/__about__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Version for Hatch
"""

__version__ = "0.0.223"
__version__ = "0.0.244"
13 changes: 10 additions & 3 deletions datajunction-clients/python/datajunction/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def push(
verbose: bool = False,
force: bool = False,
allow_empty: bool = False,
format: str = "text",
):
"""
Alias for deploy without dryrun.
Expand All @@ -57,6 +58,7 @@ def push(
verbose=verbose,
force=force,
allow_empty=allow_empty,
format=format,
)

def dryrun(
Expand Down Expand Up @@ -916,7 +918,7 @@ def create_parser(self):
type=str,
default="text",
choices=["text", "json"],
help="Output format for dry run (default: text)",
help="Output format for the deployment result (default: text)",
)
# Deployment source tracking flags
push_parser.add_argument(
Expand Down Expand Up @@ -1523,12 +1525,17 @@ def dispatch_command(self, args, parser):
verbose=args.verbose,
force=args.force,
allow_empty=args.allow_empty,
format=args.format,
)
except DJDeploymentFailure:
# Errors already displayed in the deployment panel
# Errors already displayed in the deployment panel (or, in
# --format json mode, in the JSON already printed to stdout)
raise SystemExit(1)
except DJClientException as exc:
Console().print(f"[red bold]ERROR:[/red bold] {exc}")
if args.format == "json":
print(json.dumps({"error": str(exc)}, indent=2))
else:
Console().print(f"[red bold]ERROR:[/red bold] {exc}")
raise SystemExit(1)
elif args.command == "generate-codeowners":
count = DeploymentService.build_codeowners(
Expand Down
Loading
Loading