Modernize: Node 24, Bolt 4, googleapis/Sentry/cron upgrades + GHCR/Coolify deploy - #63
Open
ddbruce wants to merge 1 commit into
Open
Modernize: Node 24, Bolt 4, googleapis/Sentry/cron upgrades + GHCR/Coolify deploy#63ddbruce wants to merge 1 commit into
ddbruce wants to merge 1 commit into
Conversation
…+Coolify Behavior-preserving modernization. Same behavior: on the chores cron it reads today's chores from the Google Sheet and posts them (or "no chores tonight") to Slack with Done! buttons; clicking Done crosses off the chore and tags the user; a second cron POSTs the chore list to the whiteboard server. Runtime / SDK: - Node 12 (EOL) -> node:24-alpine (npm ci); @slack/bolt 1.3 -> 4.7 (await ack()/next(); custom HTTPReceiver adds GET /health; /slack/events + signing verification unchanged); dotenv 8 -> 17. - googleapis 43 -> 173: switch to the stable object-form google.auth.JWT constructor (same service-account auth, same Sheets v4 read). - @sentry/node 5 -> 10 (init-only usage unchanged); node-cron 2 -> 4 (schedule(expr, fn) API unchanged). npm audit: 0 vulnerabilities. - Keep moment-timezone (bumped within 0.5.x) to preserve date/timezone behavior. - Replace axios (whiteboard POST) with native fetch (same JSON body). Correctness / robustness: - Bug fix: getTodaysChores no longer crashes when the sheet read returns null/undefined (extracted + guarded filterTodaysChores). A Sheets error now propagates and is caught/logged by runChores instead of crashing the process (same observable result: nothing posted on error). - Bug fix: randomPhrase no longer mutates the shared phrases.request array (it pushed the seasonal phrase every in-season call -> unbounded growth / skewed odds). Now copies before appending. - await markChoreDone / chat.update and the notify posts. Testability / tooling: - Extract pure logic into utilities/blocks.js and utilities/chores.js; export phrases for testing. Add Jest tests (9). - ESLint 6 (EOL, was a runtime dep) -> ESLint 9 flat config (dev), matching the repo's original rule set. - Replace the dead SSH-over-password deploy workflows with GitHub Actions (lint + test + amd64 build). Add scripts/deploy.sh + config.example.sh (amd64 -> GHCR -> Coolify redeploy); secrets in gitignored scripts/config.sh. - Add the previously-missing start script. Verified: 9 unit tests pass; boots on the new stack, /health -> 200, both crons schedule without error; amd64 image builds with 0 npm vulnerabilities. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Summary
Behavior-preserving modernization of
chorebot(the heaviest of the set — EOL Node 12 + many major deps). Same behavior: the chores cron reads today's chores from the Google Sheet and posts them (or "No chores tonight!") to Slack with Done! buttons; clicking Done crosses off the chore and tags the user; a second cron POSTs the chore list to the whiteboard server. Verified: 9 unit tests pass; boots on the new stack with/health→ 200 and both crons scheduling without error; amd64 image builds with 0 npm vulnerabilities.Changes
node:24-alpine(npm ci);@slack/bolt1.3 → 4.7 (await ack()/next(), customHTTPReceiver+GET /health;/slack/eventsand signing verification unchanged, so no Slack app config change);dotenv8 → 17.googleapis43 → 173. Switched to the stable object-formgoogle.auth.JWTconstructor (same service-account auth, same Sheets v4 read).@sentry/node5 → 10 (we only callSentry.init— API unchanged; going to v10 specifically was needed to clear transitive OpenTelemetry/uuid advisories);node-cron2 → 4 (schedule(expr, fn)unchanged). Whiteboardaxios.post→ nativefetch(same JSON body).moment-timezone(bumped within 0.5.x) to avoid changing any date/timezone behavior — Luxon migration is a follow-up.getTodaysChoresno longer crashes when the sheet read returns null/undefined; a Sheets error now propagates and is caught/logged byrunChoresinstead of killing the process (same observable result — nothing posted on error).randomPhraseno longer mutates the sharedphrases.requestarray (it pushed the seasonal phrase on every in-season call → unbounded array growth + skewed odds). Now copies before appending.awaited the previously fire-and-forgetchat.update/ notify posts.utilities/blocks.js+utilities/chores.jswith Jest tests; ESLint 6 (EOL, and it was a runtime dep) → ESLint 9 flat config (dev); replaced the dead SSH-password deploy workflows (main.yml/deploy-dev.yml,appleboy/ssh-action@master) with GitHub Actions; added the missingstartscript andscripts/deploy.sh+config.example.sh.Deploy notes (no functional change)
keys/sheets-api.json) is still mounted at runtime, not baked into the image (.dockerignoreexcludeskeys/). Container still runs as root so the mounted key is readable regardless of volume ownership (matches prior behavior).spreadsheets.values.get) is stable across these googleapis versions and boots clean, but please confirm one real chores run in staging.Recommended follow-ups (NOT done)
moment-timezonewith Luxon/date-fns.