-
Notifications
You must be signed in to change notification settings - Fork 28
feat: add Telemetry page under OSS Health section #471
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
0e37197
1a3fafc
f699fc9
48bff55
dd23bdb
1a9649a
38959d4
33ae711
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| name: Fetch Telemetry Data | ||
|
|
||
| on: | ||
| schedule: | ||
| # Run daily at 08:00 UTC (00:00 Pacific during PST, 01:00 during PDT) | ||
| - cron: '0 8 * * *' | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: write | ||
|
|
||
| jobs: | ||
| fetch-telemetry: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Fetch telemetry overview | ||
| run: | | ||
| HTTP_STATUS=$(curl -s -o /tmp/overview.json -w "%{http_code}" \ | ||
| --max-time 30 \ | ||
| https://telemetry.cozystack.io/api/overview) | ||
|
|
||
| if [ "$HTTP_STATUS" -ne 200 ]; then | ||
| echo "Telemetry API returned HTTP $HTTP_STATUS, skipping update" | ||
| exit 0 | ||
| fi | ||
|
|
||
| # Validate JSON structure (must have generated_at and periods keys) | ||
| if ! python3 -c " | ||
| import json, sys | ||
| d = json.load(open('/tmp/overview.json')) | ||
| assert 'generated_at' in d, 'missing generated_at' | ||
| assert 'periods' in d and isinstance(d['periods'], dict), 'missing or invalid periods' | ||
| "; then | ||
| echo "Invalid JSON structure, skipping update" | ||
| exit 0 | ||
| fi | ||
|
|
||
| cp /tmp/overview.json data/usage-stats/overview.json | ||
|
|
||
| - name: Check for changes | ||
| id: changes | ||
| run: | | ||
| if git diff --quiet data/usage-stats/overview.json; then | ||
| echo "changed=false" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "changed=true" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| - name: Commit and push | ||
| if: steps.changes.outputs.changed == 'true' | ||
| run: | | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "github-actions[bot]@users.noreply.github.com" | ||
| git add data/usage-stats/overview.json | ||
| git commit -m "Update telemetry data" | ||
| git push | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| /* telemetry page */ | ||
|
|
||
| .telemetry-page { | ||
| margin-top: 4rem; | ||
|
|
||
| @include media-breakpoint-down(sm) { | ||
| margin-top: 2rem; | ||
| } | ||
|
|
||
| .nav-tabs { | ||
| border-bottom: 2px solid $primary; | ||
|
|
||
| .nav-link { | ||
| color: $cozy-mid-gray; | ||
| font-weight: 600; | ||
| border: none; | ||
| border-bottom: 3px solid transparent; | ||
| padding: 0.75rem 1.5rem; | ||
|
|
||
| &:hover { | ||
| color: $primary; | ||
| border-bottom-color: rgba($primary, 0.3); | ||
| } | ||
|
|
||
| &.active { | ||
| color: $primary; | ||
| border-bottom-color: $primary; | ||
| background: transparent; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| .telemetry-card { | ||
| border: none; | ||
| border-radius: 0.75rem; | ||
| transition: transform 0.15s ease; | ||
|
|
||
| &:hover { | ||
| transform: translateY(-2px); | ||
| } | ||
|
|
||
| .telemetry-icon { | ||
| font-size: 1.75rem; | ||
| color: $primary; | ||
| margin-bottom: 0.5rem; | ||
| } | ||
|
|
||
| .telemetry-value { | ||
| font-size: 2.5rem; | ||
| font-weight: 700; | ||
| color: $cozy-black; | ||
| line-height: 1.2; | ||
| } | ||
|
|
||
| .telemetry-label { | ||
| font-size: 0.95rem; | ||
| font-weight: 600; | ||
| color: $cozy-mid-gray; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| margin-top: 0.25rem; | ||
| } | ||
|
|
||
| .telemetry-secondary { | ||
| font-size: 0.85rem; | ||
| color: $cozy-light-gray; | ||
| margin-top: 0.25rem; | ||
| } | ||
| } | ||
|
|
||
| .table { | ||
| th { | ||
| font-weight: 600; | ||
| } | ||
|
|
||
| .table-primary { | ||
| --bs-table-bg: #{rgba($primary, 0.08)}; | ||
| --bs-table-border-color: #{rgba($primary, 0.15)}; | ||
| color: $cozy-black; | ||
| } | ||
|
|
||
| code { | ||
| color: $primary; | ||
| font-weight: 500; | ||
| background: rgba($primary, 0.06); | ||
| padding: 0.15rem 0.4rem; | ||
| border-radius: 0.25rem; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -149,3 +149,4 @@ a { | |||||||||||||||
| @import "announcement-banner"; | ||||||||||||||||
| @import "tabs_alerts"; | ||||||||||||||||
| @import "override-docsy-tabs"; | ||||||||||||||||
| @import "telemetry"; | ||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move the telemetry import above all style rules. Line 152 places ♻️ Proposed fix // Import Docsy styles_project (allows further customization)
`@import` "docsy/styles_project";
+@import "telemetry";
@@
`@import` "tabs_alerts";
`@import` "override-docsy-tabs";
-@import "telemetry";📝 Committable suggestion
Suggested change
🧰 Tools🪛 Stylelint (17.6.0)[error] 152-152: Unexpected invalid position (no-invalid-position-at-import-rule) 🤖 Prompt for AI Agents |
||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| title: OSS Health | ||
| description: Open source project health metrics for Cozystack | ||
| type: oss-health | ||
| --- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| title: Telemetry | ||
| description: Anonymous usage statistics collected from Cozystack clusters | ||
| type: oss-health | ||
| layout: telemetry | ||
| --- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| { | ||
| "generated_at": "", | ||
| "periods": {} | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| <!doctype html> | ||
| <html lang="{{ .Site.Language.Lang }}" class="no-js"> | ||
| <head> | ||
| {{ partial "head.html" . }} | ||
| </head> | ||
| <body class="td-{{ .Kind }}"> | ||
| <header> | ||
| {{ partial "navbar.html" . }} | ||
| </header> | ||
| <div class="container-fluid td-outer"> | ||
| <div class="td-main"> | ||
| {{ block "main" . }}{{ end }} | ||
| </div> | ||
| {{ partial "footer.html" . }} | ||
| </div> | ||
| {{ partial "scripts.html" . }} | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| {{ define "main" }} | ||
| {{ $data := index (index .Site.Data "usage-stats") "overview" }} | ||
|
|
||
| <div class="telemetry-page container py-5"> | ||
| <div class="text-center mb-5"> | ||
| <h1 class="display-5 fw-bold">Telemetry</h1> | ||
| <p class="lead text-muted">Anonymous usage statistics collected from Cozystack clusters worldwide.</p> | ||
| </div> | ||
|
|
||
| {{ if and $data $data.periods (gt (len $data.periods) 0) }} | ||
|
|
||
| {{ $tabIds := slice "month" "quarter" "year" }} | ||
| {{ $tabLabels := dict "month" "Last Month" "quarter" "Last Quarter" "year" "Last 12 Months" }} | ||
| {{ $tabIcons := dict "month" "fa-calendar-day" "quarter" "fa-calendar-week" "year" "fa-calendar-alt" }} | ||
|
|
||
| <!-- Tabs --> | ||
| {{ $firstTab := "" }} | ||
| {{ range $id := $tabIds }} | ||
| {{ if and (eq $firstTab "") (index $data.periods $id) }} | ||
| {{ $firstTab = $id }} | ||
| {{ end }} | ||
| {{ end }} | ||
|
|
||
| <ul class="nav nav-tabs justify-content-center mb-4" id="telemetryTabs" role="tablist"> | ||
| {{ range $id := $tabIds }} | ||
| {{ if index $data.periods $id }} | ||
| <li class="nav-item" role="presentation"> | ||
| <button class="nav-link {{ if eq $id $firstTab }}active{{ end }}" id="{{ $id }}-tab" data-bs-toggle="tab" data-bs-target="#{{ $id }}-pane" type="button" role="tab" aria-controls="{{ $id }}-pane" aria-selected="{{ if eq $id $firstTab }}true{{ else }}false{{ end }}"> | ||
| <i class="fas {{ index $tabIcons $id }} me-1"></i> {{ index $tabLabels $id }} | ||
| </button> | ||
| </li> | ||
| {{ end }} | ||
| {{ end }} | ||
| </ul> | ||
|
|
||
| <!-- Tab content --> | ||
| <div class="tab-content" id="telemetryTabContent"> | ||
|
|
||
| {{ range $id := $tabIds }} | ||
| {{ $period := index $data.periods $id }} | ||
| {{ if $period }} | ||
| <div class="tab-pane fade {{ if eq $id $firstTab }}show active{{ end }}" id="{{ $id }}-pane" role="tabpanel" aria-labelledby="{{ $id }}-tab"> | ||
| <h4 class="text-center text-muted mb-4">{{ $period.label }}</h4> | ||
|
|
||
| <!-- Summary cards --> | ||
| <div class="row g-3 mb-5"> | ||
| <div class="col-md-4 col-sm-6"> | ||
| <div class="card text-center h-100 shadow-sm telemetry-card"> | ||
| <div class="card-body"> | ||
| <div class="telemetry-icon"><i class="fas fa-server"></i></div> | ||
| <div class="telemetry-value">{{ $period.clusters }}</div> | ||
| <div class="telemetry-label">Clusters</div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <div class="col-md-4 col-sm-6"> | ||
| <div class="card text-center h-100 shadow-sm telemetry-card"> | ||
| <div class="card-body"> | ||
| <div class="telemetry-icon"><i class="fas fa-microchip"></i></div> | ||
| <div class="telemetry-value">{{ $period.total_nodes }}</div> | ||
| <div class="telemetry-label">Total Nodes</div> | ||
| <div class="telemetry-secondary">avg {{ printf "%.1f" $period.avg_nodes_per_cluster }} per cluster</div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <div class="col-md-4 col-sm-6"> | ||
| <div class="card text-center h-100 shadow-sm telemetry-card"> | ||
| <div class="card-body"> | ||
| <div class="telemetry-icon"><i class="fas fa-users"></i></div> | ||
| <div class="telemetry-value">{{ $period.total_tenants }}</div> | ||
| <div class="telemetry-label">Tenants</div> | ||
| <div class="telemetry-secondary">avg {{ printf "%.1f" $period.avg_tenants_per_cluster }} per cluster</div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <!-- Application usage table --> | ||
| {{ if $period.apps }} | ||
| <h5 class="mb-3">Application Usage</h5> | ||
| <div class="table-responsive"> | ||
| <table class="table table-striped table-hover"> | ||
| <thead class="table-primary"> | ||
| <tr> | ||
| <th scope="col">Application</th> | ||
| <th scope="col" class="text-end">Instances</th> | ||
| </tr> | ||
| </thead> | ||
| <tbody> | ||
| {{ range $app, $count := $period.apps }} | ||
| <tr> | ||
| <td><code>{{ $app }}</code></td> | ||
| <td class="text-end">{{ $count }}</td> | ||
| </tr> | ||
| {{ end }} | ||
| </tbody> | ||
| </table> | ||
| </div> | ||
| {{ end }} | ||
| </div> | ||
| {{ end }} | ||
| {{ end }} | ||
|
|
||
| </div> | ||
|
|
||
| {{ if $data.generated_at }} | ||
| <p class="text-center text-muted mt-4"> | ||
| <small>Data last updated: {{ $data.generated_at }}</small> | ||
| </p> | ||
| {{ end }} | ||
|
|
||
| {{ else }} | ||
|
|
||
| <div class="text-center py-5"> | ||
| <div class="mb-3"><i class="fas fa-chart-bar fa-3x text-muted"></i></div> | ||
| <h4 class="text-muted">No telemetry data available yet</h4> | ||
| <p class="text-muted">Statistics will appear here after the first monthly collection cycle.</p> | ||
| </div> | ||
|
|
||
| {{ end }} | ||
|
|
||
| <div class="text-center mt-5 mb-3"> | ||
| <p class="text-muted"> | ||
| <small> | ||
| Cozystack collects anonymous, aggregate telemetry to understand usage patterns. | ||
| <a href="{{ "docs/telemetry/" | relURL }}">Learn more about what is collected</a>. | ||
| </small> | ||
| </p> | ||
| </div> | ||
| </div> | ||
|
|
||
| {{ end }} |
Uh oh!
There was an error while loading. Please reload this page.