Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c8b34d8
feat(integrations): load and validate webhook configuration
TartanLeGrand Sep 28, 2026
004dc94
feat(integrations): verify GitLab webhook signatures and secret token
TartanLeGrand Sep 28, 2026
dc03319
feat(integrations): verify Flux generic-hmac signatures
TartanLeGrand Sep 28, 2026
49e7bf0
feat(integrations): add deployment observation and repository URL nor…
TartanLeGrand Sep 28, 2026
abd2bcd
feat(integrations): map GitLab deployment events to observations
TartanLeGrand Sep 28, 2026
9981631
feat(integrations): map Flux notifications to observations
TartanLeGrand Sep 28, 2026
0f661fc
feat(stores): add integration_deployments collection
TartanLeGrand Sep 28, 2026
ef6820d
refactor(server): extract unauthorized event and lock cores
TartanLeGrand Sep 28, 2026
3257707
feat(server): apply deployment observations to events and locks
TartanLeGrand Sep 28, 2026
ba29f6f
fix(stores): make integration Revert conditional on the applied state
TartanLeGrand Sep 28, 2026
19c3dcf
fix(server): converge integration events on concurrent updates
TartanLeGrand Sep 28, 2026
e7d0cf7
fix(integrations): rank waiting_approval below start
TartanLeGrand Sep 28, 2026
0c0ba17
fix(server): take integration locks with their event id
TartanLeGrand Sep 28, 2026
bb86923
test(server): pin approval and start ordering for integrations
TartanLeGrand Sep 28, 2026
2869ac5
feat(server): record deployments from GitLab and Flux webhooks
TartanLeGrand Sep 28, 2026
6709463
docs: document GitLab and Flux deployment integrations
TartanLeGrand Sep 28, 2026
83c1de8
fix(integrations): derive the GitLab correlation host from the signed…
TartanLeGrand Sep 28, 2026
708bb4a
fix(server): treat a deleted Tracker event as an ignored notification
TartanLeGrand Sep 28, 2026
d5e614e
fix(server): log the cause of integration failures and bound logged d…
TartanLeGrand Sep 28, 2026
e199414
test(server): pin the remaining event RPC behaviours
TartanLeGrand Sep 28, 2026
f472a9e
fix(integrations): normalize default ports and uppercase .git suffixes
TartanLeGrand Sep 28, 2026
79ef516
docs: clarify integration retries and lock recovery
TartanLeGrand Sep 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ npm run dev
- [📊 Events Guide](./docs/EVENTS.md) - Working with events
- [📦 Catalog Guide](./docs/CATALOG.md) - Managing service catalog
- [🔒 Locks Guide](./docs/LOCKS.md) - Distributed locking
- [🔗 Deployment Integrations](./docs/INTEGRATIONS.md) - Record GitLab and Flux deployments automatically

