stackvitals 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mergescout/__init__.py +1 -0
- mergescout/archive/__init__.py +0 -0
- mergescout/archive/__main__.py +30 -0
- mergescout/archive/convert.py +83 -0
- mergescout/archive/events.py +55 -0
- mergescout/archive/manifest.py +45 -0
- mergescout/archive/stream.py +83 -0
- mergescout/bench/__init__.py +1 -0
- mergescout/bench/__main__.py +135 -0
- mergescout/bench/ci.py +257 -0
- mergescout/bench/deploys.py +115 -0
- mergescout/bench/matched.py +22 -0
- mergescout/bench/prior.py +42 -0
- mergescout/bench/report.py +230 -0
- mergescout/bench/shared.py +112 -0
- mergescout/bench/stars.py +57 -0
- mergescout/cli.py +89 -0
- mergescout/config.py +35 -0
- mergescout/crawl/__init__.py +0 -0
- mergescout/crawl/__main__.py +97 -0
- mergescout/crawl/chain.py +34 -0
- mergescout/crawl/fetch.py +146 -0
- mergescout/crawl/outcomes.py +69 -0
- mergescout/crawl/queries.py +250 -0
- mergescout/crawl/runner.py +176 -0
- mergescout/crawl/seed.py +77 -0
- mergescout/crawl/state.py +169 -0
- mergescout/crawl/writer.py +87 -0
- mergescout/discover/__init__.py +0 -0
- mergescout/discover/__main__.py +58 -0
- mergescout/discover/sweep.py +286 -0
- mergescout/github/__init__.py +0 -0
- mergescout/github/graphql.py +202 -0
- mergescout/github/identities.py +35 -0
- mergescout/github/releases.py +137 -0
- mergescout/labels.py +20 -0
- mergescout/mcp_server.py +228 -0
- mergescout/measure/__init__.py +0 -0
- mergescout/measure/__main__.py +75 -0
- mergescout/measure/api_cost.py +91 -0
- mergescout/measure/count_repos.py +30 -0
- mergescout/measure/jsonl.py +14 -0
- mergescout/measure/report.py +56 -0
- mergescout/measure/sample.py +37 -0
- mergescout/owners.py +22 -0
- mergescout/setup_check.py +64 -0
- mergescout/signals/__init__.py +0 -0
- mergescout/signals/__main__.py +96 -0
- mergescout/signals/bump_ledger.py +258 -0
- mergescout/signals/bumps.py +104 -0
- mergescout/signals/check.py +141 -0
- mergescout/signals/community_bench.py +149 -0
- mergescout/signals/delivery_bench.py +103 -0
- mergescout/signals/deps_map.py +261 -0
- mergescout/signals/deps_trends.py +117 -0
- mergescout/signals/evidence.py +94 -0
- mergescout/signals/exploits.py +43 -0
- mergescout/signals/flow_bench.py +258 -0
- mergescout/signals/identity.py +73 -0
- mergescout/signals/issues.py +76 -0
- mergescout/signals/labels.py +79 -0
- mergescout/signals/osv.py +232 -0
- mergescout/signals/owners_watch.py +80 -0
- mergescout/signals/repo_stats.py +96 -0
- mergescout/signals/rules.py +70 -0
- mergescout/signals/silver.py +33 -0
- mergescout/signals/top_packages.py +26 -0
- mergescout/signals/triage.py +71 -0
- mergescout/signals/triage_math.py +120 -0
- mergescout/signals/walls.py +91 -0
- mergescout/snapshot/__init__.py +0 -0
- mergescout/snapshot/__main__.py +215 -0
- mergescout/snapshot/build.py +147 -0
- mergescout/snapshot/publish.py +70 -0
- mergescout/snapshot/schema.py +34 -0
- mergescout/snapshot/score.py +30 -0
- mergescout/store/__init__.py +0 -0
- mergescout/store/__main__.py +46 -0
- mergescout/store/sync.py +148 -0
- mergescout/timeutil.py +11 -0
- mergescout/tool/__init__.py +0 -0
- mergescout/tool/action_pack.py +62 -0
- mergescout/tool/live.py +201 -0
- mergescout/tool/queries.py +293 -0
- mergescout/tool/refs.py +31 -0
- mergescout/tool/store.py +129 -0
- mergescout/tool/tracking.py +83 -0
- stackvitals/__init__.py +2 -0
- stackvitals/__main__.py +4 -0
- stackvitals/ci/__init__.py +1 -0
- stackvitals/ci/action.py +574 -0
- stackvitals/ci/analyze.py +577 -0
- stackvitals/ci/api.py +218 -0
- stackvitals/ci/automation.py +212 -0
- stackvitals/ci/bench.py +195 -0
- stackvitals/ci/burnin.py +102 -0
- stackvitals/ci/cli.py +570 -0
- stackvitals/ci/collect.py +239 -0
- stackvitals/ci/cost.py +107 -0
- stackvitals/ci/coverage.py +155 -0
- stackvitals/ci/feedback.py +699 -0
- stackvitals/ci/fingerprint.py +220 -0
- stackvitals/ci/gitlab.py +121 -0
- stackvitals/ci/gitlab_action.py +125 -0
- stackvitals/ci/impact.py +125 -0
- stackvitals/ci/investigate.py +59 -0
- stackvitals/ci/kinds.py +52 -0
- stackvitals/ci/monitors.py +105 -0
- stackvitals/ci/owners.py +116 -0
- stackvitals/ci/quarantine_runtime.py +90 -0
- stackvitals/ci/report.py +435 -0
- stackvitals/ci/retry.py +67 -0
- stackvitals/ci/selfheal.py +102 -0
- stackvitals/ci/shards.py +120 -0
- stackvitals/ci/share.py +59 -0
- stackvitals/ci/store.py +93 -0
- stackvitals/ci/testreports.py +351 -0
- stackvitals/ci/tickets.py +106 -0
- stackvitals/ci/track_record.json +11 -0
- stackvitals/ci/triage.py +130 -0
- stackvitals/ci/triggers.py +147 -0
- stackvitals/ci/upload.py +85 -0
- stackvitals/ci/validate.py +91 -0
- stackvitals/cli.py +111 -0
- stackvitals/codeowners.py +69 -0
- stackvitals/community/__init__.py +0 -0
- stackvitals/community/action.py +140 -0
- stackvitals/community/affiliation.py +51 -0
- stackvitals/community/analyze.py +507 -0
- stackvitals/community/build.py +56 -0
- stackvitals/community/chaoss.py +70 -0
- stackvitals/community/cli.py +79 -0
- stackvitals/community/collect.py +240 -0
- stackvitals/community/context.py +54 -0
- stackvitals/community/report.py +250 -0
- stackvitals/connectors/__init__.py +244 -0
- stackvitals/connectors/ai_spend.py +190 -0
- stackvitals/connectors/base.py +82 -0
- stackvitals/connectors/check.py +686 -0
- stackvitals/connectors/copilot.py +104 -0
- stackvitals/connectors/discord.py +103 -0
- stackvitals/connectors/discourse.py +117 -0
- stackvitals/connectors/incident_io.py +106 -0
- stackvitals/connectors/jira.py +210 -0
- stackvitals/connectors/linear.py +140 -0
- stackvitals/connectors/pagerduty.py +96 -0
- stackvitals/connectors/slack.py +110 -0
- stackvitals/connectors/sonar.py +77 -0
- stackvitals/connectors/store.py +120 -0
- stackvitals/connectors/tracker.py +108 -0
- stackvitals/connectors/writeback.py +105 -0
- stackvitals/contribute/__init__.py +1 -0
- stackvitals/contribute/brief.py +297 -0
- stackvitals/contribute/cli.py +64 -0
- stackvitals/core/__init__.py +1 -0
- stackvitals/core/actors.py +103 -0
- stackvitals/core/agent_ledger.py +120 -0
- stackvitals/core/agreements.py +95 -0
- stackvitals/core/ai_outcomes.py +276 -0
- stackvitals/core/alerts.py +224 -0
- stackvitals/core/appdata.py +112 -0
- stackvitals/core/bucket.py +190 -0
- stackvitals/core/bugs.py +43 -0
- stackvitals/core/calendar.py +34 -0
- stackvitals/core/capex.py +57 -0
- stackvitals/core/change_risk.py +181 -0
- stackvitals/core/changes.py +133 -0
- stackvitals/core/chat.py +143 -0
- stackvitals/core/config.py +373 -0
- stackvitals/core/dataflow.py +168 -0
- stackvitals/core/decisions.py +275 -0
- stackvitals/core/delivery.py +954 -0
- stackvitals/core/delivery_peers.py +98 -0
- stackvitals/core/delivery_report.py +429 -0
- stackvitals/core/deliverycli.py +147 -0
- stackvitals/core/devlake_dora.sql +485 -0
- stackvitals/core/digest.py +333 -0
- stackvitals/core/explaincli.py +107 -0
- stackvitals/core/explore.py +296 -0
- stackvitals/core/export.py +342 -0
- stackvitals/core/exportcli.py +117 -0
- stackvitals/core/findings.py +350 -0
- stackvitals/core/health.py +94 -0
- stackvitals/core/identity.py +82 -0
- stackvitals/core/insights.py +365 -0
- stackvitals/core/insightscli.py +43 -0
- stackvitals/core/installdoc.py +69 -0
- stackvitals/core/investment.py +187 -0
- stackvitals/core/joins.py +43 -0
- stackvitals/core/llmsdoc.py +40 -0
- stackvitals/core/merge_queue.py +70 -0
- stackvitals/core/metrics.py +109 -0
- stackvitals/core/model.py +285 -0
- stackvitals/core/projectcli.py +66 -0
- stackvitals/core/propose.py +86 -0
- stackvitals/core/pulse.py +85 -0
- stackvitals/core/recorded.py +145 -0
- stackvitals/core/releases.py +39 -0
- stackvitals/core/requests.py +528 -0
- stackvitals/core/resync.py +35 -0
- stackvitals/core/schema.py +205 -0
- stackvitals/core/services.py +203 -0
- stackvitals/core/settings.py +96 -0
- stackvitals/core/sqlcli.py +111 -0
- stackvitals/core/stats.py +222 -0
- stackvitals/core/store.py +125 -0
- stackvitals/core/summary.py +104 -0
- stackvitals/core/time.py +29 -0
- stackvitals/core/tomledit.py +149 -0
- stackvitals/core/uicli.py +138 -0
- stackvitals/core/webapp.py +79 -0
- stackvitals/core/what_changed.py +76 -0
- stackvitals/core/workspace.py +194 -0
- stackvitals/deps/__init__.py +1 -0
- stackvitals/deps/action.py +299 -0
- stackvitals/deps/actions_vulns.py +89 -0
- stackvitals/deps/cli.py +77 -0
- stackvitals/deps/dossier.py +341 -0
- stackvitals/deps/evidence.py +44 -0
- stackvitals/deps/fixplan.py +40 -0
- stackvitals/deps/guarddog.py +32 -0
- stackvitals/deps/hygiene.py +47 -0
- stackvitals/deps/imports.py +113 -0
- stackvitals/deps/lockfiles.py +198 -0
- stackvitals/deps/lookalike.py +80 -0
- stackvitals/deps/reach.py +59 -0
- stackvitals/deps/release.py +110 -0
- stackvitals/deps/runtimes.py +161 -0
- stackvitals/deps/supply.py +454 -0
- stackvitals/deps/vulns.py +588 -0
- stackvitals/editors/__init__.py +40 -0
- stackvitals/editors/vscode/extension.js +101 -0
- stackvitals/editors/vscode/marks.js +92 -0
- stackvitals/editors/vscode/package.json +26 -0
- stackvitals/flow/__init__.py +1 -0
- stackvitals/flow/action.py +178 -0
- stackvitals/flow/analyze.py +418 -0
- stackvitals/flow/automate.py +72 -0
- stackvitals/flow/build.py +46 -0
- stackvitals/flow/cli.py +93 -0
- stackvitals/flow/code_hosts.py +146 -0
- stackvitals/flow/collect.py +451 -0
- stackvitals/flow/gitlab_mr.py +75 -0
- stackvitals/flow/knowledge.py +202 -0
- stackvitals/flow/peers.py +90 -0
- stackvitals/flow/preflight.py +210 -0
- stackvitals/flow/report.py +253 -0
- stackvitals/flow/rules.py +135 -0
- stackvitals/flow/store.py +108 -0
- stackvitals/flow/survival.py +84 -0
- stackvitals/flow/triage.py +62 -0
- stackvitals/mcp_server.py +670 -0
- stackvitals/portal/__init__.py +0 -0
- stackvitals/portal/action.py +205 -0
- stackvitals/portal/batch.py +80 -0
- stackvitals/portal/campaigns.py +104 -0
- stackvitals/portal/catalog.py +104 -0
- stackvitals/portal/checks.py +63 -0
- stackvitals/portal/cli.py +172 -0
- stackvitals/portal/descriptor.py +105 -0
- stackvitals/portal/export.py +43 -0
- stackvitals/portal/github.py +48 -0
- stackvitals/portal/golden.py +95 -0
- stackvitals/portal/history.py +30 -0
- stackvitals/portal/inventory.py +264 -0
- stackvitals/portal/owners.py +78 -0
- stackvitals/portal/pages.py +153 -0
- stackvitals/portal/rollup.py +32 -0
- stackvitals/portal/scorecard.py +366 -0
- stackvitals/security/__init__.py +2 -0
- stackvitals/security/probes.py +154 -0
- stackvitals/security/repo_checks.py +132 -0
- stackvitals/security/workflows.py +111 -0
- stackvitals/shell.py +107 -0
- stackvitals/site.py +51 -0
- stackvitals/web/app/LICENSES.md +428 -0
- stackvitals/web/app/boot.js +1 -0
- stackvitals/web/app/chunks/AddRepo-KRPZXWCY.js +1 -0
- stackvitals/web/app/chunks/SetupRunning-XWFQAKRZ.js +1 -0
- stackvitals/web/app/chunks/accuracy-data-OQTDXI7Y.js +1 -0
- stackvitals/web/app/chunks/agents-data-BAL6QAOF.js +11 -0
- stackvitals/web/app/chunks/chunk-24LFKFZ6.js +1 -0
- stackvitals/web/app/chunks/chunk-3G4SQNUC.js +1 -0
- stackvitals/web/app/chunks/chunk-3IXWEOIJ.js +154 -0
- stackvitals/web/app/chunks/chunk-3ROQFHPV.js +1 -0
- stackvitals/web/app/chunks/chunk-45PGAQRO.js +8 -0
- stackvitals/web/app/chunks/chunk-4H53OYLG.js +1 -0
- stackvitals/web/app/chunks/chunk-4LUI7O5P.js +1 -0
- stackvitals/web/app/chunks/chunk-4SQPQ5TB.js +1 -0
- stackvitals/web/app/chunks/chunk-4WKQTWUC.js +2 -0
- stackvitals/web/app/chunks/chunk-576YHDEK.js +8 -0
- stackvitals/web/app/chunks/chunk-6BDBXA6V.js +1 -0
- stackvitals/web/app/chunks/chunk-6RLVD2DT.js +1 -0
- stackvitals/web/app/chunks/chunk-763NH7PP.js +1 -0
- stackvitals/web/app/chunks/chunk-76WAVKUG.js +1 -0
- stackvitals/web/app/chunks/chunk-7II2BD5F.js +1 -0
- stackvitals/web/app/chunks/chunk-7TJCBOAY.js +1 -0
- stackvitals/web/app/chunks/chunk-ASYNLVOP.js +1 -0
- stackvitals/web/app/chunks/chunk-CTKDYZMG.js +1 -0
- stackvitals/web/app/chunks/chunk-CVB26NMC.js +1 -0
- stackvitals/web/app/chunks/chunk-G7NGB5UX.js +1 -0
- stackvitals/web/app/chunks/chunk-GPVLJRZD.js +1 -0
- stackvitals/web/app/chunks/chunk-GQYFN4PE.js +75 -0
- stackvitals/web/app/chunks/chunk-GT4EAKAO.js +2 -0
- stackvitals/web/app/chunks/chunk-IYMBFPCV.js +1 -0
- stackvitals/web/app/chunks/chunk-LAAGCPU3.js +1 -0
- stackvitals/web/app/chunks/chunk-MAPYGK3P.js +1 -0
- stackvitals/web/app/chunks/chunk-P5TXDD45.js +1 -0
- stackvitals/web/app/chunks/chunk-PABICGBL.js +1 -0
- stackvitals/web/app/chunks/chunk-RPXHAG6F.js +1 -0
- stackvitals/web/app/chunks/chunk-RQZPSJ2Y.js +1 -0
- stackvitals/web/app/chunks/chunk-S2ZBMEVA.js +1 -0
- stackvitals/web/app/chunks/chunk-S654BBEP.js +6 -0
- stackvitals/web/app/chunks/chunk-T7QVTNXF.js +1 -0
- stackvitals/web/app/chunks/chunk-THFUOCPC.js +273 -0
- stackvitals/web/app/chunks/chunk-TO4VVRZ7.js +1 -0
- stackvitals/web/app/chunks/chunk-VQ5YXO54.js +1 -0
- stackvitals/web/app/chunks/chunk-VWKSMFWJ.js +1 -0
- stackvitals/web/app/chunks/chunk-Y6LUXDM6.js +1 -0
- stackvitals/web/app/chunks/chunk-YTANGYYH.js +1 -0
- stackvitals/web/app/chunks/connect-KONYWYTT.js +1 -0
- stackvitals/web/app/chunks/connections-data-2DYS4UVW.js +32 -0
- stackvitals/web/app/chunks/logos-N5IINUAM.js +1 -0
- stackvitals/web/app/chunks/more-KMJMJQD3.js +10 -0
- stackvitals/web/app/chunks/more-PC6FVS3P.js +1 -0
- stackvitals/web/app/chunks/more-RCIVCUDE.js +2 -0
- stackvitals/web/app/chunks/more-ZKKRY6NO.css +1 -0
- stackvitals/web/app/chunks/sealbox-35YL4EUH.js +3 -0
- stackvitals/web/app/chunks/session-DYYTOE2K.js +1 -0
- stackvitals/web/app/chunks/settingsApply-2HRJEZYM.js +1 -0
- stackvitals/web/app/chunks/settingsReview-GX2FZQ4M.js +3 -0
- stackvitals/web/app/chunks/uPlot.esm-OIWT3AHJ.js +3 -0
- stackvitals/web/app/demo/ci/impact.json +1 -0
- stackvitals/web/app/demo/ci/quarantine.json +1 -0
- stackvitals/web/app/demo/ci/tickets.json +1 -0
- stackvitals/web/app/demo/mobile/alerts.json +1 -0
- stackvitals/web/app/demo/mobile/app.json +1 -0
- stackvitals/web/app/demo/mobile/community.json +1 -0
- stackvitals/web/app/demo/mobile/connectors.json +1 -0
- stackvitals/web/app/demo/mobile/data.json +1 -0
- stackvitals/web/app/demo/mobile/decisions.json +1 -0
- stackvitals/web/app/demo/mobile/delivery.json +1 -0
- stackvitals/web/app/demo/mobile/deps.json +1 -0
- stackvitals/web/app/demo/mobile/flow.json +1 -0
- stackvitals/web/app/demo/mobile/health.json +1 -0
- stackvitals/web/app/demo/mobile/insights.json +1 -0
- stackvitals/web/app/demo/mobile/pulse.json +1 -0
- stackvitals/web/app/demo/mobile/security.json +1 -0
- stackvitals/web/app/demo/mobile/summary/ci_jobs.jsonl +5 -0
- stackvitals/web/app/demo/mobile/summary/devlake_project_pr_metrics.jsonl +14 -0
- stackvitals/web/app/demo/mobile/summary/devlake_pull_requests.jsonl +14 -0
- stackvitals/web/app/demo/mobile/summary/manifest.json +1 -0
- stackvitals/web/app/demo/mobile/summary/prs.jsonl +19 -0
- stackvitals/web/app/demo/mobile/summary/tests.jsonl +1 -0
- stackvitals/web/app/demo/mobile/supply.json +1 -0
- stackvitals/web/app/demo/mobile/vulns.json +1 -0
- stackvitals/web/app/demo/payments-api/alerts.json +1 -0
- stackvitals/web/app/demo/payments-api/app.json +1 -0
- stackvitals/web/app/demo/payments-api/community.json +1 -0
- stackvitals/web/app/demo/payments-api/connectors.json +1 -0
- stackvitals/web/app/demo/payments-api/data.json +1 -0
- stackvitals/web/app/demo/payments-api/decisions.json +1 -0
- stackvitals/web/app/demo/payments-api/delivery.json +1 -0
- stackvitals/web/app/demo/payments-api/deps.json +1 -0
- stackvitals/web/app/demo/payments-api/flow.json +1 -0
- stackvitals/web/app/demo/payments-api/health.json +1 -0
- stackvitals/web/app/demo/payments-api/insights.json +1 -0
- stackvitals/web/app/demo/payments-api/pulse.json +1 -0
- stackvitals/web/app/demo/payments-api/security.json +1 -0
- stackvitals/web/app/demo/payments-api/summary/ci_jobs.jsonl +5 -0
- stackvitals/web/app/demo/payments-api/summary/devlake_project_pr_metrics.jsonl +14 -0
- stackvitals/web/app/demo/payments-api/summary/devlake_pull_requests.jsonl +14 -0
- stackvitals/web/app/demo/payments-api/summary/manifest.json +1 -0
- stackvitals/web/app/demo/payments-api/summary/prs.jsonl +22 -0
- stackvitals/web/app/demo/payments-api/summary/tests.jsonl +3 -0
- stackvitals/web/app/demo/payments-api/supply.json +1 -0
- stackvitals/web/app/demo/payments-api/vulns.json +1 -0
- stackvitals/web/app/demo/portal.json +1 -0
- stackvitals/web/app/demo/settings.json +1 -0
- stackvitals/web/app/demo/web-app/alerts.json +1 -0
- stackvitals/web/app/demo/web-app/app.json +1 -0
- stackvitals/web/app/demo/web-app/community.json +1 -0
- stackvitals/web/app/demo/web-app/connectors.json +1 -0
- stackvitals/web/app/demo/web-app/data.json +1 -0
- stackvitals/web/app/demo/web-app/decisions.json +1 -0
- stackvitals/web/app/demo/web-app/delivery.json +1 -0
- stackvitals/web/app/demo/web-app/deps.json +1 -0
- stackvitals/web/app/demo/web-app/flow.json +1 -0
- stackvitals/web/app/demo/web-app/health.json +1 -0
- stackvitals/web/app/demo/web-app/insights.json +1 -0
- stackvitals/web/app/demo/web-app/pulse.json +1 -0
- stackvitals/web/app/demo/web-app/security.json +1 -0
- stackvitals/web/app/demo/web-app/summary/ci_jobs.jsonl +5 -0
- stackvitals/web/app/demo/web-app/summary/devlake_project_pr_metrics.jsonl +14 -0
- stackvitals/web/app/demo/web-app/summary/devlake_pull_requests.jsonl +14 -0
- stackvitals/web/app/demo/web-app/summary/manifest.json +1 -0
- stackvitals/web/app/demo/web-app/summary/prs.jsonl +18 -0
- stackvitals/web/app/demo/web-app/summary/tests.jsonl +2 -0
- stackvitals/web/app/demo/web-app/supply.json +1 -0
- stackvitals/web/app/demo/web-app/vulns.json +1 -0
- stackvitals/web/app/fonts/plex-mono-400.woff2 +0 -0
- stackvitals/web/app/fonts/plex-sans-400.woff2 +0 -0
- stackvitals/web/app/fonts/plex-sans-600.woff2 +0 -0
- stackvitals/web/app/fonts/plex-sans-condensed-700.woff2 +0 -0
- stackvitals/web/app/help/accuracy.json +1 -0
- stackvitals/web/app/help/connectors.json +1 -0
- stackvitals/web/app/help/getting-started.json +1 -0
- stackvitals/web/app/help/index.json +1 -0
- stackvitals/web/app/help/modules.json +1 -0
- stackvitals/web/app/help/privacy-and-security.json +1 -0
- stackvitals/web/app/help/reference.json +1 -0
- stackvitals/web/app/main.css +1 -0
- stackvitals/web/app/main.js +1 -0
- stackvitals/web/app/pages/about.css +1 -0
- stackvitals/web/app/pages/about.js +1 -0
- stackvitals/web/app/pages/activity.css +1 -0
- stackvitals/web/app/pages/activity.js +1 -0
- stackvitals/web/app/pages/agents.css +1 -0
- stackvitals/web/app/pages/agents.js +2 -0
- stackvitals/web/app/pages/ci.css +1 -0
- stackvitals/web/app/pages/ci.js +1 -0
- stackvitals/web/app/pages/community.css +1 -0
- stackvitals/web/app/pages/community.js +1 -0
- stackvitals/web/app/pages/connections.css +1 -0
- stackvitals/web/app/pages/connections.js +6 -0
- stackvitals/web/app/pages/delivery.css +1 -0
- stackvitals/web/app/pages/delivery.js +45 -0
- stackvitals/web/app/pages/deps.css +1 -0
- stackvitals/web/app/pages/deps.js +7 -0
- stackvitals/web/app/pages/explore.css +1 -0
- stackvitals/web/app/pages/explore.js +7 -0
- stackvitals/web/app/pages/health.css +1 -0
- stackvitals/web/app/pages/health.js +1 -0
- stackvitals/web/app/pages/help.css +1 -0
- stackvitals/web/app/pages/help.js +6 -0
- stackvitals/web/app/pages/insights.css +1 -0
- stackvitals/web/app/pages/insights.js +1 -0
- stackvitals/web/app/pages/investment.css +1 -0
- stackvitals/web/app/pages/investment.js +3 -0
- stackvitals/web/app/pages/overview.css +1 -0
- stackvitals/web/app/pages/overview.js +1 -0
- stackvitals/web/app/pages/portal.css +1 -0
- stackvitals/web/app/pages/portal.js +51 -0
- stackvitals/web/app/pages/prs.css +1 -0
- stackvitals/web/app/pages/prs.js +4 -0
- stackvitals/web/app/pages/settings.css +1 -0
- stackvitals/web/app/pages/settings.js +1 -0
- stackvitals/web/app/pages/setup.css +1 -0
- stackvitals/web/app/pages/setup.js +2 -0
- stackvitals/web/app/pages/standing.css +1 -0
- stackvitals/web/app/pages/standing.js +1 -0
- stackvitals/web/app/pages/welcome-sheet.js +1 -0
- stackvitals/web/app/pages/welcome.js +1 -0
- stackvitals/web/app/settings-workflow.json +1 -0
- stackvitals/web/index.html +34 -0
- stackvitals/web/site/index.html +108 -0
- stackvitals/web/site/integrations.html +15 -0
- stackvitals/web/site/setup.html +15 -0
- stackvitals/web/site/site.css +68 -0
- stackvitals-0.1.0.dist-info/METADATA +134 -0
- stackvitals-0.1.0.dist-info/RECORD +465 -0
- stackvitals-0.1.0.dist-info/WHEEL +4 -0
- stackvitals-0.1.0.dist-info/entry_points.txt +3 -0
- stackvitals-0.1.0.dist-info/licenses/LICENSE +202 -0
- stackvitals-0.1.0.dist-info/licenses/NOTICE +16 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as w,b as x,c as _}from"../chunks/chunk-GPVLJRZD.js";import"../chunks/chunk-PABICGBL.js";import"../chunks/chunk-GQYFN4PE.js";import{a as O}from"../chunks/chunk-3ROQFHPV.js";import"../chunks/chunk-ASYNLVOP.js";import{a as D,b as V,c as J,d as Q,e as Z}from"../chunks/chunk-3G4SQNUC.js";import"../chunks/chunk-LAAGCPU3.js";import{b as T}from"../chunks/chunk-24LFKFZ6.js";import"../chunks/chunk-6BDBXA6V.js";import"../chunks/chunk-MAPYGK3P.js";import{a as p}from"../chunks/chunk-6RLVD2DT.js";import"../chunks/chunk-T7QVTNXF.js";import"../chunks/chunk-RPXHAG6F.js";import"../chunks/chunk-45PGAQRO.js";import{d as z}from"../chunks/chunk-4WKQTWUC.js";import"../chunks/chunk-VQ5YXO54.js";import{b as E,c as k,d as P,e as b}from"../chunks/chunk-76WAVKUG.js";import{d as R,e as K,g as U,h as G}from"../chunks/chunk-VWKSMFWJ.js";import{I as y,K as H,L as q,O as I,P as f,R as j,a as S,c as N,k as g,m,n as C,o as A,w as e,z as $}from"../chunks/chunk-GT4EAKAO.js";import{d as F}from"../chunks/chunk-G7NGB5UX.js";F();F();F();var B=/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;function Y(a,n,s){let r=B.test(a.trim())?a.trim():"owner/name",u=Math.round(n);return`stackvitals community report ${r}${u&&u!==12?` --months ${u}`:""}${s==="json"?" --json > community.json":s==="html"?" --html ./community":""}`}function W({app:a}){let[n,s]=N(a.repo),[r,u]=N(12),[t,d]=N("html"),o=!!n.trim()&&!B.test(n.trim()),c=a.modules?.community?.page;return e("div",{class:"columns",children:[e(x,{title:"This repo",state:e(y,{tone:"good",icon:null,children:"done by the workflow"}),bar:c?e($,{size:"sm",variant:"tinted",href:c,external:!0,iconEnd:"external",children:"Open the printable page"}):null,children:[e(_,{rows:[["Repo",e("span",{class:"mono-soft",children:a.repo})],["Printable page",c?"Published beside the app":"Not published here"]]}),e("p",{children:["Every run rebuilds this report for ",a.repo,": the page above, and the printable page and badges the command's --html writes. Nothing to run."]})]}),e(x,{title:"Any other public repo",state:e(y,{tone:"neutral",icon:null,children:"from your machine"}),children:[e("p",{children:"It reads the repo's issues, pull requests and contributors with thousands of API calls under your own read-only token, then runs the analysis in Python, so it can't run in this tab."}),e("div",{class:"blk-form",children:[e(R,{label:"Repo",error:o?"Write it as owner/name":null,children:l=>e(K,{id:l,mono:!0,value:n,onInput:s,placeholder:"owner/name",maxLength:140})}),e(R,{label:"History on the first run",hint:"Months to read back.",children:l=>e(U,{id:l,value:r,onInput:u,min:1,max:60,unit:"months"})}),e(R,{label:"Output",children:l=>e(G,{id:l,value:t,onChange:d,options:[{value:"html",label:"Page and badges in a folder"},{value:"text",label:"Text in the terminal"},{value:"json",label:"JSON file"}]})})]}),e(I,{inline:!0,code:Y(n,r,t),note:"pip install stackvitals first; it uses GITHUB_TOKEN or your gh login, and writes nothing to GitHub."})]})]})}var v=a=>a==null,L=a=>a?v(a.median)?a.at_least?`over ${g(a.at_least)}`:"\u2013":g(a.median):"\u2013",te=a=>!a||v(a.low)?"\u2013":`${g(a.low)} ${v(a.high)?"or more":`to ${g(a.high)}`}`,ne="methods.md#waiting-times-kaplan-meier",h=(a,n)=>e(S,{children:[a,n?e("small",{children:n}):null]});function M(a,n,s){if(v(a)||v(n))return"no quarter before to compare";let r=a-n;return r===0?"same as the quarter before":`${r>0?"up":"down"} ${s(Math.abs(r))} on the quarter before`}function ae({changes:a}){if(!a)return null;let n=a.filter(t=>!t.note&&!t.shown_with),s=(t,d,o)=>v(d)?"\u2013":t.unit==="share"?m(d):t.unit==="count"?`${d}${o?" a week":""}`:`${(+d).toFixed(1)} ${t.unit}`,r=t=>t.detectable?t.unit==="share"?`${Math.round(t.detectable*100)} points`:`${t.detectable>=3?Math.round(t.detectable):t.detectable.toFixed(1)}\xD7`:"\u2013",u=t=>t.charAt(0).toUpperCase()+t.slice(1);return e(w,{title:"This week against the four before",note:"As the weekly digest reads it.",children:e(p,{name:"community-week",caption:"This week against the four weeks before",search:!1,rows:n.map((t,d)=>({id:t.metric||String(d),c:t})),columns:[{key:"label",label:"Measure",primary:!0,raw:t=>t.c.label,render:t=>u(t.c.label)},{key:"now",label:"This week",num:!0,raw:t=>t.c.now,render:t=>s(t.c,t.c.now)},{key:"base",label:"Four weeks before",num:!0,raw:t=>t.c.base,render:t=>s(t.c,t.c.base,!0)},{key:"moved",label:"Moved",raw:t=>t.c.changed?t.c.direction||"moved":"steady",render:t=>t.c.changed?e(y,{tone:"watch",icon:null,children:t.c.direction==="up"?"Rose":"Fell"}):e(y,{tone:"neutral",icon:null,children:"Steady within noise"})},{key:"detectable",label:"Smallest change it would catch",raw:t=>t.c.detectable??null,render:t=>r(t.c)}],empty:e(b,{children:e(f,{icon:"clock",title:"Too few this week to compare.",children:"This week is set against the four weeks before once there are enough items."})})})})}function re({c:a}){let n=(a.quarters||[]).filter(t=>!v(t.median_hours)),s=n[n.length-1],r=n[n.length-2],u=a.facts?.first_answer_hours??s?.median_hours;return e("div",{class:"hero",children:[e("div",{class:"hero-name",children:[e("span",{children:"First answer, median"}),e(q,{info:{title:"First answer",doc:"modules.md#community",body:"Hours from an outside issue or pull request to its first reply from someone other than the author, over the last 90 days."}})]}),e("div",{class:"hero-num num",children:g(u)}),e("div",{class:"hero-meta",children:s&&r?e(S,{children:[e(H,{kind:"hours",from:r.median_hours,to:s.median_hours,better:"down"}),e("span",{class:"hero-date",children:[s.quarter," against ",r.quarter]})]}):e("span",{class:"hero-date",children:"last 90 days"})}),n.length>1?e(O,{label:"Median first answer by quarter",kind:"hours",height:180,bars:n.map((t,d)=>({label:t.quarter,value:t.median_hours,tone:d===n.length-1?"accent":"neutral"}))}):null]})}function se({c:a}){let n=a.answers?.issues,s=a.answers?.discussions,r=[n?{id:"issues",channel:"Outside issues",km:n.km,items:n.items??null,extra:n.responders?`${n.responders} regular responders count as the project`:""}:null,s?{id:"discussions",channel:"Discussions",km:s.km,items:null,extra:v(s.answer_chosen)?"":`${m(s.answer_chosen)} of questions have an accepted answer`}:null,a.forum?{id:"forum",channel:"Forum",km:a.forum.km,items:a.forum.topics,extra:`anyone answered ${m(a.forum.anyone_answered)}, solved ${m(a.forum.solved_share)} (${a.forum.days} days)`}:null,a.chat?{id:"chat",channel:"Chat",km:a.chat.km,items:a.chat.topics,extra:`anyone answered ${m(a.chat.anyone_answered)} (${a.chat.days} days)`}:null].filter(Boolean),u=a.answers?.left_out;return e(w,{title:"The project's answer, by channel",note:"Median with items still waiting counted (Kaplan-Meier).",action:e(q,{info:{title:"Project's answer",doc:ne,body:"Median time to the first reply from a maintainer or a regular responder (10+ others' issues answered or 2+ others' PRs first-reviewed this year), with items still waiting counted."}}),children:[e(p,{name:"answer-channels",caption:"The project's answer by channel",search:!1,rows:r,columns:[{key:"channel",label:"Channel",primary:!0},{key:"median",label:"Median answer",num:!0,raw:t=>t.km?.median??t.km?.at_least??null,render:t=>t.km?.too_few?e("span",{class:"muted",children:"too few"}):L(t.km)},{key:"range",label:"Likely range",raw:t=>t.km?.low??null,render:t=>te(t.km)},{key:"answered",label:"Answered",num:!0,raw:t=>t.km?.answered??null},{key:"waiting",label:"Still waiting",num:!0,raw:t=>t.km?.waiting??null},{key:"closed",label:"Closed unanswered",num:!0,raw:t=>t.km?.closed_unanswered??0},{key:"extra",label:"Also",raw:t=>t.extra,render:t=>t.extra||e("span",{class:"muted",children:"\u2013"})}],empty:e(b,{children:e(f,{icon:"clock",title:"No outside items yet.",children:"The project's answer shows once outside issues arrive."})})}),e("p",{class:"blk-note",children:[r.some(t=>t.km?.too_few)?"Too few means under 10 answered, too few to say. ":"",u?`${C(u.items,"item")} from ${C(u.accounts,"account")} that opened many on one day (automated) are left out.`:"A median needs 10 answered items."]})]})}function oe({c:a}){let n=a.context,s=a.answers?.issues?.km,r=n.response_policy,u=s?.median,t=r&&!v(u)?u<=r.hours:null,d=(c,l)=>c?e(y,{tone:"good",icon:null,children:l||"yes"}):e(y,{tone:"neutral",icon:null,children:"no"}),o=[{id:"contributing",what:"Contributing guide",has:d(n.contributing),detail:r?`promises an answer ${r.said}`:"no response time promised"},{id:"governance",what:"Governance file",has:d(n.governance),detail:"how decisions are made"},{id:"security",what:"Security policy",has:d(n.security_policy),detail:"how to report a vulnerability"},{id:"coc",what:"Code of conduct",has:d(n.code_of_conduct),detail:n.code_of_conduct||"\u2013"},{id:"funding",what:"Funding links",has:d(n.funding?.length),detail:n.funding?.length?n.funding.join(", ").toLowerCase().replace(/_/g," "):"\u2013"},{id:"discussions",what:"Discussions",has:d(n.discussions,"on"),detail:"GitHub Discussions"},{id:"maintainers",what:"Listed maintainers",has:n.maintainers_listed?e("span",{class:"num",children:n.maintainers_listed}):d(!1),detail:"named in the repo"},{id:"owners",what:"Code owners",has:n.code_owners?e("span",{class:"num",children:n.code_owners}):d(!1),detail:"in CODEOWNERS"}];return e(S,{children:[e(P,{children:[e(x,{title:"The promise",class:t===null?"":t?"is-good":"is-bad",state:t===null?e(y,{tone:"neutral",icon:null,children:r?"can't tell yet":"none made"}):e(y,{tone:t?"good":"bad",children:t?"Kept":"Not kept"}),children:e(_,{rows:[["Promised answer",r?r.said:"No promise in the contributing guide"],["Median first answer",L(s)],["Likely range",te(s)],["Where it's read",n.contributing?"The contributing guide":"No contributing guide"]]})}),e(x,{title:"In the project's words",bar:e($,{size:"sm",variant:"tinted",href:"#/settings?module=community",children:n.stated?"Change what it says":"Say who maintains it and how it works"}),children:e(_,{rows:[["Who maintains it",n.stated?.capacity||e("span",{class:"muted",children:"Not said yet"})],["How it works",n.stated?.norms||e("span",{class:"muted",children:"Not said yet"})]]})})]}),e(w,{title:"Files and channels",note:"What the repo has, read on each run.",children:e(p,{name:"community-files",caption:"Community files and channels",search:!1,rows:o,columns:[{key:"what",label:"Item",primary:!0},{key:"has",label:"Has it",raw:()=>"",render:c=>c.has},{key:"detail",label:"Detail"}]})})]})}function ie({c:a}){let n=a.dependency,s=a.tenure,r=a.orgs,u=(o,c)=>o?[c(o.typical),`${c(o.low)} to ${c(o.high)}`]:["\u2013","no public benchmark yet"],t=n?[{id:"bus",what:"Bus factor",you:String(n.bus_factor),peers:u(n.peers?.bus_factor,String),means:"the fewest people behind half the merged pull requests"},{id:"top",what:"Most active person's share",you:m(n.top_share),peers:u(n.peers?.top_share,o=>m(o)),means:"of the merged pull requests"},{id:"people",what:"People merging work",you:String(n.people),peers:["\u2013","\u2013"],means:`${n.merged} merged pull requests`}]:[],d=[["Newcomers","first contributions",s?.newcomers],["Regulars","from people who came before",s?.regulars]];return e(S,{children:[n?e(w,{title:"Who carries the merged work",note:`Last ${n.days||180} days, no names; low means the work rests on few people.`,action:e(q,{info:{title:"Bus factor",doc:"chaoss.md",body:"The fewest people behind half of the merged pull requests. Low means the work rests on few people."}}),children:e(p,{name:"carries-work",caption:"Who carries the merged work",search:!1,rows:t,columns:[{key:"what",label:"Measure",primary:!0},{key:"you",label:"This repo",num:!0},{key:"typical",label:"Similar repos, typical",num:!0,raw:o=>o.peers[0]},{key:"range",label:"Middle half",raw:o=>o.peers[1]},{key:"means",label:"What it counts"}]})}):e(b,{children:e(f,{icon:"community",title:"Not enough merged work yet.",children:"Who carries the work shows once people have merged 20 pull requests in 180 days."})}),e(w,{title:"First answers, newcomers against regulars",note:`Last ${s?.days||90} days.`,children:e(p,{name:"tenure",caption:"First answers by tenure",search:!1,rows:d.map(([o,c,l])=>({id:o,g:o,what:c,x:l})),columns:[{key:"g",label:"Who asked",primary:!0},{key:"n",label:"Items",num:!0,raw:o=>o.x?.n??null,render:o=>o.x?.n??"\u2013"},{key:"median",label:"Median first answer",num:!0,raw:o=>o.x?.median_h??null,render:o=>g(o.x?.median_h)},{key:"day",label:"Within a day",num:!0,raw:o=>o.x?.answered_1d_share??null,render:o=>m(o.x?.answered_1d_share)},{key:"what",label:"Counts"}]})}),r?e(w,{title:"Organisations behind the work",note:"From public profiles; unknown is counted, never guessed.",children:[e(_,{class:"is-figures",rows:[["Elephant factor",h(r.factor??"\u2013",r.factor?"fewest organisations behind half the work with a known one":"can't tell: most work has no known organisation")],["No known organisation",h(m(r.unknown_share),`${r.people} people, ${r.merged} merged pull requests`)]]}),r.orgs.length?e(p,{name:"orgs",caption:"Organisations",search:!1,rows:r.orgs,columns:[{key:"org",label:"Organisation",primary:!0},{key:"prs",label:"Merged PRs",num:!0},{key:"share",label:"Share",num:!0,render:o=>m(o.share)}]}):null]}):null]})}var le={first_answer_hours:"First answer to outside PRs",newcomer_answered_share:"Newcomers answered within 2 weeks",newcomer_return_rate:"Newcomers who come back within 90 days"},ee=(a,n)=>a==="hours"?g(n):m(n);function ue({c:a}){let n=a.score,s=a.lever;if(!n?.facts&&!s)return e(b,{children:e(f,{icon:"community",title:"No public benchmark yet.",children:a.has_bench?"Too few of this repo's facts have a comparison yet; two are needed for a score.":"The score compares this repo with similar public repos once the public benchmark file is published."})});let r=s?.by_answer||[],u=s?.by_outcome||[];return e(S,{children:[n?.facts?e(w,{title:"How the score is made",note:`Against ${n.repos} public repos (${n.group}).`,children:[e(_,{class:"is-figures",rows:[["Score",h(`${n.score}/100`,"the average of Better than")],["Compared with",h(n.repos,n.group)],(n.left_out||[]).length?["Left out for too little data",e("span",{class:"cm-left",children:[n.left_out.map(t=>le[t]||t).join(", "),e("small",{children:"a missing fact is never scored as zero"})]})]:null]}),e(p,{name:"community-score",caption:"How the score is made",search:!1,rows:n.facts,columns:[{key:"name",label:"Fact",primary:!0},{key:"you",label:"This repo",num:!0,render:t=>ee(t.kind,t.you)},{key:"typical",label:"Typical",num:!0,render:t=>ee(t.kind,t.typical)},{key:"better_than",label:"Better than",num:!0,render:t=>`${t.better_than}%`}]})]}):e(b,{children:e(f,{icon:"clock",title:"No score yet.",children:"Two facts with a public comparison are needed."})}),r.length?e(P,{ratio:"lead",children:[e(x,{title:"Newcomers who came back, by their first wait",children:[e("p",{class:"blk-how",children:["Share back within 90 days, by how long their first pull request waited: ",Number(s.newcomers).toLocaleString("en-US")," newcomers in ",Number(s.repos).toLocaleString("en-US")," public repos."]}),e(O,{label:"Share of newcomers who came back within 90 days, by first answer",kind:"share",height:180,bars:r.map(t=>({label:t.bucket,value:t.returned,tone:t.bucket.includes("no answer")||t.bucket.includes("never")?"bad":"accent"}))})]}),u.length?e(w,{title:"By what happened to the first pull request",children:[e(p,{name:"newcomer-outcomes",caption:"Newcomers who came back by outcome",search:!1,rows:u.map(t=>({...t,id:t.outcome})),columns:[{key:"outcome",label:"Outcome",primary:!0},{key:"newcomers",label:"Newcomers",num:!0,render:t=>Number(t.newcomers).toLocaleString("en-US")},{key:"returned",label:"Came back",num:!0,render:t=>m(t.returned,1)}]}),e("p",{class:"blk-note",children:"An association across repos, not proof that answering brings a given newcomer back."})]}):null]}):null]})}function de({c:a}){let[n,s]=D(),[r,u]=N(null),t=a.traffic,d=[{id:"stars",name:"Stars",sub:`New stars by week; ${(a.stars?.stars??0).toLocaleString("en-US")} in total`,kind:"count",better:"up",period:"week",points:T(a.stars?.weeks),meaning:l=>`${C(l,"new star")} that week.`,info:{title:"Stars",body:"Each point is the stars gained that week (GitHub's history, no names). The total is every star the repo has now."}},{id:"views",name:"Views",sub:`per day; ${t?.views_total??0} in ${t?.days??0} days`,kind:"count",better:"up",period:"day",points:T(t?.views),meaning:l=>`${C(l,"view")} that day.`},{id:"clones",name:"Clones",sub:`per day; ${t?.clones_total??0} in all`,kind:"count",better:"up",period:"day",points:T(t?.clones),meaning:l=>`${C(l,"clone")} that day.`}].filter(l=>l.points.length),o=(t?.referrers||[]).map(l=>Array.isArray(l)?{referrer:l[0],count:l[1]}:l);if(!d.length)return e(b,{children:e(f,{icon:"community",title:"No stars or traffic recorded yet.",children:"GitHub keeps 14 days of traffic; StackVitals keeps it from the first run on."})});let c=d.find(l=>l.id===r)||d[0];return e(S,{children:[a.stars||t?e(_,{class:"is-figures cm-figs",rows:[a.stars?["Stars in total",h((a.stars.stars??0).toLocaleString("en-US"))]:null,a.stars?["New stars, last 12 weeks",h(a.stars.gained_12w??"\u2013",v(a.stars.growth_12w)?null:`${m(a.stars.growth_12w,1)} growth`)]:null,t?.views_total?["Views",h(t.views_total.toLocaleString("en-US"),`in ${t.days??0} days`)]:null,t?.views_total?["Unique visitors",h((t.visitors_total??0).toLocaleString("en-US"),"a day, added up")]:null,t?.clones_total?["Clones",h(t.clones_total.toLocaleString("en-US"),`in ${t.days??0} days`)]:null]}):null,e(P,{ratio:"lead",children:[e(b,{children:e(V,{metric:c,ranges:n,setRange:s,height:220})}),e(Q,{label:"Stars and traffic",items:d.map(l=>J(l,n[l.period]||12)),selected:c.id,onSelect:u,tones:Z(d,n)})]}),o.length?e(w,{title:"Where visitors came from",note:t?.referrers_day?`GitHub's top sites on ${A(t.referrers_day)}.`:"GitHub's top sites.",children:e(p,{name:"referrers",caption:"Referrers",search:!1,rows:o,columns:[{key:"referrer",label:"Site",primary:!0,nowrap:!0},{key:"count",label:"Views",num:!0}]})}):null]})}function me({app:a}){let n=z({c:"community.json"});if(!n.ready)return e(j,{});let s=n.c,r="Community",u="Outside contributors and newcomers: who is waiting, how fast they hear back, and whether they come back.";return!s||!a?e(E,{title:r,subtitle:u,children:[e(f,{icon:"community",title:"Community hasn't run here yet.",action:e($,{variant:"filled",href:"#/settings",icon:"settings",children:"Turn it on"}),children:["Add ",e("code",{class:"code-inline",children:"community"})," to the workflow's modules."]}),a?e(k,{title:"From your machine",description:"The same report for any public repo, from a terminal with a read-only token.",children:e(W,{app:a})}):null]}):e(ce,{app:a,c:s,title:r,sub:u})}function ce({app:a,c:n,title:s,sub:r}){let u=n.quarters||[],t=u[u.length-1]||{},d=u[u.length-2]||{},o=n.waiting||[],c=i=>`https://github.com/${a.repo}/${i.kind==="issue"?"issues":"pull"}/${i.n}`,l=n.funnel;return e(E,{title:s,subtitle:r,toolbar:a.modules?.community?.page?e($,{variant:"plain",href:a.modules.community.page,iconEnd:"external",children:"Printable page"}):null,children:[e(k,{title:"Waiting for an answer",description:"First-time contributors whose issue or pull request has no human answer yet. Answer in one click on GitHub.",action:e(y,{tone:n.waiting_total?"watch":"good",children:n.waiting_total?`${n.waiting_total} waiting`:"All answered"}),children:e(w,{title:"Newcomers waiting",note:"Longest wait first.",children:e(p,{name:"newcomers-waiting",caption:"Newcomers waiting",rows:o,initialSort:{key:"days",desc:!0},columns:[{key:"n",label:"Item",primary:!0,raw:i=>i.n,render:i=>e("a",{href:c(i),target:"_blank",rel:"noopener noreferrer",children:[i.kind==="issue"?"Issue":"Pull request"," #",i.n]})},{key:"kind",label:"Kind",filter:!0,raw:i=>i.kind==="issue"?"issue":"pull request"},{key:"created",label:"Opened",render:i=>A(i.created)},{key:"days",label:"Waiting",num:!0,render:i=>C(i.days,"day")},{key:"go",label:"Answer",raw:()=>"",render:i=>e($,{size:"sm",variant:"tinted",href:c(i),external:!0,iconEnd:"external",children:"Reply"})}],empty:e(b,{children:e(f,{icon:"check",title:"No newcomer is waiting.",children:"First-time contributors whose issue or pull request has no answer yet show up here."})})})})}),e(k,{title:"How fast people hear back",description:"The first reply to an outside issue or pull request, by channel, and this week against the four before.",children:[e(P,{ratio:"lead",children:[e(b,{children:e(re,{c:n})}),e(_,{class:"is-figures cm-side",rows:[["Score",h(n.score?`${n.score.score}/100`:"\u2013",n.score?"against similar public repos":"after launch, against public repos")],["Answered within a day",h(m(n.facts?.answered_1d_share),v(n.facts?.answered_1d_share)?"not enough outside items to say":"of outside contributors heard back")],["Project's answer, outside issues",h(L(n.answers?.issues?.km),"median, waiting items counted")]]})]}),e(se,{c:n}),e(ae,{changes:n.changes})]}),e(k,{title:"What the project states",description:"What the repo promises and says about itself, beside what it does.",children:n.context?e(oe,{c:n}):e(b,{children:e(f,{icon:"about",title:"Not read yet.",children:"The next run reads the contributing guide, governance and funding files."})})}),u.length?e(k,{title:"Quarter by quarter",description:`${t.quarter||"This quarter"} against ${d.quarter||"the one before"}.`,children:[e(_,{class:"is-figures cm-figs",rows:[["Items from outside",h(t.items??"\u2013",M(t.items,d.items,String))],["First answer, median",h(g(t.median_hours),M(t.median_hours,d.median_hours,g))],["Answered within a day",h(m(t.within_1d),M(t.within_1d,d.within_1d,i=>`${Math.round(i*100)} points`))],["Newcomers",h(t.newcomers??"\u2013",M(t.newcomers,d.newcomers,String))]]}),e(w,{title:"Every quarter",note:"Oldest first.",children:e(p,{name:"quarters",caption:"Quarters",search:!1,rows:u,columns:[{key:"quarter",label:"Quarter",primary:!0},{key:"items",label:"Items",num:!0,render:i=>i.items??"\u2013"},{key:"median_hours",label:"First answer",num:!0,render:i=>g(i.median_hours)},{key:"within_1d",label:"Within a day",num:!0,render:i=>m(i.within_1d)},{key:"newcomers",label:"Newcomers",num:!0,render:i=>i.newcomers??"\u2013"}]})})]}):null,e(k,{title:"Who carries the work",description:"How spread the merged work is, and whether newcomers wait longer than regulars.",children:e(ie,{c:n})}),e(k,{title:"Do newcomers stay",description:"Newcomers by the month they first contributed, and who came back with new work. A month too recent says \u2013.",children:[e(_,{class:"is-figures cm-figs",rows:[["Newcomers who came back",h(m(n.facts?.newcomer_return_rate??l?.return_rate),l?`${l.returned} of ${l.judged} judged, within 90 days`:null)],["Newcomers answered within 2 weeks",h(m(n.facts?.newcomer_answered_share),"first pull requests that got a reply")],["Maintainers",h(l?.maintainers??"\u2013",l?`${l.regulars??0} regulars`:null)]]}),e(w,{title:"Newcomer cohorts",note:"By the month of their first contribution.",children:e(p,{name:"cohorts",caption:"Newcomer cohorts",search:!1,rows:n.cohorts?.months||[],columns:[{key:"month",label:"First contributed",primary:!0},{key:"newcomers",label:"Newcomers",num:!0},{key:"back_by_90d",label:"Back within 90 days",num:!0,render:i=>i.back_by_90d===null?"\u2013":`${i.back_by_90d} (${m(i.back_by_90d/i.newcomers)})`},{key:"back_90_180d",label:"Back 90 to 180 days on",num:!0,render:i=>i.back_90_180d===null?"\u2013":`${i.back_90_180d} (${m(i.back_90_180d/i.newcomers)})`}],empty:e(b,{children:e(f,{icon:"community",title:"No newcomers in the last year.",children:"Each month's first-time contributors show here."})})})})]}),e(k,{title:"Against similar public repos",description:"Where the score comes from, and what the public record says about answering newcomers.",children:e(ue,{c:n})}),e(k,{title:"Stars and traffic",description:"Pick a row to chart it. Drag across the chart to read a day.",children:e(de,{c:n})}),e(k,{title:"The report for another repo",description:"This page is the report for this repo, rebuilt by every run. For any other public repo, it runs from your machine.",children:e(W,{app:a})})]})}export{me as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.conn-sum{display:flex;gap:8px;flex-wrap:wrap;margin:-4px 2px 4px}.conn-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px}@media(max-width:420px){.conn-grid{grid-template-columns:minmax(0,1fr)}}.conn{display:grid;grid-template-columns:auto minmax(0,1fr);grid-template-rows:auto auto;gap:4px 12px;align-items:start;text-align:left;padding:14px 16px;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-1);color:inherit;font:inherit;cursor:pointer;transition:transform var(--dur-1) var(--spring),box-shadow var(--dur-1) var(--ease-out),border-color var(--dur-1);min-width:0;animation:pageIn var(--dur-2) var(--spring) backwards}.conn:hover{border-color:var(--line-strong);box-shadow:var(--shadow-pop)}.conn .glyph{grid-row:span 2}.conn-text{display:flex;flex-direction:column;gap:3px;min-width:0}.conn-text b{font-size:15px;font-weight:700;overflow-wrap:anywhere}.conn-text span{font-size:13.5px;color:var(--ink-2);line-height:1.4}.conn>.pill{grid-column:2;justify-self:start;margin-top:4px}.glyph{--hue: #2160C4;width:42px;height:42px;border-radius:12px;display:grid;place-items:center;flex:none;font-weight:800;font-size:15px;letter-spacing:-.02em;color:var(--hue);background:color-mix(in srgb,var(--hue) 14%,var(--surface))}.glyph-lg{width:56px;height:56px;border-radius:16px;font-size:19px}.glyph[data-cat=work]{--hue: #3B6FD8}.glyph[data-cat=incidents]{--hue: #C2410C}.glyph[data-cat=community]{--hue: #7C3AED}.glyph[data-cat=alerts]{--hue: #B45309}.glyph[data-cat=ci]{--hue: #0F766E}.glyph[data-cat=ai]{--hue: #BE185D}.glyph[data-cat=data]{--hue: #4B5563}.glyph[data-cat=code]{--hue: #4D7C0F}.glyph[data-cat=security]{--hue: #B91C1C}.glyph[data-cat=github]{--hue: var(--ink)}@media(prefers-color-scheme:dark){:root:not([data-theme=light]) .glyph{color:color-mix(in srgb,var(--hue) 55%,#fff);background:color-mix(in srgb,var(--hue) 26%,var(--surface))}}:root[data-theme=dark] .glyph{color:color-mix(in srgb,var(--hue) 55%,#fff);background:color-mix(in srgb,var(--hue) 26%,var(--surface))}.glyph.glyph-logo{display:flex;flex-wrap:wrap;justify-content:center;align-content:center;gap:3px;color:var(--ink);background:var(--surface-2);box-shadow:inset 0 0 0 1px var(--line)}.glyph-logo .blogo{width:24px;height:24px;fill:var(--b);flex:none}.glyph-logo .ic{width:24px;height:24px}.glyph-n2 .blogo{width:16px;height:16px}.glyph-n3 .blogo{width:13px;height:13px}.glyph-lg .blogo,.glyph-lg .ic{width:32px;height:32px}.glyph-lg.glyph-n2 .blogo{width:21px;height:21px}.glyph-lg.glyph-n3 .blogo{width:18px;height:18px}:root[data-theme=dark] .glyph.glyph-logo{background:var(--surface-2);color:var(--ink)}:root[data-theme=dark] .glyph-logo .blogo{fill:var(--bd)}@media(prefers-color-scheme:dark){:root:not([data-theme=light]) .glyph.glyph-logo{background:var(--surface-2);color:var(--ink)}:root:not([data-theme=light]) .glyph-logo .blogo{fill:var(--bd)}}.conn-top{display:flex;gap:14px;align-items:flex-start;margin-bottom:6px}.conn-top>div{display:flex;flex-wrap:wrap;gap:6px 10px;align-items:center;min-width:0}.tested{display:inline-flex;gap:5px;align-items:center;font-size:13px;color:var(--ink-2)}.tested .ic{width:15px;height:15px}.conn-detail{flex-basis:100%;margin:0;font-size:14px;color:var(--ink)}.sheet-sec{margin-top:20px}.sheet-sec>h3{font-size:15px;font-weight:650;color:var(--ink);margin:0 0 10px}.ticks{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px;font-size:14.5px}.ticks li{display:flex;gap:9px;align-items:flex-start}.ticks .ic{width:17px;height:17px;color:var(--good);margin-top:2px;flex:none}.steps{list-style:none;counter-reset:st;margin:0;padding:0;display:flex;flex-direction:column;gap:14px}.steps>li{counter-increment:st;display:grid;grid-template-columns:28px minmax(0,1fr);gap:10px}.steps>li:before{content:counter(st);width:26px;height:26px;border-radius:50%;display:grid;place-items:center;font-weight:750;font-size:13px;background:var(--accent-soft);color:var(--accent)}.steps>li>div{display:flex;flex-direction:column;gap:8px;align-items:flex-start;min-width:0;font-size:14.5px}.steps>li>div>*{max-width:100%}.steps p{margin:3px 0 0}.steps .code{align-self:stretch}.secrets{list-style:none;margin:0 0 10px;padding:4px 0}.secret{padding:12px 16px;display:flex;flex-direction:column;gap:10px}.secret+.secret{border-top:1px solid var(--line)}.secret-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.secret-name{flex:1 1 180px;min-width:0;display:flex;flex-direction:column;gap:1px}.secret-name code{font-family:var(--mono);font-size:13.5px;font-weight:650;overflow-wrap:anywhere}.secret-name small{font-size:12.5px;color:var(--ink-2)}.secret-form{display:flex;gap:8px;flex-wrap:wrap;align-items:flex-start}.secret-form .input{flex:1 1 220px;min-width:0}.secret-form textarea.input{resize:vertical;min-height:64px;font-size:13px}.secret-actions{display:flex;gap:8px}.secret .note,.secret .token-box{margin:0}.field.field-row{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:12px}.field-row .field-label{display:flex;flex-direction:column;gap:2px}.field-row small{font-weight:500;color:var(--ink-2);font-size:12.5px}.choice-row{display:flex;gap:8px;flex-wrap:wrap}.choice{display:inline-flex;gap:6px;align-items:center;padding:6px 12px;border-radius:999px;background:var(--field);font-size:14px;cursor:pointer}.choice input{accent-color:var(--accent)}.conn-send{margin-top:14px;display:flex;flex-direction:column;gap:10px;align-items:flex-start}.conn-send .send{align-self:stretch}.test-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.test-row .spin{display:inline-block;vertical-align:-2px;margin-right:6px}.muted{color:var(--ink-2);font-size:13.5px;margin:0 0 10px}.note .when{display:block;color:var(--ink-2);font-size:12.5px;margin-top:4px}.conn-privacy{margin-top:30px;max-width:760px}[data-design=sheet][data-page=connections] .conn-sum{gap:8px 20px;margin:0 0 6px}[data-design=sheet][data-page=connections] .conn-grid{gap:12px;grid-template-columns:repeat(auto-fill,minmax(280px,1fr))}[data-design=sheet][data-page=connections] .conn{--cat: var(--ink);background:var(--surface);border:1.5px solid var(--frame);padding:16px 18px;gap:6px 14px;position:relative;animation:svFade var(--dur-3) var(--sv-ease) backwards}[data-design=sheet][data-page=connections] .conn:hover{background:color-mix(in srgb,var(--ink) 4%,var(--surface));border-color:var(--ink)}[data-design=sheet][data-page=connections] .conn:focus-visible{outline-offset:2px}[data-design=sheet][data-page=connections] .conn-text b{font-weight:700;font-size:16.5px}[data-design=sheet][data-page=connections] .conn-text span{font-size:15px;line-height:1.45}[data-design=sheet][data-page=connections] .conn>.pill{margin-top:8px}[data-design=sheet] :is([data-cat=work],[data-cat=ci]){--cat: var(--sv-blue)}[data-design=sheet] :is([data-cat=community],[data-cat=code]){--cat: var(--sv-mint)}[data-design=sheet] :is([data-cat=incidents],[data-cat=security]){--cat: var(--sv-coral)}[data-design=sheet] :is([data-cat=alerts],[data-cat=ai]){--cat: var(--sv-yellow)}[data-design=sheet] :is([data-cat=data],[data-cat=github]){--cat: var(--ink)}[data-design=sheet] .glyph,[data-design=sheet][data-theme=dark] .glyph{width:40px;height:40px;background:var(--surface-2);color:var(--ink);border:1.5px solid var(--ink);border-bottom:5px solid var(--cat, var(--ink));font:700 15px/1 var(--font);letter-spacing:0}@media(prefers-color-scheme:dark){:root[data-design=sheet]:not([data-theme=light]) .glyph{background:var(--surface-2);color:var(--ink)}}[data-design=sheet] .glyph-lg{width:54px;height:54px;font-size:19px;border-bottom-width:6px}[data-design=sheet] .glyph.glyph-logo{box-shadow:none}[data-design=sheet] .glyph-logo .blogo{width:22px;height:22px}[data-design=sheet] .glyph-n2 .blogo{width:15px;height:15px}[data-design=sheet] .glyph-n3 .blogo{width:12px;height:12px}[data-design=sheet] .glyph-lg .blogo{width:30px;height:30px}[data-design=sheet] .glyph-lg.glyph-n2 .blogo{width:20px;height:20px}[data-design=sheet] .glyph-lg.glyph-n3 .blogo{width:17px;height:17px}[data-design=sheet][data-page=connections] .tested{font:600 14px var(--font);color:var(--ink-2)}[data-design=sheet][data-page=connections] .conn-detail{font-size:15px}[data-design=sheet][data-page=connections] .sheet-sec{margin-top:24px}[data-design=sheet][data-page=connections] .sheet-sec>h3{display:block;margin:0 0 4px;padding:8px 12px;background:var(--strip);border-top:1.5px solid var(--frame);border-bottom:1px solid var(--line-strong);font-weight:700;font-size:16px;line-height:1.35}[data-design=sheet][data-page=connections] .ticks{gap:0;font-size:15.5px}[data-design=sheet][data-page=connections] .ticks li{padding:8px 12px 8px 4px}[data-design=sheet][data-page=connections] .ticks li+li{border-top:1px solid var(--line)}[data-design=sheet][data-page=connections] .ticks .ic{color:var(--good);width:16px;height:16px;margin-top:4px;stroke-width:2.2}[data-design=sheet][data-page=connections] .steps{gap:0}[data-design=sheet][data-page=connections] .steps>li{grid-template-columns:38px minmax(0,1fr);gap:0;padding:14px 0}[data-design=sheet][data-page=connections] .steps>li+li{border-top:1px solid var(--line)}[data-design=sheet][data-page=connections] .steps>li:before{width:28px;height:28px;border:1.5px solid var(--sv-blue);border-radius:50%!important;background:var(--accent-soft);color:var(--accent);font:700 14.5px/1 var(--font);font-variant-numeric:tabular-nums}[data-design=sheet][data-page=connections] .steps>li>div{font-size:15.5px}[data-design=sheet][data-page=connections] .secrets{padding:0}[data-design=sheet][data-page=connections] .secret-name code{border:0;padding:0;background:none;font-weight:400;font-size:14.5px}[data-design=sheet][data-page=connections] .secret-name small{font-size:14px}[data-design=sheet][data-page=connections] .secret-form textarea.input{font-family:var(--mono)}[data-design=sheet][data-page=connections] .choice{padding:5px 12px;background:var(--surface);border:1.5px solid var(--line-strong);font-size:15px}[data-design=sheet][data-page=connections] .choice:has(input:checked){background:var(--accent-soft);border-color:var(--sv-blue);font-weight:600}[data-design=sheet][data-page=connections] .choice input{accent-color:var(--sv-blue)}[data-design=sheet][data-page=connections] .field-row small{font-weight:400;font-size:14px}[data-design=sheet][data-page=connections] .note .when{font-size:14px}[data-design=sheet][data-page=connections] .muted{font-size:15px}[data-design=sheet][data-page=connections] .conn-privacy{margin-top:40px}@media(max-width:420px){[data-design=sheet][data-page=connections] .conn-grid{grid-template-columns:minmax(0,1fr)}}@media(prefers-reduced-motion:reduce){[data-design=sheet][data-page=connections] .conn{animation:none}}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{a as U,b as j}from"../chunks/chunk-P5TXDD45.js";import{d as xe,e as we,f as $,h as R,j as N,k as ye}from"../chunks/chunk-S654BBEP.js";import{f as ke}from"../chunks/chunk-CVB26NMC.js";import"../chunks/chunk-S2ZBMEVA.js";import{c as fe,d as me}from"../chunks/chunk-ASYNLVOP.js";import{f as oe}from"../chunks/chunk-T7QVTNXF.js";import{a as V,g as be}from"../chunks/chunk-RPXHAG6F.js";import{b as he,d as F,e as ue}from"../chunks/chunk-45PGAQRO.js";import{d as ie}from"../chunks/chunk-4WKQTWUC.js";import{a as J,c as _,d as K,g as Z,n as se,q as T,s as G}from"../chunks/chunk-VQ5YXO54.js";import{b as L,c as re}from"../chunks/chunk-76WAVKUG.js";import{b as le,d as ce,e as O,f as de,h as pe,i as ge}from"../chunks/chunk-VWKSMFWJ.js";import{G as X,H,I as y,M as Y,N as ee,O as te,P as ne,R as ae,S as B,a as E,c as p,d as D,q as Q,w as e,x as w,z as f}from"../chunks/chunk-GT4EAKAO.js";import{d as A}from"../chunks/chunk-G7NGB5UX.js";A();A();var k=()=>se()||F()||null,Se=()=>V(k()),Ne=/^(?!github_)[a-z_][a-z0-9_]*$/i;function Ve(t){let n="";for(let a of t)n+=String.fromCharCode(a);return btoa(n)}function Ue(t){return Uint8Array.from(atob(t.replace(/\s/g,"")),n=>n.charCodeAt(0))}async function je(t,n){let{seal:a}=await import("../chunks/sealbox-35YL4EUH.js");return Ve(a(new TextEncoder().encode(n),Ue(t)))}function Ce(t,n="secret"){return`https://github.com/${t}/settings/${n==="variable"?"variables":"secrets"}/actions/new`}function Te(t){return`https://github.com/settings/personal-access-tokens/new?${new URLSearchParams({name:"StackVitals connections",description:`Lets the StackVitals app save keys and test connections in ${t}`,target_name:t.split("/")[0],expires_in:"30",secrets:"write",actions:"write"})}`}async function $e(t){if(!k()||!J(t))return null;try{let n=await Se().call("GET",`/repos/${_(t)}/actions/secrets?per_page=100`);return n.ok?(n.data.secrets||[]).map(a=>a.name):null}catch{return null}}var ve=(t,n)=>t===401?"GitHub refused the token: it may be expired or mistyped.":t===403||t===404?`The token can't ${n} here; it needs Secrets: read and write on this repo.`:`GitHub answered ${t}.`,ze=async()=>V(await oe()||F()||null);async function Pe(t,n,a){let o=_(t);if(!Ne.test(n))throw new Error("Secret names use letters, digits and _, don't start with a digit and can't start with GITHUB_.");let s=await ze(),r=await s.call("GET",`/repos/${o}/actions/secrets/public-key`);if(!r.ok)throw new Error(ve(r.status,"read the secrets key"));let d=await je(r.data.key,a),i=await s.call("PUT",`/repos/${o}/actions/secrets/${encodeURIComponent(n)}`,{encrypted_value:d,key_id:r.data.key_id});if(!i.ok)throw new Error(ve(i.status,"save secrets"))}var Ae=new Map;async function Ee(t,n,a,o){let s=await t.call("GET",`/repos/${n}/actions/workflows/${a}/runs?event=workflow_dispatch&per_page=10`);return(s.ok?s.data?.workflow_runs||[]:[]).filter(d=>Date.parse(d.created_at)>=o).sort((d,i)=>d.id-i.id)[0]?.id}async function De(t,n){if(!t.links?.workflow)throw new Error("This page doesn't know the workflow's file name yet; test it with the GitHub issue instead.");let a=_(t.repo),o=K(t.links.workflow),s=await ze(),r=JSON.stringify({version:1,changes:[],settings:{},check:[n]}),d=await s.call("POST",`/repos/${a}/actions/workflows/${o}/dispatches`,{ref:t.default_branch||"main",inputs:{request:r}});if(!d.ok)throw new Error(d.status===422?"This repo's StackVitals workflow is older and takes no request; update it, or use the GitHub issue.":d.status===401?"GitHub refused the token.":"The token can't start this repo's workflow (it needs Actions: read and write).");let i=(Date.parse(d.headers.get("date")||"")||Date.now())-5e3,l={repo:a,source:G.get().source,since:i};return Ae.set(n,l),l.run=await Ee(s,a,o,i).catch(()=>{}),l.wf=o,{url:t.links?.actions||null,...l.run?{run:l.run}:{}}}function _e(t,n,a){let o=JSON.stringify({version:1,changes:[],settings:{},check:[n]}),s=`Sent from the StackVitals app: test the ${a} connection. The StackVitals workflow runs the check when someone with access opens this, writes the result to the app and answers here.
|
|
2
|
+
|
|
3
|
+
\`\`\`stackvitals
|
|
4
|
+
${o}
|
|
5
|
+
\`\`\`
|
|
6
|
+
`;return fe(t.repo,`StackVitals: test ${a}`,s)}async function qe(t=G.get().source){let n=await Z(t,"connectors.json");try{return n?JSON.parse(n).connectors||{}:null}catch{return null}}var Me="The run was replaced by another StackVitals run; test again.";async function Be(t,n,a,o=8e3,s=75){let r=Ae.get(t),d=r?.source||G.get().source,i=-1;for(let l=0;l<s&&a();l++){await be(o);let c=(await qe(d))?.[t];if(c?.check?.checked_at&&Date.parse(c.check.checked_at)>Date.parse(n))return c;if(!r||!k())continue;let g=Se();if(!r.run&&r.wf&&(r.run=await Ee(g,r.repo,r.wf,r.since).catch(()=>{})),!r.run)continue;let h=await g.call("GET",`/repos/${r.repo}/actions/runs/${encodeURIComponent(r.run)}`).catch(()=>null);if(h?.ok&&h.data.status==="completed"){if(h.data.conclusion==="cancelled"||h.data.conclusion==="skipped")throw new Error(Me);if(i<0)i=l;else if(l-i>=2)return null}}return null}async function Ge(t){try{let n=await fetch("https://api.linear.app/graphql",{method:"POST",headers:{Authorization:t,"Content-Type":"application/json"},body:JSON.stringify({query:"query { viewer { name } organization { name } teams(first: 100) { nodes { key } } }"}),credentials:"omit",cache:"no-store"}),a=await n.json().catch(()=>null);if(!n.ok||!a?.data)return{ok:!1,text:n.status===401||a?.errors?.length?"Linear refused the key: it is wrong or expired.":`Linear answered ${n.status}.`};let o=(a.data.teams?.nodes||[]).map(s=>s.key);return{ok:!0,text:`Signed in as ${a.data.viewer?.name||"the key's user"} (${a.data.organization?.name||"Linear"}), sees ${o.length} ${o.length===1?"team":"teams"}${o.length?": "+o.slice(0,4).join(", "):""}.`}}catch{return{ok:!1,text:"Linear couldn't be reached from this page."}}}var Oe={connected:["good","check"],builtin:["accent","link"],key:["watch","lock"],failed:["bad","warn"],recorded:["watch","clock"],off:["neutral","dot"]},Re=({s:t})=>e(y,{tone:Oe[t][0],icon:Oe[t][1],children:U[t]}),We='<path d="M20 12a7.5 7.5 0 0 1-11 6.6L4 20l1.4-4.6A7.5 7.5 0 1 1 20 12z"/><path d="M8.5 12h.01M12 12h.01M15.5 12h.01"/>';function Ie({c:t,big:n,data:a}){let o=a.LOGOS[t.id],s="glyph"+(n?" glyph-lg":"")+(o?" glyph-logo":"")+(Array.isArray(o)&&o.length>1?` glyph-n${o.length}`:"");return e("span",{class:s,"data-cat":t.cat,"aria-hidden":"true",children:o?o==="chat"?e("svg",{class:"ic",viewBox:"0 0 24 24","aria-hidden":"true",focusable:"false",dangerouslySetInnerHTML:{__html:We}}):o.map(r=>{let[,d,i,l]=a.BRANDS[r];return e("svg",{class:"blogo",viewBox:"0 0 24 24",style:`--b:${d};--bd:${i||"var(--ink)"}`,children:e("path",{d:l})})}):t.glyph})}function He({repo:t,onDone:n}){let[a,o]=p("");return e("div",{class:"card card-pad token-box",children:[e("p",{children:["Paste a fine-grained token for ",e("b",{children:t})," with ",e("b",{children:"Secrets"})," and ",e("b",{children:"Actions: read and write"}),". ",e("a",{href:Te(t),target:"_blank",rel:"noopener noreferrer",children:"Create one"})," (30 days). It stays in this tab and goes only to api.github.com."]}),e("div",{class:"inline-form",children:[e(O,{type:"password",mono:!0,value:a,onInput:s=>o(s.trim()),placeholder:"github_pat_\u2026",label:"Fine-grained GitHub token"}),e(f,{variant:"filled",disabled:!a,onClick:()=>{ue(a,!0),o(""),n()},children:"Use it"})]})]})}function Je({d:t,x:n}){let a=n.app.repo,[o,s]=p(""),[r,d]=p(!1),[i,l]=p(!1),[c,g]=p(null),h=n.secrets&&(!t.kind||t.kind==="secret")?n.secrets.includes(t.name):null,m=t.kind==="variable"?"Repository variable":t.kind==="gitlab"?"GitLab CI/CD variable, masked":"Repository secret",b=()=>{Y(t.name).then(x=>B(x?`${t.name} copied: paste it as the name`:"Opened GitHub"))},v=async()=>{if(!k()){l(!0);return}d(!0),g(null);try{await Pe(a,t.name,o),s(""),g({ok:!0,text:`Saved ${t.name}. GitHub keeps it encrypted; nobody can read it back.`}),B(`${t.name} saved`),n.onToken()}catch(x){g({ok:!1,text:x.message})}d(!1)},z=!t.kind&&!T;return e("li",{class:"secret",children:[e("div",{class:"secret-head",children:[e("span",{class:"secret-name",children:[e("code",{children:t.name}),e("small",{children:[m,t.optional?", optional":""]})]}),h===!0?e(y,{tone:"good",children:"Saved"}):h===!1?e(y,{tone:t.optional?"neutral":"watch",icon:"lock",children:"Not saved"}):null,t.kind==="gitlab"?null:e(f,{size:"sm",variant:z&&k()?"plain":"tinted",icon:"external",external:!0,href:Ce(a,t.kind==="variable"?"variable":"secret"),onClick:b,children:"Add on GitHub"})]}),z?e("div",{class:"secret-form",children:[t.multiline?e("textarea",{class:"input mono",rows:3,value:o,spellcheck:!1,"aria-label":`Value for ${t.name}`,placeholder:"Paste the value",onInput:x=>s(x.currentTarget.value)}):e(O,{type:"password",mono:!0,value:o,onInput:s,placeholder:"Paste the value",label:`Value for ${t.name}`}),e("div",{class:"secret-actions",children:[t.browser==="linear"?e(f,{size:"sm",variant:"plain",disabled:!o,onClick:async()=>g(await Ge(o.trim())),children:"Test key here"}):null,e(f,{size:"sm",variant:"filled",icon:"lock",loading:r,disabled:!o.trim(),onClick:v,children:"Save secret"})]})]}):null,i&&!k()?e(He,{repo:a,onDone:()=>{l(!1),n.onToken(),v()}}):null,c?e("p",{class:`note note-${c.ok?"good":"bad"}`,role:"status",children:[e(w,{name:c.ok?"check":"warn"}),e("span",{children:c.text})]}):null]})}function Ke({d:t,value:n,onChange:a}){let o=N(t.key,t.kind,n);if(t.kind==="bool")return e("div",{class:"field field-row",children:[e("span",{class:"field-label",id:`l-${t.key}`,children:[$(t.key),e("small",{children:R(t.help)})]}),e(le,{checked:!!n,onChange:a,labelledBy:`l-${t.key}`})]});if(t.kind==="choices"){let s=Array.isArray(n)?n:[];return e("div",{class:"field",children:[e("span",{class:"field-label",children:$(t.key)}),e("div",{class:"choice-row",children:(t.choices||[]).map(r=>e("label",{class:"choice",children:[e("input",{type:"checkbox",checked:s.includes(r),onChange:d=>a(d.currentTarget.checked?[...s,r]:s.filter(i=>i!==r))}),r]}))}),e("p",{class:"field-hint",children:R(t.help)})]})}return e(ce,{label:$(t.key),hint:R(t.help),error:o||null,children:s=>t.kind==="list"?e(ge,{id:s,values:Array.isArray(n)?n:[],onChange:a,label:$(t.key)}):t.kind==="date"?e(de,{id:s,value:String(n||""),onInput:a}):t.kind==="choice"?e(pe,{id:s,value:String(n??""),onChange:a,options:(t.choices||[]).map(r=>({value:r,label:r}))}):e(O,{id:s,value:String(n??""),onInput:a})})}function Ze({c:t,x:n}){let a=(n.settings?.settings||[]).filter(c=>c.key.startsWith(t.prefix+".")),[o,s]=p({});if(!a.length)return null;let r=c=>xe(n.settings?.values,c),d={};for(let c of a)c.key in o&&!we(o[c.key],r(c.key))&&(d[c.key]=o[c.key]);let i=Object.entries(d).some(([c,g])=>N(c,a.find(h=>h.key===c).kind,g)),l=Object.fromEntries(a.map(c=>[c.key,c.kind]));return e("section",{class:"sheet-sec",children:[e("h3",{children:"Settings"}),e("div",{class:"form",children:a.map(c=>e(Ke,{d:c,value:c.key in o?o[c.key]:r(c.key),onChange:g=>s({...o,[c.key]:g})}))}),Object.keys(d).length&&!i?e("div",{class:"conn-send",children:[e(ke,{app:n.app,title:`StackVitals: ${t.name} settings`,req:he([],d),who:"anyone with write access to this repo",lands:".github/stackvitals.toml on the default branch (a pull request when it is protected)",when:"as soon as the workflow has run"}),e(ee,{text:ye(d,l),label:"Copy as TOML"})]}):null]})}function Qe({c:t,e:n,x:a}){let[o,s]=p("idle"),[r,d]=p(""),[i]=p({on:!0});D(()=>()=>{i.on=!1},[]);let l=n?.check,c=_e(a.app,t.check,t.name),g=async()=>{s("wait");let m;try{m=await Be(t.check,l?.checked_at||"1970-01-01T00:00:00Z",()=>i.on)}catch(b){i.on&&(d(b.message),s("error"));return}i.on&&(m?(a.onChecked(t.check,m),s("idle"),B(m.check?.ok?`${t.name} works`:`${t.name}: check failed`,m.check?.ok?"good":"bad")):s("late"))},h=async()=>{try{await De(a.app,t.check),g()}catch(m){d(m.message),s("error")}};return e("section",{class:"sheet-sec",children:[e("h3",{children:"Test connection"}),l?e("div",{class:`note note-${l.ok?"good":"bad"}`,role:"status",children:[e(w,{name:l.ok?"check":"warn"}),e("span",{children:[e("b",{children:l.ok?`Working${l.as?`, as ${l.as}`:""}.`:"Not working."})," ",l.detail,!l.ok&&l.fix?e(E,{children:[e("br",{}),e("b",{children:"Fix:"})," ",l.fix]}):null,e("small",{class:"when",children:["Tested ",Q(l.checked_at)]})]})]}):e("p",{class:"muted",children:["Not tested yet. One cheap call to ",t.name," proves the key and settings work."]}),T?e("p",{class:"muted",children:"Demo data: there is nothing to test."}):e("div",{class:"test-row",children:[k()?e(f,{variant:"tinted",icon:"play",loading:o==="wait",onClick:h,children:"Test connection"}):e(f,{variant:"tinted",icon:"external",external:!0,href:c.url,onClick:()=>{me(c,"Press Submit on GitHub; the result shows here")(),g()},children:"Test on GitHub"}),o==="wait"?e("span",{class:"muted",role:"status",children:[e("span",{class:"spin","aria-hidden":"true"})," A short run tests it; this takes a minute or two."]}):null]}),o==="late"?e("p",{class:"note note-watch",children:[e(w,{name:"clock"}),e("span",{children:["No result yet. The run may still be going: ",e("a",{href:a.app.links?.actions,target:"_blank",rel:"noopener noreferrer",children:"open Actions"}),", then reopen this page."]})]}):null,o==="error"?e("p",{class:"note note-bad",role:"alert",children:[e(w,{name:"warn"}),e("span",{children:r})]}):null]})}function Xe({c:t,open:n,x:a,data:o,onClose:s}){if(!t)return null;let r=o.entryOf(t,a.entries),d=o.statusFor(t,a);return e(X,{open:n,onClose:s,title:t.name,size:"md",footer:e(E,{children:[e(f,{variant:"plain",href:H+t.doc,external:!0,iconEnd:"external",children:"Learn more"}),e(f,{variant:"filled",onClick:s,children:"Done"})]}),children:[e("div",{class:"conn-top",children:[e(Ie,{c:t,big:!0,data:o}),e("div",{children:[e(Re,{s:d}),t.tested?e("span",{class:"tested",children:[e(w,{name:t.tested==="Recorded"?"about":"check"}),t.tested==="Recorded"?"Tested on recorded answers":`Tested ${t.tested.toLowerCase()}`]}):null,e("p",{class:"conn-detail",children:o.detail(t,d,r,a)})]})]}),e("section",{class:"sheet-sec",children:[e("h3",{children:"What it unlocks"}),e("ul",{class:"ticks",children:t.unlocks.map(i=>e("li",{children:[e(w,{name:"check"}),e("span",{children:i})]}))})]}),t.id==="github"?e("section",{class:"sheet-sec",children:[e("h3",{children:"What the workflow token reads"}),e("dl",{class:"kv",children:o.PERMS.map(([i,l])=>e(E,{children:[e("dt",{children:i}),e("dd",{children:l})]}))})]}):null,e("section",{class:"sheet-sec",children:[e("h3",{children:t.kind==="builtin"?"How it works":"How to connect"}),e("ol",{class:"steps",children:t.steps(a.app.repo).map(i=>e("li",{children:e("div",{children:[e("p",{children:i.text}),i.code?e(te,{code:i.code,title:i.file}):null,i.href?e(f,{size:"sm",variant:"tinted",href:i.href,external:!0,icon:"external",children:i.action||"Open"}):null,i.to?e(f,{size:"sm",variant:"tinted",href:i.to,icon:"right",onClick:s,children:i.action||"Open Settings"}):null]})}))})]}),t.secrets?.length?e("section",{class:"sheet-sec",children:[e("h3",{children:t.secrets.length===1?"Secret":"Secrets"}),e("ul",{class:"secrets card",children:t.secrets.map(i=>e(Je,{d:i,x:a}))}),e("p",{class:"muted",children:[k()?"Sealed in this browser with the repo's key and sent straight to GitHub; nobody can read it back, StackVitals included.":"Add them on GitHub, or save them here with a token: sealed in this browser, sent only to GitHub.",t.example?" The names are examples: any name works if the workflow passes it.":""]})]}):null,t.prefix?e(Ze,{c:t,x:a}):null,t.check?e(Qe,{c:t,e:r,x:a}):null]})}function Ye({c:t,s:n,data:a,onOpen:o}){return e("button",{type:"button",class:"conn press",onClick:o,"aria-haspopup":"dialog","aria-label":`${t.name}: ${U[n]}. ${t.unlocks[0]}`,children:[e(Ie,{c:t,data:a}),e("span",{class:"conn-text",children:[e("b",{children:t.name}),e("span",{children:t.unlocks[0]})]}),e(Re,{s:n})]})}function et({app:t,route:n}){let a=ie({c:"connectors.json",s:"settings.json"}),[o,s]=p(null),[r,d]=p(null),[i,l]=p(0),[c,g]=p({}),[h,m]=p(n.rest[0]||null),[b,v]=p(!!n.rest[0]),[z,x]=p(!1);if(D(()=>{import("../chunks/connections-data-2DYS4UVW.js").then(s,()=>s(null))},[]),D(()=>{t?.repo&&!T&&$e(t.repo).then(d)},[t?.repo,i]),!t)return e(L,{title:"Connections",children:e(ne,{icon:"connections",title:"No repository data yet.",children:"Connections show once the StackVitals workflow has run."})});if(!o||!a.ready)return e(ae,{});let q={app:t,entries:{...a.c?.connectors||{},...c},secrets:r,settings:a.s,onToken:()=>l(u=>u+1),onChecked:(u,S)=>g(I=>({...I,[u]:S}))},M=o.CONNS.map(u=>({c:u,s:o.statusFor(u,q)})),Le=[["on","Connected","Reading or sending now."],["attention","Needs attention","A key is missing, expiring or refused."],["available","Available","What else StackVitals can read or send to."]],P=u=>M.filter(S=>j[S.s]===u).length;return e(L,{title:"Connections",subtitle:"What StackVitals reads and sends, and what each one unlocks.",toolbar:!T&&!k()?e(f,{variant:"tinted",icon:"lock",onClick:()=>x(!0),children:"Use a token"}):null,children:[e("div",{class:"conn-sum","aria-label":"Summary",children:[e(y,{tone:"good",icon:"check",children:[P("on")," on"]}),P("attention")?e(y,{tone:"watch",icon:"warn",children:[P("attention")," need attention"]}):null,e(y,{tone:"neutral",icon:"plus",children:[P("available")," available"]})]}),z&&!k()?e(He,{repo:t.repo,onDone:()=>{x(!1),l(u=>u+1)}}):null,Le.map(([u,S,I])=>{let W=M.filter(C=>j[C.s]===u);return W.length?e(re,{title:S,description:I,children:e("div",{class:"conn-grid",children:W.map(C=>e(Ye,{c:C.c,s:C.s,data:o,onOpen:()=>{m(C.c.id),v(!0)}}))})}):null}),e("p",{class:"note conn-privacy",children:[e(w,{name:"lock"}),e("span",{children:["Keys live in this repo's GitHub secrets and go only to their own service. Nothing passes through a StackVitals server; there isn't one."," ",e("a",{href:H+"privacy.md",target:"_blank",rel:"noopener noreferrer",children:"What leaves your repo"})]})]}),e(Xe,{c:o.CONNS.find(u=>u.id===h)||null,open:b,x:q,data:o,onClose:()=>v(!1)},h||"")]})}export{et as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.stats+.card{margin-top:12px}.card>.sub-h:first-child{margin-top:0;margin-bottom:8px}.dl-tabs{display:flex;min-width:0;margin-bottom:12px}.dl-tabs .seg{max-width:100%}.dl-panel{display:flex;flex-direction:column;gap:12px;animation:pageIn var(--dur-2) var(--spring) backwards;min-width:0}.dl-sources{display:grid;gap:14px;min-width:0}.dl-sources .code pre{max-height:420px}.dl-deps{align-items:start}.dl-chk{margin-top:14px}.dl-after{margin-top:8px}.stats+.blk-tbl{margin-top:14px}[data-design=sheet][data-page=delivery] .sub-h .muted.small{font:400 14px/1.4 var(--font);color:var(--ink-2)}[data-design=sheet][data-page=delivery] .dl-dim{margin-bottom:14px}[data-design=sheet][data-page=delivery] .stats{gap:12px;background:none;border:0;overflow:visible}[data-design=sheet][data-page=delivery] .stat{display:grid;grid-row:span 3;grid-template-rows:subgrid;row-gap:0;align-content:start;margin:0;padding:0 0 14px;background:var(--surface);border:1.5px solid var(--frame)}[data-design=sheet][data-page=delivery] .stat-label{align-items:flex-start;justify-content:space-between;min-height:42px;padding:9px 14px;background:var(--strip);border-bottom:1px solid var(--line-strong);font:700 15px/1.35 var(--font);color:var(--ink)}[data-design=sheet][data-page=delivery] .stat-value{margin:0;padding:12px 14px 4px}[data-design=sheet][data-page=delivery] .stat-note{padding:0 14px;line-height:1.5}[data-design=sheet][data-page=delivery] .stats:has(.stat-note>:is(.pill-good,.pill-watch,.pill-bad):first-child) .stat{border-top:5px solid var(--line-strong)}[data-design=sheet][data-page=delivery] .stats .stat:has(.stat-note>.pill-good:first-child){border-top:5px solid var(--sv-mint)}[data-design=sheet][data-page=delivery] .stats .stat:has(.stat-note>.pill-watch:first-child){border-top:5px solid var(--sv-amber)}[data-design=sheet][data-page=delivery] .stats .stat:has(.stat-note>.pill-bad:first-child){border-top:5px solid var(--sv-coral)}[data-design=sheet][data-page=delivery] .stats:has(.dl-gate-good,.dl-gate-bad) .stat{border-top:5px solid var(--line-strong)}[data-design=sheet][data-page=delivery] .stats .stat:has(.dl-gate-good){border-top-color:var(--sv-mint)}[data-design=sheet][data-page=delivery] .stats .stat:has(.dl-gate-bad){border-top-color:var(--sv-coral)}[data-design=sheet][data-page=delivery] .stat-note .btn{margin-top:6px;max-width:100%;white-space:normal;text-align:left}[data-design=sheet][data-page=delivery] .card{border:1.5px solid var(--frame)}[data-design=sheet][data-page=delivery] .card>.table-wrap.card{border:0}[data-design=sheet][data-page=delivery] .card-pad>.sub-h:first-child{margin:-18px -18px 14px;padding:9px 18px;min-height:42px;align-items:baseline;background:var(--strip);border-bottom:1px solid var(--line-strong);font-size:16px}[data-design=sheet][data-page=delivery] .dl-strip{display:grid;gap:10px;margin:0 0 14px;padding:0 0 14px;background:var(--surface);border:1.5px solid var(--frame)}[data-design=sheet][data-page=delivery] .dl-strip figcaption{margin:0 0 4px;padding:9px 14px;background:var(--strip);border-bottom:1px solid var(--line-strong);font:700 15px/1.35 var(--font);color:var(--ink)}[data-design=sheet][data-page=delivery] .dl-strip-row{display:grid;grid-template-columns:7.5rem minmax(0,1fr) 5.5rem;align-items:center;gap:14px;padding:0 14px;font-size:15px}[data-design=sheet][data-page=delivery] .dl-strip-k{font-weight:600;color:var(--ink)}[data-design=sheet][data-page=delivery] .dl-strip-t{position:relative;height:20px;border-inline:1px solid var(--line-strong);background:var(--surface-2)}[data-design=sheet][data-page=delivery] .dl-strip-t i{position:absolute;top:0;bottom:0;left:var(--a);width:max(var(--w),3px);background:var(--sv-yellow);border:1px solid var(--ink);transform-origin:left}[data-design=sheet][data-page=delivery] .dl-strip-v{text-align:right;font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap}[data-design=sheet][data-page=delivery] .dl-strip-sum .dl-dim{grid-column:2;margin:4px 0 0}@media(prefers-reduced-motion:no-preference){[data-design=sheet][data-page=delivery] .dl-strip-t i{animation:dlGrow .6s var(--sv-ease) calc(var(--i) * .15s) backwards}}@keyframes dlGrow{0%{transform:scaleX(0)}}@media(max-width:640px){[data-design=sheet][data-page=delivery] .dl-strip-row{grid-template-columns:minmax(0,1fr) auto;gap:4px 12px}[data-design=sheet][data-page=delivery] .dl-strip-t{grid-column:1 / -1;grid-row:2}[data-design=sheet][data-page=delivery] .dl-strip-sum .dl-dim{grid-column:1 / -1;height:auto;white-space:normal}[data-design=sheet][data-page=delivery] .dl-strip-sum .dl-dim>span{text-align:center;line-height:1.4}}[data-design=sheet][data-page=delivery] :is(.dl-panel,.section) .bar-track{max-width:32px}[data-design=sheet][data-page=delivery] .bar-value{font:700 14px/1.2 var(--font);color:var(--ink);font-variant-numeric:tabular-nums}[data-design=sheet][data-page=delivery] .bar-label{font:600 14px/1.2 var(--font);color:var(--ink-2)}[data-design=sheet][data-page=delivery] .dl-panel>.muted.small{font-size:15px}[data-design=sheet][data-page=delivery] .range-pick{display:flex;flex-wrap:wrap;gap:10px 20px}[data-design=sheet][data-page=delivery] .range-pick label{display:inline-flex;align-items:center;gap:8px;font-weight:600}[data-design=sheet][data-page=delivery] .range-pick input{height:40px;padding:0 10px;border:1.5px solid var(--line-strong);background:var(--surface);color:var(--ink);font:400 15px var(--font);font-variant-numeric:tabular-nums;color-scheme:inherit}[data-design=sheet][data-page=delivery] .range-pick input:focus-visible{border-color:var(--focus);outline:2px solid var(--focus);outline-offset:2px}[data-design=sheet][data-page=delivery] .changed{list-style:none;margin:12px 0 0;padding:0;counter-reset:ch;display:grid;gap:10px;font-size:15px}[data-design=sheet][data-page=delivery] .changed>li{counter-increment:ch;position:relative;padding-left:36px}[data-design=sheet][data-page=delivery] .changed>li:before{content:counter(ch);position:absolute;left:0;top:0;width:24px;height:24px;display:grid;place-items:center;border:1.5px solid var(--ink);border-radius:50%!important;font:700 14px/1 var(--font);font-variant-numeric:tabular-nums}[data-design=sheet][data-page=delivery] .changed .pill{font-size:15px}[data-design=sheet][data-page=delivery] .dl-gate{display:inline-flex;align-items:center;gap:10px}[data-design=sheet][data-page=delivery] :is(.dl-gate-good,.dl-gate-bad):before{content:"";width:22px;height:22px;flex:none;background:center / contain no-repeat}[data-design=sheet][data-page=delivery] .dl-gate-good:before{background-image:var(--mark-good)}[data-design=sheet][data-page=delivery] .dl-gate-bad:before{background-image:var(--mark-bad)}[data-design=sheet][data-page=delivery] .dl-gate-bad{color:var(--bad)}@media(min-width:821px){[data-design=sheet][data-page=delivery] .stats:has(>.stat:nth-child(6):last-child){grid-template-columns:repeat(3,minmax(0,1fr))}}.blk-tbl{min-width:0}.blk-tbl+.blk-tbl,.stats+.blk-tbl,.blk-tbl+.stats,.blk-facts+.blk-tbl,.blk-tbl+.blk-facts,.blk-panel+.blk-tbl,.blk-tbl+.blk-panel,.dl-strip+.blk-tbl,.blk-tbl+.blk-grid,.blk-grid+.blk-tbl,.stats+.blk-panel,.blk-panel+.blk-panel,.stats+.blk-grid{margin-top:18px}.blk-tbl-h{display:flex;gap:4px 12px;align-items:baseline;flex-wrap:wrap;padding:10px 14px;background:var(--surface);border:1px solid var(--line);border-bottom:0;border-radius:var(--r-lg) var(--r-lg) 0 0}.blk-tbl-h h3{margin:0;font-size:15.5px;font-weight:700}.blk-tbl-note{font-size:13px;color:var(--ink-2)}.blk-tbl-act{margin-left:auto;display:inline-flex;gap:8px;align-items:center}.blk-tbl .table-wrap{border-top-left-radius:0;border-top-right-radius:0}.blk-tbl>.card:not(.table-wrap){border-top-left-radius:0;border-top-right-radius:0}@media(min-width:641px){.blk-tbl .table :is(th,td)+:is(th,td){border-left:1px solid var(--line)}}.blk-h{display:flex;gap:4px 12px;align-items:baseline;flex-wrap:wrap;margin:26px 0 10px;font-size:16px;font-weight:700}.blk-h:first-child{margin-top:0}.blk-h-note{font-size:13.5px;font-weight:400;color:var(--ink-2)}.blk-panel{display:flex;flex-direction:column;min-width:0;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden}.blk-panel-h{display:flex;gap:6px 12px;align-items:center;justify-content:space-between;flex-wrap:wrap;min-height:44px;padding:10px 16px;background:var(--surface-2);border-bottom:1px solid var(--line)}.blk-panel-h h3{margin:0;font-size:15.5px;font-weight:700;overflow-wrap:anywhere;min-width:0}.blk-panel-state{font-size:14px;font-weight:600}.blk-panel-b{flex:1;display:flex;flex-direction:column;gap:14px;padding:14px 16px 16px;min-width:0}.blk-panel-b>*{margin:0}.blk-bar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;padding:10px 16px;border-top:1px solid var(--line);background:var(--surface-2)}.blk-panel.blk-good{border-top:4px solid var(--good)}.blk-panel.blk-watch{border-top:4px solid var(--watch)}.blk-panel.blk-bad{border-top:4px solid var(--bad)}.blk-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,320px),1fr));gap:12px;align-items:stretch}.blk-grid>:is(.blk-tbl,.blk-panel){margin-top:0}.blk-facts{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:8px 18px;margin:0;font-size:14px}.blk-fact{display:contents}.blk-fact dt{font-weight:650;color:var(--ink-2)}.blk-fact dd{margin:0;min-width:0;overflow-wrap:anywhere}@media(max-width:520px){.blk-facts{grid-template-columns:minmax(0,1fr);gap:2px}.blk-fact dd{margin-bottom:8px}}.blk-mark{display:inline-flex;align-items:center;gap:7px;font-weight:650;white-space:nowrap}.blk-mark i{width:9px;height:9px;border-radius:50%;flex:none;background:var(--ink-3)}.blk-mark-good i{background:var(--good)}.blk-mark-watch i{background:var(--watch)}.blk-mark-bad i{background:var(--bad)}.blk-mark-accent i{background:var(--accent)}.blk-mark-neutral i{background:none;border:1.5px solid var(--ink-2)}.blk-mark-good{color:var(--good)}.blk-mark-bad{color:var(--bad)}.blk-mark-watch{color:var(--watch)}.blk-mark-neutral{color:var(--ink-2)}.blk-machine .blk-machine-cmd{display:flex;flex-direction:column;gap:10px;min-width:0}.blk-form{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr));gap:12px 16px;align-items:start}.blk-form .field{min-width:0}.blk-form textarea.input{min-height:96px;padding:10px 12px;resize:vertical;font:400 14px/1.5 var(--mono);width:100%}.blk-note{font-size:13.5px;color:var(--ink-2);margin:0;max-width:80ch}.blk-list{margin:0;padding-left:20px;display:grid;gap:4px;font-size:14px}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-tbl-h{min-height:42px;padding:9px 14px;background:var(--strip);border:1.5px solid var(--frame);border-bottom:1px solid var(--line-strong);border-radius:0}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-tbl-h h3{font-size:15.5px;font-weight:700;color:var(--ink)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-tbl-note{font-size:14px;color:var(--ink-2)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-tbl>:is(.table-wrap,.card){border:1.5px solid var(--frame);border-top:0}@media(min-width:641px){[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-tbl .table :is(th,td)+:is(th,td){border-left-color:var(--line)}}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-panel{border:1.5px solid var(--frame);border-radius:0}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-panel.blk-good{border-top:5px solid var(--sv-mint)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-panel.blk-watch{border-top:5px solid var(--sv-amber)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-panel.blk-bad{border-top:5px solid var(--sv-coral)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-panel-h{min-height:42px;padding:9px 14px;background:var(--strip);border-bottom:1px solid var(--line-strong)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-panel-h h3{font-size:15.5px;color:var(--ink)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-panel-state{font-size:15px}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-panel-b{padding:14px;font-size:15px}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-bar{padding:10px 14px;background:var(--surface);border-top:1px solid var(--line-strong)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-facts{font-size:15px;gap:8px 20px}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-fact dt{font-weight:600;color:var(--ink-2)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-mark{font-size:15px;font-weight:700;color:var(--ink)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-mark i{width:13px;height:13px;border:0;border-radius:0;background:center / contain no-repeat}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-mark-good i{background-image:var(--mark-good)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-mark-watch i{background-image:var(--mark-watch)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-mark-bad i{background-image:var(--mark-bad)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-mark-good{color:var(--good)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-mark-bad{color:var(--bad)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-mark-watch{color:var(--watch)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-mark-neutral{color:var(--ink-2)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-mark-neutral i{width:10px;height:10px;border:1.5px solid currentColor}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-mark-accent i{width:10px;height:10px;background:var(--sv-blue)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-note{font-size:15px}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-list{font-size:15px}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-form textarea.input{border-radius:0}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-h{padding-bottom:6px;border-bottom:1.5px solid var(--frame);font-size:17px;color:var(--ink)}[data-design=sheet]:is([data-page=prs],[data-page=delivery],[data-page=investment]) .blk-h-note{font-size:15px}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import{j as Ne}from"../chunks/chunk-576YHDEK.js";import{a as $,b as Be,c as G,d as K,e as D,f as ge}from"../chunks/chunk-4H53OYLG.js";import{a as M}from"../chunks/chunk-IYMBFPCV.js";import{c as Te,d as Ie}from"../chunks/chunk-763NH7PP.js";import{a as Ae,b as Oe,c as Pe,d as qe,e as Ee}from"../chunks/chunk-3G4SQNUC.js";import"../chunks/chunk-LAAGCPU3.js";import"../chunks/chunk-24LFKFZ6.js";import"../chunks/chunk-6BDBXA6V.js";import"../chunks/chunk-MAPYGK3P.js";import{a as R}from"../chunks/chunk-6RLVD2DT.js";import"../chunks/chunk-T7QVTNXF.js";import"../chunks/chunk-RPXHAG6F.js";import"../chunks/chunk-45PGAQRO.js";import{d as me}from"../chunks/chunk-4WKQTWUC.js";import{q as he}from"../chunks/chunk-VQ5YXO54.js";import{b as F,c as S,d as Ce,e as O,h as te,i as b}from"../chunks/chunk-76WAVKUG.js";import{a as Se,d as P,e as ae,h as ne,i as De}from"../chunks/chunk-VWKSMFWJ.js";import{H as Z,I as xe,J as Re,L as X,O as ee,P as A,Q as $e,R as ue,a as c,c as _,d as V,k as u,m as w,n as Q,o as Y,r as pe,s as H,w as e,x as ce,z as k}from"../chunks/chunk-GT4EAKAO.js";import{d as J}from"../chunks/chunk-G7NGB5UX.js";J();J();var Le=[{value:"argo",label:"Argo CD (notifications)",kinds:["deployment"],file:"argocd-notifications-cm (in your cluster)"},{value:"jenkins",label:"Jenkins pipeline",kinds:["deployment","incident"],file:"Jenkinsfile (in the repo Jenkins builds)"},{value:"gitlab",label:"GitLab CI",kinds:["deployment","incident"],file:".gitlab-ci.yml (in the GitLab project)"},{value:"script",label:"Any script, Spinnaker, Octopus or a webhook (curl)",kinds:["deployment","incident"],file:"your deploy script"},{value:"cli",label:"A terminal with StackVitals installed",kinds:["deployment","incident"],file:"a shell"}],Je="$(date -u +%Y-%m-%dT%H:%M:%SZ)",Ve=a=>a.replace(/["\\$`]/g,"");function Qe(a,s,l,d){let p=Ve(d.trim()||"production"),r=`https://api.github.com/repos/${a}/dispatches`,i=(o,y,I,v="")=>l==="deployment"?`stackvitals record deployment ${a} --id "${o}" --sha "${y}" --environment "${p}"${v} --tool ${I}`:`stackvitals record incident ${a} --id "${o}" --at "$INCIDENT_STARTED_AT" --tool ${I}`;if(s==="argo")return`# argocd-notifications-cm: keep the token in argocd-notifications-secret as stackvitals-token,
|
|
2
|
+
# then subscribe the app: notifications.argoproj.io/subscribe.on-deployed.stackvitals: ""
|
|
3
|
+
service.webhook.stackvitals: |
|
|
4
|
+
url: ${r}
|
|
5
|
+
headers:
|
|
6
|
+
- name: Authorization
|
|
7
|
+
value: Bearer $stackvitals-token
|
|
8
|
+
- name: Accept
|
|
9
|
+
value: application/vnd.github+json
|
|
10
|
+
template.stackvitals-deployed: |
|
|
11
|
+
webhook:
|
|
12
|
+
stackvitals:
|
|
13
|
+
method: POST
|
|
14
|
+
body: |
|
|
15
|
+
{"event_type":"stackvitals-deployment","client_payload":{"id":"{{.app.metadata.name}}-{{.app.status.operationState.syncResult.revision}}","sha":"{{.app.status.operationState.syncResult.revision}}","finished_at":"{{.app.status.operationState.finishedAt}}","environment":"${p}","tool":"argo-cd"}}`;if(s==="jenkins"){let o=y=>`withCredentials([string(credentialsId: 'stackvitals-token', variable: 'GITHUB_TOKEN')]) {
|
|
16
|
+
sh 'pip install --quiet stackvitals && ${i("jenkins-$BUILD_TAG","$GIT_COMMIT","jenkins",y==="failure"?" --status failure":"")}'
|
|
17
|
+
}`;return l==="deployment"?`// In the deploy stage of your Jenkinsfile; the credential is a secret text with the token.
|
|
18
|
+
post {
|
|
19
|
+
success {
|
|
20
|
+
script {
|
|
21
|
+
${o("success")}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
failure {
|
|
25
|
+
script {
|
|
26
|
+
${o("failure")}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}`:`// When your pipeline opens an incident; INCIDENT_STARTED_AT is a time like 2026-10-29T09:12:00Z.
|
|
30
|
+
withCredentials([string(credentialsId: 'stackvitals-token', variable: 'GITHUB_TOKEN')]) {
|
|
31
|
+
sh 'pip install --quiet stackvitals && ${i("jenkins-$BUILD_TAG","","jenkins")}'
|
|
32
|
+
}`}if(s==="gitlab")return`# .gitlab-ci.yml: runs after your deploy job; GITHUB_TOKEN is a masked CI/CD variable holding the token.
|
|
33
|
+
stackvitals-record:
|
|
34
|
+
stage: .post
|
|
35
|
+
image: python:3.12-slim
|
|
36
|
+
rules:
|
|
37
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
|
38
|
+
script:
|
|
39
|
+
- pip install --quiet stackvitals
|
|
40
|
+
- ${l==="deployment"?i("gitlab-$CI_PIPELINE_ID","$CI_COMMIT_SHA","gitlab"):i("gitlab-$CI_PIPELINE_ID","","gitlab")}`;if(s==="cli")return`export GITHUB_TOKEN=... # the token below
|
|
41
|
+
${l==="deployment"?i("deploy-1287","$GIT_SHA","script"):`stackvitals record incident ${a} --id "INC-42" --at 2026-10-29T09:12:00Z --resolved-at 2026-10-29T10:03:00Z --tool script`}`;let n=l==="deployment"?`{"event_type":"stackvitals-deployment","client_payload":{"id":"'"$DEPLOY_ID"'","sha":"'"$GIT_SHA"'","environment":"${p}","status":"success","finished_at":"'"${Je}"'","tool":"script"}}`:`{"event_type":"stackvitals-incident","client_payload":{"id":"'"$INCIDENT_ID"'","created_at":"'"$STARTED_AT"'","tool":"script"}}`;return`# DEPLOY_ID / INCIDENT_ID: your tool's own id (sending it again updates the record); STACKVITALS_TOKEN: the token below.
|
|
42
|
+
curl -fsS -X POST "${r}" \\
|
|
43
|
+
-H "Authorization: Bearer $STACKVITALS_TOKEN" -H "Accept: application/vnd.github+json" \\
|
|
44
|
+
-d '${n}'`}var Ye=a=>`https://github.com/settings/personal-access-tokens/new?name=${encodeURIComponent("StackVitals deployments")}&description=${encodeURIComponent(`Lets a deploy tool send deployments and incidents to ${a}`)}&target_name=${encodeURIComponent(a.split("/")[0])}&expires_in=90&contents=write`;function Ze({app:a}){let[s,l]=_("argo"),[d,p]=_("deployment"),[r,i]=_("production"),[n,o]=_(void 0);V(()=>{let v=!0;if(he){o(null);return}return Ne(a).then(g=>{v&&o(g==null?null:/stackvitals-deployment/.test(g))}),()=>{v=!1}},[a.repo]);let y=Le.find(v=>v.value===s),I=y.kinds.includes(d)?d:"deployment";return e(G,{title:"Record deployments and incidents from another tool",class:"dl-rec",state:n===!1?e(D,{tone:"watch",children:"Workflow needs an update"}):n?e(D,{tone:"good",children:"Workflow listens"}):e(D,{tone:"neutral",children:"Ready to copy"}),bar:e(c,{children:[e(k,{size:"sm",variant:"tinted",href:Ye(a.repo),external:!0,iconEnd:"external",children:"Create the token"}),e(k,{size:"sm",variant:"plain",href:Z+"connectors.md#deployments-and-incidents-from-elsewhere",external:!0,iconEnd:"external",children:"All fields"})]}),children:[e("div",{class:"blk-form",children:[e(P,{label:"Your tool",children:v=>e(ne,{id:v,value:s,onChange:g=>l(g),options:Le.map(g=>({value:g.value,label:g.label}))})}),e(P,{label:"What it sends",hint:y.kinds.length===1?"Argo CD sends deployments; incidents come from a script or your pager.":void 0,children:v=>e(ne,{id:v,value:I,onChange:g=>p(g),options:y.kinds.map(g=>({value:g,label:g==="deployment"?"Deployments":"Incidents"}))})}),I==="deployment"?e(P,{label:"Environment",hint:"Only production counts in DORA.",children:v=>e(ae,{id:v,value:r,onInput:i,maxLength:60})}):null]}),e(ee,{title:`Paste into ${y.file}`,code:Qe(a.repo,s,I,r)}),e(K,{rows:[["Token it needs",e(c,{children:["A fine-grained token for ",a.repo," with ",e("b",{children:"Contents: read and write"})," (GitHub's rule for dispatch events), kept in your tool's secret store."]})],["Where it lands","The StackVitals workflow keeps each one on the data branch (delivery/recorded.ndjson); the next scheduled run counts it like GitHub's own."],["This repo's workflow",n===void 0?"Checking\u2026":n===null?he?"Not checked in the demo; the current workflow template has the repository_dispatch trigger.":"Couldn't read it from here (a private repo needs a token); it needs the repository_dispatch trigger.":n?"Has the repository_dispatch trigger for these events.":e(c,{children:["Set up before this trigger existed: ",e("a",{href:"#/setup",children:"update the workflow"})," so it accepts them."]})],["Why not a button here","Your deploy tool has to send each one as it happens; a deployment sent from this page would be made up."]]})]})}var Me=[{value:"bitbucket",label:"Bitbucket Cloud",path:"workspace/repo",env:"BITBUCKET_TOKEN (an access token)"},{value:"azure",label:"Azure DevOps",path:"org/project/repo",env:"AZURE_DEVOPS_TOKEN (a PAT with Code: read)"},{value:"gitlab",label:"GitLab",path:"group/project",env:"GITLAB_TOKEN (needed for review times, even on public projects)"}];function Xe(){let[a,s]=_("bitbucket"),[l,d]=_(""),p=Me.find(i=>i.value===a),r=l.trim().replace(/\s+/g,"")||p.path;return e(ge,{title:"Code on Bitbucket, Azure DevOps or GitLab",does:"The same review phases and DORA for code hosted there, read-only.",why:"It reads another host with that host's token; nothing from there reaches this repo's data, so the app has nothing to show.",needs:e(c,{children:[e("code",{children:"pip install stackvitals"}),"; private projects take ",p.env,"."]}),children:[e("div",{class:"blk-form",children:[e(P,{label:"Host",children:i=>e(ne,{id:i,value:a,onChange:n=>s(n),options:Me.map(n=>({value:n.value,label:n.label}))})}),e(P,{label:"Project",hint:`Like ${p.path}`,children:i=>e(ae,{id:i,value:l,onInput:d,placeholder:p.path,maxLength:200})})]}),e(ee,{inline:!0,code:`stackvitals delivery ${r} --${a}`})]})}function et({repo:a}){let[s,l]=_("web"),[d,p]=_([]),r=[a,...d.filter(n=>n!==a)],i=s.trim().replace(/[^\w.-]/g,"-")||"web";return e(ge,{title:"One project across several repos",does:"Pools the repos and runs DevLake's own DORA SQL on them, next to each repo's numbers.",why:"It reads every repo's history with your token and runs the SQL locally in DuckDB; this page holds one repo's data.",needs:e(c,{children:["Python with ",e("code",{children:"pip install 'stackvitals[sql]'"})," and a token (",e("code",{children:"GITHUB_TOKEN"}),") that can read every repo."]}),children:[e("div",{class:"blk-form",children:[e(P,{label:"Project name",children:n=>e(ae,{id:n,value:s,onInput:l,maxLength:60})}),e(P,{label:"Other repos",hint:`owner/name; ${a} is already in.`,children:n=>e(De,{id:n,values:d,onChange:o=>p(o.filter(y=>/^[\w.-]+\/[\w.-]+$/.test(y))),placeholder:"org/api",label:"Other repos"})})]}),e(ee,{title:"Terminal",code:`pip install 'stackvitals[sql]'
|
|
45
|
+
stackvitals project ${i} ${r.join(" ")}${d.length?"":" org/api org/worker"}`}),d.length?null:e("p",{class:"blk-note",children:"Add the other repos above; org/api and org/worker are placeholders."})]})}function je({app:a}){return e("div",{class:"dl-sources",children:[e(Ze,{app:a}),e("div",{class:"blk-grid",children:[e(Xe,{}),e(et,{repo:a.repo})]})]})}var ye={"deployments api":"GitHub's Deployments API",releases:"GitHub releases","deploy workflows":"deploy workflow runs","release workflows":"release workflow runs",merges:"every merged pull request (your setting)"},q={coding:"Coding",pickup:"Pickup",review:"Review",approve:"Approve",merge:"Merge",deploy:"Deploy",cycle:"Cycle time",cycle_to_merge:"Cycle time to merge"},Fe={coding:"first commit to opened",pickup:"ready to the first comment or review by someone else",review:"that first answer to merge",approve:"first answer to first approval",merge:"approval to merge",deploy:"merge to the deployment that shipped it",cycle:"coding, pickup, review and deploy",cycle_to_merge:"coding, pickup and review"},Ge={pickup:"pr_pickup_time",review:"pr_review_time",coding:"pr_coding_time",deploy:"pr_deploy_time",cycle:"pr_cycle_time"},fe=["elite","high","medium","low"],oe=(a,s)=>`https://github.com/${a.repo}${s}`,j={target:"_blank",rel:"noopener noreferrer"},be=e("a",{href:"#/settings?module=delivery",children:"Settings"}),tt=[["under_100","under 100 lines"],["100_155","100 to 155"],["156_228","156 to 228"],["over_228","over 228"]];function at({d:a,app:s}){let l=a.sizes,d=a.merged_flags,p=a.devlake_pr_cycle||[];if(!l?.n&&a.merge_frequency==null&&!p.length)return null;let r=i=>i.slice(0,6).map((n,o)=>e(c,{children:[o?", ":"",e("a",{href:oe(s,`/pull/${n.pr}`),...j,children:["#",n.pr]})]}));return e(c,{children:[e(Be,{title:"Habits",note:"LinearB's measures over the same pull requests"}),e(te,{children:[e(b,{label:"Size, median",value:l?.p50!=null?`${l.p50} lines`:"\u2013",note:l?.n?tt.map(([i,n])=>`${n}: ${l[i]??0}`).join("; "):null}),e(b,{label:"Merge frequency",value:a.merge_frequency!=null?`${a.merge_frequency} a week`:"\u2013",note:"merged PRs per author per week"}),e(b,{label:"Review depth",value:a.review_depth??"\u2013",note:"comments and reviews from others per pull request"}),e(b,{label:"Merged without a review",value:d?.without_review?.length??0,note:d?.without_review?.length?r(d.without_review):"none"}),e(b,{label:"Lightning reviews",value:d?.lightning?.length??0,note:d?.lightning?.length?e(c,{children:["reviewed in under 20 minutes: ",r(d.lightning)]}):"none"})]}),p.length?e($,{title:"DevLake's PR cycle time panel",note:"mean, merged pull requests, by month",children:e(R,{name:"devlake-pr-cycle",search:!1,caption:"DevLake's PR cycle time panel",rows:p,columns:[{key:"month",label:"Month",primary:!0,render:i=>H(i.month)},{key:"avg_hours",label:"Mean cycle time",num:!0,render:i=>u(i.avg_hours)},{key:"n",label:"PRs",num:!0}]})}):null]})}function nt({children:a}){return e("p",{class:"sv-dimline dl-dim","aria-hidden":"true",children:e("span",{children:a})})}var rt=["coding","pickup","review","deploy"];function st({phases:a}){let s=rt.map(r=>a.find(i=>i.k===r)).filter(r=>!!r&&r.enough&&r.p50_h!=null);if(s.length<2)return null;let l=s.reduce((r,i)=>r+i.p50_h,0),d=a.find(r=>r.k==="cycle"),p=0;return e("figure",{class:"dl-strip",role:"img","aria-label":`Phase medians end to end: ${s.map(r=>`${q[r.k]} ${u(r.p50_h)}`).join(", ")}; ${u(l)} in all.`,children:[e("figcaption",{class:"sv-dim",children:"Phase medians, laid end to end"}),s.map((r,i)=>{let n=p/l*100,o=r.p50_h/l*100;return p+=r.p50_h,e("div",{class:"dl-strip-row",style:`--a:${n}%;--w:${o}%;--i:${i}`,children:[e("span",{class:"dl-strip-k",children:q[r.k]}),e("span",{class:"dl-strip-t",children:e("i",{})}),e("span",{class:"dl-strip-v num",children:u(r.p50_h)})]})}),e("div",{class:"dl-strip-row dl-strip-sum",children:e("span",{class:"sv-dimline dl-dim",children:e("span",{children:[u(l)," end to end",d?.enough?`; cycle time P50 ${u(d.p50_h)}`:""]})})})]})}function T({title:a,to:s,label:l,doc:d,children:p}){return e(O,{children:e(A,{icon:"clock",title:a,action:s?e(k,{variant:"tinted",href:s,children:l}):d?e(k,{variant:"plain",href:Z+d,iconEnd:"external",children:"How it works"}):null,children:p})})}function re({t:a,span:s}){if(!a)return e("span",{class:"muted",children:"not enough data"});let l="";if(s){let[d,p]=s.map(r=>fe.indexOf(r)).sort();l=p-d>1?`could be anywhere from ${fe[d]} to ${fe[p]}`:`could be ${s.find(r=>r!==a)||s[1]}`}return e(c,{children:[e(Re,{value:a}),l?e("span",{class:"muted",children:[" (",l,")"]}):null]})}var se=(a,s)=>a?`, likely ${s(a[0])} to ${s(a[1])}`:"";function ot({changes:a}){let s=(a||[]).filter(r=>!r.note&&!r.shown_with&&r.now!=null);if(!s.length)return e(O,{children:e("p",{class:"blk-note",children:"Too few pull requests or deployments this week to compare with the four weeks before."})});let l=(r,i)=>r.unit==="share"?w(i):r.unit==="h"?u(i):r.unit==="count"?String(+(+i).toFixed(1)):`${+(+i).toFixed(1)} ${r.unit}`,d=r=>r.charAt(0).toUpperCase()+r.slice(1),p=r=>r.detectable?r.unit==="share"?`${Math.round(r.detectable*100)} points`:`${r.detectable>=3?Math.round(r.detectable):r.detectable.toFixed(1)}\xD7`:"\u2013";return e(R,{name:"this-week",search:!1,rows:s.map(r=>({...r,id:r.label})),columns:[{key:"label",label:"Measure",primary:!0,raw:r=>d(r.label)},{key:"now",label:"This week",num:!0,raw:r=>r.now,render:r=>l(r,r.now)},{key:"base",label:"Four weeks before",num:!0,raw:r=>r.base,render:r=>r.base==null?"\u2013":l(r,r.base)},{key:"changed",label:"Reading",raw:r=>r.changed?r.direction==="up"?"Rose":"Fell":"Steady",render:r=>r.changed?e(D,{tone:"accent",children:r.direction==="up"?"Rose":"Fell"}):e(D,{tone:"neutral",children:"Steady, within noise"})},{key:"n",label:"Pull requests",num:!0,raw:r=>r.n_now??null,render:r=>r.n_now!=null?`${r.n_now} vs ${r.n_base??0}`:"\u2013"},{key:"detectable",label:"Smallest change that shows",num:!0,raw:r=>r.detectable??null,render:p}]})}function it({app:a,phase:s}){let l=me({metrics:"table:devlake_project_pr_metrics",prs:"table:devlake_pull_requests"});if(!l.ready)return e(ue,{});let d=Object.fromEntries((l.prs||[]).map(i=>[i.id,i])),p=Ge[s],r=(l.metrics||[]).map(i=>({...i,pr:d[i.id]||{},value:p&&i[p]!=null?i[p]/60:null})).filter(i=>i.value!==null);return e(F,{title:`${q[s]||s}: the pull requests behind it`,subtitle:`${Fe[s]||""}. Every merged pull request in the data, longest first; the page's P75 leaves LinearB's outliers out.`,toolbar:e(k,{variant:"plain",icon:"left",href:"#/delivery",children:"Delivery"}),children:e(S,{title:"Pull requests",description:"Longest first. Search, filter or download.",children:l.metrics?e(R,{name:`delivery-${s}`,caption:`${q[s]||s} by pull request`,rows:r,initialSort:{key:"value",desc:!0},columns:[{key:"pr",label:"Pull request",primary:!0,raw:i=>i.pr.pull_request_key,render:i=>e(c,{children:[e("a",{href:i.pr.url||oe(a,`/pull/${i.pr.pull_request_key}`),...j,children:["#",i.pr.pull_request_key]})," ",pe(i.pr.title||"",70)]})},{key:"author",label:"Author",raw:i=>i.pr.author_name},{key:"value",label:q[s]||s,num:!0,render:i=>u(i.value)},{key:"pr_merged_date",label:"Merged",render:i=>Y(i.pr_merged_date)}]}):e(O,{children:e(A,{icon:"search",title:"The rows aren't published here.",children:["On public repos they sit beside the app; for a private repo open it with ",e("code",{children:"stackvitals ui"}),"."]})})})})}var He=null;function C({name:a,...s}){let[l,d]=_(null);if(V(()=>{(He||(He=import("../chunks/more-RCIVCUDE.js"))).then(d)},[]),!l)return e($e,{lines:4});let p=l[a];return e(p,{...s})}var ve=a=>[{key:"team",label:a,primary:!0},{key:"n",label:"PRs",num:!0},{key:"pickup",label:"Pickup P75",num:!0,raw:s=>s.pickup?.p75_h,render:s=>s.pickup?.enough?u(s.pickup.p75_h):"\u2013"},{key:"cycle",label:"Cycle time to merge P75",num:!0,raw:s=>s.cycle_to_merge?.p75_h,render:s=>s.cycle_to_merge?.enough?u(s.cycle_to_merge.p75_h):"\u2013"}],lt=[{key:"dd",label:"Deploy days a week",num:!0,raw:a=>a.dora?.deployment_days_per_week,render:a=>a.dora?String(a.dora.deployment_days_per_week??"\u2013"):"\u2013"},{key:"lt",label:"Lead time",num:!0,raw:a=>a.dora?.lead_time_hours,render:a=>a.dora?.lead_time_hours!=null?u(a.dora.lead_time_hours):"\u2013"},{key:"cfr",label:"Change fail rate",num:!0,raw:a=>a.dora?.change_failure_rate,render:a=>a.dora?.change_failure_rate!=null?w(a.dora.change_failure_rate):"\u2013"},{key:"rec",label:"Recovery",num:!0,raw:a=>a.dora?.recovery_hours,render:a=>a.dora?.recovery_hours!=null?u(a.dora.recovery_hours):"\u2013"}];function dt({app:a,route:s}){return a&&s.rest[0]==="rows"?e(it,{app:a,phase:s.query.get("phase")||"cycle"}):e(pt,{app:a,route:s})}function pt({app:a,route:s}){let l=me({d:"delivery.json",alerts:"alerts.json"}),[d,p]=Ae(),[r,i]=_(null);if(!a)return e(F,{title:"Delivery",children:e(A,{icon:"setup",title:"No StackVitals data here yet",action:e(k,{variant:"filled",href:"#/setup",children:"Set it up"})})});if(!l.ready)return e(ue,{});let n=l.d;if(!n)return e(F,{title:"Delivery",subtitle:"DORA measures from deploys and releases, and where pull request time goes.",children:e(A,{icon:"delivery",title:"Delivery hasn't run here yet.",action:e(k,{variant:"filled",href:"#/settings",icon:"settings",children:"Turn it on"}),children:"It runs with Team Flow on the schedule."})});let o=n.dora||{},y=l.alerts,I=y?.series||{},v=y?.limits||{},g=y?.min_n??10,W=(t,m,E,B,x)=>{let N=v[m];return{id:t,name:E,sub:`Each day over the 28 before; blank under ${g} ${x}`,kind:B,better:"down",period:"day",points:(I[m]||[]).filter(([,L,de])=>L!=null&&(de??0)>=g).map(([L,de,ze])=>({label:H(L),value:de,n:ze})),meaning:L=>`${B==="share"?w(L):u(L)} over the 28 days before${N!=null?`; the alert fires above ${B==="share"?w(N):u(N)}`:""}.`,info:{title:E,body:`Worked out each day over the 28 days before it. Days with fewer than ${g} ${x} are left out, as the alert rule ignores them.`,facts:N!=null?[["Alert limit",B==="share"?w(N):u(N)]]:[],doc:"dora-guide.md"}}},z=[{id:"deploys",name:"Deployments",sub:"By month",kind:"count",better:"up",period:"month",points:(o.monthly||[]).map(t=>({label:H(t.month),value:t.deployments})),meaning:t=>`${Q(Math.round(t),"deployment")} that month.`,info:{title:"Deployments",body:"Deployments StackVitals found, by month. DORA's deployment frequency is read from these.",doc:"dora-guide.md"}},W("pickup","pickup_p75_hours","Pickup, P75","hours","PRs"),W("review","review_p75_hours","Review, P75","hours","PRs"),W("cycle","cycle_p75_hours","Cycle time to merge, P75","hours","PRs"),W("cfr","change_failure_rate","Change fail rate","share","deployments"),...[["lead_time_hours","Lead time","hours","down"],["change_failure_rate","Change fail rate","share","down"],["recovery_hours","Recovery","hours","down"],["deployments","Deployments","count","up"]].map(([t,m,E,B])=>({id:`week-${t}`,name:m,sub:"By week, as DORA counts the window",kind:E,better:B,period:"week",points:(n.dora_weeks||[]).filter(x=>x[t]!=null).map(x=>({label:H(x.week),value:x[t]})),meaning:x=>`${E==="share"?w(x):E==="hours"?u(x):Q(Math.round(x),"deployment")} that week.`,info:{title:`${m} by week`,body:"Each Monday-to-Monday week counted with DevLake's DORA rules, as the window's number is.",doc:"dora-guide.md"}}))].filter(t=>t.points.length),ie=z.find(t=>t.id===r)||z[0],ke=[["incidents","Incidents"],["range","Any dates"],["quality","Code quality"],["deploys","Deployments"],["teams",n.by_service?"By team and service":"By team"],["ai","AI-assisted work"],["risk","Change risk"],["issues","Issues and sprints"],["bugs","Bugs"],["releases","Releases"],["queue","Merge queue"]],we=s.query.get("tab")||"",f=ke.some(([t])=>t===we)?we:"incidents",Ke=o.cfr_fix_chain!=null&&o.cfr_fix_chain!==o.change_failure_rate?`; by each revert's fix chain: ${w(o.cfr_fix_chain)}${o.linkage?.never_shipped?` (${o.linkage.never_shipped} reverted before any deployment shipped them)`:""}`:"",h=n.deploy_check,le=(h?.workflows||[]).filter(t=>!t.kind),We=h?[...(h.considered||[]).map(t=>({what:ye[t.source]||t.source,n:t.found,said:t.used?"counted":"passed over"})),...(h.workflows||[]).filter(t=>t.kind&&(!t.used||t.skipped)).map(t=>({what:t.name||t.path,n:t.used?t.skipped:"",said:t.used?"runs skipped: failed, or not a release or the default branch":`passed over: ${ye[h.source]||h.source||"nothing"} is used`})),...le.length>5?[{what:`${le.length} other workflows`,n:"",said:"names don't match deploy_workflow"}]:le.map(t=>({what:t.name||t.path,n:"",said:t.why})),...(h.environments||[]).filter(t=>!t.counted).map(t=>({what:`environment ${t.name}`,n:t.deployments,said:`not counted (${t.as})`}))]:[],_e=Object.entries(n.phases||{}).map(([t,m])=>({k:t,...m})),U=n.agreements;return e(F,{title:"Delivery",subtitle:`Last ${n.window_days} days. Deployments from ${ye[n.deploy_source]||n.deploy_source||"nowhere yet"}${n.deploy_confidence?` (${n.deploy_confidence} confidence)`:""}. DORA is counted as DevLake's dashboard counts it (${o.report||"2023"} report).${n.incident_source?` Incidents from ${n.incident_source}.`:""}`,toolbar:a.modules?.delivery?.page?e(k,{variant:"plain",href:a.modules.delivery.page,iconEnd:"external",children:"Printable page"}):null,children:[n.deployments_unreadable?e("div",{class:"note note-watch",children:[e(ce,{name:"warn"}),e("span",{children:["GitHub's Deployments API couldn't be read with the workflow's token (add ",e("code",{children:"deployments: read"}),"), so deployments come from releases or workflows."]})]}):null,n.deploy_confidence==="low"?e("div",{class:"note note-watch",children:[e(ce,{name:"warn"}),e("span",{children:["Deployments are guessed from workflow names. If those don't put code in front of users, choose the source in ",be,"."]})]}):null,ie?e(S,{title:"Trends",description:"Deployments by month and the 90-day review trends. Drag across the chart, or pick a row to chart it.",children:[e(Ce,{ratio:"lead",children:[e(O,{children:e(Oe,{metric:ie,ranges:d,setRange:p})}),e(qe,{label:"Delivery trends",items:z.map(t=>Pe(t,d[t.period]||12)),selected:ie.id,onSelect:i,tones:Ee(z,d)})]}),y?null:e("p",{class:"muted small",children:"The 90-day trend appears after the first scheduled run with this version."})]}):null,e(S,{title:"DORA",description:"The four keys plus rework, each with its tier and likely range.",info:e(X,{info:{title:"DORA tiers",body:"Each measure is placed in DORA's elite, high, medium or low band. When the likely range crosses a band edge, the other possible tier is named too. Change fail rate ties a failure to the last deployment before it, as DevLake does.",doc:"dora-guide.md"}}),children:[e(nt,{children:["last ",n.window_days," days, DevLake's rules"]}),e(te,{children:[e(b,{label:"Deployment frequency",value:o.frequency_tier&&o.deployment_days_per_week!=null?`${o.deployment_days_per_week} days a week`:`${o.deployments??0} in ${n.window_days} days`,note:e(c,{children:[e(re,{t:o.frequency_tier}),o.deployment_days!=null?e(c,{children:[e("br",{}),o.deployment_days," days with a deployment; ",o.deployment_days_per_month??"\u2013"," a month",o.deployment_days_per_six_months!=null?`, ${o.deployment_days_per_six_months} in six months`:""]}):null,e("br",{}),e(M,{kind:"deployments",ev:o.evidence,repo:a.repo})]})}),e(b,{label:"Change lead time",value:u(o.lead_time_hours),note:e(c,{children:[e(re,{t:o.lead_time_tier,span:o.lead_time_tier_span}),e("br",{}),"n = ",o.lead_time_n??0,se(o.lead_time_range_hours,u),o.lead_time_working_hours!=null?e(c,{children:[e("br",{}),u(o.lead_time_working_hours)," in working hours, by your working calendar"]}):null,e("br",{}),e(M,{kind:"lead_time",ev:o.evidence,repo:a.repo})]})}),e(b,{label:"Change fail rate",value:w(o.change_failure_rate),note:e(c,{children:[e(re,{t:o.cfr_tier,span:o.cfr_tier_span}),e("br",{}),o.failed_deployments??0," failed",se(o.cfr_range,w),Ke,o.linkage?e(c,{children:[e("br",{}),e("span",{class:"muted small",children:[o.linkage.reverts??0," reverts, ",o.linkage.fix_chain_found??0," traced through their fix chain",o.linkage.disagree?`, ${o.linkage.disagree} where time order and the fix chain blame different deployments`:""]})]}):null,e("br",{}),e(M,{kind:"failed",ev:o.evidence,repo:a.repo})]})}),e(b,{label:"Failed deployment recovery",value:u(o.recovery_hours),note:e(c,{children:[e(re,{t:o.recovery_tier,span:o.recovery_tier_span}),e("br",{}),"n = ",o.recovery_n??0,se(o.recovery_range_hours,u),e("br",{}),e(M,{kind:"recovery",ev:o.evidence,repo:a.repo})]})}),e(b,{label:"Deployment rework rate",value:w(o.rework_rate),info:e(X,{info:{title:"Rework rate",body:"Deployments that shipped a revert or a hotfix, over all deployments."}}),note:o.rework_deployments===void 0?"not measured yet":`${o.rework_deployments} of ${o.deployments_n??0} shipped a fix${se(o.rework_range,w)}`})]}),e($,{title:"This week",note:"against the four weeks before; a change counts only beyond the usual noise",children:e(ot,{changes:n.changes})})]}),e(S,{title:"Deployments",description:"The latest ones, and what StackVitals counts as a deployment.",action:e(k,{size:"sm",variant:"plain",href:"#/settings?module=delivery",iconEnd:"right",children:"Change the source"}),children:[e("div",{class:h?"blk-grid dl-deps":"",children:[e($,{title:"Latest deployments",note:"newest first",children:e(R,{name:"deployments",caption:"Latest deployments",search:!1,rows:[...n.deployments||[]].reverse(),columns:[{key:"name",label:"What",primary:!0,raw:t=>pe(t.name||t.id,50)},{key:"sha",label:"Commit",nowrap:!0,render:t=>e("a",{class:"mono-soft",href:oe(a,`/commit/${t.sha}`),...j,children:String(t.sha||"").slice(0,7)})},{key:"finished",label:"Finished",nowrap:!0,render:t=>Y(t.finished)}],empty:e(O,{children:e(A,{icon:"delivery",title:"No deployments found.",children:["Tell StackVitals where deployments come from in ",be,"."]})})})}),h?e($,{title:"What counts as a deployment",note:"each source and workflow StackVitals looked at",children:e(R,{name:"deploy-check",caption:"What counts as a deployment",search:!1,rows:We,columns:[{key:"what",label:"What",primary:!0},{key:"n",label:"How many",num:!0},{key:"said",label:"Counted?",render:t=>t.said==="counted"?e(D,{tone:"good",children:"Counted"}):e("span",{children:t.said.charAt(0).toUpperCase()+t.said.slice(1)})}]})}):null]}),h?e(G,{title:"How the source was chosen",class:"dl-chk",children:e(K,{rows:[h.order_reason?["Why this source",`${h.order_reason}.`]:null,h.settings?["Source setting",e("code",{children:h.settings.source})]:null,h.settings?["Deploy workflow",e("code",{children:h.settings.deploy_workflow})]:null,h.settings?["Production environments",(h.settings.production||[]).join(", ")||"none"]:null,h.releases?["Releases",`${h.releases.published} published${h.releases.prereleases_skipped?`, ${h.releases.prereleases_skipped} pre-releases left out`:""}`]:null,n.deploy_names?.length?["Counted by name",n.deploy_names.map(t=>`${t.name||"unnamed"} (${t.n})`).join(", ")]:null]})}):null]}),e(S,{title:"Where pull request time goes",description:`${n.prs??0} merged pull requests by people; ${n.outliers?.over_60_days||0} over 60 days and ${n.outliers?.over_p98||0} past the 98th percentile left out${n.excluded?`, ${n.excluded} excluded by the team`:""}.`,children:[e(st,{phases:_e}),e($,{title:"Cycle time phases",note:"P50 and P75 of merged pull requests; each phase opens its pull requests",children:e(R,{name:"phases",caption:"Cycle time phases",search:!1,rows:_e,columns:[{key:"k",label:"Phase",primary:!0,raw:t=>q[t.k]||t.k,render:t=>e(c,{children:[e("b",{children:q[t.k]||t.k}),e("div",{class:"muted small",children:Fe[t.k]})]})},{key:"p50_h",label:"P50",num:!0,render:t=>t.enough?u(t.p50_h):e("span",{class:"muted",children:"fewer than 10"})},{key:"p75_h",label:"P75",num:!0,render:t=>t.enough?u(t.p75_h):"\u2013"},{key:"n",label:"PRs",num:!0},{key:"linearb_band",label:"LinearB band",render:t=>t.linearb_band?e(xe,{tone:t.linearb_band==="elite"||t.linearb_band==="good"?"good":t.linearb_band==="fair"?"watch":"bad",children:t.linearb_band.charAt(0).toUpperCase()+t.linearb_band.slice(1)}):"\u2013"},{key:"rows",label:"Pull requests",nowrap:!0,raw:()=>"",render:t=>Ge[t.k]&&a.summary!==null?e(k,{size:"sm",variant:"tinted",href:`#/delivery/rows?phase=${t.k}`,children:"The PRs"}):e(M,{kind:"phase",ev:{phase:t.evidence},repo:a.repo})}]})}),e(at,{d:n,app:a})]}),e(S,{title:"Other sources",class:"dl-other",description:"Deployments from tools GitHub can't see, delivery for code on Bitbucket, Azure DevOps or GitLab, and one view across several repos.",info:e(X,{info:{title:"Other sources",body:"Recorded deployments and incidents land on the data branch and count from the next run. The delivery commands run from your machine, read-only.",doc:"connectors.md"}}),children:e(je,{app:a})}),e(S,{title:"In depth",description:"Incidents, teams, AI-assisted work and more. Where the work went has its own page.",action:e(k,{size:"sm",variant:"plain",href:"#/investment",iconEnd:"right",children:"Investment"}),children:[e("div",{class:"dl-tabs",children:e(Se,{label:"Show",scroll:!0,value:f,onChange:t=>Te(Ie(s,"tab",t==="incidents"?null:t)),options:ke.map(([t,m])=>({value:t,label:e(c,{children:[m,t==="incidents"?e("span",{class:"seg-count num",children:(n.incidents||[]).length}):null]})}))})}),e("div",{class:"dl-panel",children:[f==="incidents"&&n.deploy_attempts?e(te,{children:[e(b,{label:"Deploy attempts",value:n.deploy_attempts.attempts,note:"in the window"}),e(b,{label:"Failed",value:n.deploy_attempts.failed,note:n.deploy_attempts.cancelled?`${n.deploy_attempts.cancelled} more cancelled`:"none cancelled"}),e(b,{label:"Succeeded",value:w(n.deploy_attempts.success_rate),note:"of attempts that finished"}),e(b,{label:"A successful deploy, median",value:n.deploy_attempts.median_minutes!=null?`${n.deploy_attempts.median_minutes} min`:"\u2013"})]}):null,f==="incidents"?e($,{title:"Incidents",note:"issues labelled as incidents, reverts, hotfixes and recorded ones",children:e(R,{name:"incidents",caption:"Incidents",rows:n.incidents||[],columns:[{key:"number",label:"Item",primary:!0,render:t=>String(t.url||"").startsWith("https://")?e("a",{href:t.url,...j,children:t.number}):e("a",{href:oe(a,`/${t.source==="issue"?"issues":"pull"}/${t.number}`),...j,children:["#",t.number]})},{key:"source",label:"From",filter:!0,raw:t=>({issue:"Issue",pull:"Pull request",pr:"Pull request",revert:"Revert",hotfix:"Hotfix",recorded:"Recorded"})[t.source]||String(t.source||"\u2013").replace(/^./,m=>m.toUpperCase())},{key:"created",label:"Began",render:t=>Y(t.created)},{key:"hours_to_restore",label:"Restored after",num:!0,render:t=>u(t.hours_to_restore)}],empty:e(O,{children:e(A,{icon:"check",title:"No incidents in this window.",children:"Issues labelled as incidents, reverts and hotfix pull requests count here."})})})}):null,f==="incidents"&&n.what_changed?.length?e(C,{name:"Changed",timelines:n.what_changed,repo:a?.repo}):null,f==="teams"?e(c,{children:[Object.keys(n.by_team||{}).length?e($,{title:"By team",note:"your teams in Settings",children:e(R,{name:"by-team",caption:"By team (your settings)",rows:Object.entries(n.by_team).map(([t,m])=>({team:t,...m})),columns:[...ve("Team"),...Object.values(n.by_team).some(t=>t.dora)?lt:[]]})}):e(O,{children:e(A,{icon:"community",title:"No teams set.",action:e(k,{variant:"tinted",href:"#/settings?module=people",children:"Name your teams"}),children:"Name your teams in Settings to see cycle time per team."})}),Object.keys(n.by_service||{}).length?e(c,{children:[e($,{title:"By service",note:"your [services] paths; a pull request touching two counts in both",children:e(R,{name:"by-service",caption:"By service",rows:Object.entries(n.by_service).map(([t,m])=>({team:t,...m})),columns:[...ve("Service"),{key:"deployments",label:"Deployments",num:!0,render:t=>t.dora?t.deployments:"\u2013"},{key:"lead",label:"Lead time",num:!0,raw:t=>t.dora?.lead_time_hours,render:t=>t.dora?u(t.dora.lead_time_hours):"\u2013"},{key:"cfr",label:"Change failure",num:!0,raw:t=>t.dora?.change_failure_rate,render:t=>t.dora&&t.dora.change_failure_rate!=null?`${Math.round(t.dora.change_failure_rate*100)}%`:"\u2013"}]})}),Object.values(n.by_service).some(t=>!t.dora)?e("p",{class:"blk-note",children:"DORA shows for a service once it has a deploy pattern telling its deployments apart."}):null]}):null,Object.keys(n.by_owner||{}).length?e($,{title:"By owning team",note:"from CODEOWNERS",children:e(R,{name:"by-owner",caption:"By owning team",rows:Object.entries(n.by_owner).map(([t,m])=>({team:t,...m})),columns:ve("Owners")})}):null]}):null,f==="ai"?n.ai?.outcomes===void 0?e(T,{title:"Not measured yet.",to:"#/connections",label:"Connect AI tools",children:"The next run with this version fills it in. Copilot reports and AI spend (Anthropic, OpenAI, Cursor) are added from Connections."}):e(C,{name:"AI",ai:n.ai,survival:n.code_survival}):null,f==="issues"?n.tracker?e(C,{name:"Issues",t:n.tracker}):e(T,{title:"No issue tracker connected.",to:"#/connections",label:"Connect Jira or Linear",children:"Connect Jira or Linear to see lead and cycle time by issue type, sprints and cycles, and tracker incidents in DORA."}):null,f==="risk"?n.change_risk?e(C,{name:"Risk",r:n.change_risk,repo:a?.repo}):e(T,{title:"Not measured yet.",children:"The next run with this version scores open pull requests, once this repo's own backtest shows the score works here."}):null,f==="quality"?n.code_quality?e(C,{name:"Quality",q:n.code_quality}):e(T,{title:"No SonarQube or SonarCloud project connected.",to:"#/connections",label:"Connect SonarQube",doc:"connectors.md#code-quality-sonarqube-or-sonarcloud",children:"Name the Sonar project to read its quality gate, bugs, vulnerabilities, code smells, coverage and duplication once a day. A public SonarCloud project needs no key."}):null,f==="range"?n.dora_rows?.deployments?.length?e(C,{name:"Range",rows:n.dora_rows}):e(T,{title:n.dora_rows?"No deployments in the last year.":"Not measured yet.",children:n.dora_rows?e(c,{children:["Tell StackVitals where deployments come from in ",be,", then pick any dates here."]}):"The next scheduled run with this version keeps a year of deployments, so DORA can be read for any dates."}):null,f==="deploys"?n.deployment_list?.length?e(C,{name:"Deploys",rows:n.deployment_list,repo:a.repo}):e(T,{title:"No deployments in the window.",to:"#/settings?module=delivery",label:"Choose the source",children:["Each deployment lists the pull requests it shipped, their authors, lead time and the incidents tied to it. Deployments come from GitHub's Deployments API, releases or deploy workflows, or from any tool with ",e("code",{children:"stackvitals record"}),"."]}):null,f==="releases"?n.releases?.length?e(C,{name:"Releases",rows:n.releases,repo:a.repo}):e(T,{title:"No releases to compare yet.",children:"Publish GitHub releases (two or more) to see what each shipped, the share that were fixes, and the reverts and incidents that followed it."}):null,f==="bugs"?n.bugs?e(C,{name:"Bugs",b:n.bugs,repo:a.repo}):e(T,{title:"No bugs found.",to:"#/settings?module=delivery",label:"Name the bug labels",children:"Issues with a bug label, and Jira or Linear bugs when connected, fill the weekly backlog: opened, closed and still open."}):null,f==="queue"?n.merge_queue?e(C,{name:"Queue",app:a,q:n.merge_queue}):e(T,{title:"This repo doesn't use a merge queue.",doc:"modules.md",children:"With GitHub's merge queue on, this shows how long pull requests wait in it and which removals a flaky job caused."}):null]},f)]}),n.peers||U?e(S,{title:"Elsewhere",description:"Related readings on other pages.",children:e(G,{title:"See also",children:e(K,{rows:[n.peers?["Similar public repos",e(c,{children:["Compared with ",n.peers.population||"similar public repos"," on the ",a.modules?.delivery?.page?e("a",{href:a.modules.delivery.page,...j,children:"printable page"}):"printable page","."]})]:null,U?["Working agreements",e(c,{children:[e(D,{tone:(U.exceptions||[]).length?"watch":"good",children:Q((U.exceptions||[]).length,"exception")})," ",e("a",{href:"#/prs",children:"See them on Pull Requests"})]})]:null]})})}):null]})}export{dt as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.dp-gap{margin-top:18px}.section-body>.blk-facts{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);margin-bottom:18px}.sec-trend{display:flex;flex-direction:column;gap:4px}.sec-trend .spark{max-width:160px}.dp-form-go{display:flex;padding-top:27px}.blk-form.dp-check-form{grid-template-columns:minmax(0,440px) auto;justify-content:start}@media(max-width:480px){.blk-form.dp-check-form{grid-template-columns:minmax(0,1fr)}}@media(max-width:480px){.dp-form-go{padding-top:0}}.section-body>.note{margin-bottom:14px}[data-design=sheet][data-page=deps] .bar-fill.tone-neutral{background:transparent;border:1px solid var(--ink);border-bottom:0}[data-design=sheet][data-page=deps] .bar-label:before{content:"";display:inline-block;width:11px;height:11px;margin-right:5px;vertical-align:-1px;background:center / contain no-repeat}[data-design=sheet][data-page=deps] .bar:has(.tone-bad) .bar-label:before{background-image:var(--mark-bad)}[data-design=sheet][data-page=deps] .bar:has(.tone-watch) .bar-label:before{background-image:var(--mark-watch)}[data-design=sheet][data-page=deps] .bar:has(.tone-good) .bar-label:before{background-image:var(--mark-good)}[data-design=sheet][data-page=deps] .bar:has(.tone-neutral) .bar-label:before{width:9px;height:9px;border:1px solid currentColor}[data-design=sheet][data-page=deps] .bar:has(.tone-accent) .bar-label:before{display:none}[data-design=sheet][data-page=deps] .bar-label{font-size:14px;font-weight:600;color:var(--ink)}[data-design=sheet][data-page=deps] .bar-value{font-size:14px}[data-design=sheet][data-page=deps] .card:has(>.hero),[data-design=sheet][data-page=deps] .columns>.stats.card,[data-design=sheet][data-page=deps] .card:has(>.stats.card){border:1.5px solid var(--frame)}[data-design=sheet][data-page=deps] .hero-name{margin:-18px -18px 14px;padding:9px 18px;min-height:42px;background:var(--strip);border-bottom:1px solid var(--line-strong);color:var(--ink)}[data-design=sheet][data-page=deps] .section:has(table .pill-watch) .card:has(>.hero){border-top:5px solid var(--sv-amber)}[data-design=sheet][data-page=deps] .section:has(table .pill-bad) .card:has(>.hero){border-top:5px solid var(--sv-coral)}[data-design=sheet][data-page=deps] .table tbody tr:has(>td .pill-bad){background:var(--bad-soft)}[data-design=sheet][data-page=deps] .table td:is([data-label=Risk],[data-label="Effort to fix"],[data-label=Severity],[data-label=Result],[data-label="Your code"],[data-label=Scope]){overflow-wrap:normal}[data-design=sheet][data-page=deps] .card>.stats.card{border:0}[data-design=sheet][data-page=deps] .card>.stats.card .stat{border:0;margin:0;padding:0 0 8px}[data-design=sheet][data-page=deps] .section-body>.blk-facts{border:1.5px solid var(--frame)}[data-design=sheet][data-page=deps] .dp-gap{margin-top:24px}@media(max-width:480px){[data-design=sheet][data-page=deps] .bar-label{flex:none}[data-design=sheet][data-page=deps] .bar-label:before{display:block;margin:0 auto 3px}}.blk-tbl{margin-top:18px;min-width:0}.blk-tbl:first-child{margin-top:0}.blk-tbl-h{display:flex;gap:4px 12px;align-items:baseline;flex-wrap:wrap;padding:10px 14px;background:var(--surface);border:1px solid var(--line);border-bottom:0;border-radius:var(--r-lg) var(--r-lg) 0 0}.blk-tbl-h h3{margin:0;font-size:15.5px;font-weight:700}.blk-tbl-note{font-size:13.5px;color:var(--ink-2);flex:1 1 200px;min-width:0}.blk-tbl-act{margin-left:auto}.blk-tbl .table-wrap{border-top-left-radius:0;border-top-right-radius:0}.blk-tbl>.card:not(.table-wrap),.blk-tbl>.blk-facts{border-top-left-radius:0;border-top-right-radius:0}.blk-tbl>.blk-facts{border:1px solid var(--line);background:var(--surface)}.blk-tbl>.blk-note{margin:0;padding:9px 14px;border:1px solid var(--line);border-top:0;background:var(--surface-2);font-size:13.5px;color:var(--ink-2);border-radius:0 0 var(--r-lg) var(--r-lg);overflow-wrap:anywhere}.blk-tbl:has(>.blk-note) .table-wrap{border-bottom-left-radius:0;border-bottom-right-radius:0}@media(min-width:641px){.blk-tbl .table :is(th,td)+:is(th,td){border-left:1px solid var(--line)}}.blk-panel{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;min-width:0;display:flex;flex-direction:column}.blk-panel+.blk-panel,.blk-tbl+.blk-panel,.blk-panel+.blk-tbl{margin-top:18px}.columns>.blk-panel+.blk-panel,.columns>.blk-tbl+.blk-panel,.columns>.blk-panel+.blk-tbl,.columns>.blk-tbl+.blk-tbl{margin-top:0}.blk-panel-h{display:flex;gap:6px 12px;align-items:center;justify-content:space-between;flex-wrap:wrap;padding:10px 16px;background:var(--surface-2);border-bottom:1px solid var(--line)}.blk-panel-h h3{margin:0;font-size:15.5px;font-weight:700}.blk-panel-b{padding:14px 16px;display:flex;flex-direction:column;gap:12px;flex:1;min-width:0}.blk-panel-b>p{margin:0;overflow-wrap:anywhere}.blk-panel-b>.blk-facts{margin:-14px -16px 0;border-bottom:1px solid var(--line)}.blk-panel-b>.blk-facts:last-child{margin-bottom:-14px;border-bottom:0}.blk-bar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;padding:10px 16px;border-top:1px solid var(--line);background:var(--surface-2)}.blk-bar-flat{padding:0;border:0;background:none}.blk-how{margin:0;font-size:13.5px;color:var(--ink-2)}.blk-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));margin:0;overflow:hidden}.blk-fact{padding:9px 16px;border-right:1px solid var(--line);border-bottom:1px solid var(--line);margin:0 -1px -1px 0;min-width:0}.blk-fact dt{font-size:13px;font-weight:700;color:var(--ink-2)}.blk-fact dd{margin:2px 0 0;font-size:14.5px;overflow-wrap:anywhere}.blk-fact dd.num,.blk-fact .num{font-variant-numeric:tabular-nums}.blk-facts.is-figures .blk-fact dd{font-size:24px;font-weight:800;letter-spacing:-.01em;line-height:1.2}.blk-facts.is-figures .blk-fact dd small{display:block;font-size:13px;font-weight:400;letter-spacing:0;color:var(--ink-2);line-height:1.4}.blk-form{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:12px 16px;align-items:start}.blk-form .field{margin:0}.blk-out{overflow-wrap:anywhere}@media(max-width:480px){.blk-panel-h,.blk-panel-b,.blk-bar:not(.blk-bar-flat),.blk-tbl-h{padding-left:14px;padding-right:14px}.blk-panel-b>.blk-facts{margin-left:-14px;margin-right:-14px}.blk-facts{grid-template-columns:repeat(2,minmax(0,1fr))}.blk-fact{padding:9px 14px}.blk-facts>.blk-fact:last-child:nth-child(odd){grid-column:span 2}}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl{margin-top:24px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl:first-child{margin-top:0}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl-h{background:var(--strip);border:1.5px solid var(--frame);border-bottom:1px solid var(--line-strong);padding:10px 14px;min-height:42px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl-h h3{font-size:16px;font-weight:700}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl-note{font-size:14px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl .table-wrap,[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl>.card:not(.table-wrap),[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl>.blk-facts{border:1.5px solid var(--frame);border-top:0}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl>.blk-note{border:1.5px solid var(--frame);border-top:1px solid var(--line-strong);background:var(--strip);font-size:14px}@media(min-width:641px){[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl .table :is(th,td)+:is(th,td){border-left:1px solid var(--line-strong)}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl .table td{padding:10px 14px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl .th-btn{padding:9px 14px}}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-panel{border:1.5px solid var(--frame)}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-panel-h{background:var(--strip);border-bottom-color:var(--line-strong);min-height:42px;padding:9px 16px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-panel-h h3{font-size:16px;font-weight:700}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-panel-b{font-size:15px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-panel-b>.blk-facts{border-bottom-color:var(--line-strong)}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-bar:not(.blk-bar-flat){background:var(--strip);border-top-color:var(--line-strong)}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-how{font-size:14px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-fact{border-color:var(--line-strong);padding:10px 16px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-fact dt{font-size:14px;font-weight:600;color:var(--ink-2)}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-fact dd{font-size:15px;color:var(--ink)}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-facts.is-figures .blk-fact dd{font-family:var(--display);font-weight:700;font-size:32px;letter-spacing:0;line-height:1.1;margin-top:4px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-facts.is-figures .blk-fact dd small{font-family:var(--font);font-size:14px;font-weight:400;margin-top:2px}.blk-link{background:none;border:0;padding:0;font:inherit;color:var(--accent);cursor:pointer;text-decoration:underline;text-underline-offset:3px}.blk-link:hover{text-decoration-thickness:2px}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import{a as b,b as x,c as F,d as re}from"../chunks/chunk-GPVLJRZD.js";import"../chunks/chunk-PABICGBL.js";import{i as se,j as oe}from"../chunks/chunk-GQYFN4PE.js";import{a as M}from"../chunks/chunk-3ROQFHPV.js";import{g as ne}from"../chunks/chunk-CVB26NMC.js";import"../chunks/chunk-S2ZBMEVA.js";import"../chunks/chunk-ASYNLVOP.js";import{a as ae}from"../chunks/chunk-LAAGCPU3.js";import{a as y}from"../chunks/chunk-6RLVD2DT.js";import"../chunks/chunk-T7QVTNXF.js";import"../chunks/chunk-RPXHAG6F.js";import"../chunks/chunk-45PGAQRO.js";import{d as ee}from"../chunks/chunk-4WKQTWUC.js";import"../chunks/chunk-VQ5YXO54.js";import{b as D,c as $,d as U,e as C,h as G,i as w}from"../chunks/chunk-76WAVKUG.js";import{d as L,e as te,h as z}from"../chunks/chunk-VWKSMFWJ.js";import{I as h,L as W,N as I,O as j,P as S,R as Z,a as m,c as _,k as B,o as P,r as k,w as e,x as H,y as X,z as A}from"../chunks/chunk-GT4EAKAO.js";import{d as v}from"../chunks/chunk-G7NGB5UX.js";v();v();var $e={met:["good","met"],missing:["bad","missing"],unknown:["neutral","can't read"]},Pe={S:"small",M:"medium",L:"large"},le=a=>a?Pe[a]||a:"\u2013",R=(a,n="yes",s="no")=>a?e(h,{tone:"good",children:n}):a===!1?e(h,{tone:"watch",children:s}):e(h,{tone:"neutral",children:"can't read"}),q=a=>a&&a.length?k(a.join(", "),80):e("span",{class:"muted",children:"none"});function ie({sec:a}){let n=(a.history||[]).filter(([,d])=>d!=null),s=n[0],c=n[n.length-1],p=a.probes||[],i=d=>p.filter(t=>t.result===d).length;return e(F,{class:"is-figures",rows:[["Score",e(m,{children:[a.score==null?"\u2013":a.score,e("small",{children:a.score==null?"not scored yet":"of 10, weighed by risk"})]})],n.length>1&&s&&c?["Weekly score",e("span",{class:"sec-trend",children:[e(ae,{values:n.map(([,d])=>d),tone:c[1]>=s[1]?"good":"bad",label:`Weekly score from ${s[1]} to ${c[1]}`}),e("small",{children:[s[1]," on ",P(s[0])," to ",c[1]," now, ",n.length," weeks kept"]})]})]:null,["Failing",e(m,{children:[i("fail"),e("small",{children:"checks to fix"})]})],["Passing",e(m,{children:[i("pass"),e("small",{children:"checks"})]})],["Can't read",e(m,{children:[i("unknown"),e("small",{children:"need more access"})]})]]})}function ce({f:a,repo:n}){let s=a.branch||{},c=a.tree||{},p=a.workflows||{},i=a.policy||{},d=a.releases||{};return e(m,{children:[e(b,{title:"Branch protection on the default branch",note:`Level ${s.level??"\u2013"} of 5, counted up to the first tier not met`,children:[e(y,{name:"branch-tiers",caption:"Branch protection tiers",search:!1,rows:s.tiers||[],columns:[{key:"tier",label:"Tier",num:!0},{key:"rule",label:"Rule",primary:!0},{key:"state",label:"State",filter:!0,render:t=>{let[g,l]=$e[t.state]||["neutral",t.state];return e(h,{tone:g,children:l})}}]}),e("p",{class:"blk-note",children:s.readable?"Read from rulesets and the branch's public summary; a tier that needs an admin token to read says can't read, never met.":"Nothing about protection was readable with this token. Rulesets are readable by anyone with read access; classic protection needs an admin token."})]}),e(b,{title:"What the repo holds",note:`${p.files??0} workflow files read`,children:e(y,{name:"security-facts",caption:"Security facts",search:!1,rows:[{what:"Committed binaries",has:(c.binaries||[]).length?e(h,{tone:"bad",children:[(c.binaries||[]).length," found"]}):e(h,{tone:"good",children:"none"}),detail:q(c.binaries),note:"By file name (Scorecard's list) and, for executable files with no extension, by reading their first bytes."},{what:"SBOM files",has:R(!!((c.sbom_files||[]).length||(d.sbom||[]).length)),detail:e(m,{children:[q(c.sbom_files),(d.sbom||[]).length?`; attached to ${d.sbom.join(", ")}`:""]})},{what:"Fuzzing",has:R(!!((c.fuzzing||[]).length||a.oss_fuzz)),detail:e(m,{children:[q(c.fuzzing),a.oss_fuzz?"; listed in OSS-Fuzz":""]})},{what:"Security policy",has:R(!!i.file),detail:i.file?e("span",{class:"mono-soft",children:i.file.replace(`${n}:`,"")}):e("span",{class:"muted",children:"no SECURITY.md here or in the owner's .github repo"})},{what:"Policy names a contact",has:i.file?R(i.contact):e("span",{class:"muted",children:"\u2013"}),detail:"An email address, a link or GitHub's security advisories"},{what:"Policy says how disclosure works",has:i.file?R(i.disclosure):e("span",{class:"muted",children:"\u2013"}),detail:"Words about reporting, vulnerabilities or a timeline"},{what:"Private vulnerability reporting",has:R(i.private_reporting,"on","off"),detail:"GitHub's Report a vulnerability button"},{what:"Signed releases",has:d.releases?R((d.signed||[]).length>0):e("span",{class:"muted",children:"no releases"}),detail:d.releases?`${(d.signed||[]).length} of the last ${d.releases} carry a signature or provenance file${(d.signed||[]).length?` (${d.signed.join(", ")})`:""}`:"\u2013"},{what:"Code scanning on pull requests",has:R((p.sast||[]).length>0),detail:q(p.sast)},{what:"Published by a workflow",has:R((p.publish||[]).length>0),detail:q(p.publish)},{what:"OpenSSF Best Practices badge",has:a.badge?e(h,{tone:"good",children:a.badge}):e(h,{tone:"neutral",children:"none"}),detail:"From bestpractices.dev"}],columns:[{key:"what",label:"Check",primary:!0,render:t=>e(m,{children:[t.what,t.note?e("div",{class:"muted small",children:t.note}):null]})},{key:"has",label:"Found",raw:()=>"",render:t=>t.has},{key:"detail",label:"Where",raw:()=>"",render:t=>t.detail}]})})]})}v();v();function J(a){let n=a.trim(),s=/^(npm|pypi|go|maven|cargo|crates\.io|nuget|rubygems|packagist|pub|hex):(.+)$/i.exec(n);return s?{eco:s[1].toLowerCase(),name:s[2].trim()}:{eco:null,name:n}}var Y=a=>(a||"").trim().toLowerCase().replace(/_/g,"-"),K=(a,n)=>!a||!n||Y(n).replace("crates.io","cargo")===a.replace("crates.io","cargo");function de(a,n,s){let{eco:c,name:p}=J(a),i=Y(p);if(!i)return[];let d=[],t=o=>Y(o)===i;for(let o of n.vulns?.open||[]){if(!t(o.package)||!K(c,o.ecosystem))continue;let r=o.malware?"malware":o.severity||"unrated";d.push({id:`v-${o.id}-${o.file}`,area:"Known vulnerability",where:o.id,href:`https://osv.dev/vulnerability/${encodeURIComponent(o.id)}`,detail:`${o.version||"?"} in ${o.file||"a lockfile"}; ${o.fixed?`fixed in ${o.fixed}`:"no fix yet"}${o.ignored?"; ignored in osv-scanner.toml":""}`,tone:o.malware||r==="critical"||r==="high"?"bad":r==="moderate"?"watch":"neutral",state:r})}let g=new Set((n.deps?.security_overdue||[]).map(o=>o.pr));for(let o of n.deps?.open||[])!t(o.package)||!K(c,o.ecosystem)||d.push({id:`pr-${o.pr}`,area:"Update pull request",where:`#${o.pr}`,href:`https://github.com/${s}/pull/${o.pr}`,detail:`${o.from||"?"} to ${o.to||"?"}${o.bump?` (${o.bump})`:""}${o.decision?`; accepted until ${o.decision.until}`:""}`,tone:g.has(o.pr)?"bad":o.security?"watch":"neutral",state:g.has(o.pr)?"security, overdue":o.security?"security update":"open"});for(let o of n.supply?.licences?.denied||[])!t(o.name)||!K(c,o.system)||d.push({id:`lic-${o.name}`,area:"Licence rule",where:o.licence||"?",detail:`${o.version||""} is under a licence this repo's rule denies`.trim(),tone:"bad",state:"against the rule"});for(let o of n.supply?.drift||[])t(o.name)&&d.push({id:`drift-${o.name}`,area:"Licence change",where:`${o.from} to ${o.to}`,detail:`between ${o.from_quarter||"?"} and ${o.to_quarter||"?"}`,tone:"watch",state:"changed"});let l=n.supply?.scorecards||{},f=[["fell",l.drops||[]],["low",l.low||[]],["policy",l.below_policy||[]]];for(let[o,r]of f)for(let u of r){if(!u.packages.some(t))continue;let O=o==="fell"?u.after:u.score;d.push({id:`sc-${o}-${u.project}`,area:"Scorecard",where:u.project.replace("github.com/",""),href:`https://${u.project}`,detail:o==="fell"?`fell from ${u.before} to ${O}`:o==="policy"?`${O}, under this repo's minimum`:`${O}, under 4`,tone:"watch",state:o==="fell"?"dropped":"low"})}for(let o of n.supply?.reachability?.findings||[])t(o.module)&&d.push({id:`reach-${o.id}`,area:"Reachability (Go)",where:o.id,href:`https://pkg.go.dev/vuln/${o.id}`,detail:o.reach==="called"?`your code calls it${(o.path||[]).length?` via ${(o.path||[]).join(" \u2192 ")}`:""}`:o.reach==="imported"?"your code imports the package":"only required by the module",tone:o.reach==="called"?"bad":"neutral",state:o.reach});return d}function pe(a){let n=new Set;for(let c of a.vulns?.open||[])n.add(c.package);for(let c of a.deps?.open||[])c.package&&n.add(c.package);for(let c of a.supply?.licences?.denied||[])n.add(c.name);for(let c of a.supply?.drift||[])n.add(c.name);let s=a.supply?.scorecards||{};for(let c of[...s.drops||[],...s.low||[],...s.below_policy||[]])for(let p of c.packages)n.add(p);return[...n].sort((c,p)=>c.localeCompare(p))}function ue(a){let{eco:n,name:s}=J(a),c=s.replace(/[^\w@./:-]/g,"")||"<package>";return`stackvitals deps check ${n==="npm"||n==="pypi"?`${n}:${c}`:c}`}v();var V=".github/workflows/stackvitals-code-scanning.yml",he="stackvitals-code-scanning",me="actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4",Ce="github/codeql-action/upload-sarif@v4",Re=a=>JSON.stringify(a);function fe(a,n="StackVitals"){let s=(c,p,i)=>[` - name: ${c}`,` if: hashFiles('stackvitals-data/docs/${p}') != ''`,` uses: ${Ce}`," with:",` sarif_file: stackvitals-data/docs/${p}`,` category: ${i}`," ref: refs/heads/${{ github.event.repository.default_branch }}"," sha: ${{ github.sha }}"];return[`# Made in the StackVitals app (Dependencies, "GitHub's Security tab") for ${a}.`,"# After each StackVitals run, uploads its findings to code scanning: known vulnerabilities in every lockfile","# (vulns.sarif) and the weekly security checks (security.sarif), read from the stackvitals-data branch.","name: StackVitals code scanning","on:"," workflow_run:",` workflows: [${Re(n)}]`," types: [completed]"," workflow_dispatch:","permissions:"," contents: read # reads the stackvitals-data branch"," security-events: write # uploads the results to code scanning","jobs:"," upload:"," # Runs on the default branch's data only: not after the dependency check on a pull request."," if: >-"," github.event_name == 'workflow_dispatch' ||"," (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event != 'pull_request')"," runs-on: ubuntu-latest"," steps:",` - uses: ${me}`," with:"," ref: stackvitals-data"," path: stackvitals-data"," persist-credentials: false",...s("Known vulnerabilities","vulns.sarif","stackvitals-vulns"),...s("Security checks","security.sarif","stackvitals-security")].join(`
|
|
2
|
+
`)+`
|
|
3
|
+
`}function ge(a){let n=[` - uses: ${me}`," with:"," persist-credentials: false"];return a==="govulncheck"?{input:" govulncheck-json: govulncheck.json",steps:[...n,` - uses: ${se}`," with:"," go-version-file: go.mod"," - name: Which advisories your code calls"," run: |"," go install golang.org/x/vuln/cmd/govulncheck@latest"," govulncheck -format json ./... > govulncheck.json || true"].join(`
|
|
4
|
+
`)+`
|
|
5
|
+
`}:{input:" imports-json: imports.json",steps:[...n,...oe(6)," - name: Which packages your code imports"," run: stackvitals deps imports . --out imports.json"].join(`
|
|
6
|
+
`)+`
|
|
7
|
+
`}}function be(a,n,s){return`https://github.com/${a}/edit/${encodeURIComponent(n)}/${s.split("/").map(encodeURIComponent).join("/")}`}function ye({app:a,sast:n}){let s=fe(a.repo);return e(x,{title:"Show findings in GitHub's Security tab",state:e(h,{tone:"neutral",icon:null,children:"a new workflow file"}),children:[e(F,{rows:[["What it adds",e("span",{class:"mono-soft",children:V})],["When it runs","After each StackVitals run on the default branch, or by hand"],["What it uploads","Known vulnerabilities (vulns.sarif) and the security checks (security.sarif)"],["Where they show","Security tab, Code scanning, beside CodeQL's alerts"],["Private repos",a.visibility==="private"?"Needs GitHub Code Security on this repo":"Needs GitHub Code Security (public repos have it free)"],(n||[]).length?["Code scanning here now",(n||[]).join(", ")]:null]}),e("p",{children:"Each run already writes both files to the stackvitals-data branch. This workflow reads them there and uploads them; your own code is never checked out."}),e(re,{app:a,needs:"Contents, Pull requests and Workflows: read and write",f:{file:V,content:s,branch:he,message:"Upload StackVitals findings to code scanning",title:"Show StackVitals findings in code scanning",body:`Adds \`${V}\`, made in the StackVitals app. After each StackVitals run it uploads the known vulnerabilities and security checks from the stackvitals-data branch to code scanning (it needs security-events: write).`}})]})}var Fe=[{key:"area",label:"Found in",filter:!0,primary:!0},{key:"where",label:"Item",raw:a=>a.where,render:a=>a.href?e("a",{href:X(a.href),target:"_blank",rel:"noopener noreferrer",children:a.where}):a.where},{key:"state",label:"State",raw:a=>a.state,render:a=>e(h,{tone:a.tone,icon:null,children:a.state})},{key:"detail",label:"Detail",raw:a=>a.detail}];function ke({app:a,f:n}){let s=pe(n),[c,p]=_(""),[i,d]=_(null),t=i?de(i,n,a.repo):[],g=(f=c)=>{f.trim()&&d(f.trim())},l=i?J(i).name:"";return e(x,{title:"Check a package",state:e(h,{tone:"good",icon:null,children:"in the app"}),children:[e("form",{class:"blk-form dp-check-form",onSubmit:f=>{f.preventDefault(),g()},children:[e(L,{label:"Package name",hint:"A name like lodash, or npm:lodash and pypi:requests to name the ecosystem.",children:f=>e(te,{id:f,mono:!0,value:c,onInput:p,placeholder:"lodash",maxLength:200})}),e("div",{class:"dp-form-go",children:e(A,{type:"submit",variant:"filled",icon:"search",disabled:!c.trim(),children:"Check it"})})]}),s.length?e("p",{class:"blk-how",children:["Flagged in this repo: ",s.slice(0,12).map((f,o)=>e(m,{children:[o?", ":"",e("button",{type:"button",class:"blk-link mono-soft",onClick:()=>{p(f),g(f)},children:f})]})),s.length>12?` and ${s.length-12} more`:"","."]}):null,i?e("div",{class:"blk-out","aria-live":"polite",children:t.length?e(y,{name:"package-check",caption:`What this repo's files say about ${l}`,search:!1,rows:t,columns:Fe}):e(S,{icon:"check",title:`Nothing flagged for ${l}.`,children:"No open vulnerability, update pull request, licence rule, licence change or Scorecard drop names it here. These files list only packages with a finding, so this can't say whether the repo uses it."})}):null,e("p",{class:"blk-how",children:"Answered from this repo's own files (vulnerabilities, update pull requests, supply chain), no token and nothing sent."}),i?e(j,{inline:!0,code:ue(i),note:"The verdict (use, monitor or investigate) weighs the public crawl's upgrade outcomes and maintainer history, which the app doesn't load, so it runs from your machine."}):null]})}function we({v:a}){let n=a?.files||[],[s,c]=_(n[0]||"package-lock.json"),p=`stackvitals deps scan ${/\s/.test(s)?JSON.stringify(s):s}`;return e(x,{title:"Scan a lockfile",state:e(h,{tone:"neutral",icon:null,children:"from your machine"}),children:[e("p",{children:"Every package in a lockfile with a verdict, the ones to look at first on top. It runs on your machine because it reads a lockfile you haven't pushed yet and weighs the public crawl's evidence, which the app doesn't load."}),n.length?e(L,{label:"Lockfile",hint:"The lockfiles the last scan read in this repo.",children:i=>e(z,{id:i,value:s,onChange:c,options:n.map(d=>({value:d,label:d}))})}):null,e(j,{inline:!0,code:p,note:"Add --all to list the packages with a use verdict too, --json for the raw evidence. pip install stackvitals first."}),e("p",{class:"blk-how",children:"In the repo this already happens by itself: a pull request that changes a lockfile gets the dependency check from the StackVitals workflow."})]})}var _e={go:"govulncheck",npm:"imports",pypi:"imports",cargo:"imports","crates.io":"imports",maven:null,nuget:null};function ve({app:a,v:n,s}){let c=new Set([...(n?.open||[]).map(u=>(u.ecosystem||"").toLowerCase()),...Object.keys(s?.systems||{}).map(u=>u.toLowerCase())]),p=c.has("go")?"govulncheck":"imports",[i,d]=_(p),t=[...c].filter(u=>u&&u!=="actions"&&_e[u]===null),{steps:g,input:l}=ge(i),f=`.github/workflows/${a.links?.workflow||"stackvitals.yml"}`,o=a.default_branch||"main",r=i==="govulncheck"?!!s?.reachability?.findings?.length:(n?.open||[]).some(u=>u.imported===!0||u.imported===!1);return e(x,{title:"Fill the Your code column",state:r?e(h,{tone:"good",icon:null,children:"on"}):e(h,{tone:"neutral",icon:null,children:"a workflow edit"}),bar:e(m,{children:[e(A,{variant:"filled",href:be(a.repo,o,f),external:!0,icon:"external",children:"Edit the workflow on GitHub"}),e(I,{text:g,label:"Copy the steps"}),e(I,{text:l.trim(),label:"Copy the input"})]}),children:[e("p",{children:["Whether your own code reaches a vulnerable package. It reads your code, so it runs in your workflow: add these steps before the StackVitals step, and the input under its ",e("span",{class:"mono-soft",children:"with:"}),". The workflow file already exists, so GitHub's editor opens on it; paste and commit there."]}),e(L,{label:"Tool",hint:i==="govulncheck"?"Go: call paths from govulncheck (your code calls it, imports it, or only requires it).":"Python, JavaScript and Rust: whether your code imports the package (an import, not a call path).",children:u=>e(z,{id:u,value:i,onChange:d,options:[{value:"imports",label:"Imports (Python, JavaScript, Rust)"},{value:"govulncheck",label:"govulncheck (Go)"}]})}),e(j,{title:`Steps for ${f}`,code:g}),e(j,{inline:!0,code:l.trim(),note:"Under the StackVitals step's with:"}),t.length?e("p",{class:"blk-how",children:["No free tool tells this for ",t.join(", ")," yet, so those rows stay unknown."]}):null,i==="imports"?e("p",{class:"blk-how",children:["The same list from your machine: ",e("span",{class:"mono-soft",children:"stackvitals deps imports . --out imports.json"}),"."]}):null]})}var T={critical:0,high:1,moderate:2,low:3,unrated:4},je={critical:"bad",high:"bad",moderate:"watch"},E={target:"_blank",rel:"noopener noreferrer"},Q=(a,n)=>e(ne,{app:a,kind:"package",id:n,verbs:["accept"],label:"Accept the risk"}),Se=({name:a,version:n})=>e(m,{children:[e("span",{class:"mono-soft",children:k(a,50)})," ",e("span",{class:"muted",children:n||""})]}),xe={title:"Your code",doc:"modules.md#dependency-steward",body:e(m,{children:["Whether your code reaches the vulnerable part. ",e("b",{children:"Calls it"}),": govulncheck found a call path (Go). ",e("b",{children:"Imports the package"}),": an import, never a call path (Python, JavaScript and Rust). ",e("b",{children:"Unknown"})," means no free tool tells it for that language yet, so it says unknown rather than guess. Fix what your code calls first."]})};function Oe({app:a,v:n,s}){let[c,p]=_(null),i=n.open||[],d=Object.fromEntries((s?.reachability?.findings||[]).flatMap(r=>[r.id,...r.aliases||[]].map(u=>[u,r.reach]))),t=r=>d[r.id]||(r.aliases||[]).map(u=>d[u]).find(Boolean)||(r.imported===!0?"imports it":r.imported===!1?"no direct import":"unknown"),g=["critical","high","moderate","low","unrated"],l=[{label:"Malware",value:i.filter(r=>r.malware).length,tone:"bad"},...g.map(r=>({label:r[0].toUpperCase()+r.slice(1),value:i.filter(u=>!u.malware&&(T[u.severity||""]??4)===T[r]).length,tone:T[r]<=1?"bad":T[r]===2?"watch":"neutral"}))].filter(r=>r.value||r.label!=="Malware"&&r.label!=="Unrated"),f=i.filter(r=>["called","imports it","imported"].includes(t(r))).length,o=c===null?null:l[c];return e(m,{children:[e(U,{ratio:"lead",children:[e(C,{children:e("div",{class:"hero",children:[e("div",{class:"hero-name",children:[e("span",{children:o?`${o.label}${o.label==="Malware"?"":" severity"}`:"Open vulnerabilities"}),e(W,{info:xe})]}),e("div",{class:"hero-num num",children:o?o.value:i.length}),e("div",{class:"hero-meta",children:e("span",{class:"hero-date",children:o?`of ${i.length} open`:`in ${new Set(i.map(r=>`${r.ecosystem}:${r.package}`)).size} packages, ${(n.closed||[]).length} fixed lately`})}),e("p",{class:"hero-meaning",children:i.length?f?`${f} of them sit in code your project imports or calls. Fix ${f===1?"that one":"those"} first.`:"None is known to sit in code your project imports or calls.":"No known vulnerability in this repo's lockfiles."}),i.length?e(M,{label:"Open vulnerabilities by severity",bars:l,height:170,onPick:p}):null]})}),e(G,{children:[e(w,{label:"Open",value:i.length}),e(w,{label:"Malware",value:i.filter(r=>r.malware).length}),e(w,{label:"Critical or high",value:i.filter(r=>(T[r.severity||""]??4)<=1).length}),e(w,{label:"With a fix",value:i.filter(r=>r.fixed).length}),e(w,{label:"Fixed lately",value:(n.closed||[]).length})]})]}),e(b,{title:"Open vulnerabilities",note:"Most severe first; filter by ecosystem, severity or your code.",children:[e(y,{name:"vulnerabilities",caption:"Known vulnerabilities",rows:i,initialSort:{key:"severity",desc:!1},columns:[{key:"package",label:"Package",primary:!0,nowrap:!0,render:r=>e(m,{children:[e("span",{class:"mono-soft",children:k(r.package,50)}),r.dev?e("div",{class:"muted small",children:"development only"}):null]})},{key:"version",label:"Version",nowrap:!0,render:r=>r.version||"\u2013"},{key:"ecosystem",label:"Ecosystem",filter:!0},{key:"id",label:"Advisory",nowrap:!0,render:r=>e(m,{children:[e("a",{href:`https://osv.dev/vulnerability/${encodeURIComponent(r.id)}`,...E,children:r.id}),r.ignored?e(m,{children:[" ",e("span",{title:`${r.ignored.reason||"no reason given"}${r.ignored.until?`, until ${r.ignored.until}`:""}`,children:e(h,{tone:"neutral",children:"ignored"})})]}):null]})},{key:"severity",label:"Severity",filter:!0,raw:r=>r.malware?-1:T[r.severity||""]??4,render:r=>r.malware?e(h,{tone:"bad",children:"malware"}):e(h,{tone:je[r.severity||""]||"neutral",icon:null,children:r.severity||"unrated"})},{key:"fixed",label:"Fixed in",nowrap:!0,render:r=>r.fixed?e("span",{title:r.fix_plan?.command,children:[r.fixed,r.fix_plan?e("span",{class:"muted small",children:[" (",r.fix_plan.bump," jump)"]}):null]}):e("span",{class:"muted",children:"no fix yet"})},{key:"reach",label:"Your code",filter:!0,raw:t,render:r=>{let u=r.reach?.level||t(r),O=r.reach?.path?.join(" > ");return u==="called"?e("b",{title:O,children:"calls it"}):u==="imported"||u==="imports it"?e("span",{title:O,children:"imports the package"}):u==="required"?"only requires it":u==="no direct import"?e("span",{class:"muted",title:"It may still run through another package",children:"no direct import"}):e("span",{class:"muted",children:"unknown"})}},{key:"file",label:"Lockfile",nowrap:!0,render:r=>e("span",{class:"mono-soft small",children:k(r.file,40)})},{key:"first_seen",label:"Open since",nowrap:!0,render:r=>P(r.first_seen)},{key:"accept",label:"Decide",raw:()=>"",render:r=>Q(a,r.package)}],empty:e(C,{children:e(S,{icon:"check",title:"No known vulnerability in this repo's lockfiles."})})}),e("p",{class:"blk-note",children:"Your code comes from govulncheck (Go) or the imports list (Python, JavaScript, Rust) run in your workflow; without them it says unknown rather than guess."})]}),e(b,{title:"Where this comes from",children:e(F,{rows:[["Scanner",n.scanner||"\u2013"],["Lockfiles read",e("span",{class:"num",children:(n.files||[]).length})],["Branch",e("span",{class:"mono-soft",children:n.ref||"\u2013"})],["Scanned",P(n.generated)],["Advisories",n.offline?"OSV's downloaded databases (no package names sent)":"OSV"],n.skipped?.length?["Too large to scan",e("span",{class:"mono-soft",children:n.skipped.join(", ")})]:null]})})]})}var Te=a=>Object.entries(a.by_licence||{}).slice(0,3).map(([n,s])=>`${n} ${s}`).join(", ")||null;function Ae({app:a,s:n}){let s=n.licences||{},c=n.scorecards||{},p=n.policy?.deny||[],i=[...(c.drops||[]).map(t=>({...t,kind:"dropped",now:t.after})),...(c.low||[]).map(t=>({...t,kind:"low",now:t.score})),...(c.below_policy||[]).map(t=>({...t,kind:"policy",now:t.score}))],d=n.reachability;return e(m,{children:[e(F,{class:"is-figures dp-supply-facts",rows:[["Packages",e(m,{children:[n.packages??"\u2013",e("small",{children:Object.entries(n.systems||{}).map(([t,g])=>`${t.toLowerCase()} ${g}`).join(", ")||"\u2013"})]})],["Against the licence rule",e(m,{children:[p.length?(s.denied||[]).length:"\u2013",e("small",{children:p.length?`${(s.accepted||[]).length} accepted by a decision`:e("a",{href:"#/settings?module=deps",children:"Set a rule"})})]})],["No known licence",e(m,{children:[s.unknown??"\u2013",e("small",{children:"packages"})]})],["Licences in use",e(m,{children:[Object.keys(s.by_licence||{}).length||"\u2013",e("small",{children:Te(s)||"\u2013"})]})],["Scorecard drops",e(m,{children:[(c.drops||[]).length,e("small",{children:[c.checked??0," of ",c.projects??0," projects scored"]})]})]]}),e(b,{title:"Where this comes from",children:e(F,{rows:[["Package list","GitHub's SBOM of the default branch"],["Read on",P(n.generated)],["Licences and Scorecards",n.outside_lookups===!1?"Outside lookups off (docs/privacy.md): none":"deps.dev and OpenSSF Scorecard"],["Licence rule",p.length?e("span",{class:"mono-soft",children:p.join(", ")}):e("a",{href:"#/settings?module=deps",children:"None set"})],n.policy?.min_scorecard!=null?["Minimum Scorecard",e("span",{class:"num",children:n.policy.min_scorecard})]:null]})}),(s.denied||[]).length?e(b,{title:"Against the licence rule",note:"Accept one to keep it, with the reason in the log.",children:e(y,{name:"licence-denied",caption:"Licences against the rule",rows:s.denied,columns:[{key:"name",label:"Package",primary:!0,nowrap:!0,render:t=>e(Se,{name:t.name,version:t.version})},{key:"licence",label:"Licence"},{key:"system",label:"Ecosystem",filter:!0},{key:"accept",label:"Decide",raw:()=>"",render:t=>Q(a,t.name)}]})}):null,Object.keys(s.by_licence||{}).length?e(b,{title:"Licences in use",note:"Packages by licence, the twelve most used.",children:e(C,{children:e(M,{label:"Packages by licence",kind:"count",height:180,bars:Object.entries(s.by_licence).slice(0,12).map(([t,g])=>({label:t,value:g,tone:p.includes(t)?"bad":"accent"}))})})}):null,(n.drift||[]).length?e(b,{title:"Licences that changed",note:"Between quarters, from deps.dev.",children:e(y,{name:"licence-drift",caption:"Licence changes",rows:n.drift,columns:[{key:"name",label:"Package",primary:!0,render:t=>e("span",{class:"mono-soft",children:t.name})},{key:"from",label:"Was",render:t=>t.from},{key:"from_quarter",label:"Until"},{key:"to",label:"Now",render:t=>t.to},{key:"to_quarter",label:"Since"}]})}):null,i.length?e(b,{title:"Scorecards to look at",note:"OpenSSF Scorecard of the projects behind your packages.",children:[e(y,{name:"scorecards",caption:"Scorecards",rows:i,columns:[{key:"project",label:"Project",primary:!0,render:t=>e("a",{href:`https://${t.project}`,...E,children:t.project.replace("github.com/","")})},{key:"kind",label:"Why",filter:!0,raw:t=>t.kind,render:t=>t.kind==="dropped"?`fell from ${t.before} since ${P(t.since)}`:t.kind==="policy"?`under this repo's minimum (${n.policy?.min_scorecard})`:"under 4"},{key:"now",label:"Scorecard",num:!0},{key:"packages",label:"Used as",raw:t=>t.packages.join(" "),render:t=>e("span",{class:"mono-soft",children:k(t.packages.join(", "),60)})}]}),e("p",{class:"blk-note",children:"Scorecard is one input shown here; it never decides a package's verdict, which rests on upgrade outcomes, maintainers and advisories."})]}):null,(n.runtimes||[]).length?e(b,{title:"Runtimes",note:"Support dates from endoflife.date.",children:[e(y,{name:"runtimes",caption:"Runtimes",search:!1,rows:n.runtimes,columns:[{key:"runtime",label:"Runtime",primary:!0,nowrap:!0,render:t=>e("span",{class:"mono-soft",children:[t.runtime," ",t.version]})},{key:"use",label:"Use",filter:!0,raw:t=>t.use==="tests"?"tested on":"runs on"},{key:"status",label:"Status",filter:!0,render:t=>t.status==="end of life"?e(h,{tone:t.use==="tests"?"neutral":"bad",children:["out of support since ",t.eol||"?"]}):t.status==="behind the newest patch"?e(h,{tone:"watch",children:"behind the newest patch"}):t.status==="unknown"?e(h,{tone:"neutral",children:"not listed"}):e(h,{tone:"good",children:`supported${t.eol?` until ${t.eol}`:""}`})},{key:"latest",label:"Newest release",nowrap:!0,render:t=>t.latest?e(m,{children:[t.latest,t.latest_date?e("span",{class:"muted",children:[" (",P(t.latest_date),")"]}):null]}):"\u2013"},{key:"file",label:"Named in",nowrap:!0,render:t=>e("span",{class:"mono-soft small",children:k(t.file,50)})}]}),e("p",{class:"blk-note",children:"Lockfile scans don't see runtime flaws, and a line out of support gets no security fixes. Old lines in a test matrix are often kept on purpose, so only what the project runs on is marked red."})]}):null,d?.error?e("p",{class:"note note-watch small",children:["The govulncheck file couldn't be read: ",d.error]}):null,(d?.findings||[]).length?e(b,{title:"Reachability",note:`From ${d.scanner||"govulncheck"} in this repo's workflow: ${d.counts?.called??0} called, ${d.counts?.imported??0} imported only, ${d.counts?.required??0} required only.`,children:e(y,{name:"reachability",caption:"Reachability",rows:d.findings,columns:[{key:"id",label:"Advisory",primary:!0,nowrap:!0,render:t=>e("a",{href:`https://pkg.go.dev/vuln/${t.id}`,...E,children:t.id})},{key:"reach",label:"Your code",filter:!0,render:t=>t.reach==="called"?e("b",{children:"calls it"}):t.reach==="imported"?"imports the package":"only requires the module"},{key:"module",label:"Module",nowrap:!0,render:t=>e(Se,{name:t.module||"",version:t.version})},{key:"fixed",label:"Fixed in",render:t=>t.fixed||"no fix yet"},{key:"path",label:"How",raw:t=>(t.path||[]).join(" "),render:t=>e("span",{class:"mono-soft small",children:k((t.path||[]).join(" \u2192 "),60)})}]})}):null]})}var qe=a=>a.result==="pass"?"good":a.result==="fail"?a.risk==="critical"||a.risk==="high"?"bad":"watch":"neutral";function Le({app:a}){let n=ee({deps:"deps.json",supply:"supply.json",vulns:"vulns.json",security:"security.json"});if(!n.ready)return e(Z,{});let s=n.deps,c=n.supply,p=n.vulns,i=n.security,d=l=>e($,{title:"Before a package lands",description:"Check one package here, scan a lockfile from your machine, and let your workflow say whether your code reaches each vulnerability.",children:[e(ke,{app:l,f:{deps:s,supply:c,vulns:p}}),e("div",{class:"dp-gap",children:e(U,{children:[e(we,{v:p}),e(ve,{app:l,v:p,s:c})]})})]});if(!a||!s&&!c&&!p&&!i)return e(D,{title:"Dependencies",subtitle:"Update pull requests, security updates and supply-chain checks.",children:[e(S,{icon:"deps",title:"No update bot has opened a pull request here.",children:"Dependabot or Renovate updates appear here once they do; a pull request that changes a lockfile also gets the dependency check."}),a?d(a):null]});let t=s?.open||[],g=l=>`https://github.com/${a.repo}/pull/${l}`;return e(D,{title:"Dependencies",subtitle:s?`Update pull requests from ${(s.bots||[]).join(" and ")||"the update bots"}, from the history StackVitals keeps.`:"Known vulnerabilities and the supply chain of this repo's dependencies.",children:[(s?.quiet_days??0)>=30?e("div",{class:"note note-watch",children:[e(H,{name:"clock"}),e("span",{children:["No update pull request for ",s.quiet_days," days. Dependabot pauses itself after 90 days without interaction; check its settings if this repo still has dependencies to update."]})]}):null,i?e($,{title:"This repo's own security",description:"OpenSSF Scorecard's checks, run by your workflow on this repo (private repos too), each with what to do.",action:e(h,{tone:i.score==null?"neutral":i.score>=7?"good":i.score>=4?"watch":"bad",children:i.score==null?"not scored":`${i.score} of 10`}),children:[i.weakened?e("div",{class:"note note-watch",children:[e(H,{name:"warn"}),e("span",{children:"Branch protection is weaker than last week."})]}):null,e(ie,{sec:i}),e(b,{title:"Checks",note:"Failing first; each with the risk, the effort and what to do.",children:e(y,{name:"security-probes",caption:"Security checks",rows:i.probes||[],initialSort:{key:"result",desc:!1},columns:[{key:"probe",label:"Check",primary:!0,raw:l=>l.why,render:l=>e(m,{children:[e("span",{children:l.why}),e("div",{class:"muted small mono-soft",children:l.probe})]})},{key:"result",label:"Result",filter:!0,render:l=>e(h,{tone:qe(l),children:l.result==="unknown"?"can't read":l.result})},{key:"risk",label:"Risk",filter:!0},{key:"effort",label:"Effort to fix",filter:!0,raw:l=>le(l.effort)},{key:"fix",label:"What to do",render:l=>l.result==="fail"?e(m,{children:[e("span",{children:l.fix}),(l.findings||[]).slice(0,3).map(f=>e("div",{class:"muted small mono-soft",children:[f.path,f.job?` (${f.job})`:"",": ",k(f.detail,80)]}))]}):(l.guarded||[]).length?e("span",{class:"muted small",children:["guarded: ",l.guarded[0].guarded]}):e("span",{class:"muted",children:"\u2013"})}],empty:e(C,{children:e(S,{icon:"clock",title:"Not checked yet.",children:"The weekly security step fills this in on its next run."})})})}),i.facts?e(ce,{f:i.facts,repo:a.repo}):null]}):e($,{title:"This repo's own security",description:"OpenSSF Scorecard's checks, run by your workflow on this repo (private repos too).",children:e(C,{children:e(S,{icon:"clock",title:"Not checked yet.",children:"The security step runs once a week; this fills in after its first run with this version."})})}),p?e($,{title:"Known vulnerabilities",description:"In every lockfile, every ecosystem. Tap a bar to read one severity; fix what your code reaches first.",children:e(Oe,{app:a,v:p,s:c})}):e($,{title:"Known vulnerabilities",description:"In every lockfile, every ecosystem.",children:e(C,{children:e(S,{icon:"clock",title:"No scan yet.",children:["The vulnerability scan runs with the dependency check; this fills in after its next run. Turn it on under ",e("a",{href:"#/settings?module=deps",children:"Settings"})," if it's off."]})})}),s?e($,{title:"Update pull requests",description:"What the bots opened, oldest first. A security update open past 48 hours is overdue.",action:e(h,{tone:s.security_overdue?.length?"bad":"good",children:s.security_overdue?.length?`${s.security_overdue.length} overdue`:"None overdue"}),children:[e(G,{children:[e(w,{label:"Open updates",value:t.length}),e(w,{label:"Security updates past 48 h",value:(s.security_overdue||[]).length}),e(w,{label:"Merged, last 90 days",value:s.merged_90d??"\u2013"}),e(w,{label:"Time to merge, median",value:B(s.merge_median_hours)}),e(w,{label:"Last one opened",value:s.last_opened?P(s.last_opened):"\u2013",note:s.quiet_days!==void 0?`${s.quiet_days} days ago`:null})]}),e(b,{title:"Open update pull requests",note:"Longest open first.",children:e(y,{name:"dependency-updates",caption:"Open update pull requests",rows:t,initialSort:{key:"age_hours",desc:!0},columns:[{key:"pr",label:"PR",primary:!0,nowrap:!0,render:l=>e("a",{href:g(l.pr),...E,children:["#",l.pr]})},{key:"package",label:"Package",nowrap:!0,render:l=>e("span",{class:"mono-soft",children:k(l.package||"\u2013",50)})},{key:"security",label:"Kind",filter:!0,raw:l=>l.security?"security":"update",render:l=>l.security?e(h,{tone:"bad",children:"security"}):e(h,{tone:"neutral",children:"update"})},{key:"from",label:"From",nowrap:!0,raw:l=>l.from||"",render:l=>l.from||e("span",{class:"muted",children:"\u2013"})},{key:"to",label:"To",nowrap:!0,raw:l=>l.to||"",render:l=>l.to||e("span",{class:"muted",children:"not in the title"})},{key:"bump",label:"Change",filter:!0,raw:l=>l.bump||"",render:l=>l.bump||e("span",{class:"muted",children:"\u2013"})},{key:"ecosystem",label:"Ecosystem",filter:!0},{key:"age_hours",label:"Open for",num:!0,render:l=>B(l.age_hours)},{key:"decision",label:"Decision",raw:l=>l.decision?.verb||"",render:l=>l.decision?e("span",{class:"muted",children:["accepted until ",l.decision.until,l.decision.reason?`: ${l.decision.reason}`:""]}):l.package?Q(a,l.package):null}],empty:e(C,{children:e(S,{icon:"check",title:"No update is waiting."})})})}),e(x,{title:"Accept a known risk",state:e(h,{tone:"neutral",icon:null,children:"as a comment"}),children:[e("p",{children:"Takes a security update off the overdue list until the date, with the reason kept in the log. Accept the risk on a row does the same in one click; this is the comment form."}),e(j,{inline:!0,code:"/stackvitals accept package:<name> for 30d because <reason>",note:"Paste on any issue or pull request in this repo."})]})]}):null,c?e($,{title:"Supply chain",description:"Licences, licence changes, Scorecard drops, runtimes out of support and reachability.",info:e(W,{info:{title:"Supply chain",doc:"modules.md#dependency-steward",body:"Read from GitHub's SBOM of the default branch, with licences from deps.dev and OpenSSF Scorecards. Outside lookups can be turned off in the settings."}}),children:e(Ae,{app:a,s:c})}):e($,{title:"Supply chain",description:"Licences, licence changes, Scorecard drops, runtimes out of support and reachability.",children:e(C,{children:e(S,{icon:"clock",title:"Not read yet.",action:e(A,{size:"sm",variant:"tinted",href:"#/settings?module=deps",children:"Settings"}),children:"The supply chain shows once the dependency check reads GitHub's SBOM."})})}),e($,{title:"GitHub's Security tab",description:"The vulnerabilities and security checks as code scanning alerts, added as one new workflow file.",children:e(ye,{app:a,sast:i?.facts?.workflows?.sast})}),d(a)]})}export{Le as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.row-btns{display:inline-flex;gap:6px;align-items:center;flex-wrap:wrap}.ex-id{font-family:var(--mono);font-size:.92em;font-weight:600}.ex-ask{display:grid;grid-template-columns:minmax(240px,320px) minmax(0,1fr);gap:16px;align-items:start}.ex-qs{display:flex;flex-direction:column;gap:8px;min-width:0}.ex-q{display:flex;flex-direction:column;gap:3px;text-align:left;padding:11px 14px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface);color:var(--ink);font:inherit;cursor:pointer}.ex-q:hover{border-color:var(--line-strong)}.ex-q.is-on{border-color:var(--accent);background:var(--accent-soft)}.ex-q-t{font-size:15px;font-weight:700;line-height:1.3}.ex-q-s{font-size:14px;color:var(--ink-2);line-height:1.4}.ex-result{min-width:0}.ex-result .pn-body{padding:0}.ex-result .pn-body>.skeleton,.ex-result .pn-body>.empty{padding:16px}.ex-sql{flex-basis:100%;min-width:0}.ex-build{display:flex;flex-direction:column;gap:16px}.ex-form{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px 16px}.ex-form .field{margin:0}.ex-filters{display:flex;flex-direction:column;gap:8px;align-items:flex-start;border-top:1px solid var(--line);padding-top:12px}.ex-sub{margin:0;font-size:14px;font-weight:600;color:var(--ink-2)}.ex-filter{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,1fr) minmax(0,1.2fr) auto;gap:8px;align-items:center;width:100%}@media(max-width:900px){.ex-ask{grid-template-columns:minmax(0,1fr)}}@media(max-width:560px){.ex-filter{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}}[data-design=sheet][data-page=explore] .row-btns{gap:10px}[data-design=sheet][data-page=explore] .ex-q{border:1.5px solid var(--line-strong);border-radius:2px;padding:11px 14px}[data-design=sheet][data-page=explore] .ex-q:hover{border-color:var(--ink)}[data-design=sheet][data-page=explore] .ex-q.is-on{border-color:var(--frame);border-left:5px solid var(--sv-blue);background:var(--strip)}[data-design=sheet][data-page=explore] .ex-q-t{font-size:15.5px}[data-design=sheet][data-page=explore] .ex-q-s{font-size:14.5px}[data-design=sheet][data-page=explore] .ex-filters{border-top-color:var(--line-strong)}[data-design=sheet][data-page=explore] .ex-sub{color:var(--ink);font-weight:600}[data-design=sheet][data-page=explore] .stats.card{border:1.5px solid var(--frame)}.pn{min-width:0;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden}.pn-strip{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:4px 12px;min-height:42px;padding:9px 16px;background:var(--surface-2);border-bottom:1px solid var(--line)}.pn-title{margin:0;font-size:15.5px;font-weight:700;line-height:1.35;overflow-wrap:anywhere;display:flex;align-items:center;gap:8px;flex-wrap:wrap}.pn-meta{font-size:14px;font-weight:600;color:var(--ink-2);display:inline-flex;align-items:center;gap:10px;flex-wrap:wrap}.pn-body{padding:14px 16px;display:flex;flex-direction:column;gap:12px;min-width:0}.pn-body.is-flush{padding:0;gap:0}.pn-body>p{margin:0;font-size:15px;line-height:1.5}.pn-foot{display:flex;align-items:center;gap:8px 10px;flex-wrap:wrap;padding:10px 16px;border-top:1px solid var(--line)}.pn-foot .pn-hint{font-size:14px;color:var(--ink-2);flex-basis:100%}.pn .table-wrap.card{border:0;border-radius:0;box-shadow:none;background:transparent}.pn-good{border-top:4px solid var(--good)}.pn-watch{border-top:4px solid var(--watch)}.pn-bad{border-top:4px solid var(--bad)}.pn-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:0;margin:0}.pn-facts.cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.pn-fact{padding:8px 12px 8px 0;border-bottom:1px solid var(--line);min-width:0}.pn-fact dt{font-size:14px;font-weight:600;color:var(--ink-2)}.pn-fact dd{margin:2px 0 0;font-size:15.5px;font-weight:600;color:var(--ink);overflow-wrap:anywhere}.pn-steps{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}.pn-steps>li{display:grid;grid-template-columns:32px minmax(0,1fr) auto;gap:4px 14px;align-items:start;padding:14px 16px;border-top:1px solid var(--line)}.pn-steps>li:first-child{border-top:0}.pn-step-n{width:28px;height:28px;border-radius:50%;display:grid;place-items:center;background:var(--accent-soft);color:var(--accent);font-weight:700}.pn-step-h{margin:0;font-size:15.5px;font-weight:700}.pn-step-p{margin-top:3px;font-size:14.5px;line-height:1.5;color:var(--ink-2)}.pn-step-act{display:flex;gap:8px;flex-wrap:wrap;align-items:center;justify-content:flex-end}@media(max-width:640px){.pn-steps>li{grid-template-columns:28px minmax(0,1fr)}.pn-step-act{grid-column:2;justify-content:flex-start}.pn-facts.cols-2{grid-template-columns:minmax(0,1fr)}}[data-design=sheet] .pn{border:1.5px solid var(--frame);border-radius:2px}[data-design=sheet] .pn-strip{background:var(--strip);border-bottom:1px solid var(--line-strong);padding:9px 14px}[data-design=sheet] .pn-title{font-size:16px;font-weight:700;color:var(--ink)}[data-design=sheet] .pn-meta{font-size:14.5px;color:var(--ink-2)}[data-design=sheet] .pn-body{padding:14px}[data-design=sheet] .pn-body.is-flush{padding:0}[data-design=sheet] .pn-foot{padding:10px 14px;border-top:1px solid var(--line-strong)}[data-design=sheet] .pn-good{border-top:5px solid var(--sv-mint)}[data-design=sheet] .pn-watch{border-top:5px solid var(--sv-amber)}[data-design=sheet] .pn-bad{border-top:5px solid var(--sv-coral)}[data-design=sheet] .pn-fact dt{font-size:14px;font-weight:600;color:var(--ink-2)}[data-design=sheet] .pn-fact dd{font-size:16px;font-weight:600}[data-design=sheet] .pn-step-n{border-radius:2px;background:var(--ink);color:var(--bg);font-family:var(--display)}[data-design=sheet] .pn-steps>li.is-done .pn-step-n{background:var(--sv-mint);color:#000}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import{a as R}from"../chunks/chunk-TO4VVRZ7.js";import{a as G}from"../chunks/chunk-6RLVD2DT.js";import{a as ye,b as W}from"../chunks/chunk-4WKQTWUC.js";import{s as V}from"../chunks/chunk-VQ5YXO54.js";import{b as ke,c as O,e as Se,h as $e,i as I}from"../chunks/chunk-76WAVKUG.js";import{a as ne,d as _,e as Ce,h as S}from"../chunks/chunk-VWKSMFWJ.js";import{I as be,L as he,N as we,O as D,P as T,Q as te,R as xe,S as ve,a as Q,c as h,d as P,i as z,n as ee,o as ge,t as fe,w as t,z as k}from"../chunks/chunk-GT4EAKAO.js";import{d as L}from"../chunks/chunk-G7NGB5UX.js";L();L();L();var Te=[{value:"=",label:"is"},{value:"!=",label:"is not"},{value:">",label:"more than"},{value:">=",label:"at least"},{value:"<",label:"less than"},{value:"<=",label:"at most"},{value:"contains",label:"contains"},{value:"set",label:"has a value"},{value:"unset",label:"is empty"}],Oe=[{value:"median",label:"Median"},{value:"avg",label:"Average"},{value:"sum",label:"Sum"},{value:"min",label:"Smallest"},{value:"max",label:"Largest"},{value:"count",label:"Count with a value"}],N=e=>!!e&&/_s$/.test(e),Be=e=>typeof e=="string"&&/^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/.test(e);function J(e){let n=[];for(let s of e)for(let r of Object.keys(s))n.includes(r)||n.push(r);return n.map(s=>{let r=e.map(u=>u[s]).filter(u=>u!=null&&u!==""),l=r.length&&r.every(u=>typeof u=="number")?"number":r.length&&r.every(u=>typeof u=="boolean")?"bool":r.length&&r.every(Be)?"date":"text",a=[...new Set(r.filter(u=>typeof u!="object").map(String))].sort();return{name:s,kind:l,values:a.length<=40?a:[]}})}var $=e=>/^[a-z_][a-z0-9_]*$/.test(e)?e:`"${e.replace(/"/g,'""')}"`,H=(e,n)=>n&&e.trim()!==""&&Number.isFinite(Number(e))?String(Number(e)):`'${e.replace(/'/g,"''")}'`,oe=e=>e.fn==="count"&&!e.col?"rows":`${e.fn}_${N(e.col)?e.col.replace(/_s$/,"_hours"):e.col}`;function _e(e,n){let s=10**n;return Math.sign(e)*Math.round(Math.abs(e)*s+1e-9)/s}function We(e,n){if(!Be(e))return null;let s=new Date(String(e).length===10?`${e}T00:00:00Z`:String(e));if(Number.isNaN(s.getTime()))return null;let r=new Date(Date.UTC(s.getUTCFullYear(),s.getUTCMonth(),n==="month"?1:s.getUTCDate()));return n==="week"&&r.setUTCDate(r.getUTCDate()-(r.getUTCDay()+6)%7),r.toISOString().slice(0,10)}function Re(e,n){return e===n?0:e==null?1:n==null?-1:typeof e=="number"&&typeof n=="number"?e-n:String(e)<String(n)?-1:1}var qe=e=>new Date(String(e).length===10?`${e}T00:00:00Z`:String(e)).getTime();function Ge(e,n,s){let r=s==="number",l=s==="date",a=e[n.col],u=a==null||a==="";if(n.op==="set")return!u;if(n.op==="unset")return a==null;if(u)return!1;let p=n.value??"";if(n.op==="contains")return String(a).toLowerCase().includes(p.toLowerCase());let i=r?Number(a):l&&n.op!=="="&&n.op!=="!="?qe(a):String(a),o=r?Number(p):l&&n.op!=="="&&n.op!=="!="?qe(p):p;if((r||typeof o=="number")&&!Number.isFinite(o))return!1;switch(n.op){case"=":return i===o;case"!=":return i!==o;case">":return i>o;case">=":return i>=o;case"<":return i<o;default:return i<=o}}function He(e){if(!e.length)return null;let n=[...e].sort((r,l)=>r-l),s=n.length>>1;return n.length%2?n[s]:(n[s-1]+n[s])/2}function Je(e,n){if(n.fn==="count")return n.col?e.filter(l=>l[n.col]!==null&&l[n.col]!==void 0).length:e.length;let s=e.map(l=>l[n.col]).filter(l=>typeof l=="number");if(!s.length)return null;let r=n.fn==="sum"?s.reduce((l,a)=>l+a,0):n.fn==="avg"?s.reduce((l,a)=>l+a,0)/s.length:n.fn==="median"?He(s):n.fn==="min"?Math.min(...s):Math.max(...s);return N(n.col)?_e(r/3600,1):n.fn==="avg"||n.fn==="median"?_e(r,2):r}function Z(e,n,s=J(n)){let r=p=>s.find(i=>i.name===p)?.kind,l=n.filter(p=>e.where.every(i=>Ge(p,i,r(i.col)))),a,u;if(e.group||e.aggs&&e.aggs.length){let p=e.aggs?.length?e.aggs:[{fn:"count"}],i=e.group?e.bucket?e.bucket:e.group:null,o=new Map;for(let d of l){let f=e.group?e.bucket?We(d[e.group],e.bucket):d[e.group]??null:null,b=JSON.stringify(f);o.has(b)||o.set(b,{k:f,rows:[]}),o.get(b).rows.push(d)}!e.group&&!o.size&&o.set("null",{k:null,rows:[]}),u=[...i?[i]:[],...p.map(oe)],a=[...o.values()].map(d=>({...i?{[i]:d.k}:{},...Object.fromEntries(p.map(f=>[oe(f),Je(d.rows,f)]))}))}else{let p=e.select?.length?e.select:s.map(i=>i.name);u=p.map(i=>typeof i=="string"?i:i.as),a=l.map(i=>Object.fromEntries(p.map(o=>typeof o=="string"?[o,i[o]??null]:[o.as,o.f(i)])))}if(e.order){let p=e.order;a=a.map((i,o)=>({r:i,i:o})).sort((i,o)=>{let d=i.r[p.col],f=o.r[p.col];return d==null?f==null?i.i-o.i:1:f==null?-1:(p.desc?-Re(d,f):Re(d,f))||i.i-o.i}).map(i=>i.r)}return{columns:u,rows:e.limit?a.slice(0,e.limit):a}}function Ze(e){if(e.fn==="count")return e.col?`count(${$(e.col)})`:"count(*)";let n=`${e.fn}(${$(e.col)})`;return N(e.col)?`round(${n} / 3600, 1)`:e.fn==="avg"||e.fn==="median"?`round(${n}, 2)`:n}function j(e,n=[],s=e.table){let r=o=>n.find(d=>d.name===o)?.kind==="number",l=o=>n.find(d=>d.name===o)?.kind==="date",a=e.where.map(o=>{let d=$(o.col);if(o.op==="set")return`${d} is not null${r(o.col)?"":` and cast(${d} as varchar) <> ''`}`;if(o.op==="unset")return`${d} is null`;if(o.op==="contains")return`contains(lower(cast(${d} as varchar)), ${H((o.value||"").toLowerCase(),!1)})`;let f=o.value??"",b=o.op==="!="?"<>":o.op;return r(o.col)?`${d} ${b} ${H(f,!0)}`:l(o.col)&&b!=="="&&b!=="<>"?`cast(${d} as timestamp) ${b} cast(${H(f,!1)} as timestamp)`:`cast(${d} as varchar) ${b} ${H(f,!1)}`}),u,p="";if(e.group||e.aggs&&e.aggs.length){let o=e.aggs?.length?e.aggs:[{fn:"count"}],d=e.group?e.bucket?`cast(date_trunc('${e.bucket}', cast(${$(e.group)} as timestamp)) as date) as ${e.bucket}`:$(e.group):null;u=[...d?[d]:[],...o.map(f=>`${Ze(f)} as ${$(oe(f))}`)].join(", "),e.group&&(p=`
|
|
2
|
+
group by 1`)}else u=e.select?.length?e.select.map(o=>typeof o=="string"?$(o):`${o.sql} as ${$(o.as)}`).join(", "):"*";let i=e.order?`
|
|
3
|
+
order by ${$(e.order.col)}${e.order.desc?" desc":""}`:"";return`select ${u}
|
|
4
|
+
from ${$(s)}${a.length?`
|
|
5
|
+
where ${a.join(`
|
|
6
|
+
and `)}`:""}${p}${i}${e.limit?`
|
|
7
|
+
limit ${e.limit}`:""}`}var A=e=>e.replace(/^devlake_/,""),re=[{title:"Pull requests merged per week, with median pickup",about:"People's pull requests only; pickup is opened to first review.",q:{table:"prs",where:[{col:"merged_at",op:"set"},{col:"author_kind",op:"=",value:"person"}],group:"merged_at",bucket:"week",aggs:[{fn:"count"},{fn:"median",col:"pickup_s"}],order:{col:"week",desc:!1}}},{title:"Insiders and outside contributors",about:"Pull requests, how many merged and the median pickup, by the author's role.",q:{table:"prs",where:[{col:"author_kind",op:"=",value:"person"}],group:"role",aggs:[{fn:"count"},{fn:"count",col:"merged_at"},{fn:"median",col:"pickup_s"}],order:{col:"rows",desc:!0}}},{title:"Flakiest CI jobs",about:"Jobs that failed and then passed on the same code most often.",q:{table:"ci_jobs",where:[],select:["workflow","job","state","flake_events","failures","runs","flake_rate","prs_affected","retry_minutes"],order:{col:"flake_events",desc:!0},limit:20}},{title:"Tests that fail most",about:"Failures, flakes and the latest message for each test.",q:{table:"tests",where:[],select:["test_id","state","failures","flake_events","runs","message"],order:{col:"failures",desc:!0},limit:20}},{title:"Biggest open pull requests",about:"Open pull requests by lines changed.",q:{table:"prs",where:[{col:"status",op:"=",value:"OPEN"}],select:["number","author_kind","role",{as:"lines",sql:"additions + deletions",f:e=>typeof e.additions=="number"&&typeof e.deletions=="number"?e.additions+e.deletions:null},"created_at"],order:{col:"lines",desc:!0},limit:20}}];var le=new Map;function Fe(e){let n=z(V),[s,r]=h(void 0);return P(()=>{if(!e){r(null);return}let l=!0,a=`${n.id}|${e}`;return le.has(a)||le.set(a,W(e,n.source)),r(void 0),le.get(a).then(u=>l&&r(u)),()=>{l=!1}},[n.id,e]),s}var Qe=e=>e==="rows"?"Rows":e.replace(/_s$/," (seconds)").replace(/_/g," ").replace(/^./,n=>n.toUpperCase());function Ke(e,n){return e==null||e===""?"\u2013":typeof e=="number"?/_hours$/.test(n)?`${e.toLocaleString()} h`:Number.isInteger(e)?e.toLocaleString():e.toLocaleString(void 0,{maximumFractionDigits:3}):typeof e=="string"&&/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(e)?e.slice(0,16).replace("T"," "):Array.isArray(e)?e.join(", "):String(e)}function Pe({res:e,name:n}){let s=e.columns.map((r,l)=>({key:r,label:Qe(r),primary:l===0,num:e.rows.some(a=>typeof a[r]=="number"),nowrap:/(_id|^job|^workflow|_at)$/.test(r)||e.rows.every(a=>String(a[r]??"").length<=12),raw:a=>a[r],render:a=>Ke(a[r],r)}));return t(G,{name:n,rows:e.rows.map((r,l)=>({...r,__i:l})),columns:s,limit:25,empty:t(T,{icon:"explore",title:"No rows match.",children:"Loosen a filter or pick another table."})})}var Ye=e=>`"${e.replace(/\s*\n\s*/g," ").replace(/(["\\$`])/g,"\\$1")}"`,se=(e,n)=>`stackvitals sql --repo ${e} ${Ye(n)}`;function De({repo:e,sql:n,explorer:s,explorerSql:r}){let[l,a]=h(!1);return t(Q,{children:[s?t(k,{variant:"tinted",href:`${s}#sql=${encodeURIComponent(r)}`,external:!0,iconEnd:"external",children:"Open in the SQL explorer"}):null,t(k,{variant:"plain",expanded:l,onClick:()=>a(!l),iconEnd:l?"up":"down",children:l?"Hide the SQL":"Show the SQL"}),t(we,{text:se(e,n),label:"Copy as a command"}),l?t("div",{class:"ex-sql",children:t(D,{title:"SQL",code:n})}):null]})}function Ie({repo:e,explorer:n,tables:s}){let r=re.map((f,b)=>({...f,i:b})).filter(f=>s.includes(f.q.table)),[l,a]=h(r[0]?.i??0),u=re[l],p=Fe(r.length?u.q.table:null);if(!r.length)return t(R,{title:"No question fits these tables",children:t("p",{children:"The ready questions read prs, ci_jobs and tests; use the query builder below on the tables here."})});let i=p?J(p):[],o=p?Z(u.q,p,i):null,d=j(u.q,i);return t("div",{class:"ex-ask",children:[t("div",{class:"ex-qs",role:"group","aria-label":"Questions",children:r.map(f=>t("button",{type:"button",class:"ex-q press"+(f.i===l?" is-on":""),"aria-pressed":f.i===l,onClick:()=>a(f.i),children:[t("span",{class:"ex-q-t",children:f.title}),t("span",{class:"ex-q-s",children:f.about})]}))}),t(R,{title:u.title,meta:o?`${o.rows.length.toLocaleString()} rows from ${u.q.table}`:u.q.table,class:"ex-result",foot:t(De,{repo:e,sql:d,explorer:n,explorerSql:j(u.q,i,A(u.q.table))}),children:p===void 0?t(te,{lines:4}):p===null?t(T,{icon:"explore",title:`No ${u.q.table} table here.`}):t(Pe,{res:o,name:u.title.toLowerCase().replace(/\W+/g,"-")})})]})}var Xe=["10","20","50","100","500"],et=()=>({col:"",op:"=",value:""});function Ne({repo:e,explorer:n,tables:s,start:r}){let[l,a]=h(r);P(()=>{a(r),pe()},[r]);let[u,p]=h([]),[i,o]=h(""),[d,f]=h("week"),[b,g]=h(""),[x,q]=h(""),[B,ae]=h(""),[ie,Ae]=h("desc"),[ue,Ee]=h("50"),v=Fe(l||null);function pe(){p([]),o(""),g(""),q(""),ae("")}let C=v?J(v):[],F=C.filter(m=>!v?.some(c=>Array.isArray(c[m.name])||c[m.name]!==null&&typeof c[m.name]=="object")),E=C.filter(m=>m.kind==="number"),ce=F.find(m=>m.name===i),Ue=i||b?[{fn:"count"},...b&&(b==="count"||x)?[{fn:b,col:x||void 0}]:[]]:[],Me=u.filter(m=>m.col&&(m.op==="set"||m.op==="unset"||(m.value??"")!=="")),de={table:l,where:Me,group:i||null,bucket:ce?.kind==="date"?d:null,aggs:Ue},me=v?Z({...de,limit:1},v,C).columns:[],K={...de,order:B&&me.includes(B)?{col:B,desc:ie==="desc"}:null,limit:Number(ue)},Y=v?Z(K,v,C):null,ze=j(K,C),U=(m,c)=>p(u.map((y,X)=>m===X?{...y,...c}:y)),M=m=>m.map(c=>({value:c.name,label:c.name}));return t("div",{class:"ex-build",children:[t(R,{title:"Query",meta:v?`${v.length.toLocaleString()} rows, ${C.length} columns in ${l}`:l,children:[t("div",{class:"ex-form",children:[t(_,{label:"Table",children:m=>t(S,{id:m,value:l,onChange:c=>{a(c),pe()},options:s.map(c=>({value:c,label:c}))})}),t(_,{label:"Group by",hint:"One row per value; leave it off to list rows.",children:m=>t(S,{id:m,value:i,onChange:o,options:[{value:"",label:"No grouping"},...M(F)]})}),ce?.kind==="date"?t("div",{class:"field",children:[t("span",{class:"field-label",children:"Dates by"}),t(ne,{label:"Dates by",value:d,onChange:f,options:[{value:"day",label:"Day"},{value:"week",label:"Week"},{value:"month",label:"Month"}]})]}):null,t(_,{label:"Measure",hint:i?"Rows are always counted; add one more figure.":"Adding one sums up the whole table in one row.",children:m=>t(S,{id:m,value:b,onChange:c=>{g(c),c&&c!=="count"&&!E.some(y=>y.name===x)&&q(E[0]?.name||"")},options:[{value:"",label:i?"Rows only":"None: list rows"},...Oe.filter(c=>c.value==="count"||E.length).map(c=>({value:c.value,label:c.label}))]})}),b?t(_,{label:"Of",hint:N(x)?"Seconds, shown in hours.":void 0,children:m=>t(S,{id:m,value:x,onChange:q,options:M(b==="count"?F:E)})}):null,t(_,{label:"Sort by",children:m=>t(S,{id:m,value:B,onChange:ae,options:[{value:"",label:"As stored"},...me.map(c=>({value:c,label:Qe(c)}))]})}),B?t("div",{class:"field",children:[t("span",{class:"field-label",children:"Order"}),t(ne,{label:"Order",value:ie,onChange:Ae,options:[{value:"desc",label:"Largest first"},{value:"asc",label:"Smallest first"}]})]}):null,t(_,{label:"At most",children:m=>t(S,{id:m,value:ue,onChange:Ee,options:Xe.map(c=>({value:c,label:`${c} rows`}))})})]}),t("div",{class:"ex-filters",children:[t("p",{class:"ex-sub",children:"Filters"}),u.map((m,c)=>{let y=F.find(w=>w.name===m.col),X=m.op!=="set"&&m.op!=="unset";return t("div",{class:"ex-filter",children:[t(S,{label:`Filter ${c+1}: column`,value:m.col,onChange:w=>U(c,{col:w,value:""}),options:[{value:"",label:"Column"},...M(F)]}),t(S,{label:`Filter ${c+1}: test`,value:m.op,onChange:w=>U(c,{op:w}),options:Te.filter(w=>y?.kind==="number"||y?.kind==="date"||!/[<>]/.test(w.value))}),X?y?.values.length&&m.op!=="contains"&&y.kind!=="number"&&y.kind!=="date"?t(S,{label:`Filter ${c+1}: value`,value:m.value||"",onChange:w=>U(c,{value:w}),options:[{value:"",label:"Value"},...y.values.map(w=>({value:w,label:w}))]}):t(Ce,{label:`Filter ${c+1}: value`,value:m.value||"",onInput:w=>U(c,{value:w}),placeholder:y?.kind==="date"?"2026-01-31":"Value"}):t("span",{}),t(k,{size:"sm",variant:"plain",icon:"x",label:`Remove filter ${c+1}`,onClick:()=>p(u.filter((w,Ve)=>Ve!==c))})]})}),u.length<4?t(k,{size:"sm",variant:"tinted",icon:"plus",onClick:()=>p([...u,et()]),disabled:!v,children:"Add a filter"}):null]})]}),t(R,{title:"Result",meta:Y?`${Y.rows.length.toLocaleString()} rows`:"",class:"ex-result",foot:v?t(De,{repo:e,sql:ze,explorer:n,explorerSql:j(K,C,A(l))}):null,children:v===void 0?t(te,{lines:4}):v===null?t(T,{icon:"explore",title:`No ${l} table here.`}):t(Pe,{res:Y,name:`${l}-query`})})]})}var je={prs:"One row per pull request",ci_jobs:"One row per CI job",tests:"One row per test",devlake_pull_requests:"DevLake's pull_requests",devlake_project_pr_metrics:"DevLake's PR cycle-time phases",devlake_cicd_deployment_commits:"DevLake's deployments",devlake_incidents:"DevLake's incidents",devlake_issues:"DevLake's issues"};function tt(e,n,s){return e?.pages&&!e.pages.includes("explore.html")?null:n==="here"?"explore.html":n==="pages"&&s?new URL("explore.html",new URL(s,location.href)).href:null}async function nt(e){let n=await W(e)||[],s=[...new Set(n.flatMap(a=>Object.keys(a)))],r=URL.createObjectURL(new Blob([fe(n,s.map(a=>({key:a,label:a})))],{type:"text/csv"})),l=Object.assign(document.createElement("a"),{href:r,download:`${e}.csv`});document.body.appendChild(l),l.click(),l.remove(),setTimeout(()=>URL.revokeObjectURL(r),1e3),ve(`Saved ${e}.csv, ${ee(n.length,"row")}`)}function ot({app:e}){let n=z(V),[s,r]=h(void 0),[l,a]=h(null);if(P(()=>{let g=!0;return r(void 0),(async()=>{for(let x of e?.summary===null?["../summary/"]:["summary/","../summary/"]){let q=await ye(x+"manifest.json",n.source);if(q)return q}return null})().then(x=>g&&r(x)),()=>{g=!1}},[n.id,e?.repo]),s===void 0)return t(xe,{});let u=n.source,p=tt(e,u.kind,u.kind==="pages"?u.url:void 0),i=e?.repo||n.label||"owner/repo",o=Object.entries(s?.files||{}).map(([g,x])=>({id:g,name:g.replace(/\.jsonl$/,""),...x})),d=o.map(g=>g.name),f=o.reduce((g,x)=>g+(x.rows||0),0),b=g=>{a(g),requestAnimationFrame(()=>document.getElementById("ex-builder")?.scrollIntoView({behavior:"smooth",block:"start"}))};return t(ke,{title:"Explore",subtitle:"The tables behind every page: ask a question, build a query or download a table. It all runs in this browser; nothing leaves the page.",toolbar:p?t(k,{variant:"plain",href:p,external:!0,iconEnd:"external",children:"Open the SQL explorer"}):null,children:[o.length?t(Q,{children:[t($e,{children:[t(I,{label:"Tables",value:o.length}),t(I,{label:"Rows",value:f.toLocaleString()}),t(I,{label:"Written",value:s?.generated?ge(String(s.generated)):"\u2013"}),t(I,{label:"SQL explorer",value:p?"Published":"Not here",note:p?"beside this app":"the questions and builder below work without it"})]}),t(O,{title:"Ask a question",description:"Pick one: it runs on this repo's tables right here. Open it in the SQL explorer or copy it as a command to take it further.",info:t(he,{info:{title:"How questions run",doc:"app.md",body:t(Q,{children:["The questions and the builder read the summary tables this app already loads and compute in your browser, with no download beyond the tables. Each prints the same query as DuckDB SQL: the SQL explorer runs it on DuckDB in your browser (about 35 MB once, from jsDelivr, pinned with integrity hashes), and ",t("code",{class:"code-inline",children:"stackvitals sql"})," runs it in a terminal."]})}}),children:t(Ie,{repo:i,explorer:p,tables:d})}),t(O,{title:"Build a query",description:"Filter, group and measure any table without writing SQL. The SQL it stands for is one click away.",id:"ex-builder",children:t(Ne,{repo:i,explorer:p,tables:d,start:l&&d.includes(l)?l:d.includes("prs")?"prs":d[0]})}),t(O,{title:"Tables",description:"Each summary table with its size; query it above or download it as CSV.",action:t(be,{tone:"neutral",icon:null,children:ee(o.length,"table")}),children:t(G,{name:"tables",caption:"Tables",search:!1,rows:o,columns:[{key:"name",label:"Table",primary:!0,nowrap:!0,render:g=>t("span",{class:"ex-id",children:g.name})},{key:"about",label:"What it holds",raw:g=>je[g.name]||"",render:g=>je[g.name]||"\u2013"},{key:"rows",label:"Rows",num:!0,raw:g=>g.rows??null,render:g=>(g.rows??0).toLocaleString()},{key:"generated",label:"Written",raw:g=>String(g.generated||""),render:g=>g.generated?String(g.generated).slice(0,16).replace("T"," "):"\u2013"},{key:"act",label:"Actions",raw:()=>"",render:g=>t("span",{class:"row-btns",children:[t(k,{size:"sm",variant:"tinted",onClick:()=>b(g.name),label:`Query ${g.name}`,children:"Query"}),p?t(k,{size:"sm",variant:"plain",href:`${p}#sql=${encodeURIComponent(`select * from ${A(g.name)} limit 50`)}`,external:!0,iconEnd:"external",label:`Open ${g.name} in the SQL explorer`,children:"SQL explorer"}):null,t(k,{size:"sm",variant:"plain",icon:"down",onClick:()=>nt(g.name),label:`Download ${g.name} as CSV`,children:"CSV"})]})}]})})]}):t(O,{title:"Tables",description:"Public repos publish them beside this app; a private repo's live on its data branch.",children:t(Se,{children:t(T,{icon:"explore",title:"No summary tables here.",children:"Open a repo that runs StackVitals, or open a private repo's app from your machine (below)."})})}),t(O,{title:"From your machine",description:"For tables too big for a browser, several repos in one query, or a private repo without a published app.",children:t(R,{title:"stackvitals sql",meta:'pip install "stackvitals[sql]"',children:[t("p",{children:["Reads the tables from the repo's stackvitals-data branch with your GitHub token (GITHUB_TOKEN, or gh auth login) and runs DuckDB on your computer, so size is limited only by your machine. Repeat ",t("code",{class:"code-inline",children:"--repo"})," to query several repos at once; each row gets a repo column."]}),t(D,{inline:!0,code:se(i,"select role, count(*) as prs, round(median(pickup_s) / 3600, 1) as median_pickup_hours from prs where author_kind = 'person' group by 1 order by 2 desc"),note:"Any query from this page: Copy as a command fills it in."}),t(D,{inline:!0,code:`stackvitals ui ${i}`,note:"Opens this app, the SQL explorer included, from a local copy of the data branch: the way to explore a private repo."})]})})]})}export{ot as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[data-design=sheet][data-page=health] .card.tone-bad:after{content:none}[data-design=sheet][data-page=health] .card.tone-bad{background:color-mix(in srgb,var(--bad-soft) 60%,var(--surface));border:1.5px solid var(--frame);border-top:5px solid var(--sv-coral)}[data-design=sheet][data-page=health] .card.tone-bad+.card.tone-bad{margin-top:12px}[data-design=sheet][data-page=health] .card.tone-bad>p:first-child{position:relative;padding-left:22px;font-size:16px}[data-design=sheet][data-page=health] .card.tone-bad>p:first-child:before{content:"";position:absolute;left:0;top:.32em;width:13px;height:13px;background:var(--mark-bad) center / contain no-repeat}[data-design=sheet][data-page=health] .card.tone-bad b{font-weight:700}[data-design=sheet][data-page=health] .card.tone-bad .muted{margin:6px 0 0 22px;font:400 15px/1.5 var(--font);color:var(--ink-2);overflow-wrap:anywhere}[data-design=sheet][data-page=health] .muted.small{font-size:14.5px;color:var(--ink-2)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as R}from"../chunks/chunk-S2ZBMEVA.js";import{d as j}from"../chunks/chunk-ASYNLVOP.js";import{a as b}from"../chunks/chunk-6RLVD2DT.js";import{b as $}from"../chunks/chunk-45PGAQRO.js";import{d as x}from"../chunks/chunk-4WKQTWUC.js";import"../chunks/chunk-VQ5YXO54.js";import{b as f,c as d,e as h,h as D,i as c}from"../chunks/chunk-76WAVKUG.js";import"../chunks/chunk-VWKSMFWJ.js";import{I as v,L as _,P as m,R as P,a as p,n as w,q as i,w as t,z as S}from"../chunks/chunk-GT4EAKAO.js";import{d as k}from"../chunks/chunk-G7NGB5UX.js";k();var z=2*864e5,E={"ci/days":"CI runs","ci/transitions":"CI test changes","flow/closed":"Closed pull requests","community/issues/closed":"Closed issues","portal/history":"Portal scores"},C=a=>E[a]||(a.startsWith("connectors/")?`${a.split("/")[1]} ${a.endsWith("/raw")?"answers kept":"records"}`:a),F={"CI Health":"ci","Team Flow":"flow",Jira:"jira",Linear:"linear",PagerDuty:"pagerduty","incident.io":"incident_io",Copilot:"copilot",Discourse:"discourse",Slack:"slack",Discord:"discord"},I=(a,o)=>Math.round((Date.parse(o)-Date.parse(a))/864e5)+1;function L(a){return a.error?["bad","Failing"]:a.last_try&&Date.now()-Date.parse(a.last_try)>z?["watch","No recent run"]:a.complete===!1?["watch","Filling in"]:["good","Current"]}function T(a,o){let n=F[o];if(!a||!n)return null;let s=new Date(Date.now()-30*864e5).toISOString().slice(0,10);return R(a,`StackVitals: read ${o} again`,{...$(),resync:[{source:n,since:s}]},`Read ${o} again from ${s} (change the date below, or set it to null for the whole 180 days). Records already kept stay.`)}function N({app:a}){let o=x({h:"health.json"});if(!o.ready)return t(P,{});let n=o.h;if(!n?.sources)return t(f,{title:"Data health",subtitle:"Is the data current, and is it complete?",children:t(h,{children:t(m,{icon:"clock",title:"No health report yet.",children:"The next StackVitals run writes one: every source, when it last worked, and how far back each history goes."})})});let s=Object.entries(n.sources).map(([e,r])=>({name:e,...r,st:L(r)})),l=s.filter(e=>e.st[0]==="bad"),u=s.filter(e=>e.st[1]==="Filling in"),H=s.filter(e=>e.st[1]==="No recent run"),O=Object.entries(n.history||{}).map(([e,r])=>({k:e,...r,span:I(r.oldest,r.newest)})),y=n.dropped?[...Object.entries(n.dropped.retention||{}),...Object.entries(n.dropped.size_cap||{})]:[];return t(f,{title:"Data health",subtitle:"Is the data current, did each source finish reading, and what is missing and why.",children:[t(d,{title:"At a glance",description:n.as_of?`As of the run ${i(n.as_of)}.`:void 0,info:t(_,{info:{title:"Data health",doc:"app.md#data-health",body:t(p,{children:"Each run records when every source last worked. A failing source keeps its last good data; the next run tries again. Filling in means a big history is still being read, a few hundred items a run, within GitHub's and each service's limits."})}}),children:t(D,{children:[t(c,{label:"Sources",value:s.length,note:`${s.filter(e=>e.kind==="connector").length} connected services`}),t(c,{label:"Failing",value:l.length,note:l.length?l.map(e=>e.name).join(", "):"none"}),t(c,{label:"Still filling in",value:u.length,note:u.length?u.map(e=>e.name).join(", "):"every history read"}),t(c,{label:"No recent run",value:H.length,note:"over two days"})]})}),l.length?t(d,{title:"What to fix",description:"Each failing source, since when, and what it said.",children:l.map(e=>t(h,{tone:"bad",children:[t("p",{children:[t("b",{children:e.name})," has failed since ",i(e.failing_since),e.last_ok?t(p,{children:["; its data is from ",i(e.last_ok)]}):t(p,{children:"; it has not worked yet"}),"."]}),t("p",{class:"muted",children:e.error})]}))}):null,t(d,{title:"Sources",description:"Every step of the run and every connected service.",children:t(b,{name:"health-sources",caption:"Sources",rows:s,columns:[{key:"name",label:"Source",primary:!0},{key:"st",label:"State",filter:!0,raw:e=>e.st[1],render:e=>t(v,{tone:e.st[0],children:e.st[1]})},{key:"last_ok",label:"Last worked",raw:e=>e.last_ok||"",render:e=>t("span",{title:e.last_ok||"",children:e.last_ok?i(e.last_ok):"never"})},{key:"read",label:"Read last time",raw:e=>Object.entries(e.read||{}).map(([r,g])=>`${g} ${r}`).join(", "),render:e=>t("span",{class:"muted",children:Object.entries(e.read||{}).map(([r,g])=>`${g.toLocaleString()} ${r.replace(/_/g," ")}`).join(", ")||"\u2013"})},{key:"error",label:"Problem",render:e=>t("span",{class:"muted",children:e.error||"\u2013"})},{key:"again",label:"",render:e=>{let r=T(a,e.name);return r?t(S,{variant:"tinted",size:"sm",href:r.url,onClick:j(r,"Opened on GitHub: press Submit there"),children:"Read again"}):null}}]})}),t(d,{title:"History kept",description:"How far back each kind of history goes, against how long it is kept.",children:[t(b,{name:"health-history",caption:"History",rows:O,columns:[{key:"k",label:"History",primary:!0,raw:e=>C(e.k)},{key:"oldest",label:"From"},{key:"newest",label:"To"},{key:"span",label:"Days covered",num:!0,render:e=>`${e.span} of ${e.keeps_days}`},{key:"files",label:"Day files",num:!0},{key:"bytes",label:"Size",num:!0,render:e=>`${(e.bytes/1048576).toFixed(1)} MB`}],empty:t(h,{children:t(m,{icon:"clock",title:"No history yet.",children:"The first run starts it."})})}),y.length?t("p",{class:"muted small",children:["Last removed ",i(n.dropped.at),": ",y.map(([e,r])=>`${w(r,"day file")} of ${C(e)}`).join(", "),Object.keys(n.dropped.size_cap||{}).length?" (the size cap; raise size-cap-mb to keep more)":" (past its keep window)","."]}):null]})]})}export{N as default};
|