Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,37 @@

![Dashboard Logo](./docs/logos/logo.320.160.png)

**A personal metrics dashboard for open-source creators, right in your terminal.**
A metrics tool for open-source creators.

Dashboard is a modular, configurable, and robust Bash script that provides a consolidated view of your key metrics from various services like GitHub, Hacker News, and more. With support for multiple output formats including JSON, XML, HTML, and Markdown, you can easily integrate your dashboard into websites, reports, or other tools.

---
## Example output

```bash
./dashboard.sh -f tsv
```

```csv
Date module name value
2025-09-01T20:28:46Z discord discord.online 3
2025-09-01T20:28:46Z github repo.attogram.games.stars 135
2025-09-01T20:28:46Z github repo.attogram.games.forks 68
2025-09-01T20:28:46Z github repo.attogram.games.open_issues 1
2025-09-01T20:28:46Z github repo.attogram.games.watchers 7
2025-09-01T20:28:47Z github repo.attogram.EightQueens.stars 15
2025-09-01T20:28:47Z github repo.attogram.EightQueens.forks 4
2025-09-01T20:28:47Z github repo.attogram.EightQueens.open_issues 0
2025-09-01T20:28:47Z github repo.attogram.EightQueens.watchers 2
2025-09-01T20:28:47Z github repo.attogram.base.stars 2
2025-09-01T20:28:47Z github repo.attogram.base.forks 1
2025-09-01T20:28:47Z github repo.attogram.base.open_issues 0
2025-09-01T20:28:47Z github repo.attogram.base.watchers 2
2025-09-01T20:28:48Z github repo.attogram.dashboard.stars 1
2025-09-01T20:28:48Z github repo.attogram.dashboard.forks 0
2025-09-01T20:28:48Z github repo.attogram.dashboard.open_issues 0
2025-09-01T20:28:48Z github repo.attogram.dashboard.watchers 1
2025-09-01T20:28:54Z hackernews hackernews.karma 30
```

## Features

Expand Down
6 changes: 5 additions & 1 deletion dashboard.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ DASHBOARD_DEBUG=0 # 0 = off, 1 = on
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FORMAT="plain"
MODULE_TO_RUN=""
VALID_FORMATS=("plain" "pretty" "json" "xml" "html" "yaml" "csv" "markdown")
VALID_FORMATS=("plain" "pretty" "json" "xml" "html" "yaml" "csv" "markdown" "tsv")

_debug() {
(( DASHBOARD_DEBUG )) || return 0
Expand Down Expand Up @@ -174,6 +174,10 @@ case "$FORMAT" in
echo "module,key,value"
printf '%s\n' "${OUTPUTS[@]}"
;;
tsv)
echo -e "Date\tmodule\tname\tvalue"
printf '%s\n' "${OUTPUTS[@]}"
;;
*)
# For plain, pretty, yaml, markdown, just print the outputs
printf '%s\n' "${OUTPUTS[@]}"
Expand Down
4 changes: 4 additions & 0 deletions modules/crypto.sh
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ case "$FORMAT" in
echo 'module,chain,address,token_symbol,balance'
echo "$DATA" | jq -r '.[] | . as $parent | .tokens[] | "crypto,\($parent.chain),\($parent.address),\(.symbol),\(.balance)"'
;;
tsv)
now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
echo "$DATA" | jq -r --arg now "$now" '.[] | . as $parent | .tokens[] | [$now, "crypto", "crypto." + $parent.chain + "." + $parent.address + "." + .symbol, .balance] | @tsv'
;;
markdown)
echo '### Crypto Donations'
echo "$DATA" | jq -r '.[] | "* **\(.chain)** (`\(.address)`)\n" + (.tokens[] | " * **\(.symbol)**: \(.balance)")'
Expand Down
4 changes: 4 additions & 0 deletions modules/discord.sh
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ case "$FORMAT" in
csv)
echo "discord,online,${ONLINE_COUNT}"
;;
tsv)
now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
printf "%s\tdiscord\tdiscord.online\t%s\n" "$now" "$ONLINE_COUNT"
;;
markdown)
echo "### Discord"
echo "- Online: ${ONLINE_COUNT}"
Expand Down
4 changes: 4 additions & 0 deletions modules/github-sponsors.sh
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ case "$FORMAT" in
csv)
echo "github-sponsors,sponsors,${SPONSORS_COUNT}"
;;
tsv)
now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
printf "%s\tgithub-sponsors\tgithub-sponsors.sponsors\t%s\n" "$now" "$SPONSORS_COUNT"
;;
markdown)
echo "### GitHub Sponsors"
echo "- Sponsors: ${SPONSORS_COUNT}"
Expand Down
11 changes: 11 additions & 0 deletions modules/github.sh
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ fetch_repo_data() {
echo "github,${repo_name},issues,${issues}"
echo "github,${repo_name},watchers,${watchers}"
;;
tsv)
local now
now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
printf "%s\tgithub\trepo.%s.%s.stars\t%s\n" "$now" "$GITHUB_USER" "$repo_name" "$stars"
printf "%s\tgithub\trepo.%s.%s.forks\t%s\n" "$now" "$GITHUB_USER" "$repo_name" "$forks"
printf "%s\tgithub\trepo.%s.%s.open_issues\t%s\n" "$now" "$GITHUB_USER" "$repo_name" "$issues"
printf "%s\tgithub\trepo.%s.%s.watchers\t%s\n" "$now" "$GITHUB_USER" "$repo_name" "$watchers"
;;
markdown)
echo "#### ${repo_name}"
echo "- Stars: ${stars}"
Expand Down Expand Up @@ -177,6 +185,9 @@ case "$FORMAT" in
csv)
for repo in "${REPOS[@]}"; do fetch_repo_data "$repo" "$FORMAT"; done
;;
tsv)
for repo in "${REPOS[@]}"; do fetch_repo_data "$repo" "$FORMAT"; done
;;
markdown)
echo "### GitHub Repositories"
for repo in "${REPOS[@]}"; do fetch_repo_data "$repo" "$FORMAT"; done
Expand Down
4 changes: 4 additions & 0 deletions modules/hackernews.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ case "$FORMAT" in
csv)
echo "hackernews,karma,${KARMA}"
;;
tsv)
now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
printf "%s\thackernews\thackernews.karma\t%s\n" "$now" "$KARMA"
;;
markdown)
echo "### Hacker News"
echo "- Karma: ${KARMA}"
Expand Down
2 changes: 2 additions & 0 deletions reports/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore
27 changes: 16 additions & 11 deletions test/dashboard.bats
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@

