Thanks for helping improve dlthub-start. This project uses a
standard Python src/ layout, uv for environment management, ruff for
formatting/linting, and mypy for type checking.
The package itself supports Python 3.10+. Generated dltHub workspaces currently target Python 3.12+ because their scaffold dependencies do.
Install development dependencies into a local virtual environment:
make devRun the CLI from the checkout:
uv run dlthub-start --helpCreate a workspace non-interactively (for tests/CI). --setup-only and
--scaffold-only are hidden testing shortcuts that cut the guided setup short —
both skip login, playground connection, and the agent hand-off. --setup-only
still installs dependencies (and uses the default agent); --scaffold-only stops
right after scaffolding, before the dependency sync. Both are deliberately absent
from --help; the normal, complete path is interactive (no flag):
uv run dlthub-start my-workspace --setup-onlyCreate a workspace without installing the generated workspace's dependencies:
uv run dlthub-start my-workspace --scaffold-onlyChoose an AI workbench explicitly:
uv run dlthub-start my-workspace --agent claude
uv run dlthub-start my-workspace --agent claude --agent codexCreate a disposable test workspace under examples/:
make workspaceThe workspace target recreates examples/my-workspace by default. It
pre-deletes that directory before running the CLI, so only use it for
throwaway local workspaces. To target a different directory:
make workspace WORKSPACE_DIR=examples/my-demomake workspace targets prod (the released dlthub-client from PyPI). To scaffold
a throwaway workspace pointed at a non-prod runtime instead:
| Target | Stack | Notes |
|---|---|---|
make workspace-dev |
api.dlthub.dev |
editable dlthub-client from a local checkout |
make workspace-local |
api.dlthub.test (+ auth.dlthub.test) |
editable client; skips TLS verify (mkcert CA isn't in Python's bundle) |
make workspace-stage |
api.dlthub.net |
released client |
workspace-dev/workspace-local pin the stack's api_base_url (and, for local,
auth_base_url — it sits on its own host) into the workspace's .dlt/config.toml
at scaffold time via the CLI's --api-base-url / --auth-base-url flags. They also
point dlthub-client at a local runtime checkout (editable, via
--dlthub-client-source) so the client matches an API that may be ahead of the
released package. The checkout defaults to the sibling ../runtime/clients/cli;
override it:
DLTHUB_CLIENT_SOURCE=/path/to/runtime/clients/cli make workspace-devWhen running dlthub commands by hand in a local workspace, set
DLT_RUNTIME_INSECURE=true (the mkcert cert isn't in Python's trust bundle), e.g.
DLT_RUNTIME_INSECURE=true uv run dlthub login. Browser login against the local
stack also needs the runtime's mock host resolvable — add 127.0.0.1 dev-mock-services
to /etc/hosts.
The CLI sends anonymous usage events to PostHog. Users opt out with --no-telemetry,
DLTHUB_START_TELEMETRY=0, or DO_NOT_TRACK=1, and an existing dlt opt-out
(runtime.dlthub_telemetry = false in dlt's global config.toml, or
RUNTIME__DLTHUB_TELEMETRY=0) is honored.
For development and testing, three environment variables override the defaults:
| Variable | Effect |
|---|---|
DLTHUB_START_TELEMETRY |
Force telemetry on (1/true/yes/on) or off (any other value). |
DLTHUB_START_POSTHOG_KEY |
Override the bundled PostHog project key. |
DLTHUB_START_POSTHOG_HOST |
Override the PostHog host (default https://eu.i.posthog.com). |
Released builds bake the project key into a gitignored _telemetry_key.py; a dev
checkout has no key, so telemetry stays disabled until you set
DLTHUB_START_POSTHOG_KEY. To exercise the full path against a throwaway PostHog
project:
DLTHUB_START_TELEMETRY=1 \
DLTHUB_START_POSTHOG_KEY=phc_your_test_key \
DLTHUB_START_POSTHOG_HOST=https://eu.i.posthog.com \
uv run dlthub-start my-workspace --setup-onlyFor releases, put the real key in a gitignored .make.env
(DLTHUB_START_POSTHOG_KEY=phc_...); the Makefile loads it into uv build, and
make publish refuses to run without it.
Run the fast unit test suite:
make testRun the fast end-to-end coverage that avoids a real generated-workspace dependency sync:
uv run python -m unittest \
tests_integration.test_e2e_workspace.WorkspaceCreationFastTests \
tests_integration.test_e2e_workspace.InstalledEntryPointTestsRun all integration tests:
make test-integrationmake test-integration includes a slow path that invokes the real CLI and runs
uv sync in a generated workspace. It may require network access and can take
noticeably longer than the unit suite.
Run a quick bytecode compile check:
make compiledisplay.console is a plain Console() that auto-detects color from stdout, so
captured panel output is plain text when piped (CI) but carries ANSI styling and
wraps to the terminal width in an interactive shell. A test that asserts on
console.capture() output without normalizing will pass in one environment and
fail in the other. Use _panel_text() in tests/test_display.py — it strips ANSI
styling and panel borders and collapses whitespace, so substring checks don't
depend on color or terminal width.
Format code:
make formatRun linting and type checks:
make lintRun the same format/lint/type-check sequence used by CI:
make lint-ciRun the full local CI workflow:
make cimake ci runs compile checks, linting, unit tests, integration tests, lockfile
drift checks, AI scaffold drift checks, and package build.
Build the package:
make buildThe build artifacts are written to dist/.
The generated workspace includes vendored dltHub AI workbench files for Claude, Cursor, and Codex. These files are generated into each bundled scaffold, not downloaded during normal CLI execution.
The source ref is pinned in WORKBENCH_REF in
src/create_dlthub_workspace/config.py. To refresh the vendored AI files:
- Run
make update-aito bumpWORKBENCH_REFto the latestdlt-hub/dlthub-ai-workbenchcommit and regenerate the scaffolds. Passmake update-ai REF=<sha>to pin a specific commit instead. - Review the scaffold diff carefully.
- Run
make check-ai. - Commit the
WORKBENCH_REFchange and regenerated scaffold files together.
make update-ai rewrites WORKBENCH_REF and then runs make generate-ai; if
you only need to regenerate against the already-pinned ref, run make generate-ai directly. make check-ai reruns generation and fails if the
committed scaffolds drift from the pinned workbench ref.
There are two committed uv.lock files: the root project's, and the bundled
workspace scaffold's (src/create_dlthub_workspace/scaffolds/minimal_workspace/uv.lock,
shipped so new workspaces install from pinned versions). Each has a symmetric
pair of make targets:
| Upgrade | Drift check | |
|---|---|---|
| Root | make lock-upgrade |
make lock-check |
| Scaffold | make scaffold-lock-upgrade |
make scaffold-lock-check |
*-upgradere-resolves the lockfile to the newest dependency versions itspyproject.tomlallows. PassPKG=<name>to bump a single package instead of everything. Review the diff and commit.*-checkfails if the lockfile is out of sync with itspyproject.toml. Both checks run in CI and are part ofmake ci; if one fails, run the matching*-upgradetarget and commit.
Bump the version — this rewrites pyproject.toml and uv.lock together.
config.VERSION is read from package metadata, so the version in pyproject.toml
is the single source; nothing else needs editing.
make version-upgrade # prompts for major / minor / patch
make version-upgrade-patch # non-interactive (also -minor / -major)version-upgrade prompts interactively; the version-upgrade-{patch,minor,major}
variants (or make version-upgrade LEVEL=patch) are non-interactive for CI/agents.
Before publishing, verify:
uv run dlthub-start --help
make ciThe package exposes a single console command:
dlthub-start
Then build and publish to PyPI (prompts for a PyPI API token):
make publish