pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/apache/airflow/pull/64124

bd40328c.css" /> Skip pnpm install in UI pre-commit hook when deps are unchanged by dstandish · Pull Request #64124 · apache/airflow · GitHub
Skip to content

Skip pnpm install in UI pre-commit hook when deps are unchanged#64124

Open
dstandish wants to merge 1 commit into
apache:mainfrom
astronomer:worktree-skip-pnpm-install-when-deps-unchanged
Open

Skip pnpm install in UI pre-commit hook when deps are unchanged#64124
dstandish wants to merge 1 commit into
apache:mainfrom
astronomer:worktree-skip-pnpm-install-when-deps-unchanged

Conversation

@dstandish
Copy link
Copy Markdown
Contributor

@dstandish dstandish commented Mar 23, 2026

The Compile / format / lint UI pre-commit hook runs pnpm install --frozen-lockfile on every invocation, even when package.json and pnpm-lock.yaml haven't changed. This is the main source of slowness in the hook.

This PR adds a hash-based guard: SHA256 of package.json + pnpm-lock.yaml is stored in .build/ui/pnpm-install-hash.txt (consistent with the caching pattern already used in compile_ui_assets.py). When the hash matches and node_modules/ exists, pnpm install is skipped entirely.

Benchmarks

Measured on macOS Darwin 25.3.0, Apple Silicon, pnpm 9.9.0:

Scenario Wall clock Notes
Old: pnpm install (cold pnpm store) ~22 s First-ever install, downloading from registry
Old: pnpm install (warm pnpm store) ~1.0–1.5 s Typical dev scenario — "Already up to date" fast path
PR: cache miss (hash mismatch / no hash file) ~1.0–1.5 s + 0.08 s Negligible overhead; still runs install
PR: cache hit (hash matches, node_modules exists) ~0.07 s Pure Python hash check, skips install entirely

Typical dev savings: ~1.0–1.4 s per commit (~14–20x faster on the hot path) for contributors who frequently touch UI files without changing deps — the common case.

Cold-store savings (~22 s) apply on a fresh checkout. Cache miss adds only ~0.08 s of overhead before falling through to the normal install path.

The hash covers both package.json and pnpm-lock.yaml, so any direct or transitive dep change correctly invalidates the cache.


Was generative AI tooling used to co-author this PR?
  • Yes — Claude Sonnet 4.6

Generated-by: Claude Sonnet 4.6 following the guidelines

Hashing package.json + pnpm-lock.yaml and caching the result in
.build/ui/pnpm-install-hash.txt allows the hook to skip the expensive
pnpm install step when neither file has changed and node_modules already
exists. This mirrors the hash-based caching pattern already used in
compile_ui_assets.py.
@dstandish
Copy link
Copy Markdown
Contributor Author

@potiuk not a huge time savings but 1s is something

Copy link
Copy Markdown
Contributor

@bugraoz93 bugraoz93 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks good! Only added a nit on utilising the common processes to leverage the same file rather than creating additional one although it is not blocking

Comment thread scripts/ci/prek/ts_compile_lint_ui.py
@potiuk
Copy link
Copy Markdown
Member

potiuk commented Mar 24, 2026

@potiuk not a huge time savings but 1s is something

Oh absolutely - anythind we can shave-off from a thing that is executed 1000s of times but 100s of people is good.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR speeds up the ts-compile-lint-ui prek hook by avoiding repeated pnpm install runs when UI dependencies have not changed, using a stored SHA256 hash of package.json + pnpm-lock.yaml as a cache key.

Changes:

  • Compute a SHA256 hash of airflow-core/src/airflow/ui/package.json and pnpm-lock.yaml.
  • Skip pnpm install --frozen-lockfile when the stored hash matches and node_modules/ exists.
  • Persist the computed hash in .build/ui/pnpm-install-hash.txt after a successful install.

Comment on lines +55 to +66
hash_file = AIRFLOW_ROOT_PATH / ".build" / "ui" / "pnpm-install-hash.txt"
node_modules = dir / "node_modules"
current_hash = hashlib.sha256(
(dir / "package.json").read_bytes() + (dir / "pnpm-lock.yaml").read_bytes()
).hexdigest()
stored_hash = hash_file.read_text().strip() if hash_file.exists() else ""

if node_modules.exists() and current_hash == stored_hash:
print("pnpm deps unchanged — skipping install.")
else:
run_command(["pnpm", "config", "set", "store-dir", ".pnpm-store"], cwd=dir)
run_command(["pnpm", "install", "--frozen-lockfile", "--config.confirmModulesPurge=false"], cwd=dir)
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache-hit condition can become a false positive if node_modules/ is modified outside this hook (e.g., running pnpm install manually or via another prek hook) without updating .build/ui/pnpm-install-hash.txt. In that case current_hash == stored_hash can still be true while node_modules no longer corresponds to the current lockfile, causing the hook to skip the needed install and then run eslint/tsc against mismatched deps. Consider adding an extra validity check tied to the actual node_modules state (e.g., compare timestamps, or read pnpm’s node_modules/.modules.yaml lockfile checksum) before skipping.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 3 comments.

Comment on lines +57 to +59
current_hash = hashlib.sha256(
(dir / "package.json").read_bytes() + (dir / "pnpm-lock.yaml").read_bytes()
).hexdigest()
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hashing by simple byte concatenation can (in rare but real cases) produce the same combined byte string for different (package.json, pnpm-lock.yaml) pairs (e.g., if bytes are shifted between files), causing a false cache hit and skipping a required install. To make the hash unambiguous, include a delimiter and/or the length of each file, or hash each file separately and combine the digests.

Suggested change
current_hash = hashlib.sha256(
(dir / "package.json").read_bytes() + (dir / "pnpm-lock.yaml").read_bytes()
).hexdigest()
package_json_bytes = (dir / "package.json").read_bytes()
pnpm_lock_bytes = (dir / "pnpm-lock.yaml").read_bytes()
current_hash_hasher = hashlib.sha256()
current_hash_hasher.update(f"package.json:{len(package_json_bytes)}:".encode("utf-8"))
current_hash_hasher.update(package_json_bytes)
current_hash_hasher.update(f"pnpm-lock.yaml:{len(pnpm_lock_bytes)}:".encode("utf-8"))
current_hash_hasher.update(pnpm_lock_bytes)
current_hash = current_hash_hasher.hexdigest()

Copilot uses AI. Check for mistakes.
Comment on lines +67 to +68
hash_file.parent.mkdir(parents=True, exist_ok=True)
hash_file.write_text(current_hash)
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writing the hash file non-atomically can leave a truncated/partial hash if the process is interrupted mid-write (or if multiple runs overlap), leading to unnecessary reinstalls or inconsistent behavior. Prefer an atomic write pattern (write to a temp file in the same directory, then replace/rename) to make the cache robust.

Copilot uses AI. Check for mistakes.
).hexdigest()
stored_hash = hash_file.read_text().strip() if hash_file.exists() else ""

if node_modules.exists() and current_hash == stored_hash:
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node_modules.exists() will also be true if there’s a file (or unexpected path type) named node_modules. Using node_modules.is_dir() (or checking exists() + is_dir()) makes the guard more precise and avoids incorrectly skipping installs in edge cases.

Suggested change
if node_modules.exists() and current_hash == stored_hash:
if node_modules.is_dir() and current_hash == stored_hash:

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy