Commit Graph

206 Commits

Author SHA1 Message Date
a8f0828727 "fix-sort-and-display-name" 2026-07-07 20:42:45 -04:00
github-actions[bot]
b250492925 chore(release): 0.16.0 [skip ci]
# [0.16.0](https://github.com/offendingcommit/openconcho/compare/v0.15.0...v0.16.0) (2026-06-10)

### Bug Fixes

* **ci:** replace userEvent.selectOptions with fireEvent.change; bump setup-node to v6 ([1c28cae](1c28cae3f2))
* **dashboard:** guard setMetricsById against same-value calls to end loop ([3b88a41](3b88a41afd))
* **dashboard:** remove lastSeen from metrics useMemo deps to break render loop ([f79cdaf](f79cdafba7))
* **dashboard:** use primitive deps in onMetrics effect to break render loop ([9cc8637](9cc8637dc7))
* **test:** remove unused within import from fleet.test.tsx ([36fb6ee](36fb6ee519))
* **web:** break metricsEqual lastSeen dep-loop; add staleTime to test ([173f096](173f096e33))
* **web:** reset serverFilter when the selected instance is removed ([699ec38](699ec38480))
* **web:** stop Sidebar re-render loop from cache-event subscriber ([7e529c8](7e529c8c44))

### Features

