Skip to main content

Tame your monorepo. Make CI fast again.

Project description

Qik

Qik (quick) is an extensible command runner for monorepos. Like make, but with hash-based caching, advanced dependencies like globs and module imports, and execution in managed virtual environments.

Qik's command caching and module parametrization can dramatically improve local development and CI/CD time. Qik's plugin ecosystem offers remote S3 command caching, Python import graph linting, and much more.

Although qik is currently tailored towards Python repos, any project can use it.

Read the qik docs here or this blog post on why I built Qik.

Installation

pip install qik

For local development, we recommend installing most optional dependencies with:

pip install "qik[dev]"

Qik is compatible with Python 3.10 - 3.13, Linux, OSX, and WSL. It requires git for command caching.

Getting Started

Commands

Configure commands in qik.toml:

[commands]
format = "ruff format ."
lint = "ruff check . --fix"
type-check = "pyright"

Run qik to execute every command across available cores. qik <cmd> ... runs specific commands.

Specify deps to cache commands. Here we use pip-compile from pip-tools to lock requirements:

[commands.lock]
exec = "pip-compile > requirements.txt"
deps = ["requirements.in"]

The requirements file from qik lock is cached locally until requirements.in changes.

Along with globs, commands can depend on constants, other commands, Python distributions, and more.

Here we update our previous command to depend on the pip-tools distribution, breaking the cache when the version changes:

[commands.lock]
exec = "pip-compile > requirements.txt"
deps = [
    "requirements.in",
    {type = "pydist", name = "pip-tools"}
]

Here we use command dependencies to ensure our linter runs after formatting:

[commands.format]
exec = "ruff format ."
deps = ["**.py"]

[commands.lint]
exec = "ruff check . --fix"
deps = ["**.py", {type = "command", name = "format"}]

The qik.pygraph plugin provides the pygraph dependency. Here we run pyright over the hello.world module whenever its code or imported code changes:

[plugins]
pygraph = "qik.pygraph"

[commands.check-types]
exec = "pyright hello/world"
deps = [{type = "pygraph", pyimport = "hello.world"}]

Caches

Cache results directly in your project with the repo cache:

[commands.lock]
exec = "pip-compile > requirements.txt"
deps = ["requirements.in"]
cache = "repo"

Us the qik.s3 plugin to create a shared remote cache using AWS S3:

[plugins]
s3 = "qik.s3"

[caches.remote]
type = "s3"
bucket = "qik-cache"

[commands.lock]
exec = "pip-compile > requirements.txt"
deps = ["requirements.in"]
artifacts = ["requirements.txt"]
cache = "remote"

We specify artifacts here to ensure requirements.txt is cached and restored.

Spaces

Qik spaces define isolated environments and metadata for commands.

Here we create a space for our linting command. The qik.uv plugin uses uv to build virtualenvs:

[plugins]
uv = "qik.uv"

[spaces]
ruff = "ruff-requirements.txt"

[commands.format]
exec = "ruff format ."
deps = ["**.py"]
space = "ruff"

[commands.lint]
exec = "ruff check . --fix"
deps = ["**.py", {type = "command", name = "format"}]
space = "ruff"

Running qik will lock and install the virtualenv for the ruff space and execute commands inside it. Changes to ruff-requirements.txt will break the cache of downstream commands.

Along with managing virtual environments, spaces can:

  • Declare a dotenv file to automatically set environment variables.
  • Declare a root. Running qik under this folder only selects commands in the space.

Here's a more complete example:

[spaces.my-app]
root = "app"
dotenv = "app.env"
venv = "requirements.txt"

Modules

Spaces can define modules for command parametrization. Here we parametrize pyright over six modules across two spaces:

[spaces.first]
modules = ["a", "b", "c"]
venv = "requirements-first.txt"

[spaces.second]
modules = ["d", "e", "f"]
venv = "requirements-second.txt"

[commands.check-types]
exec = "pyright {module.dir}"
deps = [{type = "pygraph", pyimport = "{module.pyimport}"}]

Using {module...} in a command definition will automatically parametrize it over ever module in every space.

Use qik check-types -s first to specify spaces or qik check-types -m b -m c to specific modules.

Fences

Plugins like qik.pygraph can leverage the fence of a space to perform import linting and other useful tasks:

[plugins]
pygraph = "qik.pygraph"

