{"id":35582787,"url":"https://github.com/groda/github-traffic-archiver","last_synced_at":"2026-01-13T23:42:40.652Z","repository":{"id":331372420,"uuid":"1126374959","full_name":"groda/github-traffic-archiver","owner":"groda","description":"This is a GitHub Action designed to automate the collection and archival of repository clone statistics. It solves the \"14-day limit\" problem of GitHub's native traffic insights by persisting data into a central repository.","archived":false,"fork":false,"pushed_at":"2026-01-10T20:05:19.000Z","size":486,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-13T19:25:48.937Z","etag":null,"topics":["data-archiver","github-actions","github-traffic","reusable-workflow","traffic-stats"],"latest_commit_sha":null,"homepage":"https://github.com/marketplace/actions/github-traffic-archiver","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/groda.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-01T19:22:00.000Z","updated_at":"2026-01-10T20:05:22.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/groda/github-traffic-archiver","commit_stats":null,"previous_names":["groda/github-clone-archiver"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/groda/github-traffic-archiver","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/groda%2Fgithub-traffic-archiver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/groda%2Fgithub-traffic-archiver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/groda%2Fgithub-traffic-archiver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/groda%2Fgithub-traffic-archiver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/groda","download_url":"https://codeload.github.com/groda/github-traffic-archiver/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/groda%2Fgithub-traffic-archiver/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28399589,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T14:36:09.778Z","status":"ssl_error","status_checked_at":"2026-01-13T14:35:19.697Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-archiver","github-actions","github-traffic","reusable-workflow","traffic-stats"],"created_at":"2026-01-04T21:09:52.717Z","updated_at":"2026-01-13T23:42:40.647Z","avatar_url":"https://github.com/groda.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# GitHub Traffic Archiver 📊\n\n![Main Workflow Status](https://img.shields.io/github/actions/workflow/status/groda/github-traffic-archiver/run-archiver.yml?branch=main\u0026label=last%20backup\u0026style=flat-square)\n![Version](https://img.shields.io/github/v/release/groda/github-traffic-archiver?include_prereleases\u0026label=version\u0026sort=semver\u0026style=flat-square\u0026color=blue)\n![License](https://img.shields.io/github/license/groda/github-traffic-archiver?style=flat-square\u0026color=orange)\n\nThis is a GitHub Action designed to automate the collection and archival of repository traffic statistics (clones and views). It solves the \"14-day limit\" problem of GitHub's native traffic insights by persisting daily metrics into a central storage repository, creating a permanent historical record.\n\n---\n\n## Beyond the 14-Day Limit\n\nGitHub only keeps your traffic data for 14 days. This action ensures you never lose a single view or clone by archiving them into a permanent vault.\n\n```mermaid\ngraph LR\n    %% 1. Define the Nodes\n    A[Observed Repo A]\n    B[Observed Repo B]\n    C[Observed Repo C]\n    D[(Observer Vault)]\n    E[Infinite History]\n\n    %% 2. Define the Connections\n    A --\u003e|Daily Sync| D\n    B --\u003e|Daily Sync| D\n    C --\u003e|Daily Sync| D\n    D --\u003e E\n\n    %% 3. Apply Styles\n    style D fill:#238636,stroke:#308d46,color:#fff,stroke-width:2px\n    style A fill:#f6f8fa,stroke:#d0d7de,color:#000\n    style B fill:#f6f8fa,stroke:#d0d7de,color:#000\n    style C fill:#f6f8fa,stroke:#d0d7de,color:#000\n    style E fill:#f6f8fa,stroke:#d0d7de,color:#000,stroke-dasharray: 5 5\n```\n\n---\n\n### Why use this?\n\n| Feature | The Observer Advantage |\n| --- | --- |\n| **Speed** | **60-Second Setup.** No complex Docker or Python environments; just a pure Composite Action. |\n| **Privacy** | **Total Ownership.** Your metrics never leave GitHub. They move from one private repo to another. |\n| **Portability** | **Clean CSVs.** No proprietary formats. Just raw data ready for Excel, Sheets, or Python. |\n| **Centralization** | **Multi-Repo Support.** One \"Observer\" repo can track an unlimited number of \"Observed\" repos. |\n| **Automation** | **Set \u0026 Forget.** Runs on a cron schedule so your history grows while you sleep. |\n\n---\n\n## 🚀 Quick Start (60 Seconds)\n\nStop losing your data in three easy steps:\n\n### 1. Launch your Observatory\n\nClick [**Use this template**](https://github.com/groda/observatory-dashboard/generate) to create an _observer_ repository to act as your database. \n\n\u003e [!IMPORTANT]\n\u003e Make sure to set the repository to **Private** during setup to keep your traffic data secure.\n\n### 2. Generate a Token\n\nCreate a [Fine-grained Personal Access Token](https://github.com/settings/tokens?type=beta) with **Read \u0026 Write** access to:\n - your new _observer_ repo\n - all repos you want to collect metrics for (the _observed_ projects).\n \n Add it as an actions Secret named `METRICS_PAT` in your observer and observed repositories.\n\n### 3. Add the Workflow\n\nCreate `.github/workflows/metrics.yml` each the _observed_ project (the project you want to collect metrics for) and paste this:\n\n```yaml\nname: Archive Traffic\non:\n  schedule:\n    - cron: '0 0 * * *' \n  workflow_dispatch:\n\njobs:\n  backup:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: groda/github-traffic-archiver@v2\n        with:\n          metrics-repo: 'YOUR-USERNAME/observatory-dashboard'\n          metrics-pat: ${{ secrets.METRICS_PAT }}\n```\n\u003e [!IMPORTANT] \n\u003e The `METRICS_PAT` must be a Personal Access Token with **'Repository Contents: Write'** access for both the repository you are monitoring and the repository where you are storing the data.\n\n---\n\n## 📊 Visualize Your Data\nDon't just collect data—see it! \n\nUse the [**Observatory Dashboard Template**](https://github.com/YOUR_USERNAME/observatory-dashboard) to instantly generate interactive, dark-mode charts from the data collected by this Action.\n\n[![Dashboard Preview](./dashboard_preview.png)](https://github.com/YOUR_USERNAME/observatory-dashboard)\n*Click to view the Template Repository*\n\n---\n\n## 🏗 Architecture\n\nThe system follows a streamlined **Action-based architecture** to maintain security and organization:\n\n1. **The Action (`traffic-archiver-action`)**: This repository contains the `action.yml` logic. It is the engine that fetches data and processes the CSVs.\n2. **Observed Repo(s)**: The repositories you want to track. Each one runs a simple daily workflow that calls the Action.\n3. **Metrics Vault (`metrics-database`)**: The central storage repository where `.csv` files are maintained and updated.\n\n### 🔒 Recommended \"Metrics Vault\" Setup\n\nFor maximum security and clarity, your storage repository should be configured as follows:\n\n* **Visibility: Private** 🔒\nYour traffic statistics, clones, and unique viewer counts are sensitive business intelligence. Keeping this repo private ensures your analytics aren't publicly exposed.\n* **Purpose**: A dedicated central \"database\" for your `.csv` files.\n* **Why a Separate Repo?**:\n* **Clean History:** Prevents your source code repos from being cluttered with daily automated \"metrics\" commits.\n* **Centralized Analytics:** You can track 50 different projects and have them all report to this single repo, making it easy to run one dashboard for your entire portfolio.\n* **Persistent Data:** Even if you delete or rename a source repository, your historical data remains safe in the vault.\n\n### 🔑 Security Note: The `METRICS_PAT`\n\nBecause the **Metrics Vault** is private, the Action requires a Personal Access Token (PAT) with `repo` scope. This token allows the Action to securely \"tunnel\" the data from your public repositories into your private vault.\n\n---\n\n### 🔍 How it Works\n\nGitHub only keeps traffic data (clones and visitors) for **14 days**. This workflow acts as a \"Data Logger\":\n\n1. It wakes up every day and asks the GitHub API for the clone and view history of the **Observed Repo**.\n2. It compares this data with the existing logs in your **Observer Repo**.\n3. It appends only the most recent data and updates the \"14-day total\" summary.\n4. It deduplicates the file and sorts it so the most recent stats are always at the top.\n\n\u003e [!IMPORTANT]\n\u003e **Access Requirement:** Access Requirement: You must have owner or collaborator permissions on **both** the Observed and Observer repositories. This workflow uses a Personal Access Token (PAT) to **read** private traffic data from the source and **write** the archived logs to this repository.\n\n---\n\n## 🔐 Security \u0026 Permissions\n\nTo allow a workflow running in an **Observed Repo** to write data to the **Observer Repo**, specific permissions must be configured via a Fine-Grained Personal Access Token (PAT).\n\n### 1. The Personal Access Token (PAT)\n\nCreate a token named **\"Metrics Workflow\"** in your [Developer Settings](https://github.com/settings/tokens?type=beta) under Personal Access Tokens/Fine-grained tokens:\n\n* **Repository access**:\n * Select **Only select repositories**.\n * Include all **Observed Repositories** AND the **Observer Repository**.\n\n\n* **Permissions**:\n * `Administration`: Read-only (required for some traffic API metadata).\n * `Metadata`: Read-only.\n * `Contents`: **Read \u0026 Write** (Required to push `.csv` updates).\n\n![Create a Personal Access Token](PAT.png)\n\n### 2. Repository Secrets\n\nIn **each** Observed Repo (Settings \u003e Secrets and variables \u003e Actions), add the following secret:\n\n* **Name**: `METRICS_PAT`\n* **Value**: Paste the token generated above.\n\n\u003e [!NOTE]\n\u003e While it may seem like the Observer repo doesn't need a \"separate\" PAT, it is actually covered by the **\"Metrics Workflow\" PAT** you created. Because that single token has \"Write\" access to the Observer repo, it can push the data once the workflow finishes gathering it.\n\n![Personal Access Token permissions](PAT_permissions.png)\n---\n\n## 🚀 Usage\n\n\u003e [!IMPORTANT]\n\u003e The auto-generated \"Installation\" snippet on the Marketplace sidebar is incomplete. \n\u003e To archive data, you **must** include the permissions and token blocks below.\n\nCreate a file `.github/workflows/run-archiver.yml` in an **Observed Repo**:\n\n```yaml\nname: Collect Metrics\n\non:\n  schedule:\n    - cron: '0 0 * * *' # Runs daily at midnight\n  workflow_dispatch:    # Allows manual triggering\n\npermissions:\n  contents: read\n\njobs:\n  record-stats:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Run Traffic Archiver\n        uses: groda/github-traffic-archiver@v2\n        with:\n          metrics-repo: 'YOUR-USERNAME/observer-repo'\n          metrics-pat: ${{ secrets.METRICS_PAT }}\n          # observed-repo defaults to the current repo if omitted\n```\n\nThis is your caller workflow for the observed repository.\n\n---\n\n## 📊 Data Architecture\n\nThe workflow automates the collection of traffic data into a persistent CSV format. Unlike the default GitHub dashboard, this data never expires.\n\n### Storage Logic\n\nThe system generates or updates a CSV file named using the pattern `[YOUR-USERNAME]_[repo].csv` (e.g., `groda_my-project.csv`). This file is automatically committed to your **Observer Repository**.\n\n### Configuration\n\nTo set up your storage destination, update the `with` block in your workflow file:\n\n```yaml\nwith:\n  # The central repository where CSV files are stored\n  metrics-repo: YOUR-USERNAME/observer-repo\n\n```\n\n### File Structure\n\nEach CSV is optimized for easy sorting and long-term analysis:\n\n| Column | Description | Example |\n| --- | --- | --- |\n| **Date** | The date the data was captured (YYYY-MM-DD) | `2024-05-20` |\n| **Repository** | The name of the observed repository | `groda/color-combinations` |\n| **Type** | Type of metric (\"view\" or \"clone\") | `view` |\n| **Count** | Total number of times the repo was cloned that day | `42` |\n| **Uniques** | Number of unique GitHub accounts that cloned | `12` |\n\n\u003e **Note:** The workflow uses a \"smart merge\" logic. It checks the existing CSV in your Observer repo first, appends only the newest data, and ensures no duplicate dates are recorded.\n\nIn addition to daily stats **14-day Totals** records are recorded as `YYYY-MM-DD~ 14-day total`. The use of the tilde (`~`) ensures that in a descending sort, the **Total** summary for a specific day appears immediately **above** the individual daily stats for that same day.\n\n---\n\n## 🛠 Maintenance\n\n* **Adding Repos**: To track a new repository, simply add the `METRICS_PAT` secret to the new repo and create the caller workflow.\n* **Data Integrity**: The workflow uses `awk` to ensure that if it runs multiple times in one day, only the most recent (most complete) data point is saved, preventing duplicates.\n\n---\n\n## 🛡️🔐 Single Token vs. High Security\n\nThis workflow requires cross-repository permissions. You can choose between a **Standard** setup (the current setup, easier to maintain) or a **High Security** setup (follows the Principle of Least Privilege).\n\n### Option 1: Standard Setup (Single Token)\n\nRecommended for solo developers or small setups.\n\n* **Token Name**: `Metrics-Unified-Token`\n* **Scope**: All Observed Repos **AND** the Observer Repo.\n* **Permissions**:\n* `Metadata`: Read-only\n* `Administration`: Read-only\n* `Contents`: **Read \u0026 Write**\n\n\n* **Workflow Secret**: Store as `METRICS_PAT` in all Observed repos.\n\n### Option 2: High Security Setup (Dual Token)\n\nRecommended for teams or sensitive source code. This ensures the \"Writer\" token cannot be used to modify source code in your Observed repositories.\n\n#### A. The \"Traffic Reader\" Token\n\n* **Scope**: All **Observed Repos** only.\n* **Permissions**: `Metadata` (Read), `Administration` (Read).\n* **Usage**: Used by the workflow to fetch clone data from the GitHub API.\n* **Secret Name**: `READER_PAT`\n\n#### B. The \"Database Writer\" Token\n\n* **Scope**: The **Observer Repo** only.\n* **Permissions**: `Contents` (Read \u0026 Write).\n* **Usage**: Used by the workflow to `git push` the CSV file.\n* **Secret Name**: `WRITER_PAT`\n\n---\n\n## 🛡️🚀 Usage (High Security Example)\n\nIf you choose the **High Security** route, update your caller workflow in the Observed Repo as follows:\n\n```yaml\njobs:\n  update-metrics:\n    # You can point this to your own forked workflows repo if preferred\n    uses: groda/github-clone-archiver/.github/workflows/metrics.yml@v1\n    with:\n      metrics-repo: YOUR-USERNAME/observer-repo\n    secrets:\n      # We pass the Writer token to the reusable workflow\n      # so it can push to the central database repo\n      METRICS_PAT: ${{ secrets.WRITER_PAT }}\n\n```\n\n\u003e [!TIP]\n\u003e **Why do we pass the Writer token?** \u003e The GitHub Actions default `GITHUB_TOKEN` can read the current repo's traffic. By passing the `WRITER_PAT` as the `METRICS_PAT` secret, the workflow gains the specific authority needed to write to the **Observer Repo** without needing permission to write to your source code.\n\n---\n\n## 🛡️🗂️ Permission Table Reference\n\n| Permission | Requirement | Reason |\n| --- | --- | --- |\n| `Metadata` | Read | Basic repository access |\n| `Administration` | Read | Required to access `/traffic/clones` API |\n| `Contents` | Read/Write | Required to push `.csv` changes to Observer Repo |\n\n---\n\n### How to verify your permissions\n\nIf the workflow fails with a `403 Forbidden` error during the **push** phase, check that your PAT (the one passed to `METRICS_PAT`) has `Contents: Write` access specifically for the **Observer Repo**.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgroda%2Fgithub-traffic-archiver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgroda%2Fgithub-traffic-archiver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgroda%2Fgithub-traffic-archiver/lists"}