* **web:** merge Fleet into a server-filterable Dashboard ([e66f927](e66f927f89)), closes [#54](https://github.com/offendingcommit/openconcho/issues/54)
* **web:** redirect /fleet to Dashboard; update fleet tests ([da126b2](da126b2e74))
2026-06-10 12:20:29 +00:00
Offending Commit
01371db6ae Merge pull request #61 from offendingcommit/feat/dashboard-fleet-merge 2026-06-10 07:19:26 -05:00
Offending Commit
7e529c8c44 fix(web): stop Sidebar re-render loop from cache-event subscriber
setNow(Date.now()) was called on every query-cache event, including
events dispatched synchronously during ServerWorkspaceRows' render.
On CI, consecutive Date.now() calls cross millisecond boundaries so
each call returns a new value — React always re-renders Sidebar, which
re-renders the layout, which re-renders ServerWorkspaceRows, which
fires more cache events. After ~25 cycles React throws "Maximum update
depth exceeded."

Fix: remove setNow from the cache subscriber. setNow now fires only
from the 30s interval timer, where it correctly refreshes the "X ago"
display text without triggering a render loop.

Also adds a deterministic CI-repro test that mocks Date.now to return
incrementing values and asserts the filter-change path completes without
looping.
2026-06-03 18:11:30 -05:00
Offending Commit
3b88a41afd fix(dashboard): guard setMetricsById against same-value calls to end loop
Even with primitive useEffect deps, TanStack Query or React concurrent
rendering can cause onMetrics to fire with identical values mid-render-cycle.
Add a ref-based equality check in Dashboard.onMetrics: if all five metric
values are unchanged, skip setMetricsById entirely — no state update, no
Dashboard re-render, loop terminates.

Also fixes vi.fn<TFunction>() typing in server-workspace-rows.test.tsx to
satisfy tsc (Vitest 4 single-type-arg signature).
2026-06-03 17:48:22 -05:00
Offending Commit
9cc8637dc7 fix(dashboard): use primitive deps in onMetrics effect to break render loop
Using a metrics object as a useEffect dep causes the loop:
  onMetrics → setMetricsById → Dashboard re-renders → ServerWorkspaceRows
  re-renders → useQueries runs → TanStack Query cache subscriber fires
  (Sidebar's setNow) → query result objects are new references → metrics
  useMemo returns new object → effect dep changed → onMetrics again → ∞

Fix: depend on the five primitive values (workspaceCount, conclusionCount,
queueActive, queuePending, health) directly. React compares primitives by
value, so the effect only fires when actual data changes, not on reference
churn from re-renders.

Also adds server-workspace-rows.test.tsx with three focused unit tests:
correct health:ok report, stability after load (no re-fire), and health
transition coverage.
2026-06-03 17:48:21 -05:00
Offending Commit
e59bfd908a Merge branch 'main' into feat/dashboard-fleet-merge 2026-06-03 17:28:52 -05:00
Offending Commit
3c99af7143 refactor(dashboard): drop unused lastSeen local var after metrics fix 2026-06-03 17:27:55 -05:00
Offending Commit
f79cdafba7 fix(dashboard): remove lastSeen from metrics useMemo deps to break render loop
lastSeen tracked workspacesQ.dataUpdatedAt which updates on every background
refetch, creating a new metrics object reference each cycle. The useEffect
dep on metrics then fired unconditionally, calling onMetrics → setMetricsById
→ Dashboard re-render → another refetch → infinite loop.

computeFleetAggregates never reads lastSeen, so reporting it upward was
pointless. Hardcode null and drop it from the dep array entirely.
2026-06-03 17:27:42 -05:00
Offending Commit
36fb6ee519 fix(test): remove unused within import from fleet.test.tsx 2026-06-03 17:10:32 -05:00
Offending Commit
da126b2e74 feat(web): redirect /fleet to Dashboard; update fleet tests
/fleet now redirects to / so bookmarks and muscle-memory links land on the
unified Dashboard rather than a 404-style dead route. Fleet tests updated
to assert the redirect and the per-instance rows the Dashboard renders.
2026-06-03 17:10:02 -05:00
Offending Commit
699ec38480 fix(web): reset serverFilter when the selected instance is removed
If the user filters to a specific server and that instance is then deleted
(e.g. from Settings), shownInstances becomes [] — empty table, no message.
A useEffect resets the filter to ALL_SERVERS whenever the selected ID
disappears from the instances list.
2026-06-03 17:03:10 -05:00
Offending Commit
173f096e33 fix(web): break metricsEqual lastSeen dep-loop; add staleTime to test
Remove lastSeen from metricsEqual in ServerWorkspaceRows: computeFleetAggregates
never reads lastSeen, so comparing it in metricsEqual causes background TanStack
Query refetches (dataUpdatedAt changes) to trigger spurious onMetrics callbacks,
cascading into a Dashboard → ServerWorkspaceRows render loop that hits React's
100-render limit in CI.

Add staleTime: Infinity to the test QueryClient to prevent background refetches
from interfering with test assertions.
2026-06-03 17:01:19 -05:00
Offending Commit
1c28cae3f2 fix(ci): replace userEvent.selectOptions with fireEvent.change; bump setup-node to v6
userEvent.selectOptions hangs in jsdom when firing pointer + change event
sequences — fireEvent.change fires the React-controlled onChange directly
and is deterministic. Removes userEvent import (no longer used).

Bump actions/setup-node from v4 to v6 to clear the Node.js 20 deprecation
warning on GitHub Actions runners.
2026-06-03 16:41:40 -05:00
github-actions[bot]
2a6ddb9f78 chore(release): 0.15.0 [skip ci]
# [0.15.0](https://github.com/offendingcommit/openconcho/compare/v0.14.0...v0.15.0) (2026-06-03)

### Bug Fixes

* **helm:** guard tmpfs blocks when empty, cap volume names at 63 chars ([d5a65d7](d5a65d73b5))
* **helm:** pdb mutual exclusion, ingress null rules guard, hpa nil utilization guard ([b4939bd](b4939bd57f))
* **helm:** pin busybox:1.36, add -T 10 timeout, use --spider, add activeDeadlineSeconds ([8fac5d0](8fac5d060f))
* **helm:** use http://json-schema.org/draft-07/schema# for Helm compatibility ([8d41455](8d41455e39))

### Features

* **helm:** add _helpers.tpl with name, label, and imageTag partials ([0268275](02682750ab))
* **helm:** add Deployment template with read-only FS, tmpfs, probes ([514e1d4](514e1d46c0))
* **helm:** add NOTES.txt with access instructions and NetworkPolicy/Ingress warning ([ce211df](ce211df48c))
* **helm:** add optional HPA, PDB, and NetworkPolicy templates ([b0b648b](b0b648bdcf))
* **helm:** add optional Ingress template ([9aa106c](9aa106cede))
* **helm:** add Service and ServiceAccount templates ([ee916ea](ee916eabc4))
* **helm:** add test-healthz and test-spa-root helm test jobs ([ee4630e](ee4630e79c))
* **helm:** chart scaffold — Chart.yaml, values, schema ([4112270](411227046a))
2026-06-03 21:37:08 +00:00
Offending Commit
11eec585fa Merge pull request #62 from offendingcommit/worktree-feat+helm-chart
feat(helm): add Helm chart for self-hosted openconcho web UI
2026-06-03 16:35:58 -05:00
Offending Commit
d81e7f17ac docs(helm): annotate values.yaml, add chart README, ArgoCD example, update root README and AGENTS.md 2026-06-03 16:32:28 -05:00
Offending Commit
4ebd4cc211 ci(helm): publish chart to ghcr oci on release tags 2026-06-03 11:29:04 -05:00
Offending Commit
8fac5d060f fix(helm): pin busybox:1.36, add -T 10 timeout, use --spider, add activeDeadlineSeconds 2026-06-03 11:22:19 -05:00
Offending Commit
ee4630e79c feat(helm): add test-healthz and test-spa-root helm test jobs 2026-06-03 11:16:27 -05:00
Offending Commit
ce211df48c feat(helm): add NOTES.txt with access instructions and NetworkPolicy/Ingress warning 2026-06-03 11:16:23 -05:00
Offending Commit
b4939bd57f fix(helm): pdb mutual exclusion, ingress null rules guard, hpa nil utilization guard 2026-06-03 11:14:47 -05:00
Offending Commit
b0b648bdcf feat(helm): add optional HPA, PDB, and NetworkPolicy templates 2026-06-03 11:07:11 -05:00
Offending Commit
9aa106cede feat(helm): add optional Ingress template 2026-06-03 11:07:03 -05:00
Offending Commit
ee916eabc4 feat(helm): add Service and ServiceAccount templates 2026-06-03 11:06:53 -05:00
Offending Commit
d5a65d73b5 fix(helm): guard tmpfs blocks when empty, cap volume names at 63 chars 2026-06-03 11:04:15 -05:00
Offending Commit
514e1d46c0 feat(helm): add Deployment template with read-only FS, tmpfs, probes 2026-06-03 11:00:54 -05:00
Offending Commit
02682750ab feat(helm): add _helpers.tpl with name, label, and imageTag partials 2026-06-03 10:59:04 -05:00
Offending Commit
8d41455e39 fix(helm): use http://json-schema.org/draft-07/schema# for Helm compatibility 2026-06-03 10:56:37 -05:00
Offending Commit
411227046a feat(helm): chart scaffold — Chart.yaml, values, schema 2026-06-03 10:51:38 -05:00
Offending Commit
e66f927f89 feat(web): merge Fleet into a server-filterable Dashboard
Implements Phase 1 of the UI navigation rework (docs/superpowers/specs/
2026-06-02-ui-navigation-rework.md): the Dashboard now lists every workspace
across every configured server as <workspace> (<server>), filterable by server,
with cross-server aggregate cards (reusing computeFleetAggregates). Opening a
workspace activates its server then drills into the existing detail route.
Per-server fan-out lives in a ServerWorkspaceRows child (rules-of-hooks safe,
mirrors FleetRow). Fleet removed from the sidebar nav; the /fleet route is left
intact for now (full removal deferred to avoid churning fleet.test.tsx while #54
is open).
2026-06-02 16:41:51 -05:00
Offending Commit
3677575f65 Merge pull request #57 from offendingcommit/chore/deps-consolidated
chore(deps): consolidate Dependabot PRs #34-53 + widen grouping
2026-06-02 15:41:21 -05:00
Offending Commit
765c618a76 ci(dependabot): widen grouping to cut PR volume
Collapse minor+patch bumps into one PR per ecosystem (npm/cargo) with majors in
their own grouped PR; add a github-actions group so action bumps batch instead of
one PR each. Replaces the many narrow per-family groups.
2026-06-02 15:38:23 -05:00
Offending Commit
85b56ca0f8 chore(deps): consolidate dependency bumps (supersedes #34-53)
npm (lockfile + catalog): tanstack, @tauri-apps/*, turbo 2.9.x, biome, vite,
@vitejs/plugin-react, vitest+coverage-v8 4.1.8 (lockstep), @playwright/test,
and jsdom 26->29 (major). cargo: tauri 2.11.2, tauri-plugin-http 2.5.9,
tauri-build 2.6.2, serde_json. github-actions: docker setup-qemu v4,
setup-buildx v4, build-push v7. Validated: make ci-web, cargo-check, actionlint.
2026-06-02 15:38:22 -05:00
github-actions[bot]
c474767ba1 chore(release): 0.14.0 [skip ci]
# [0.14.0](https://github.com/offendingcommit/openconcho/compare/v0.13.1...v0.14.0) (2026-06-02)

### Bug Fixes

* **docker:** derive nginx resolver from container DNS ([66b299a](66b299a28e))
* **docker:** drop dead HONCHO_UPSTREAM and same-origin default ([a2854ab](a2854ab8ea))
* **web:** enforce upstream allowlist in vite dev proxy ([b4fac95](b4fac95f37))
* **web:** raise connection-test timeout for cold upstreams ([409d7d8](409d7d8be7))
* **web:** strip content-encoding from vite dev proxy responses ([6b602c0](6b602c05bb))

### Features

* **docker:** header-driven /api reverse proxy in nginx ([753c978](753c978f56))
* **docker:** render SSRF allowlist map from env ([0af1ad9](0af1ad923c))
* **docker:** split compose into dev-forward build and prod pull ([c9bd2db](c9bd2db07d))
* **web:** add dispatchFor transport helper for same-origin proxy ([9945e4c](9945e4cf14))
* **web:** dev /api proxy middleware mirroring nginx ([ab8a1ba](ab8a1ba866))
* **web:** route checkConnection and discovery through the proxy ([9893230](9893230cde))
* **web:** route web build through same-origin /api proxy ([0935099](0935099bc2))
2026-06-02 20:20:40 +00:00
Offending Commit
5a2543592a Merge pull request #54 from offendingcommit/feat/web-api-proxy
feat(web): eliminate browser CORS via header-driven /api proxy
2026-06-02 15:19:32 -05:00
Offending Commit
4349864234 Merge pull request #56 from offendingcommit/chore/env-specific-hardening
chore: harden against environment-specific leaks
2026-06-02 15:19:05 -05:00
Offending Commit
1aa1c0456f chore(hooks): flag tailnet hostnames and CGNAT IPs in the secret scan
Extend the pre-commit secret-scan to catch environment-specific values
(*.ts.net MagicDNS names and 100.64.0.0/10 tailnet IPs) so live infra can't be
committed into code, docs, or examples. Verified: detects leaks, no false
positive on 192.0.2.x or non-CGNAT 100.x, and the script does not self-trip.
2026-06-02 15:12:33 -05:00
Offending Commit
239eb3327a test(web): use a documentation IP instead of a tailnet-range fixture
Swap the 100.x CGNAT example for 192.0.2.10 (RFC 5737 TEST-NET-1) in the
token-transport guard tests — keeps the non-loopback-HTTP assertion, drops an
environment-specific address.
2026-06-02 15:11:17 -05:00
Offending Commit
08b77839b1 docs: scrub environment-specific endpoint from proxy spec
Replace the real tailnet hostname and IP with honcho.example.net / a generic
tailnet reference — specs and PRs should carry examples, not live endpoints.
2026-06-02 15:05:25 -05:00
Offending Commit
3ea6a73833 test(web): cover instance store CRUD and legacy migration
Closes the multi-instance coverage gap: add (first becomes active, later adds
don't steal focus, insertion order), switch (incl. unknown-id no-op), delete
(active->first-remaining fallback, non-active unchanged, last clears active),
update (patch + unknown-id no-op), active config, and legacy-key migration.
2026-06-02 14:36:23 -05:00
Offending Commit
96dff8900e refactor(web): use top-level z.url() over deprecated z.string().url()
Zod v4 deprecates the chained .url() in favor of the top-level format validator.
2026-06-02 14:31:40 -05:00
Offending Commit
409d7d8be7 fix(web): raise connection-test timeout for cold upstreams
A cold/idle self-hosted Honcho can take ~5s on its first request (DB pool, tunnel
wake); the hardcoded 5s budget aborted just before the response and reported a
live instance as 'Connection timed out'. Extract CONNECTION_TIMEOUT_MS (15s),
make checkConnection's timeout injectable, and cover the budget behavior.
2026-06-02 14:18:19 -05:00
Offending Commit
4ccf8f2746 refactor(docker): use compose profiles instead of a prod override file
Collapse docker-compose.yml + docker-compose.prod.yml into one file with dev/prod
profiles sharing a YAML anchor: dev builds from source, prod pulls ghcr latest.
make up/prod select the profile; down passes both so it stops either. Drops the
separate prod file and the !reset hack.
2026-06-02 14:02:36 -05:00
Offending Commit
4e4843ce1a refactor(make): rename compose targets to up/prod/down/clean
Concise verbs: make up (dev build+run), make prod (pull published image),
make down (stop regardless of mode), make clean (down + drop local image).
Updates all docs and compose-file comment headers to match.
2026-06-02 13:59:04 -05:00
Offending Commit
e1285c73ad docs: document dev/prod compose modes and make targets
README, docs/docker.md, and AGENTS.md now cover make compose-up (dev-forward,
builds from source) vs make compose-up-prod (pulls ghcr latest) and compose-down.
2026-06-02 13:51:23 -05:00
Offending Commit
c9bd2db07d feat(docker): split compose into dev-forward build and prod pull
docker-compose.yml now builds from source (dev-forward: run your local changes);
docker-compose.prod.yml overrides it to pull ghcr latest (build reset via !reset).
Adds make compose-up / compose-up-prod / compose-down. Env, ports, and extra_hosts
stay defined once in the base file; the prod override only swaps build -> image.
2026-06-02 13:50:27 -05:00
Offending Commit
56b3d18f40 test(docker): add hermetic /api proxy smoke test
make smoke-docker builds the image, stands up a stub upstream + the container
on a shared network, and asserts forward+prefix-strip, upstream-header cleared,
421 on missing header, and 403 + reject sentinel on allowlist miss. Self-
contained (no tailnet), idempotent, local-only (Docker) like cargo-check.
2026-06-02 13:43:07 -05:00
Offending Commit
66b299a28e fix(docker): derive nginx resolver from container DNS
Hardcoded resolver 127.0.0.11 only exists on user-defined networks, so a plain
docker run on the default bridge 502'd (DNS connection refused). Render the
resolver from /etc/resolv.conf at start so per-request proxy_pass resolves on
both the default bridge (host DNS) and compose networks (embedded DNS).
2026-06-02 13:33:50 -05:00
Offending Commit
6b602c05bb fix(web): strip content-encoding from vite dev proxy responses
undici fetch auto-decompresses the body, so re-sending the upstream
content-encoding/length would cause ERR_CONTENT_DECODING_FAILED if Honcho
gzips. Drop those and hop-by-hop headers when relaying. nginx is unaffected.
2026-06-02 13:28:32 -05:00