Skip to main content

Hacking on dxaws – The Workspace Makefile

·649 words·4 mins

Why the Makefile exists
#

The dxaws workspace is intentionally not a single monolithic repository. Instead, it is a multi-mono workspace made up of many small, focused modules:

  • dxaws-core
  • dxaws-dns
  • dxaws-acm
  • dxaws-cloudfront
  • …and more to come

Each module is:

  • a real Python package
  • installable with pip install -e
  • testable on its own

The workspace Makefile exists to orchestrate these modules without hiding that fact. It provides convenience and consistency, not magic.

If you understand the Makefile, you understand how the entire dxaws workspace fits together.


Core design principles
#

The Makefile is built around a few deliberate constraints:

  • No hard-coded module lists
    Repos are discovered dynamically from the filesystem.

  • Editable installs are the contract
    If something imports, it’s because it was installed, not because of path hacks.

  • Repo-by-repo execution
    Tests and installs happen one module at a time to avoid cross-repo collisions.

  • Make is the entry point
    You can run pytest or pip directly, but make encodes the happy path.

This keeps the workspace predictable even as the number of modules grows.


Repo discovery
#

The Makefile discovers dxaws modules automatically:

REPOS_DIR := repos
REPOS := $(notdir $(wildcard $(REPOS_DIR)/dxaws-*))

Any directory under repos/ whose name starts with dxaws- is treated as a module.

This means:

  • adding a new module requires no Makefile changes
  • removing a module automatically removes it from all targets

You can see what the Makefile sees at any time:

make list

Example output:

dxaws repos detected under repos:
 - dxaws-core
 - dxaws-dns
 - dxaws-acm
 - dxaws-cloudfront

Installing modules
#

Install everything
#

make install

This will:

  1. ensure shared dev tools are installed
  2. perform an editable install (pip install -e .[dev]) for each discovered repo

The order is filesystem order, which is fine because dependencies are explicit.


Install a single module
#

make install-one REPO=dxaws-dns

This is useful when:

  • working on a single module
  • iterating on packaging or dependencies

There are also pattern targets:

make install-dxaws-dns
make install-dxaws-core

These are thin wrappers around the same logic.


Running tests
#

Test everything
#

make test

This runs pytest repo by repo, not as one giant test suite.

This is intentional.

Why?

  • pytest imports tests as top-level modules
  • duplicate filenames across repos (test_models.py, etc.) can collide
  • repo-local execution avoids those collisions entirely

Each repo is tested in isolation, just like it would be in CI.


Test a single module
#

make test-one REPO=dxaws-cloudfront

Or via pattern targets:

make test-dxaws-cloudfront

This is the fastest way to iterate when developing a module.


Linting and type checking
#

Linting and type checking are also repo-scoped:

make lint
make typecheck

These iterate over all discovered repos and run the appropriate tool only if it is available in the environment.

This keeps the workspace flexible without forcing global constraints.


Overriding repo selection
#

All aggregate targets respect the REPOS variable.

You can override it manually:

make test REPOS="dxaws-core dxaws-dns"

This is useful when:

  • narrowing scope temporarily
  • debugging interactions between a small set of modules

Why not just run pytest at the workspace root?
#

You can — and the workspace pytest.ini is configured to behave reasonably.

But the Makefile is the recommended interface because it:

  • encodes the intended execution model
  • avoids test name collisions by design
  • mirrors how CI will execute modules

If something passes under make test, it will pass under automation.


Mental model
#

Think of the Makefile as a module runner, not a build system.

It answers questions like:

  • “What dxaws modules exist right now?”
  • “Install this one, not all of them.”
  • “Test everything safely.”

It does not:

  • hide Python packaging rules
  • invent a new dependency system
  • blur module boundaries

That restraint is what keeps dxaws understandable as it grows.


Summary
#

  • The Makefile discovers modules dynamically
  • Editable installs are the source of truth
  • Tests run per-repo by design
  • make is the happy path, not a replacement for understanding

If you understand this Makefile, you understand how to hack on dxaws.