diff --git a/.serena/memories/critical-info.md b/.serena/memories/critical-info.md index 63bbce922a..fc9133326a 100644 --- a/.serena/memories/critical-info.md +++ b/.serena/memories/critical-info.md @@ -16,6 +16,7 @@ You are working on the GitHub project `penpot/penpot`, a monorepo. - Before `gh issue create` → `mem:workflow/creating-issues` (title derivation, body template, labels, Issue Type) - Before `gh pr create` / `gh pr edit` → `mem:workflow/creating-prs` (title format, body structure, "Note:" line) - Before a repo-wide pnpm version update → `mem:workflow/updating-pnpm` (workspace layout, `scripts/sync-pnpm-version` flow, the stamp-missing-field and ignored-builds gotchas, verification steps) +- Before bumping a non-JS dependency pinned in `docker/images/` (JDK, ImageMagick) → `mem:workflow/docker-images` - **Never `git push`, force-push, or modify `git origin`** (or any other remote). The user pushes from their own shell; if a push is required, say so and wait. Never amend a commit that the user has already pushed unless explicitly asked. - **Never edit `CHANGES.md` by hand.** The changelog is generated from GitHub milestones during the release process; update it only via the `update-changelog` skill flow or on explicit user request. - You have access to the GitHub CLI `gh` or corresponding MCP tools. diff --git a/.serena/memories/memory-maintenance.md b/.serena/memories/memory-maintenance.md index fbb800b99c..67a480a770 100644 --- a/.serena/memories/memory-maintenance.md +++ b/.serena/memories/memory-maintenance.md @@ -26,6 +26,7 @@ No manual line wrapping: one line per bullet or paragraph, however long. Memorie ## Add/update threshold Add or update memories only with stable, non-obvious project conventions that avoid complex rediscovery in the future. +Do not restate what the code already says (comments included): keep only what code cannot represent — decisions and their reasons, cross-file invariants, ordering constraints, environment and tool behavior, and traps that break builds. Do not add: quick-read facts; generic language/framework knowledge; one-off task notes; volatile line-level details; behavior likely to change soon. ## Maintenance Actions diff --git a/.serena/memories/workflow/docker-images.md b/.serena/memories/workflow/docker-images.md new file mode 100644 index 0000000000..28d42bc9e7 --- /dev/null +++ b/.serena/memories/workflow/docker-images.md @@ -0,0 +1,6 @@ +# Non-JS dependencies in the production images + +Node and pnpm pins: `mem:workflow/updating-node` and `mem:workflow/updating-pnpm`. Only the parts the Dockerfiles cannot show: + +- JDK: the image tracks a non-LTS line, so there is no minor to pick — the last build of the line is the ceiling; moving line is a major and needs a real build to validate the `jlink` module list. +- ImageMagick: publish `penpotapp/imagemagick:` **before** bumping the `COPY --from` refs in the backend, exporter and devenv Dockerfiles; otherwise both builds break on the missing tag. diff --git a/.serena/memories/workflow/updating-pnpm.md b/.serena/memories/workflow/updating-pnpm.md index 1bb1596959..e355c47450 100644 --- a/.serena/memories/workflow/updating-pnpm.md +++ b/.serena/memories/workflow/updating-pnpm.md @@ -27,8 +27,8 @@ file (never pipe tool output through filters). members) must carry a `packageManager` field with the identical `pnpm@+sha512.` value. Do not let them drift. `external/` (vendored trees with their own lifecycles), `.opencode/`, - `.pnpm-store/`, `bundles/` and `docker/images/bundle-*` copies are - never stamped. + `.pnpm-store/`, `bundles/`, `target/` build output and + `docker/images/bundle-*` copies are never stamped. - pnpm is a system binary everywhere (devenv image, CI runners, Docker images). Nothing may call corepack: it is gone from Node 25+. pnpm auto-downloads the `packageManager` version on mismatch @@ -60,6 +60,7 @@ file (never pipe tool output through filters). 4. Run `scripts/sync-pnpm-version --install` (plain `pnpm install` in each of the 11 workspace roots) to refresh lockfile metadata, then fix any failing workspace (see gotchas) and re-run that directory. + For a version-only bump, `pnpm install --lockfile-only` in each root is enough: it rewrites only the pnpm self-reference entries and needs no node_modules. The repo-root `pnpm-lock.yaml` is rewritten by itself on the first `pnpm` call that self-installs the target version. ## Gotchas diff --git a/scripts/sync-pnpm-version b/scripts/sync-pnpm-version index e87cf17500..149b072955 100755 --- a/scripts/sync-pnpm-version +++ b/scripts/sync-pnpm-version @@ -27,7 +27,8 @@ Options: -h, --help Show this help. external/ (vendored trees with their own lifecycles), .opencode/, -.pnpm-store/, bundles/ and docker build-context copies are never stamped. +.pnpm-store/, bundles/, build output (target/) and docker build-context +copies are never stamped. EOF } @@ -91,7 +92,7 @@ mapfile -t files < <( find . \ \( -name .git -o -name node_modules -o -name .pnpm-store \ -o -name external -o -name .opencode -o -name bundles \ - -o -name .angular \) -type d -prune \ + -o -name target -o -name .angular \) -type d -prune \ -o -path './docker/images/bundle-*' -prune \ -o -name package.json -type f -print | sort ) diff --git a/scripts/test_sync_pnpm_version.py b/scripts/test_sync_pnpm_version.py index 889250f433..91089632f2 100644 --- a/scripts/test_sync_pnpm_version.py +++ b/scripts/test_sync_pnpm_version.py @@ -134,6 +134,23 @@ class SyncPnpmVersionTests(unittest.TestCase): self.assertNotEqual(proc.returncode, 0) self.assertIn("missing/package.json", proc.stdout + proc.stderr) + def test_target_dirs_are_never_stamped(self): + generated = self.tmp / "module" / "target" + generated.mkdir(parents=True) + package = generated / "package.json" + package.write_text( + '{\n' + ' "name": "generated",\n' + ' "packageManager": "pnpm@10.0.0+sha512.' + "00" * 64 + '"\n' + '}\n' + ) + before = package.read_bytes() + + self.assertEqual(self.stamp().returncode, 0) + self.assertEqual(package.read_bytes(), before) + # The stale copy must not make --check fail either. + self.assertEqual(self.stamp("--check").returncode, 0) + def test_stamp_is_idempotent(self): self.assertEqual(self.stamp().returncode, 0) first = {