Hot-patch the deployed dev frontend

For unblocking testers when the permanent fix is still in review. Use sparingly — the next web deploy overwrites everything you do here. Record every live patch in dev-environment and open the real PR at the same time.

Why editing S3 is not enough

Three caches sit in front of the tester. Skipping any one of them means they keep running old code:

  1. CloudFront edge cache
  2. The Workbox service worker precache — pins each asset to a revision hash and ignores the network while the hash matches
  3. The browser HTTP cache

Layer 2 is the one people miss. A correct file in S3 plus a completed invalidation plus a hard refresh still serves the old bundle if the precache revision didn’t change.

Procedure

export AWS_PROFILE=default
BUCKET=s3://dev.villamarket.net
KEY=js/app~0e6551fc.875fa3bb.js

1. Pull the live file and keep a backup.

aws s3 cp "$BUCKET/$KEY" /tmp/patch.js
cp /tmp/patch.js "/tmp/patch.backup.$(date +%Y%m%d-%H%M%S).js"

2. Edit it, asserting on the replacement. Blind sed on a minified bundle is how you take dev down. Count matches, and re-assert any patch already applied so you don’t silently revert it.

old = 'https://km797c84si.execute-api.ap-southeast-1.amazonaws.com/Prod/shipping-cost'
new = '/api/polygon/getPolygonCost'
assert text.count(old) == 1
text = text.replace(old, new)
assert 'm4q33ulvm9' not in text          # earlier patch still in place

3. Parse it before uploading. A syntax error here breaks checkout for everyone.

node --check /tmp/patch.js

4. Upload and note the new hash. The precache revision is the first 20 characters of the md5.

md5 -q /tmp/patch.js        # first 20 chars = new revision
aws s3 cp /tmp/patch.js "$BUCKET/$KEY" \
  --content-type 'application/javascript' --cache-control 'public, max-age=60'

5. Update the precache manifest. Without this the service worker keeps the old copy.

aws s3 cp "$BUCKET/precache-manifest.e7cc117c58b6990b15fde28b76678bd7.js" /tmp/pc.js
# replace the "revision" next to "url": "/js/app~0e6551fc.875fa3bb.js"
aws s3 cp /tmp/pc.js "$BUCKET/precache-manifest.e7cc117c58b6990b15fde28b76678bd7.js" \
  --content-type 'application/javascript' --cache-control 'public, max-age=60'

6. Bump the service worker so browsers reinstall it. A trailing comment is enough — the byte change is the point.

aws s3 cp "$BUCKET/service-worker.js" /tmp/sw.js
grep -v '^/\* cache-bust' /tmp/sw.js > /tmp/sw.new.js
echo "/* cache-bust $(date -u +%Y-%m-%dT%H:%M:%SZ) <what changed> */" >> /tmp/sw.new.js
aws s3 cp /tmp/sw.new.js "$BUCKET/service-worker.js" \
  --content-type 'application/javascript' --cache-control 'public, max-age=0, must-revalidate'

7. Invalidate and wait. See invalidate-dev-cloudfront.

Verify from the edge

curl -sS https://dev.villamarket.net/js/app~0e6551fc.875fa3bb.js | grep -c m4q33ulvm9   # expect 0
curl -sS https://dev.villamarket.net/precache-manifest.e7cc117c58b6990b15fde28b76678bd7.js \
  | grep -B1 'app~0e6551fc.875fa3bb.js'                                                 # expect the new revision

In a browser, clear the worker before testing or you will validate your own stale cache:

for (const r of await navigator.serviceWorker.getRegistrations()) await r.unregister();
for (const k of await caches.keys()) await caches.delete(k);
location.reload();

Tell the testers

They need one hard refresh. Until the new service worker installs they are running old code, and will report the fix as missing.