Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
157 commits
Select commit Hold shift + click to select a range
bb5ad1e
docs(helix-org): add DDD redesign analysis under design/2026-05-21-re…
philwinder May 21, 2026
686a8a2
refactor(helix-org): pin terminology (ADR-0001) and align CLAUDE.md/C…
philwinder May 21, 2026
634f5b6
docs(helix-org): pin TDD-with-characterisation-tests and canonical-lo…
philwinder May 21, 2026
b49295f
test(api/pkg/org/transport): characterise transport behaviour before …
philwinder May 21, 2026
99503e8
docs(helix-org): pin no-switch-on-discriminator OOP rule in CLAUDE.md
philwinder May 21, 2026
c461138
refactor(api/pkg/org/transport): lift transport types with strategy-p…
philwinder May 21, 2026
b771de4
refactor(api/pkg/org): lift Role + distribute org-graph IDs to per-co…
philwinder May 21, 2026
33aa8ad
chore(helix-org): delete the standalone CLI binary (H7)
philwinder May 21, 2026
71dbe05
refactor(api/pkg/org/broadcast): lift wake-only broadcaster to canoni…
philwinder May 21, 2026
aad3bc0
refactor(api/pkg/org/broadcast): rename Broadcaster -> Hub (avoid -er…
philwinder May 21, 2026
61e1867
refactor(api/pkg/org): lift Runtime port + distribute its prerequisit…
philwinder May 21, 2026
6505552
updated plans
philwinder May 21, 2026
c5e4854
test(helix-org/tools): characterise hire_worker side-effect order
philwinder May 22, 2026
85e13b5
test(helix-org/agent/helix): characterise ProjectApplier.Ensure
philwinder May 22, 2026
7c4bba5
test(helix-org/agent/helix): characterise Workspace.MirrorFile intent
philwinder May 22, 2026
a9c51b4
test(helix-org/agent/helix): augment spawner_test for SubscribeUpdate…
philwinder May 22, 2026
ea8f839
test(helix-org/server/chat): augment helix_bridge_test for owner-chat
philwinder May 22, 2026
c5443b1
feat(api/pkg/org/runtime): add HireHandler port
philwinder May 22, 2026
aa2e519
refactor(api/pkg/org/runtime/helix): lift state.go to canonical home
philwinder May 22, 2026
6959f81
feat(api/pkg/org/runtime/helix): add helix HireRecorder
philwinder May 22, 2026
1722a59
refactor(helix-org/tools): wire HireHandler into hire_worker
philwinder May 22, 2026
560629a
refactor(api/pkg/org/runtime/helix): lift auth-context helpers (H1.0)
philwinder May 22, 2026
98c80b9
refactor(api/pkg/org/runtime/helix): lift workspace + replace PutFile…
philwinder May 22, 2026
488cb24
refactor(api/pkg/org/runtime/helix): lift project + introduce Project…
philwinder May 22, 2026
fb6cd47
refactor(api/pkg/org/runtime/helix): lift EntryStream + EnsureAndSend…
philwinder May 22, 2026
34e6a7c
feat(api/pkg/org/runtime/helix): substitute pubsub for WebSocket sess…
philwinder May 22, 2026
6465c48
docs(api/pkg/org/runtime/helix): pin SessionClient port semantics (H1…
philwinder May 22, 2026
ffaacbf
refactor(api/pkg/org/runtime/helix): lift spawner + swap to in-proces…
philwinder May 22, 2026
0e13069
docs(helix-org): pin helixclient transitional state + deferred H1.4
philwinder May 22, 2026
56916e6
refactor(helix-org): address review comments on B4/H1
philwinder May 22, 2026
b1d0cb9
refactor(helix-org): never re-export; qualify foreign types at the ca…
philwinder May 22, 2026
02ad915
refactor(helix-org): delete dead claude invocation sites (B9)
philwinder May 22, 2026
b1a1a57
refactor(api/pkg/org/activation): lift ActivationStreamID (B5.1)
philwinder May 22, 2026
893f1ec
feat(api/pkg/org/activation): typed Outcome VO + marker (B5.2)
philwinder May 22, 2026
2c83bb9
feat(api/pkg/org/activation): typed TranscriptSegment VO (B5.3)
philwinder May 22, 2026
00cc408
feat(api/pkg/org/activation): Activation aggregate + Repository port …
philwinder May 22, 2026
fed787a
feat(helix-org/store): sqlite-backed activation.Repository (B5.5)
philwinder May 22, 2026
b1256ad
feat(api/pkg/org/runtime/helix): persist Activation rows per spawn (B…
philwinder May 22, 2026
b41b882
feat(helix-org/tools): worker_log activationId filter (B5.7)
philwinder May 22, 2026
ef1a3ff
feat(helix-org/tools): hire_worker returns activation_id (B5.8)
philwinder May 22, 2026
d900fd7
refactor(helix-org): single activation-Stream creation site (B5.9)
philwinder May 22, 2026
45ea26f
refactor(api/pkg/org/activation): extract per-Worker Queue (B5.10)
philwinder May 22, 2026
040ff1c
feat(helix-org/server/chat): owner-chat Activation rows (B5.11)
philwinder May 22, 2026
088c790
refactor(helix-org/server/chat): decouple bridge from "owner" (B5.12)
philwinder May 22, 2026
37adef0
feat(api/pkg/org/principal): Principal VO + strict dispatcher parse (…
philwinder May 22, 2026
6520856
feat(api/pkg/org/{message,domain}): typed Principal accessors (B6.3)
philwinder May 22, 2026
cf09d1e
refactor(api/pkg/org/config): lift config registry to canonical home …
philwinder May 22, 2026
06c4763
refactor(api/pkg/org/store): lift store package to canonical home (H4.1)
philwinder May 22, 2026
dc0bf6e
feat(api/pkg/org/store): dialect-agnostic OpenWithDB + Postgres impl …
philwinder May 22, 2026
2a89760
feat(api/pkg/server): wire helix_org to Postgres, drop fs requirement…
philwinder May 22, 2026
bb50001
docs(helix-org): mark landed migrations in 09-integration-reframe + C…
philwinder May 22, 2026
446a1cb
feat(helix-org/domain, api/pkg/org/store/sqlite): Worker.Organization…
philwinder May 22, 2026
8f96d4d
feat(helix-org/bootstrap): OwnerWorker carries OrgID (H5.2)
philwinder May 22, 2026
6e3446a
feat(helix-org/tools): hire_worker inherits caller's OrgID (H5.3)
philwinder May 22, 2026
e12d730
feat(api/pkg/org/runtime/helix): HelixIdentity VO + context (H6.1)
philwinder May 22, 2026
a3ce148
feat(api/pkg/server): build HelixIdentity in withHelixUserBearer midd…
philwinder May 22, 2026
2a50726
refactor(api/pkg/org/agent): lift agent package to canonical home (H8.1)
philwinder May 22, 2026
0b11915
refactor(api/pkg/org/prompts): lift prompts package to canonical home…
philwinder May 22, 2026
631abc9
refactor(api/pkg/org/dispatch): lift dispatcher to canonical home (H8.3)
philwinder May 22, 2026
4657ea1
refactor(api/pkg/org/bootstrap): lift bootstrap to canonical home (H8.4)
philwinder May 22, 2026
6f8a640
refactor(api/pkg/org/transports): lift transports to canonical home (…
philwinder May 22, 2026
6af9789
refactor(api/pkg/org/tools): lift tools to canonical home (H8.6)
philwinder May 22, 2026
198bf29
refactor(api/pkg/org/domain): lift domain types to canonical home (H8.7)
philwinder May 22, 2026
b948fbd
refactor(api/pkg/org/helixclient): lift helixclient to canonical home…
philwinder May 22, 2026
5172282
refactor(api/pkg/org/server): lift server tree to canonical home (H8.9)
philwinder May 22, 2026
3b30355
docs(helix-org): mark H5+H6+H8 landed in design index + CLAUDE.md
philwinder May 22, 2026
b70ff00
chore(helix-org): remove old unused files.
philwinder May 22, 2026
050a3f9
feat(api/pkg/server): in-process ProjectService + SpawnerClient adapt…
philwinder May 22, 2026
a06ba97
feat(api/pkg/org/server/chat): extract ChatBridgeClient port (H1-chat)
philwinder May 22, 2026
323dde0
refactor(api/pkg/org): delete helixclient package (H1.4)
philwinder May 22, 2026
ed346ab
refactor(api/pkg/org): replace broadcast with pubsub-backed streamhub…
philwinder May 22, 2026
6145676
feat(api/pkg/org/server): JSON API handlers for org graph/settings/st…
philwinder May 22, 2026
c1cb49c
feat(frontend): React pages for helix-org chart/workers/settings/stre…
philwinder May 22, 2026
190122a
refactor(api/pkg/org/server): delete htmx SSR UI (Phase C of UI migra…
philwinder May 22, 2026
5dcf15d
refactor(api/pkg/org/store): drop sqlite, use Postgres for org store …
philwinder May 22, 2026
5a739cb
refactor(api/pkg/org/server): delete dead chat package
philwinder May 25, 2026
818c557
fix(api/pkg/org/store): namespace org tables with org_ prefix
philwinder May 25, 2026
da4bc01
refactor(api/pkg/org): multi-tenant store schema with composite (id, …
philwinder Jun 2, 2026
be48939
refactor(api/pkg/org): plumb orgID through tools + bootstrap + runtim…
philwinder Jun 2, 2026
8ada5d0
docs(design): handoff for org-scoping refactor
philwinder Jun 2, 2026
39b79aa
refactor(api/pkg/org): thread orgID through runtime, dispatch, handlers
philwinder Jun 2, 2026
3c1a99a
refactor(frontend): per-org URLs for helix-org pages
philwinder Jun 2, 2026
b6e007e
fix(api/pkg/org/store): include org_id in stream uniqueIndex
philwinder Jun 2, 2026
89124ba
chore: untrack browser screenshots from playwright
philwinder Jun 2, 2026
de5b69d
chore: gitignore browser screenshots
philwinder Jun 2, 2026
dbc9ace
docs(design): status of org-scoping refactor — verified end-to-end on…
philwinder Jun 2, 2026
14cf79f
test(api/pkg/org): pass orgID to constructors and store methods acros…
philwinder Jun 2, 2026
c49152e
test(api/pkg/org/bootstrap): require OrganizationID
philwinder Jun 2, 2026
d630d92
chore: untrack additional browser screenshots
philwinder Jun 2, 2026
e146197
refactor(api/pkg/org): drop /helix-org infix, add roles+positions CRU…
philwinder Jun 2, 2026
b9b2464
chore: clean up dead code and screenshots from org-scoping refactor
philwinder Jun 2, 2026
8ebd05e
refactor(api/pkg/org/store): introduce kodit-style generic Repository…
philwinder Jun 2, 2026
f72ce93
refactor(api/pkg/org/store/gorm): migrate every repo to generic Repos…
philwinder Jun 2, 2026
371e5cf
refactor(api/pkg/org): DDD restructure into domain/infrastructure/app…
philwinder Jun 3, 2026
48d1f10
refactor(api/pkg/org/infrastructure/persistence/gorm): migrate worker…
philwinder Jun 3, 2026
f46a27e
refactor(frontend): trim helix-org UI to the chart + promote it to to…
philwinder Jun 3, 2026
ba54243
refactor(frontend): rebuild helix-org chart page to match Projects la…
philwinder Jun 3, 2026
91ed77d
docs(api/pkg/org): add QA.md test plan for the chart UI
philwinder Jun 3, 2026
d2d94f2
feat(api/pkg/org, frontend): role groups in chart + cascade-delete ro…
philwinder Jun 3, 2026
639e841
refactor(frontend): restore ReactFlow chart with top-down dagre + rol…
philwinder Jun 3, 2026
b9289ba
refactor(frontend): chart roles as subflow parents with worker-to-wor…
philwinder Jun 3, 2026
04412e8
fix(frontend): prevent role frame overlap in chart layout
philwinder Jun 3, 2026
cd6200b
fix(frontend): position-inside-role centering + clickable hire/delete…
philwinder Jun 3, 2026
a6e2fae
feat(frontend): chart drag-and-drop reparenting + add-role-below button
philwinder Jun 3, 2026
b395817
refactor(frontend): flat roles + edges-as-hierarchy chart model
philwinder Jun 3, 2026
90b641b
fix(frontend): switch reporting edges from smoothstep to bezier
philwinder Jun 3, 2026
299f623
docs(org): rewrite QA.md for flat-roles + edges-as-hierarchy chart
philwinder Jun 3, 2026
6d160e2
fix(api): wire ProjectService through helix-org spawner
philwinder Jun 3, 2026
7b140f8
feat(frontend): role click opens a side drawer with role details
philwinder Jun 3, 2026
6cf58c4
feat(frontend): roles list + role-detail editor pages
philwinder Jun 3, 2026
d174682
feat(api,frontend): tools catalogue endpoint + role-editor multi-select
philwinder Jun 3, 2026
9d07292
fix(api/org/bootstrap): seed r-owner.Tools manifest with default grants
philwinder Jun 3, 2026
a5f285c
docs(org/QA): roles list page + r-owner bootstrap check + add-tool-to…
philwinder Jun 3, 2026
dec6fba
fix(api/org/runtime/helix): guard fakePubSub map with mutex to stop C…
philwinder Jun 3, 2026
9c85869
feat(api,frontend): workers list + detail pages with fresh-session ch…
philwinder Jun 3, 2026
b63d251
feat(api,frontend): provision worker agent app on first chat click
philwinder Jun 3, 2026
d581db3
docs(org/QA): workers list page + chat-with-owner flow
philwinder Jun 3, 2026
faa19c3
fix(desktop): let zed_external sessions launch without HELIX_REPOSITO…
philwinder Jun 3, 2026
302dc17
chore(frontend): regenerate yarn.lock to match package.json
philwinder Jun 3, 2026
cc6526d
fix(frontend): add submitTitle and children props to DeleteConfirmWindow
philwinder Jun 3, 2026
30a23be
fix(api/org/runtime/helix): require per-Worker repo on Ensure (red-th…
philwinder Jun 3, 2026
e142e30
fix(api/server): surface agent runtime errors on the interaction (red…
philwinder Jun 3, 2026
8c92b9d
feat(frontend): helix-org settings page with helix-integrated provide…
philwinder Jun 3, 2026
4f7837a
fix(api/org/runtime/helix): settings auto-apply to existing workers
philwinder Jun 3, 2026
a44324a
feat(api,frontend): streams list page + chart subscription edges
philwinder Jun 3, 2026
73a0ff2
fix(frontend): stop mobile sidebar collapsing to zero height
philwinder Jun 3, 2026
53ddbb3
Revert "fix(frontend): stop mobile sidebar collapsing to zero height"
philwinder Jun 3, 2026
6595b4b
fix(api/server): serialize per-org helix-org bootstrap (red-then-green)
philwinder Jun 3, 2026
47b8695
fix(api/server): stop dropping org_* tables on every API restart
philwinder Jun 3, 2026
62954ef
feat(helix-org): chat button opens the Human Desktop, not the bare agent
philwinder Jun 3, 2026
38700f4
fix(frontend/helix-org): accept Delete key for edge severing on Linux…
philwinder Jun 3, 2026
510fbb3
fix(api/org): wire PubSub through helix-org spawner config (red-then-…
philwinder Jun 3, 2026
df73f8f
fix(frontend/helix-org): anchor stream edges to subject + dedicated h…
philwinder Jun 3, 2026
36eaefb
docs(helix-org/QA): shorten the QA plan to ~275 lines without losing …
philwinder Jun 3, 2026
02e45dd
feat(helix-org/streams): chart stream-node click deep-links + scrolls
philwinder Jun 3, 2026
07a1c84
feat(helix-org/streams): reinstate GitHub stream + reuse OAuth for auth
philwinder Jun 3, 2026
24b05e3
feat(helix-org/streams): per-stream detail page with live SSE message…
philwinder Jun 4, 2026
0462e77
docs(helix-org/QA): add §11 streams test plan (5 subsections + regres…
philwinder Jun 4, 2026
7621a2b
feat(helix-org): Fire cascades activation stream + chart stream delete
philwinder Jun 4, 2026
c77a6ad
docs(helix-org/QA): add §11f stream deletion + Fire cascade test
philwinder Jun 4, 2026
f7136f4
feat(helix-org): launching spinner on chat button + open desktop in n…
philwinder Jun 4, 2026
a40d892
fix(helix-org/streams): surface usable webhook URL + Copy button + lo…
philwinder Jun 4, 2026
7f3bc73
refactor(helix-org): position-anchor subscriptions (worker→position p…
philwinder Jun 4, 2026
aa9693a
docs(helix-org/QA): add §11g position-anchored subscriptions
philwinder Jun 4, 2026
8bcb018
feat(helix-org/streams): structured Repo + Events fields for github t…
philwinder Jun 4, 2026
8560c3b
feat(helix-org/streams): one-click GitHub webhook setup + auto-install
philwinder Jun 4, 2026
ae46cfe
test(helix-org/streams): pin one-click webhook + wildcard + form decode
philwinder Jun 4, 2026
9cc57fd
feat(helix-org): inject GH_TOKEN from org OAuth into worker sandbox
philwinder Jun 4, 2026
035f71a
test(api/pkg/org/dispatch): pin event-on-activation-stream + Dispatch…
philwinder Jun 4, 2026
6931b39
feat(helix-org/streams): request read:org in github OAuth scope set
philwinder Jun 4, 2026
192a206
refactor(helix-org): generic SpawnSecretInjector hook for transports
philwinder Jun 4, 2026
92c07c0
feat(helix-org/tools): MCP tools to read + patch a Worker's helix pro…
philwinder Jun 4, 2026
6984656
fix(sandbox/gh): don't invoke gh during docker build (root-owns .local)
philwinder Jun 4, 2026
0810b22
docs(helix-org/QA): pin §13 chat-with-worker E2E (Zed launch + gh + r…
philwinder Jun 4, 2026
3ec1322
docs(helix-org/QA): condense to UI-only, single-source-per-feature
philwinder Jun 4, 2026
9cc2082
docs(org/qa): clarify activation streams are AI-only
philwinder Jun 4, 2026
7175f18
refactor(org): PR review cleanup
philwinder Jun 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,12 @@ api/settings-sync-daemon
/scanout-stream-test
core.*
nohup.out
# CI validation Mon Mar 30 17:38:01 BST 2026

# Browser screenshots (Playwright runs, ad-hoc debug captures). The
# tracked PNGs under design/cloud-smoke-screenshots/ and for-mac/ are
# already in git; *.png only excludes new untracked ones.
*.png

# Playwright MCP trace dir (per-session screenshots, console logs,
# accessibility snapshots). Local only — never check in.
.playwright-mcp/
10 changes: 10 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,16 @@ These rules keep our list pages visually consistent. When in doubt, mirror `Sand
- **RBAC**: `authorizeUserToResource()` — unified AccessGrants
- **Enterprise**: Support internal DNS, proxies, air-gapped, private CAs

## helix-org design philosophy

Anything under `api/pkg/org/` is the org-graph runtime (Workers, Positions, Roles, Streams, Grants). Behaviour lives in the prompt/profile, not in Go code. The code is scaffolding.

- **Prefer data and text over code.** If a feature can be expressed as a Role/Position prompt edit, a scope value, or a tool description, do that before adding Go logic.
- **Keep the MCP surface small.** MCP tools are reserved for org-graph primitives (reads + mutations of Workers, Positions, Roles, Streams, Grants). Anything else a Worker needs goes through shell tools provisioned in their environment (`bash`, `curl`, `git`, `gh`, `python`). Don't add MCP wrappers like `publish_to_blog` or `fetch_url` — describe the shell usage in the Role text instead.
- **No workflow in code.** Tools do exactly one thing. Code does not orchestrate multi-step sequences on behalf of an agent — it does not subscribe Workers, grant tools implicitly, auto-create related records. Orchestration lives in the prompt. `DefaultTools` / `DefaultStreams` on a Role are reference data the hiring manager's prompt reads, not triggers the code acts on. When reviewing a tool, ask: "is the code making a decision the agent should be making?" If yes, remove it.
- **Social enforcement first.** A Worker reads scope from its prompt and complies. Reach for hard enforcement only when the cost of a violation is high.
- **Keep the core generic.** Tool definitions and scope shapes live with the tool, not in the registry, server, or domain layer. New tools must be addable without editing the core.

## Dev Environment (Helix-in-Helix)

**`helix-4` is a symlink to `helix`** — they are the same directory. Always use `/home/retro/work/helix/`.
Expand Down
6 changes: 3 additions & 3 deletions api/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ type ServerConfig struct {
// HelixOrgEnabled is the deployment-wide kill switch for the
// embedded helix-org alpha. When false (the default), none of
// the helix-org init runs and none of its HTTP surfaces
// (/api/v1/org/, /ui/, /api/v1/mcp/helix-org/) are mounted —
// the per-user alpha feature flag in the DB has no effect.
// Set HELIX_ORG_ENABLED=true to opt in.
// (/api/v1/orgs/{org}/, /api/v1/mcp/helix-org/) are mounted —
// the per-user alpha feature flag in the DB has no effect. Set
// HELIX_ORG_ENABLED=true to opt in.
HelixOrgEnabled bool `envconfig:"HELIX_ORG_ENABLED" default:"false"`
}

Expand Down
120 changes: 93 additions & 27 deletions api/pkg/github/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package github

import (
"context"
"fmt"
"slices"

"github.com/google/go-github/v61/github"
"golang.org/x/oauth2"
Expand Down Expand Up @@ -49,32 +51,34 @@ func NewGithubClient(options ClientOptions) (*Client, error) {
}, nil
}

const loadReposMaxPages = 10

// LoadRepos returns full_name for every repo the token can see,
// sorted by most-recently-pushed first. Caps at loadReposMaxPages so
// heavy accounts don't take minutes.
func (c *Client) LoadRepos() ([]string, error) {
repos := []*github.Repository{}
opts := github.ListOptions{
PerPage: 100,
Page: 0,
}
for {
result, meta, err := c.client.Repositories.ListByUser(c.ctx, "", &github.RepositoryListByUserOptions{
var repos []*github.Repository
opts := github.ListOptions{PerPage: 100, Page: 1}
for i := 0; i < loadReposMaxPages; i++ {
result, meta, err := c.client.Repositories.ListByAuthenticatedUser(c.ctx, &github.RepositoryListByAuthenticatedUserOptions{
Sort: "pushed",
Direction: "desc",
ListOptions: opts,
})
if err != nil {
return nil, err
}
for _, repo := range result {
if repo != nil {
repos = append(repos, repo)
}
}
opts.Page = opts.Page + 1
if opts.Page > meta.LastPage {
repos = append(repos, result...)
if meta == nil || meta.NextPage == 0 {
break
}
opts.Page = meta.NextPage
}
results := []string{}
results := make([]string, 0, len(repos))
for _, repo := range repos {
results = append(results, *repo.FullName)
if repo != nil && repo.FullName != nil {
results = append(results, *repo.FullName)
}
}
return results, nil
}
Expand Down Expand Up @@ -105,32 +109,94 @@ func (c *Client) AddWebhookToRepo(
events []string,
secret string,
) error {
_, err := c.UpsertWebhook(owner, repo, name, url, events, secret)
return err
}

type WebhookSummary struct {
ID int64
Events []string
}

// UpsertWebhook creates a webhook on the repo if none points at `url`,
// or PATCHes the existing one to bring content_type / events / active
// in line. Events of `["*"]` is GitHub's wildcard.
func (c *Client) UpsertWebhook(
owner string,
repo string,
name string,
url string,
events []string,
secret string,
) (WebhookSummary, error) {
active := true
json := "application/json"
jsonCT := "application/json"

hooks, _, err := c.client.Repositories.ListHooks(c.ctx, owner, repo, nil)
if err != nil {
return err
return WebhookSummary{}, err
}

for _, hook := range hooks {
if hook.Config.URL != nil && *hook.Config.URL == url {
// Hook already exists, no need to add it again
return nil
if hook.Config == nil || hook.Config.URL == nil || *hook.Config.URL != url {
continue
}
needsUpdate := hook.Config.ContentType == nil || *hook.Config.ContentType != jsonCT ||
!sameEvents(hook.Events, events) ||
hook.Active == nil || !*hook.Active
if !needsUpdate || hook.ID == nil {
return summarizeHook(hook), nil
}
patched, _, err := c.client.Repositories.EditHook(context.Background(), owner, repo, *hook.ID, &github.Hook{
Active: &active,
Events: events,
Config: &github.HookConfig{
ContentType: &jsonCT,
URL: &url,
Secret: &secret,
},
})
if err != nil {
return WebhookSummary{}, fmt.Errorf("update github webhook: %w", err)
}
return summarizeHook(patched), nil
}

// Add the new hook
_, _, err = c.client.Repositories.CreateHook(context.Background(), owner, repo, &github.Hook{
created, _, err := c.client.Repositories.CreateHook(context.Background(), owner, repo, &github.Hook{
Active: &active,
Name: &name,
URL: &url,
Events: events,
Config: &github.HookConfig{
ContentType: &json,
ContentType: &jsonCT,
URL: &url,
Secret: &secret,
},
})
return err
if err != nil {
return WebhookSummary{}, err
}
return summarizeHook(created), nil
}

// WebhookSettingsURL returns the operator-facing edit page for a hook.
func WebhookSettingsURL(owner, repo string, hookID int64) string {
return fmt.Sprintf("https://github.com/%s/%s/settings/hooks/%d", owner, repo, hookID)
}

func sameEvents(a, b []string) bool {
ca := slices.Clone(a)
cb := slices.Clone(b)
slices.Sort(ca)
slices.Sort(cb)
return slices.Equal(ca, cb)
}

func summarizeHook(h *github.Hook) WebhookSummary {
if h == nil {
return WebhookSummary{}
}
out := WebhookSummary{Events: h.Events}
if h.ID != nil {
out.ID = *h.ID
}
return out
}
Loading