-
Notifications
You must be signed in to change notification settings - Fork 7.7k
149 lines (129 loc) · 5.16 KB
/
Copy pathplugin-stat.yml
File metadata and controls
149 lines (129 loc) · 5.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
name: Pull plugin stats
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
permissions:
contents: write
issues: write
concurrency:
group: plugin-stat
cancel-in-progress: false
env:
STATS_URL: https://community.obsidian.md/assets/community-plugin-stats.json
STATS_FILE: community-plugin-stats.json
# New file must retain at least this fraction of the current file's entry count.
MIN_RETENTION_PCT: 95
# Hard floor guards against the percent-check passing on an already-small file.
STATS_MIN_ABSOLUTE: 4000
jobs:
pull-stats:
if: github.repository == 'obsidianmd/obsidian-releases'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: "22.x"
- name: Fetch plugin stats JSON
run: |
curl -fsSL --retry 5 --retry-delay 10 --max-time 60 \
-o /tmp/stats.new "$STATS_URL"
- name: Validate plugin stats JSON
run: |
set -euo pipefail
NEW=/tmp/stats.new
OLD="$STATS_FILE"
jq -e 'type == "object"' "$NEW" > /dev/null \
|| { echo "::error::stats: top-level is not a JSON object"; exit 1; }
# Every entry is an object with numeric downloads/updated, and every value
# within each entry (per-version counts included) is a number.
jq -e '
all(.[];
type == "object"
and (.downloads | type) == "number"
and (.updated | type) == "number"
and all(.[]; type == "number"))
' "$NEW" > /dev/null \
|| { echo "::error::stats: one or more entries are malformed"; exit 1; }
NEW_COUNT=$(jq 'length' "$NEW")
OLD_COUNT=$(jq 'length' "$OLD")
MIN_RETAINED=$(( OLD_COUNT * MIN_RETENTION_PCT / 100 ))
echo "stats: new=$NEW_COUNT old=$OLD_COUNT min_retained=$MIN_RETAINED absolute_floor=$STATS_MIN_ABSOLUTE"
if [ "$NEW_COUNT" -lt "$MIN_RETAINED" ]; then
echo "::error::stats: new count $NEW_COUNT < ${MIN_RETENTION_PCT}% of current $OLD_COUNT"
exit 1
fi
if [ "$NEW_COUNT" -lt "$STATS_MIN_ABSOLUTE" ]; then
echo "::error::stats: new count $NEW_COUNT below absolute floor $STATS_MIN_ABSOLUTE"
exit 1
fi
- name: Compute diff summary
run: |
set -euo pipefail
DIFF=$(jq -n \
--slurpfile old "$STATS_FILE" \
--slurpfile new /tmp/stats.new \
'
($old[0]) as $o |
($new[0]) as $n |
($o | keys) as $ok |
($n | keys) as $nk |
{
total_old: ($ok | length),
total_new: ($nk | length),
added: ($nk - $ok | length),
removed: ($ok - $nk | length),
downloads_old: ([$o[].downloads // 0] | add),
downloads_new: ([$n[].downloads // 0] | add)
}
')
echo "$DIFF"
# Expose added/removed counts for the commit message.
{
echo "STATS_ADDED=$(jq -r '.added' <<< "$DIFF")"
echo "STATS_REMOVED=$(jq -r '.removed' <<< "$DIFF")"
} >> "$GITHUB_ENV"
{
echo "### Plugin stats diff summary"
echo ""
echo "| Entries (old) | Entries (new) | Added | Removed | Downloads (old) | Downloads (new) |"
echo "|---:|---:|---:|---:|---:|---:|"
jq -r '"| \(.total_old) | \(.total_new) | \(.added) | \(.removed) | \(.downloads_old) | \(.downloads_new) |"' <<< "$DIFF"
} >> "$GITHUB_STEP_SUMMARY"
- name: Stage validated file
run: mv /tmp/stats.new "$STATS_FILE"
- name: Format with prettier
run: |
npm ci --no-save
npm run format
- name: Commit and push if changed
run: |
set -euo pipefail
if git diff --quiet -- "$STATS_FILE"; then
echo "No changes to commit"
exit 0
fi
git config --local user.name 'Obsidian Bot'
git config --local user.email 'admin@obsidian.md'
git add "$STATS_FILE"
git commit -m "chore: Update plugin stats (-${STATS_REMOVED}/+${STATS_ADDED} plugins)"
git push
- name: Report failure as issue
if: failure()
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
BODY=$'Plugin stats mirror workflow failed.\n\nRun: '"$RUN_URL"
EXISTING=$(gh issue list --label plugin-stat-failure --state open --limit 1 --json number --jq '.[0].number // empty')
if [ -n "$EXISTING" ]; then
gh issue comment "$EXISTING" --body "$BODY"
else
gh label create plugin-stat-failure --color B60205 --description "Automated plugin stats mirror workflow failed" 2>/dev/null || true
gh issue create \
--title "Plugin stats mirror workflow failed" \
--label plugin-stat-failure \
--body "$BODY"
fi