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.1.tar.gz (46.2 kB view details)

Uploaded Source

Built Distribution

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

Uploaded Python 3

File details

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

File metadata

  • Download URL: qik-0.2.1.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.1.tar.gz
Algorithm Hash digest
SHA256 1763c690b514bfe1d27b4322483b7df29f401c1c8936c21d0ffb3a9f457b9cd7
MD5 bb2a8f7b2e535c586eb2b46fe2c1164c
BLAKE2b-256 52d6153cefdc0710f59f2e5055733b3041f31844afc59eda3b30d5a4b3e721a1

See more details on using hashes here.

File details

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

File metadata

  • Download URL: qik-0.2.1-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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a50096d9f21156fe53bae78ac4ba8bc42a1f5ab5188faf9a81090132add8a1b3
MD5 9e7bbad9f8af300b98ad6cbc9608390b
BLAKE2b-256 98d2ceccb771b927635c152aaca2000dc582ec3e60489ae4651311a8fe29b387

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