setup() {
# This setup function is run before each test.
# We ensure a valid config.sh is present for the modules to use.
if [ ! -f "config.sh" ]; then
cp config.dist.sh config.sh
fi
sed -i 's/HN_USER=".*"/HN_USER="pg"/' config.sh
sed -i 's/GITHUB_USER=".*"/GITHUB_USER="attogram"/' config.sh
sed -i 's/REPOS=(.*)/REPOS=("base" "2048-lite")/' config.sh
# Ensure GITHUB_TOKEN is empty so the sponsors module is skipped
sed -i 's/GITHUB_TOKEN=".*"/GITHUB_TOKEN=""/' config.sh
# Add the discord server ID for testing
sed -i 's/DISCORD_SERVER_ID=".*"/DISCORD_SERVER_ID="1400382194509287426"/' config.sh
# We create a consistent config.sh for all dashboard integration tests.
cat > config.sh <<'EOL'
# Test Configuration
HN_USER='pg'
GITHUB_USER='attogram'
REPOS=('base' '2048-lite')
DISCORD_SERVER_ID='1400382194509287426'
GITHUB_TOKEN=''
CRYPTO_WALLET_BTC='1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'
CRYPTO_WALLET_ETH='0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae'
EOL
}

teardown() {
# This teardown function is run after each test.
rm -f config.sh
}

@test "dashboard.sh should be executable" {
Expand Down
21 changes: 15 additions & 6 deletions test/discord.bats
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@

setup() {
# This setup function is run before each test.
if [ ! -f "config.sh" ]; then
cp config.dist.sh config.sh
fi
# Use the server ID provided by the user
sed -i 's/DISCORD_SERVER_ID=".*"/DISCORD_SERVER_ID="1400382194509287426"/' config.sh
# We create a consistent config.sh for all discord tests.
cat > config.sh <<'EOL'
# Test Configuration
DISCORD_SERVER_ID='1400382194509287426'
EOL
}

teardown() {
# This teardown function is run after each test.
rm -f config.sh
}

@test "discord module (plain)" {
Expand Down Expand Up @@ -62,7 +67,11 @@ setup() {
}

@test "discord module with no server id" {
sed -i 's/DISCORD_SERVER_ID=".*"/DISCORD_SERVER_ID=""/' config.sh
# Overwrite the config.sh created by setup()
cat > config.sh <<'EOL'
# Test Configuration
DISCORD_SERVER_ID=''
EOL
run ./modules/discord.sh plain
[ "$status" -eq 0 ]
[ -z "$output" ] # Should produce no output
Expand Down
16 changes: 11 additions & 5 deletions test/github.bats
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@

setup() {
# This setup function is run before each test.
if [ ! -f "config.sh" ]; then
cp config.dist.sh config.sh
fi
sed -i 's/GITHUB_USER=".*"/GITHUB_USER="attogram"/' config.sh
sed -i 's/REPOS=(.*)/REPOS=("base" "2048-lite")/' config.sh
# We create a consistent config.sh for all github tests.
cat > config.sh <<'EOL'
# Test Configuration
GITHUB_USER='attogram'
REPOS=('base' '2048-lite')
EOL
}

teardown() {
# This teardown function is run after each test.
rm -f config.sh
}

@test "github module (plain)" {
Expand Down
15 changes: 10 additions & 5 deletions test/hackernews.bats
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@

setup() {
# This setup function is run before each test.
# We ensure a valid config.sh is present for the module to use.
if [ ! -f "config.sh" ]; then
cp config.dist.sh config.sh
fi
sed -i 's/HN_USER=".*"/HN_USER="pg"/' config.sh
# We create a consistent config.sh for all hackernews tests.
cat > config.sh <<'EOL'
# Test Configuration
HN_USER='pg'
EOL
}

teardown() {
# This teardown function is run after each test.
rm -f config.sh
}

@test "hackernews module (plain)" {
Expand Down