-
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 2 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,54 @@ | ||
| 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 | ||
| if ! python3 -c "import json; json.load(open('/tmp/overview.json'))"; then | ||
| echo "Invalid JSON response, 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 | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,3 +12,4 @@ | |||||||||||||||
| @import "support"; | ||||||||||||||||
| @import "ecosystem"; | ||||||||||||||||
| @import "adopters_wall"; | ||||||||||||||||
| @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,136 @@ | ||||||
| {{ 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) }} | ||||||
|
|
||||||
| <!-- Tabs --> | ||||||
| <ul class="nav nav-tabs justify-content-center mb-4" id="telemetryTabs" role="tablist"> | ||||||
| {{ with index $data.periods "month" }} | ||||||
| <li class="nav-item" role="presentation"> | ||||||
| <button class="nav-link active" id="month-tab" data-bs-toggle="tab" data-bs-target="#month-pane" type="button" role="tab" aria-controls="month-pane" aria-selected="true"> | ||||||
| <i class="fas fa-calendar-day me-1"></i> Last Month | ||||||
| </button> | ||||||
| </li> | ||||||
| {{ end }} | ||||||
| {{ with index $data.periods "quarter" }} | ||||||
| <li class="nav-item" role="presentation"> | ||||||
| <button class="nav-link" id="quarter-tab" data-bs-toggle="tab" data-bs-target="#quarter-pane" type="button" role="tab" aria-controls="quarter-pane" aria-selected="false"> | ||||||
| <i class="fas fa-calendar-week me-1"></i> Last Quarter | ||||||
| </button> | ||||||
| </li> | ||||||
| {{ end }} | ||||||
| {{ with index $data.periods "year" }} | ||||||
| <li class="nav-item" role="presentation"> | ||||||
| <button class="nav-link" id="year-tab" data-bs-toggle="tab" data-bs-target="#year-pane" type="button" role="tab" aria-controls="year-pane" aria-selected="false"> | ||||||
| <i class="fas fa-calendar-alt me-1"></i> Last 12 Months | ||||||
| </button> | ||||||
| </li> | ||||||
| {{ end }} | ||||||
| </ul> | ||||||
|
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. The current tab implementation hardcodes the Refactoring the tabs to use a loop similar to the panes ensures that the active state remains synchronized and makes the code more maintainable. <ul class="nav nav-tabs justify-content-center mb-4" id="telemetryTabs" role="tablist">
{{ $firstTab := true }}
{{ $tabIds := slice "month" "quarter" "year" }}
{{ $tabIcons := dict "month" "fa-calendar-day" "quarter" "fa-calendar-week" "year" "fa-calendar-alt" }}
{{ $tabLabels := dict "month" "Last Month" "quarter" "Last Quarter" "year" "Last 12 Months" }}
{{ range $id := $tabIds }}
{{ with index $data.periods $id }}
<li class="nav-item" role="presentation">
<button class="nav-link {{ if $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 $firstTab }}true{{ else }}false{{ end }}">
<i class="fas {{ index $tabIcons $id }} me-1"></i> {{ index $tabLabels $id }}
</button>
</li>
{{ $firstTab = false }}
{{ end }}
{{ end }}
</ul>
Contributor
Author
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. Fixed in f699fc9 — tab activation is now dynamic: a |
||||||
|
|
||||||
| <!-- Tab content --> | ||||||
| <div class="tab-content" id="telemetryTabContent"> | ||||||
|
|
||||||
| {{ $first := true }} | ||||||
| {{ $tabIds := slice "month" "quarter" "year" }} | ||||||
| {{ range $id := $tabIds }} | ||||||
| {{ $period := index $data.periods $id }} | ||||||
| {{ if $period }} | ||||||
| <div class="tab-pane fade {{ if $first }}show active{{ end }}" id="{{ $id }}-pane" role="tabpanel" aria-labelledby="{{ $id }}-tab"> | ||||||
| <h4 class="text-center text-muted mb-4">{{ $period.label }}</h4> | ||||||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| <!-- 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 {{ $period.avg_nodes_per_cluster }} per cluster</div> | ||||||
|
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. Average values calculated from telemetry data might contain many decimal places. It is recommended to format these values to a single decimal place for a cleaner user interface.
Suggested change
Contributor
Author
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. Fixed in f699fc9 — now uses |
||||||
| </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 {{ $period.avg_tenants_per_cluster }} per cluster</div> | ||||||
|
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. Similar to the nodes average, the tenants average should be formatted to avoid displaying excessive decimal places.
Suggested change
Contributor
Author
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. Fixed in f699fc9 — same |
||||||
| </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> | ||||||
| {{ $first = false }} | ||||||
| {{ 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="https://cozystack.io/docs/telemetry/">Learn more about what is collected</a>. | ||||||
|
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. Using a hardcoded absolute URL for internal documentation links can break in preview environments or if the domain changes. It is better to use the
Suggested change
Contributor
Author
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. Fixed in f699fc9 — now uses |
||||||
| </small> | ||||||
| </p> | ||||||
| </div> | ||||||
| </div> | ||||||
|
|
||||||
| {{ end }} | ||||||
Uh oh!
There was an error while loading. Please reload this page.