Skip to content

Commit 6cf4ab2

Browse files
authored
Implement docker_image optimization hook (#57)
1 parent 5f3f690 commit 6cf4ab2

File tree

3 files changed

+73
-0
lines changed

3 files changed

+73
-0
lines changed

.pre-commit-hooks.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
language: docker_image
55
types: [text]
66

7+
- id: precache-docker
8+
name: Precache Docker images
9+
entry: ./precache-docker.sh
10+
language: script
11+
pass_filenames: false
12+
713
- id: sync-ai-rules
814
name: Sync AI Rules
915
entry: duolingo/pre-commit-hooks:1.12.0 sh -c "PYTHONPATH=/ python3 -m sync_ai_rules"

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ To minimize developer friction, we enable only rules whose violations can be fix
3434

3535
We run this hook on developer workstations and enforce it in CI for all production repos at Duolingo.
3636

37+
## Precache Docker Images Hook (`precache-docker`)
38+
39+
Pre-commit parallelizes runs across CPUs by default (similar to `make -j`), but its `language: docker_image` [doesn't deduplicate image pulls](https://github.com/pre-commit/pre-commit/pull/3573). This hook speeds up pre-commit and minimizes data usage by precaching each Docker image once instead of pulling it $CPU_COUNT times on a cold cache.
40+
41+
All `docker_image` entries found in `.pre-commit-config.yaml` will be included, as well as any additional images provided to this hook as `args`. You should declare this hook as early as possible in config, before any other hooks that are meant to benefit from this one.
42+
3743
## Sync AI Rules Hook (`sync-ai-rules`)
3844

3945
This hook synchronizes AI coding rules from .cursor/rules/\*.mdc files to other AI assistant configuration files (CLAUDE.md, AGENTS.md, etc.). This ensures all AI coding assistants in your project stay aware of the same rules and conventions, eliminating the need to manually copy rules between different AI config files.
@@ -46,6 +52,10 @@ Repo maintainers can declare these hooks in `.pre-commit-config.yaml`:
4652
- repo: https://github.com/duolingo/pre-commit-hooks.git
4753
rev: 1.12.0
4854
hooks:
55+
# Optimization hook for `language: docker_image`
56+
- id: precache-docker
57+
args: # Optional list of additional images to precache
58+
- ubuntu:22.04
4959
# Code formatting hook
5060
- id: duolingo
5161
args: # Optional

precache-docker.sh

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env sh
2+
# Adapted from https://github.com/pre-commit/pre-commit/compare/main...artnc:pre-commit:optimize-docker-image-pull
3+
4+
set -eu
5+
6+
# Abort if Docker not found
7+
if ! command -v docker > /dev/null 2>&1; then
8+
exit
9+
fi
10+
export DOCKER_CLI_HINTS=false
11+
12+
# Collect Docker images provided as arguments
13+
images="$@"
14+
15+
# Search .pre-commit-config.yaml for more Docker image usages
16+
if [ -f .pre-commit-config.yaml ]; then
17+
# Fall back to dockerized yq if yq not found
18+
if ! command -v yq > /dev/null 2>&1; then
19+
echo "Local yq not found, using dockerized yq"
20+
yq() {
21+
docker run --init --network none --rm -v "${PWD}:/code" -w /code \
22+
mikefarah/yq:4.48.1 "$@"
23+
}
24+
fi
25+
26+
# Extract image names
27+
images="${images} $(yq '.repos[].hooks[] |
28+
# Extract `entry` from hooks with language `docker_image`
29+
select(.language=="docker_image") | .entry |
30+
31+
# Remove leading --entrypoint option
32+
sub("^ *--entrypoint(=| +)[^ ]+ +"; "") |
33+
34+
# Extract first word as possible image name
35+
split(" ")[0] |
36+
37+
# Lightly validate image name
38+
select(test("^[a-zA-Z0-9][a-zA-Z0-9.:/_-]*$"))' \
39+
.pre-commit-config.yaml 2> /dev/null | tr '\n' ' ')"
40+
fi
41+
42+
# Trim, deduplicate, and sort image list
43+
images="$(echo "${images}" | tr ' ' '\n' | sort -u | tr '\n' ' ' | xargs)"
44+
45+
# Pull images in parallel
46+
pids=""
47+
for image in ${images}; do
48+
docker pull --quiet "${image}" &
49+
pids="${pids} $!"
50+
done
51+
exit_code=0
52+
for pid in ${pids}; do
53+
if ! wait "${pid}"; then
54+
exit_code=1
55+
fi
56+
done
57+
exit "${exit_code}"

0 commit comments

Comments
 (0)