diff --git a/README.md b/README.md index 38bc96a..7fde666 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/dashboard.sh b/dashboard.sh index dcc4406..652c101 100755 --- a/dashboard.sh +++ b/dashboard.sh @@ -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 @@ -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[@]}" diff --git a/modules/crypto.sh b/modules/crypto.sh index abe3684..83ceda1 100644 --- a/modules/crypto.sh +++ b/modules/crypto.sh @@ -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)")' diff --git a/modules/discord.sh b/modules/discord.sh index 67f3086..e7c26e2 100755 --- a/modules/discord.sh +++ b/modules/discord.sh @@ -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}" diff --git a/modules/github-sponsors.sh b/modules/github-sponsors.sh index 67886f6..927b7b7 100755 --- a/modules/github-sponsors.sh +++ b/modules/github-sponsors.sh @@ -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}" diff --git a/modules/github.sh b/modules/github.sh index 5a78f82..f2e4fdf 100755 --- a/modules/github.sh +++ b/modules/github.sh @@ -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}" @@ -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 diff --git a/modules/hackernews.sh b/modules/hackernews.sh index 693cf78..523af64 100755 --- a/modules/hackernews.sh +++ b/modules/hackernews.sh @@ -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}" diff --git a/reports/.gitignore b/reports/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/reports/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/test/dashboard.bats b/test/dashboard.bats index f867819..239a2de 100644 --- a/test/dashboard.bats +++ b/test/dashboard.bats @@ -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" { diff --git a/test/discord.bats b/test/discord.bats index 3bb4651..a17552d 100644 --- a/test/discord.bats +++ b/test/discord.bats @@ -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)" { @@ -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 diff --git a/test/github.bats b/test/github.bats index d4ffdf7..4e398da 100644 --- a/test/github.bats +++ b/test/github.bats @@ -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)" { diff --git a/test/hackernews.bats b/test/hackernews.bats index a89e92e..2647ff5 100644 --- a/test/hackernews.bats +++ b/test/hackernews.bats @@ -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)" {