[spaces.first]
modules = ["a", "b", "c"]
fence = true

Running qik pygraph.check will ensure these modules only import each other. Add additional internal imports or a virtual environment to extend the fence:

[plugins]
pygraph = "qik.pygraph"

[spaces.first]
modules = ["a", "b", "c"]
fence = ["other/module"]
venv = "requirements.txt"

Include another space in the fence:

[spaces.first]
modules = ["a", "b", "c"]

[spaces.second]
modules = ["d", "e", "f"]
fence = [{type = "space", name = "first"}]

Running qik pygraph.check -s second will ensure the second space can import its modules and the first space's modules.

Context

Set context variables and use them in your configuration. Below we create a context variable for the bundle cache:

ctx = [{name = "bundle_cache", default = "local"}]

[command.build-bundle]
exec = "npm run build"
artifacts = ["js-build/*"]
cache = "{ctx.bundle_cache}"

Context variables can be supplied in the environment:

BUNDLE_CACHE=remote qik build-bundle

Command Line Interface

The core qik CLI functionality is as follows:

  • qik to run all commands.
  • qik <cmd_name> <cmd_name> to select commands by name.
  • -m to select by module.
  • -s to select by space.
  • --watch to reactively run selected commands.
  • --since to select commands based on changes since a git reference.
  • -f to run without the cache.
  • -n to set the number of workers.
  • --ls to list commands instead of running them.

See the command runner section for other advanced options, such as selecting commands based on cache status.

The qikx utility is also available for running commands in spaces:

# Run in the default space
qikx my_command --args val

# Run in a specific space
qikx my_command@space_name --args val

Next Steps

Read the following guide to become a qik expert:

  • Spaces: How spaces work, their functionality, and how commands and plugins leverage them.
  • Commands: Configuring and running commands. Learn the dependencies, selectors, and runtime behavior.
  • Context: Using environment-based runtime context.
  • Caching: How caching works and how to configure different cache types.

Learn more about plugins:

  • Intro: How to configure and create plugins.
  • Pygraph: Using the qik.pygraph plugin for import-based dependencies and import linting.
  • UV: How to leverage and configure the qik.uv plugin for virtualenvs, including constraint files, custom PyPI indices, and more.
  • S3: Configuring the qik.s3 plugin for a remote S3 cache.

Read the cookbook for more examples and guides:

  • Spaces: More examples of leveraging spaces.
  • Commands: Common command definition examples.
  • CLI Usage: Command line interface snippets.
  • CI/CD: Patterns for optimizing CI/CD time.

Finish by checking out:

  • Roadmap for all the exciting upcoming features.
  • Blog for updates, how-tos, and other articles.

Questions or thoughts? Open a discussion. Report bugs here.

Disclaimer

Qik is currently in beta. Bumping the minor version (e.g. 0.1.0 to 0.2.0) will indicate an API break until we release version 1.0.0.

Be diligent when using qik in your CI/CD. We recommend including a base dependency in your commands to regularly break the cache. We also recommend understanding how the import graph is built when using pygraph dependencies.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qik-0.2.0.tar.gz (46.2 kB view details)

Uploaded Source

Built Distribution

qik-0.2.0-py3-none-any.whl (56.0 kB view details)

Uploaded Python 3

File details

Details for the file qik-0.2.0.tar.gz.

File metadata

  • Download URL: qik-0.2.0.tar.gz
  • Upload date:
  • Size: 46.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.2 CPython/3.12.3 Linux/5.15.0-1057-aws

File hashes

Hashes for qik-0.2.0.tar.gz
Algorithm Hash digest
SHA256 bcb5b34ff807410cf8eafeea0c4c0da339ed4cbf4d5f1c7a8769f81c709f9322
MD5 be45d325397e817f27256dd3eb33798f
BLAKE2b-256 2bce3581ece3723ce9d8a33c6d1d5fa04432c72331d103ef2369634893d19112

See more details on using hashes here.

File details

Details for the file qik-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: qik-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 56.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.2 CPython/3.12.3 Linux/5.15.0-1057-aws

File hashes

Hashes for qik-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4349d5bc2e94cd675327562f1130502781e31cc76b9cd0e802ac4fadfe057432
MD5 85fcd655734595eb8af46da2f93355f8
BLAKE2b-256 a8292f9c5642822bbb0660e284d23c662e5f597fff7c404b482e7c403202a482

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page