feat(CLI): add --cache flag to phrase pull for conditional requests#1066
Open
Thibaut Etienne (tetienne) wants to merge 1 commit intophrase:mainfrom
Open
feat(CLI): add --cache flag to phrase pull for conditional requests#1066Thibaut Etienne (tetienne) wants to merge 1 commit intophrase:mainfrom
Thibaut Etienne (tetienne) wants to merge 1 commit intophrase:mainfrom
Conversation
edb8391 to
5788c0a
Compare
Store ETag and Last-Modified response headers locally and send them as If-None-Match / If-Modified-Since on subsequent pulls. When the server returns 304 Not Modified, the locale file is skipped. Cache is stored at os.UserCacheDir()/phrase/download_cache.json. Only supported in sync mode; --cache with --async prints a warning and falls back to normal behavior. Fixes phrase/phrase-cli#164
5788c0a to
559bf93
Compare
Member
|
Thank you Thibaut Etienne (@tetienne) for the suggested improvement. The team will review this PR within the next business days. |
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
We rely heavily on
phrase pullin our CI pipelines and have been frustrated by the slowness of repeated pulls. Every invocation downloads all locale files regardless of whether translations have changed, which is wasteful and frequently pushes us into rate limiting. This PR adds HTTP conditional request support to avoid re-downloading unchanged locales.Summary
--cacheflag tophrase pullthat stores ETag/Last-Modified response headers locally and sends them asIf-None-Match/If-Modified-Sinceon subsequent pullsos.UserCacheDir()/phrase/download_cache.json(XDG-compliant)--cachewith--asyncprints a warning and ignores cachingFixes phrase/phrase-cli#164
Usage
Real-world results
Tested against a production project with 379 locale files across 3 Phrase projects:
First run (populates cache, downloads everything):
Second run (sends ETags, all 304 Not Modified):
Cache file (
~/Library/Caches/phrase/download_cache.json):{ "version": 1, "entries": { "a1b2c3d4e5f6...": { "etag": "W/\"070896511a30e4295c7cd1825d0f49ee\"", "last_modified": "Wed, 18 Mar 2026 15:04:42 GMT" } } }phrase pullphrase pull --cacheWall-clock improvement is ~15%, but the main benefits are zero bandwidth on cache hit, reduced rate limit pressure, and lower server load. The bottleneck is 379 sequential HTTP round-trips; parallelizing downloads (out of scope) would amplify the time savings further.
GitHub Actions example
The
--cacheflag pairs well withactions/cacheto persist ETags between CI runs, significantly reducing both download time and API rate limit consumption:Design decisions
updated_since: The SDK'sUpdatedSinceparameter produces partial files (only keys updated after that date), not "skip if unchanged" semantics. ETags are the correct HTTP mechanism for conditional full-file downloads.LocaleDownloadOptsstruct via reflection to extract setoptional.*values. This ensures new SDK fields automatically affect the key without code changes.Save()short-circuits when no entries were modified, avoiding unnecessary writes on all-304 runs.>= 300as an error. We intercept 304 before checkingerrand returnerrNotModifiedso the caller can print "Not modified" instead of "Downloaded".If-None-Match/If-Modified-Sinceto ensure a full response.Test plan
go build ./...compiles (pre-existingcmd/error unrelated)go test ./cmd/internal/...passes (12 tests)go vet ./cmd/internal/cleanphrase pullwithout--cachebehaves identically to beforephrase pull --cache --asyncwarns and ignores cache