Keeping dependencies up-to-date is an important aspect of software maintenance. Update-time is a command line tool that scans your repository for dependencies and updates them to their latest versions. It looks at the files you already have — pyproject.toml, hand-written requirements.txt files, package.json, Dockerfiles, GitHub Actions workflows, CircleCI configs, GitLab CI configs, Docker Compose and Helm manifests, and jsDelivr URLs — and rewrites the pinned versions in place. To avoid adopting freshly published releases that may still be buggy, it applies a cooldown period (see Cooldown below).
Run Update-time without installing it using uvx:
uvx update-timeOr install it as a uv tool so it's always available on your PATH:
uv tool install update-time
update-timeUpdate-time has a small command-line interface. Run update-time -h/--help to see all options, update-time -V/--version to print the version, update-time --cooldown DAYS to override the default cooldown period (see Cooldown below), and update-time --log-level LEVEL to set how much is logged (one of DEBUG, INFO, WARNING, ERROR; defaults to INFO). Available new versions are logged at INFO, so use --log-level WARNING to see only genuine problems, or --log-level DEBUG to also see which files are checked. Running update-time with no options in the root folder of a repository updates all supported dependencies.
The recommended workflow is to run Update-time on a dedicated branch, push it, and let CI do the verification:
- Create a branch for the updates.
- Run
update-timein the root of your repository to update the dependencies in place. - Commit the changes and open a pull request.
- Let your tests and checks run in CI to confirm nothing is broken before merging.
To raise API rate limits while updating, set the following environment variables before running Update-time:
GITHUB_TOKEN— increases the GitHub API rate limit when updating GitHub Actions. The token only needs to read public release and commit data, so no specific scope is required: both a classic token with no scopes selected and a fine-grained token with default read-only access to public repositories work.DOCKER_HUB_USERNAMEandDOCKER_HUB_TOKEN— authenticate to the Docker Hub API (both must be set) to increase its rate limit when updating Docker images.
Update-time runs a set of updater scripts, each responsible for one kind of dependency. The file-rewriting scripts run concurrently where it's safe to do so; package.json engine and dependency updates run sequentially because they touch the same files.
| Dependency | Files | Source |
|---|---|---|
Python dependencies pinned with == |
pyproject.toml |
PyPI |
Python dependencies pinned with == |
hand-written requirements*.txt, requirements/*.txt |
PyPI |
| npm dependencies | package.json (and package-lock.json) |
npm registry |
| Node engine version | package.json |
the Node base image in the project's Dockerfile |
| Dockerfile base images (tag + digest) | Dockerfile |
Docker Hub |
| CircleCI images (tag + digest) | CircleCI YAML configs | Docker Hub |
| GitLab CI images (tag + digest) | .gitlab-ci.yml |
Docker Hub |
| Docker Compose and Helm images (tag + digest) | Compose files and Helm folder | Docker Hub |
| GitHub Action versions (SHA + tag) | workflow YAML files | GitHub releases API |
| jsDelivr npm URLs (version + SRI hash) | Sphinx config | npm registry |
Only versions specified with an exact match (== for Python, a concrete tag — optionally already pinned as tag@sha256:digest — for images) are updated; looser version specifiers are left untouched, so you can pin a maximum version to opt a dependency out of automatic updates. Where available, Update-time prints the changelog entries between the current and new version so you can review what changed.
In requirements.txt files only exact == pins are updated. The following are left untouched:
- Git, VCS, and URL dependencies (e.g.
git+https://github.com/org/repo.git@v8.0.3.0, direct URLs, and-e/editable installs) — these are not registry versions, so Update-time does not bump their refs; update them manually. - Compiled or hash-pinned files — a
requirements.txtgenerated by pip-tools oruv pip compile(recognised by an autogenerated header, a sibling.infile, or--hash=lines) is skipped entirely, because bumping a single pin without recompiling its transitive dependencies and hashes would corrupt the file. Regenerate these with your compiler instead.
References that are not yet pinned are pinned automatically:
- Docker images referenced by tag only — base images in Dockerfiles (
FROM image:tag), CircleCI images, GitLab CI images, and Docker Compose / Helm manifest images — get the@sha256:digestof the (latest) tag appended, so the image is reproducible. Images without a concrete version tag are ignored: references through a template ({{ ... }}) or variable substitution (${VAR}), and tagless base images such asFROM scratchor stage references. CircleCI machine-executor images (theimage:under amachine:key, such asubuntu-2204:2024.01.1) are also left alone, since they are not Docker Hub images. - GitHub Actions referenced by version tag only (e.g.
uses: actions/checkout@v4) are pinned to the commit SHA of the latest version, with the version added as a trailing comment (e.g.uses: actions/checkout@<sha> # v4.1.1). Actions referenced by a branch (e.g.@main) are left untouched because they don't resolve to a version.
To avoid adopting releases that are too fresh to trust, Update-time honours a cooldown period during which newly published versions are not yet picked up. It defaults to 7 days and can be changed with the --cooldown option, for example update-time --cooldown 14. How the cooldown is applied depends on the dependency type:
- Docker images, GitHub Actions, and
requirements.txtdependencies — Update-time enforces the cooldown itself, based on each image tag's push date and each release's publication date. - npm dependencies — Update-time passes the cooldown to
npmvia npm'smin-release-ageoption (also measured in days), which npm added in 11.10.0; older npm versions ignore the option, so updates still run but without a cooldown. If your project already configures a cooldown in its.npmrc(min-release-ageorbefore), Update-time leaves that in place instead of overriding it. pyproject.tomldependencies — Update-time passes the cooldown to uv via uv'sexclude-newersetting. If yourpyproject.tomlalready setsexclude-newerunder[tool.uv], or theUV_EXCLUDE_NEWERenvironment variable is set, Update-time leaves that in place instead of overriding it.
Point of contact for this repository is Frank Niessink.