diff --git a/.github/tests/readme-translations.test.ts b/.github/tests/readme-translations.test.ts index ed7fdf77e..0ece41ebb 100644 --- a/.github/tests/readme-translations.test.ts +++ b/.github/tests/readme-translations.test.ts @@ -172,7 +172,7 @@ const requiredFragments = [ 'v1.6.0-rc.12', 'v1.6.0-rc.11', './CHANGELOG.md#160--2026-08-11', - './CHANGELOG.md#170-rc1--2026-08-13', + './CHANGELOG.md#170-rc1--2026-08-14', 'Portwing 0.9.0+', 'Standard HTTP', '`DD_EXPERIMENTAL_PORTWING=false`', diff --git a/CHANGELOG.md b/CHANGELOG.md index 973cc6893..ce85f8db6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [1.7.0-rc.1] — 2026-08-13 +## [1.7.0-rc.1] — 2026-08-14 ### Added +- **Edge Portwing polling cadence is configurable** ([#688](https://github.com/CodesWhat/drydock/issues/688)). `DD_PORTWING_POLL_INTERVAL` sets the controller-owned Edge container-refresh interval in positive integer seconds; the authenticated welcome frame and reported agent metadata use the same value, while absent or invalid values retain the 300-second default. - **Installable PWA support** (Roadmap Phase 6.9). Drydock is now an installable Progressive Web App via `vite-plugin-pwa`: a web app manifest (`Drydock`, standalone display, theme/background color matched to the One Dark default `--dd-bg`, 192/512 icons plus dedicated maskable variants with safe-zone padding) and an auto-updating service worker (`registerType: 'autoUpdate'`) that precaches the SPA shell so the dashboard boots offline. `/api/**` is explicitly excluded from all service-worker handling — no navigation fallback, no runtime caching — so a live dashboard never serves stale API data from cache; those requests always hit the network and surface a normal error if it's unreachable. A dismissible install banner (new `InstallBanner` component, following the existing `AnnouncementBanner` pattern) listens for the browser's `beforeinstallprompt` event and offers a one-click install, with the dismissal persisted under a versioned localStorage key. iOS home-screen install is supported via `apple-mobile-web-app-capable` and the existing `apple-touch-icon`. The backend's static UI server now serves `sw.js` with `Cache-Control: no-cache` so a new deploy is never masked by a browser-cached service worker script. - **Clickable port links in the container list and detail views.** Each host-published port in a container's `details.ports` now renders as a link (opened in a new tab, `rel="noopener noreferrer"`) instead of inert text — in the side panel, the full-page detail tabs, and new opt-in "Ports" columns/rows in the table and card views. The scheme is auto-detected from the container-side port (`443`/`8443` → `https://`, everything else → `http://`); the link target host prefers the port's own bound `HostIp` when it's a real address (not `0.0.0.0`/`::`/`::0`), falling back to the agent's configured host for agent-watched containers, or the browser's own hostname otherwise. Internal-only (unpublished) ports still render as plain text. A new `dd.port.label` container label lets you attach a friendly name to a specific port (`dd.port.label=80=Web UI,443=Admin Console`) shown in place of the raw `hostPort->containerPort/protocol` mapping. - **Container uptime, with a live-refreshing display.** The existing `details.startedAt` field (from Docker's `State.StartedAt`) now also drives an opt-in "Uptime" tooltip showing the exact start timestamp in the container list, and a live "Up …" indicator in the card view's footer — both refresh on a timer and update immediately on SSE container-state changes, matching the full-page detail view's existing uptime display. @@ -42,6 +43,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- **Edge exec completion reasons now reach the controller-side consumer** ([#635](https://github.com/CodesWhat/drydock/issues/635)). String `exec_end.reason` values are forwarded through the internal `startExec` end callback; sessions are removed before consumer code runs, and a throwing callback cannot leak state or stop disconnect cleanup. +- **Controller-owned Portwing containers now use the controller's configured registry identity before native checks** ([#687](https://github.com/CodesWhat/drydock/issues/687)). Complete Docker-transport inventory and event records are normalized to the canonical registry name, URL, credentials, and image identity, so watch-now no longer delegates registry work to Portwing's intentional 501 stub; traditional agents and partial events keep their existing behavior. +- **Manifest `created` metadata failures no longer become successful missing dates** ([#606](https://github.com/CodesWhat/drydock/issues/606)). Exhausted 4xx, 5xx, network, and non-object failures now propagate the original failure; only 301/302/303/307/308 responses on optional manifest/blob metadata may omit `created`, preserving an already resolved digest while redirect following stays disabled. - **Demo site favicon now matches the refreshed branding.** The v1.5.1 brand refresh (#439) moved the website to the cropped whale "headshot" icon and the app UI followed, but demo.getdrydock.com kept showing the old full-body whale: its stale `favicon.svg` — which modern browsers preferred over the PNGs — was never replaced. The demo now ships the same headshot icon set as the website and app UI, the `favicon.svg` is removed, and the icon links carry a `?v=2` cache-buster so browsers re-fetch instead of serving the aggressively cached old icon. (#689, forward-ported in #690) - **Audit nav icon no longer renders blank for the Lucide icon-library preference, and the icon bundle no longer silently drops aliased or renamed icons at image build time.** PR [#668](https://github.com/CodesWhat/drydock/pull/668)'s icon-bundle regeneration against tabler 1.2.38 dropped `lucide:history` from `ui/src/boot/icon-bundle.json` because the installed `@iconify-json/lucide` (1.2.121) demoted `history` to an alias of `rotate-ccw-clock`; `ui/scripts/extract-icons.mjs`'s bundler only ever looked up plain icon entries, never aliases, so every image built since that pin shipped without it (no network fallback, since the iconify API module is offline-only). The extractor now resolves alias chains (parent-following, depth-capped at 5), refusing any alias that carries a `rotate`/`hFlip`/`vFlip` transform the body-only bundle can't represent. `ui/src/icons.ts`'s `iconMap.audit.lucide` is repointed to `lucide:rotate-ccw-clock` directly (the same clock-with-counter-clockwise-arrow concept `fa6-solid:clock-rotate-left`/`ph:clock-counter-clockwise` already use for the same entry) rather than relying on alias resolution for that one. A bundle-consistency sweep against the currently installed iconsets turned up more references that never resolved against the locked collections at all: `iconoir:history` → `iconoir:clock-rotate-right`, `iconoir:gitlab` → `iconoir:gitlab-full`, `iconoir:stack` → `iconoir:multiple-pages`, `lucide:more-vertical` → `lucide:ellipsis-vertical`, `iconoir:key-alt` → `iconoir:key`, plus four `fa6-brands:*` icons that were never bundled because `@iconify-json/fa6-brands` (now exact-pinned as a devDependency) was missing from `ui/package.json` entirely — all fixed or backfilled so every `:` reference in `iconMap` now resolves. Two guard tests now cover this class of regression: `ui/tests/icons.spec.ts` asserts every `iconMap` entry has a matching `icon-bundle.json` key, and `ui/tests/boot/icon-bundle.spec.ts` independently asserts every `'prefix:name'` reference found in `icons.ts` exists in the generated bundle with a body. - **Compose-derived dependency detection now works when drydock itself runs in a container (v1.7, [discussion #219](https://github.com/CodesWhat/drydock/discussions/219)).** `resolveComposeDependsOn` (`app/dependencies/compose-dependency-resolver.ts`) read a container's compose file at its host-side label path — which doesn't exist inside drydock's own container filesystem unless a bind mount happens to line up 1:1 — so most non-trivial layouts silently detected zero dependencies. Host compose-file paths are now translated through drydock's own bind mounts (reusing the existing Docker Compose trigger's translation logic, `ComposePathBindMounts.ts`, cached per Docker API instance) before being read; when none of a container's configured compose file paths can be translated and read, that now surfaces as a single warning naming every path tried instead of a silent empty result. diff --git a/README.de.md b/README.de.md index 564b2738c..bf7f4a9bf 100644 --- a/README.de.md +++ b/README.de.md @@ -187,7 +187,7 @@ Weitere Informationen zu Docker Compose, Socket-Sicherheit, Reverse-Proxy und al - **Sicherheits- und Lifecycle-Härtung** – Authentifizierung, Agent-Anfragen, Protokolle, WebSockets und Registry-Anfragen sind explizit begrenzt; vertrauliche Befehls- und Hook-Werte werden redigiert; die Home-Assistant-Erkennung synchronisiert sich nach dem Start neu und beendet Provider-Arbeit ohne veraltete Veröffentlichungen. ([#708](https://github.com/CodesWhat/drydock/issues/708)) - **First-Party-Star-History** – der Same-Origin-Provider `/api/star-history` stellt Diagramme für die freigegebenen Repositories Drydock, Sockguard und Portwing ohne Drittanbieter-Tracker bereit. -Vollständige Versionshinweise in [CHANGELOG.md](./CHANGELOG.md#170-rc1--2026-08-13). +Vollständige Versionshinweise in [CHANGELOG.md](./CHANGELOG.md#170-rc1--2026-08-14). diff --git a/README.es.md b/README.es.md index 3140973e3..3a79ec49f 100644 --- a/README.es.md +++ b/README.es.md @@ -187,7 +187,7 @@ Consulte la [guía de inicio rápido](https://getdrydock.com/docs/quickstart) pa - **Refuerzo de seguridad y ciclo de vida**: la autenticación, las solicitudes de agentes, los registros, WebSockets y las solicitudes a registros tienen límites explícitos; se ocultan valores sensibles de comandos y hooks; el descubrimiento de Home Assistant se resincroniza después del arranque y retira trabajo de proveedores sin publicaciones obsoletas. ([#708](https://github.com/CodesWhat/drydock/issues/708)) - **Historial de estrellas propio**: el proveedor de mismo origen `/api/star-history` sirve gráficos de Drydock, Sockguard y Portwing mediante una lista permitida, sin depender de un rastreador externo. -Notas completas en [CHANGELOG.md](./CHANGELOG.md#170-rc1--2026-08-13). +Notas completas en [CHANGELOG.md](./CHANGELOG.md#170-rc1--2026-08-14). diff --git a/README.fr.md b/README.fr.md index aadac4153..c9fe4ea31 100644 --- a/README.fr.md +++ b/README.fr.md @@ -187,7 +187,7 @@ Consultez le [Guide de démarrage rapide](https://getdrydock.com/docs/quickstart - **Renforcement de la sécurité et du cycle de vie** : l'authentification, les requêtes d'agents, les journaux, les WebSockets et les requêtes de registres sont explicitement bornés ; les valeurs sensibles de commandes et hooks sont masquées ; la découverte Home Assistant se resynchronise après le démarrage et retire les tâches des fournisseurs sans publication périmée. ([#708](https://github.com/CodesWhat/drydock/issues/708)) - **Historique des étoiles propriétaire** : le fournisseur même origine `/api/star-history` sert les graphiques Drydock, Sockguard et Portwing autorisés sans dépendre d'un service tiers. -Notes complètes dans [CHANGELOG.md](./CHANGELOG.md#170-rc1--2026-08-13). +Notes complètes dans [CHANGELOG.md](./CHANGELOG.md#170-rc1--2026-08-14). diff --git a/README.md b/README.md index ec7eeaae3..7ff5fb5f2 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ See the [Quick Start guide](https://getdrydock.com/docs/quickstart) for Docker C - **Security and lifecycle hardening** — bounded authentication, agent, log, WebSocket, and registry operations; sensitive command and hook values are redacted; Home Assistant discovery resynchronizes after startup and retires provider work without stale publishes. ([#708](https://github.com/CodesWhat/drydock/issues/708)) - **First-party star history** — the same-origin `/api/star-history` provider now serves allowlisted Drydock, Sockguard, and Portwing charts without a third-party tracker dependency. -Full release notes in [CHANGELOG.md](./CHANGELOG.md#170-rc1--2026-08-13). +Full release notes in [CHANGELOG.md](./CHANGELOG.md#170-rc1--2026-08-14). diff --git a/README.pl.md b/README.pl.md index 2452ef521..09bf1f1f4 100644 --- a/README.pl.md +++ b/README.pl.md @@ -187,7 +187,7 @@ Zobacz [Przewodnik szybkiego startu](https://getdrydock.com/docs/quickstart) dla - **Wzmocnienie bezpieczeństwa i cyklu życia** — uwierzytelnianie, żądania agentów, dzienniki, WebSockety i żądania do rejestrów mają jawne limity; poufne wartości poleceń i hooków są maskowane; wykrywanie Home Assistant ponownie synchronizuje się po starcie i wycofuje zadania dostawców bez publikowania nieaktualnych danych. ([#708](https://github.com/CodesWhat/drydock/issues/708)) - **Własna historia gwiazdek** — dostawca same-origin `/api/star-history` udostępnia wykresy dla dozwolonych repozytoriów Drydock, Sockguard i Portwing bez zewnętrznego modułu śledzącego. -Pełne informacje o wersji znajdują się w [CHANGELOG.md](./CHANGELOG.md#170-rc1--2026-08-13). +Pełne informacje o wersji znajdują się w [CHANGELOG.md](./CHANGELOG.md#170-rc1--2026-08-14). diff --git a/README.pt-BR.md b/README.pt-BR.md index 76c6480b9..fb8740af6 100644 --- a/README.pt-BR.md +++ b/README.pt-BR.md @@ -187,7 +187,7 @@ Consulte o [Guia de início rápido](https://getdrydock.com/docs/quickstart) par - **Reforço de segurança e ciclo de vida** — autenticação, solicitações de agentes, logs, WebSockets e solicitações a registros têm limites explícitos; valores sensíveis de comandos e hooks são ocultados; a descoberta do Home Assistant sincroniza novamente após a inicialização e encerra o trabalho dos provedores sem publicações obsoletas. ([#708](https://github.com/CodesWhat/drydock/issues/708)) - **Histórico de estrelas próprio** — o provedor de mesma origem `/api/star-history` serve gráficos permitidos de Drydock, Sockguard e Portwing sem depender de um rastreador de terceiros. -Notas completas em [CHANGELOG.md](./CHANGELOG.md#170-rc1--2026-08-13). +Notas completas em [CHANGELOG.md](./CHANGELOG.md#170-rc1--2026-08-14). diff --git a/README.zh-CN.md b/README.zh-CN.md index 35b425148..959b923a2 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -187,7 +187,7 @@ docker run -d \ - **安全与生命周期强化** — 身份验证、代理请求、日志、WebSocket 和镜像仓库请求均有明确的资源限制;敏感的命令和钩子值会被遮盖;Home Assistant 发现会在启动后重新同步,并在停用提供程序任务时避免发布过期数据。([#708](https://github.com/CodesWhat/drydock/issues/708)) - **第一方 Star History** — 同源 `/api/star-history` 提供程序仅为允许的 Drydock、Sockguard 和 Portwing 仓库生成图表,不再依赖第三方跟踪服务。 -完整发行说明请参阅 [CHANGELOG.md](./CHANGELOG.md#170-rc1--2026-08-13)。 +完整发行说明请参阅 [CHANGELOG.md](./CHANGELOG.md#170-rc1--2026-08-14)。 diff --git a/content/docs/current/updates/index.mdx b/content/docs/current/updates/index.mdx index 7e1f1c2df..6d2d01b07 100644 --- a/content/docs/current/updates/index.mdx +++ b/content/docs/current/updates/index.mdx @@ -5,12 +5,14 @@ description: "Release update notes and feature highlights, with direct links to ## Unreleased -## v1.7.0-rc.1 Highlights — August 13, 2026 +## v1.7.0-rc.1 Highlights — August 14, 2026 - **Dependency-aware updates** — labels or Compose metadata resolve into deterministic update waves, with preview-bound confirmation, restart-only dependents, cycle handling, and explicit skipped-dependency results ([Discussion #219](https://github.com/CodesWhat/drydock/discussions/219)). - **Operator UX additions** — installable PWA support, clickable named port links, live container uptime, keyboard shortcuts, and a configurable settling window for newly discovered containers. - **Trigger migration reaches removal** — `DD_TRIGGER_*` now fails startup and legacy `dd.trigger.include` / `dd.trigger.exclude` labels no longer route work; use the action/notification-specific replacements. - **Security and lifecycle hardening** — authentication, agent requests, logs, WebSockets, and registry requests have explicit resource bounds; sensitive command/hook values are redacted; Home Assistant discovery now resynchronizes after startup and drains retiring provider work without stale publishes ([#708](https://github.com/CodesWhat/drydock/issues/708)). +- **Portwing controller and Edge reliability** — Edge refresh cadence is configurable with `DD_PORTWING_POLL_INTERVAL`; `exec_end.reason` reaches internal consumers without leaking sessions; and controller-owned Docker inventory/events normalize against configured registries so native watch-now checks no longer round-trip through Portwing's intentional 501 registry stub ([#688](https://github.com/CodesWhat/drydock/issues/688), [#635](https://github.com/CodesWhat/drydock/issues/635), [#687](https://github.com/CodesWhat/drydock/issues/687)). +- **Registry metadata failures stay visible** — optional manifest/blob responses with status 301, 302, 303, 307, or 308 can omit `created` without discarding a resolved digest, while exhausted 4xx, 5xx, network, and non-object failures now propagate instead of silently becoming a successful result with unstable metadata ([#606](https://github.com/CodesWhat/drydock/issues/606)). - **First-party star history** — the same-origin `/api/star-history` provider serves allowlisted Drydock, Sockguard, and Portwing charts without relying on a third-party tracker. ## v1.6.0 Highlights — August 11, 2026 diff --git a/scripts/release-docs-identity.test.mjs b/scripts/release-docs-identity.test.mjs index 352be92a4..f6742f072 100644 --- a/scripts/release-docs-identity.test.mjs +++ b/scripts/release-docs-identity.test.mjs @@ -4,8 +4,8 @@ import test from 'node:test'; const RC_VERSION = '1.7.0-rc.1'; const PREV_RC_VERSION = '1.6.0'; -const RC_DATE = '2026-08-13'; -const RC_DISPLAY_DATE = 'August 13, 2026'; +const RC_DATE = '2026-08-14'; +const RC_DISPLAY_DATE = 'August 14, 2026'; const DOC_ROOTS = ['content/docs/current', 'content/docs/v1.6', 'content/docs/v1.5']; const BROAD_401_CLAIM = /(?:all|every) API (?:call|request)s?(?: (?:is|are) rejected with| returns?) `401`/iu; @@ -18,6 +18,13 @@ function escapeRegExp(value) { return value.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&'); } +function extractMarkdownSection(document, heading) { + const sectionStart = document.indexOf(`${heading}\n`); + assert.notEqual(sectionStart, -1, `missing Markdown section: ${heading}`); + const nextSectionStart = document.indexOf('\n## ', sectionStart + heading.length + 1); + return document.slice(sectionStart, nextSectionStart === -1 ? undefined : nextSectionStart); +} + test('public release surfaces identify the v1.7 release candidate', () => { const readme = read('README.md'); const siteConfig = read('apps/web/src/lib/site-config.ts'); @@ -90,6 +97,47 @@ test('public release surfaces identify the v1.7 release candidate', () => { ); }); +test('release-note section extraction stops at the next level-two heading', () => { + assert.equal( + extractMarkdownSection('## Target\nkeep\n\n## Later\nignore\n', '## Target'), + '## Target\nkeep\n', + ); +}); + +test('release candidate notes cover the post-promotion fixes', () => { + const changelog = extractMarkdownSection(read('CHANGELOG.md'), `## [${RC_VERSION}] — ${RC_DATE}`); + const updates = extractMarkdownSection( + read('content/docs/current/updates/index.mdx'), + `## v${RC_VERSION} Highlights — ${RC_DISPLAY_DATE}`, + ); + + for (const issue of [606, 635, 687, 688]) { + const issueLink = `https://github.com/CodesWhat/drydock/issues/${issue}`; + assert.ok(changelog.includes(issueLink), `CHANGELOG.md must link issue #${issue}`); + assert.ok(updates.includes(issueLink), `updates page must link issue #${issue}`); + } + + assert.doesNotMatch(updates, /\b3xx\b/iu); + for (const fragment of [ + '`DD_PORTWING_POLL_INTERVAL`', + '`exec_end.reason`', + "controller's configured registry identity", + '4xx, 5xx, network, and non-object failures', + ]) { + assert.ok(changelog.includes(fragment), `CHANGELOG.md must include ${fragment}`); + } + + for (const fragment of [ + '`DD_PORTWING_POLL_INTERVAL`', + '`exec_end.reason`', + 'normalize against configured registries', + '301, 302, 303, 307, or 308', + '4xx, 5xx, network, and non-object failures', + ]) { + assert.ok(updates.includes(fragment), `updates page must include ${fragment}`); + } +}); + test('v1.6.0 is released and public release routing advances to v1.7', () => { const readme = read('README.md'); const siteContent = read('apps/web/src/lib/site-content.ts');