### API Documentation
- [🔌 API Specification](./docs/api-specification.md) - API reference
Expand Down
21 changes: 21 additions & 0 deletions cmd/serv.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/bananaops/tracker/internal/auth"
"github.com/bananaops/tracker/internal/auth/identity"
"github.com/bananaops/tracker/internal/auth/sso"
"github.com/bananaops/tracker/internal/integrations"
store "github.com/bananaops/tracker/internal/stores"
"github.com/bananaops/tracker/server"
"github.com/go-openapi/runtime/middleware"
Expand All @@ -49,6 +50,18 @@ var serv = &cobra.Command{
if authCfg.AnonymousDefaulted {
slog.Warn("AUTH_ANONYMOUS_PERMISSIONS is not set: anonymous callers keep every permission except access:manage. This default becomes empty in the next major release.")
}

// Deployment integrations (GitLab and Flux webhooks). The routes are
// registered once the mux exists; an invalid configuration stops here.
integrationsCfg, err := integrations.LoadConfig(os.LookupEnv)
if err != nil {
log.Fatalf("invalid integrations configuration: %v", err)
}
for _, w := range integrationsCfg.Warnings {
slog.Warn(w)
}
slog.Info("deployment integrations", integrationsCfg.LogAttrs()...)

userStore := store.NewAuthUserStore()
teamStore := store.NewAuthTeamStore()
keyStore := store.NewAuthAPIKeyStore()
Expand Down Expand Up @@ -175,6 +188,14 @@ var serv = &cobra.Command{
// Register custom links CRUD endpoints
server.RegisterLinksHandler(mux)

// Register the GitLab and Flux deployment webhooks (configured sources only).
// NewIntegrationDeps opens collections, so it only runs when a source is set.
if integrationsCfg.Enabled() {
if err := server.RegisterIntegrationHandlers(mux, integrationsCfg, server.NewIntegrationDeps(events)); err != nil {
log.Fatalf("cannot register integration webhooks: %v", err)
}
}

// Setup Swagger documentation with go-swagger
opts := middleware.SwaggerUIOpts{SpecURL: "/swagger.json"}
sh := middleware.SwaggerUI(opts, nil)
Expand Down
31 changes: 31 additions & 0 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,37 @@ AUTH_OIDC_CLIENT_SECRET=<from your secret store>
# Redirect URI to register: https://tracker.example.com/api/v1alpha1/auth/oidc/callback
```

### Deployment integrations

| Variable | Default | Description |
|----------|---------|-------------|
| `INTEGRATION_GITLAB_SIGNING_TOKEN` | - | GitLab signing token, `whsec_<base64>`. When set, the `webhook-signature` header is required and `INTEGRATION_GITLAB_SECRET_TOKEN` is ignored. |
| `INTEGRATION_GITLAB_SECRET_TOKEN` | - | Legacy GitLab secret token, compared against `X-Gitlab-Token`. Ignored when the signing token is set. |
| `INTEGRATION_FLUX_HMAC_KEY` | - | HMAC key shared with the Flux `Provider` Secret (key `token`). Enables the Flux webhook endpoint. |
| `INTEGRATION_WEBHOOK_TOLERANCE` | `5m` | Freshness window (Go duration) for GitLab's `webhook-timestamp` and Flux's event `timestamp`. |
| `INTEGRATION_ENVIRONMENTS` | `production=production,staging=preproduction` | Comma separated `source=tracker` pairs mapping a GitLab or Flux environment name to a Tracker environment. Source keys are matched case-insensitively. |

An invalid value stops Tracker at startup, with an error that never includes the secret itself:

- `INTEGRATION_GITLAB_SIGNING_TOKEN` not prefixed with `whsec_`, or the part after the prefix is
not valid base64, or decodes to an empty value.
- `INTEGRATION_GITLAB_SECRET_TOKEN` shorter than 16 characters.
- `INTEGRATION_FLUX_HMAC_KEY` shorter than 32 bytes.
- `INTEGRATION_WEBHOOK_TOLERANCE` not a positive Go duration (for example `5m`).
- `INTEGRATION_ENVIRONMENTS` malformed (an entry without `=`, an empty key, or a duplicate key),
or a value that is not one of the Tracker environment names: `development`, `integration`,
`TNR`, `UAT`, `recette`, `preproduction`, `production`, `mco` (these are case-sensitive).

See [INTEGRATIONS.md](INTEGRATIONS.md) for the GitLab and Flux setup this configures.

**Example:**
```bash
INTEGRATION_GITLAB_SIGNING_TOKEN=whsec_<base64-value-from-secret-manager>
INTEGRATION_FLUX_HMAC_KEY=<32-byte-value-from-secret-manager>
INTEGRATION_WEBHOOK_TOLERANCE=5m
INTEGRATION_ENVIRONMENTS=production=production,staging=preproduction
```

### Slack Integration

| Variable | Default | Description |
Expand Down
9 changes: 8 additions & 1 deletion docs/EVENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Tracker supports five main event types:
#### Optional Fields

- **attributes.message** (string): Detailed description
- **attributes.source** (string): Event origin (e.g., `github_actions`, `jenkins`, `manual`)
- **attributes.source** (string): Event origin (e.g., `github_actions`, `jenkins`, `manual`, `gitlab`, `flux`)
- **attributes.priority** (int): Priority level (1=P1/Critical, 5=P5/Low)
- **attributes.status** (int): Current status (see Status Values below)
- **attributes.environment** (int): Target environment (see Environment Values below)
Expand Down Expand Up @@ -387,6 +387,13 @@ curl -X POST http://localhost:8080/api/v1alpha1/event \
}'
```

### Automatic deployment events

Tracker can create and update deployment events on its own from GitLab and Flux webhooks,
without any call to the REST or gRPC API. Each deployment produces a single event, taken through
its lifecycle as notifications arrive: `start`, then `success`, `failure`, `warning` or `close`.
See [INTEGRATIONS.md](INTEGRATIONS.md) for setup, environment mapping and troubleshooting.

## Best Practices

### 1. Use Descriptive Titles
Expand Down
Loading
Loading