├── .github └── workflows │ └── analyze-code.yml ├── README.md ├── analyze-code.yml └── output └── cloc-output.json /.github/workflows/analyze-code.yml: -------------------------------------------------------------------------------- 1 | name: Update Lines of Code in Readme 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * 0" # Runs weekly on Sunday at midnight (UTC) 6 | workflow_dispatch: # Allows manual trigger 7 | 8 | jobs: 9 | count-lines: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout Code 14 | uses: actions/checkout@v3 15 | 16 | # Install required dependencies: jq (JSON processor), cloc (count lines of code), and locale settings 17 | - name: Install Dependencies 18 | run: | 19 | sudo apt-get update 20 | sudo apt-get install -y jq cloc locales 21 | sudo locale-gen en_US.UTF-8 22 | 23 | # Fetch public repositories (excluding forks) and clone only the default branch 24 | - name: Fetch and Clone Repositories 25 | env: 26 | GH_PAT: ${{ secrets.GH_PAT }} 27 | run: | 28 | USERNAME="arhamkhnz" 29 | 30 | # Get a list of public repositories that are not forks 31 | REPOS=$(curl -H "Authorization: token $GH_PAT" -s "https://api.github.com/user/repos?per_page=100" | jq -r '.[] | select(.fork == false) | .full_name') || echo "Error fetching repositories" 32 | 33 | mkdir -p public-repos 34 | cd public-repos 35 | 36 | for REPO in $REPOS; do 37 | REPO_URL="https://github.com/$REPO.git" 38 | AUTHENTICATED_REPO=$(echo "$REPO_URL" | sed "s/https:\/\//https:\/\/$GH_PAT@/g") 39 | 40 | # Determine the default branch dynamically and clone only that branch 41 | DEFAULT_BRANCH=$(curl -H "Authorization: token $GH_PAT" -s "https://api.github.com/repos/$REPO" | jq -r '.default_branch') 42 | 43 | # echo "Cloning $REPO (default branch: $DEFAULT_BRANCH)..." 44 | git clone --branch "$DEFAULT_BRANCH" --single-branch "$AUTHENTICATED_REPO" "$(basename $REPO)-$DEFAULT_BRANCH" || echo "Failed to clone a repository" 45 | done 46 | 47 | # Run cloc to analyze lines of code, excluding non-source code files 48 | echo "Calculating lines of code..." 49 | mkdir -p ../output 50 | cloc . --exclude-ext=json,html,css,svg,md,py,ps1,scss --json > ../output/cloc-output.json 51 | 52 | # Commit and push the updated cloc-output.json and README.md to the current branch 53 | - name: Commit and Push Output 54 | env: 55 | GH_PAT: ${{ secrets.GH_PAT }} 56 | run: | 57 | git config --global user.name "github-actions[bot]" 58 | git config --global user.email "github-actions[bot]@users.noreply.github.com" 59 | 60 | # Format and update README 61 | TOTAL_LINES=$(jq '.SUM.code // 0' output/cloc-output.json) 62 | JS_LINES=$(jq '.JavaScript.code // 0' output/cloc-output.json) 63 | TS_LINES=$(jq '.TypeScript.code // 0' output/cloc-output.json) 64 | JSX_LINES=$(jq '.JSX.code // 0' output/cloc-output.json) 65 | CSHARP_LINES=$(jq '."C#".code // 0' output/cloc-output.json) 66 | VUE_LINES=$(jq '."Vuejs Component".code // 0' output/cloc-output.json) 67 | PHP_LINES=$(jq '.PHP.code // 0' output/cloc-output.json) 68 | OTHER_LINES=$((TOTAL_LINES - JS_LINES - TS_LINES - JSX_LINES - PHP_LINES - CSHARP_LINES - VUE_LINES)) 69 | 70 | # Function to format numbers with commas (ensuring proper locale settings) 71 | format_number() { 72 | export LC_ALL="en_US.UTF-8" 73 | printf "%'d\n" $1 74 | } 75 | 76 | FORMATTED_TOTAL=$(format_number $TOTAL_LINES) 77 | FORMATTED_JS=$(format_number $JS_LINES) 78 | FORMATTED_TS=$(format_number $TS_LINES) 79 | FORMATTED_JSX=$(format_number $JSX_LINES) 80 | FORMATTED_CSHARP=$(format_number $CSHARP_LINES) 81 | FORMATTED_VUE=$(format_number $VUE_LINES) 82 | FORMATTED_PHP=$(format_number $PHP_LINES) 83 | FORMATTED_OTHER=$(format_number $OTHER_LINES) 84 | 85 | CODE_BLOCK="\`\`\` 86 | [ LANGUAGES BREAKDOWN ] 87 | 88 | JavaScript --> $FORMATTED_JS lines 89 | TypeScript --> $FORMATTED_TS lines 90 | JSX --> $FORMATTED_JSX lines 91 | Vue.js --> $FORMATTED_VUE lines 92 | PHP --> $FORMATTED_PHP lines 93 | C# --> $FORMATTED_CSHARP lines 94 | Other --> $FORMATTED_OTHER lines 95 | 96 | [ TOTAL LINES OF CODE: $FORMATTED_TOTAL ] 97 | \`\`\`" 98 | 99 | # Update README.md by replacing the section between predefined comment markers 100 | echo "$CODE_BLOCK" > temp_block.txt 101 | sed -i '//,//{ 102 | //!d 103 | //r temp_block.txt 104 | }' README.md 105 | 106 | rm temp_block.txt 107 | 108 | git add output/cloc-output.json README.md 109 | git commit -m "chore: update README and cloc-output.json with latest code stats" || echo "No changes to commit" 110 | git push origin HEAD 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHub Code Analyzer 2 | 3 | A fully automated GitHub repository analyzer that counts lines of code across all your repositories and updates stats dynamically. Runs on the default branch of each repo and skips forks. 4 | 5 | 6 | ``` 7 | [ LANGUAGES BREAKDOWN ] 8 | 9 | JavaScript --> 376,043 lines 10 | TypeScript --> 109,269 lines 11 | JSX --> 20,312 lines 12 | Vue.js --> 0 lines 13 | PHP --> 5,248 lines 14 | C# --> 0 lines 15 | Other --> 14,160 lines 16 | 17 | [ TOTAL LINES OF CODE: 525,032 ] 18 | ``` 19 | 20 | *Stats update automatically via GitHub Actions.* 21 | 22 | ## How It Works 23 | This GitHub Action automatically fetches all your public repositories (excluding forks), clones the **default branch**, and analyzes lines of code using [`cloc`](https://github.com/AlDanial/cloc). It then updates the repository’s `README.md` with the latest code statistics. The workflow runs **by default every Sunday at midnight UTC (customizable)**, keeping your stats up to date. 24 | 25 | ## Usage 26 | 27 | ### **Setting Up the GitHub Action** 28 | 1. **Add the Workflow File** 29 | Copy the `analyze-code.yml` file into your repository at: 30 | ``` 31 | .github/workflows/analyze-code.yml 32 | ``` 33 | Then commit and push the changes. 34 | 35 | *Make sure to update the workflow file with your GitHub username wherever required.* 36 | 37 | 2. **Generate a GitHub Personal Access Token (PAT)** 38 | You need a **Personal Access Token (PAT)** with **`repo`** permissions. 39 | Refer to [GitHub Docs](https://github.com/settings/tokens) on how to generate one. 40 | 41 | 3. **Add the Token to Repository Secrets** 42 | - Go to **Settings → Secrets and variables → Actions → New repository secret** 43 | - Name the secret **`GH_PAT`** 44 | - Paste the generated token and save. 45 | 46 | 4. **Update Workflow Permissions** 47 | In the repository where you're running the action, make sure to update workflow permissions: 48 | - Go to **Settings → Actions → General**. 49 | - Under **Workflow permissions**, select **"Read and write permissions"**. 50 | - This allows the workflow to update files like `README.md` automatically. 51 | 52 | 5. **Trigger the Workflow** 53 | - The workflow runs **by default every Sunday at midnight UTC (customizable)**. 54 | - To **run manually**, go to **GitHub Actions → Select Workflow → Run Workflow**. 55 | - To **run on every push**, modify the workflow's `on:` section to: 56 | ```yaml 57 | on: 58 | push: 59 | branches: 60 | - main 61 | ``` 62 | 63 | 6. **Wait for Processing** 64 | The time taken depends on the number of repositories and their sizes. Once completed, your `README.md` will be updated with the latest **lines of code breakdown**. 65 | 66 | 7. **Ensure Placeholders Are Present** 67 | To allow automatic updates, your `README.md` must include the following placeholders: 68 | ``` 69 | 70 | 71 | 72 | ``` 73 | The workflow will update the stats between these markers. 74 | *Remove `(STATIC EXAMPLE)` when adding it in your README, as it's just a placeholder. It's included here only to prevent automatic updates in this README.* 75 | 76 | ### **Configure Language Detection** 77 | By default, the workflow excludes some file types from counting: 78 | ```bash 79 | cloc . --exclude-ext=json,html,css,svg,md,py,ps1,scss --json > ../output/cloc-output.json 80 | ``` 81 | You can modify this list in the workflow file to include or exclude specific languages based on your needs. 82 | 83 | After execution, you can check the **`cloc-output.json`** file inside the `output` folder to see the full language breakdown. 84 | For a complete list of supported languages, refer to [`cloc` documentation](https://github.com/AlDanial/cloc). 85 | 86 | 87 | ## Upcoming Features 88 | 89 | Soon, I'll be pushing changes to configure workflow execution based on a JSON configuration. 90 | This will allow you to: 91 | - Specify **which repositories** the script should run on by default. 92 | - Define **whether to run on all branches** or only specific branches for certain repositories. 93 | 94 | This feature will be updated shortly. 95 | 96 | 97 | ## Contributing 98 | 99 | Contributions are welcome! Feel free to fork the repository, submit a PR, or open an issue. -------------------------------------------------------------------------------- /analyze-code.yml: -------------------------------------------------------------------------------- 1 | name: Update Lines of Code in Readme 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * 0" # Runs weekly on Sunday at midnight (UTC) 6 | workflow_dispatch: # Allows manual trigger 7 | 8 | jobs: 9 | count-lines: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout Code 14 | uses: actions/checkout@v3 15 | 16 | # Install required dependencies: jq (JSON processor), cloc (count lines of code), and locale settings 17 | - name: Install Dependencies 18 | run: | 19 | sudo apt-get update 20 | sudo apt-get install -y jq cloc locales 21 | sudo locale-gen en_US.UTF-8 22 | 23 | # Fetch public repositories (excluding forks) and clone only the default branch 24 | - name: Fetch and Clone Repositories 25 | env: 26 | GH_PAT: ${{ secrets.GH_PAT }} 27 | run: | 28 | # Set your GitHub username 29 | USERNAME="" 30 | 31 | # Get a list of public repositories that are not forks 32 | REPOS=$(curl -H "Authorization: token $GH_PAT" -s "https://api.github.com/user/repos?per_page=100" | jq -r '.[] | select(.fork == false) | .full_name') || echo "Error fetching repositories" 33 | 34 | mkdir -p public-repos 35 | cd public-repos 36 | 37 | for REPO in $REPOS; do 38 | REPO_URL="https://github.com/$REPO.git" 39 | AUTHENTICATED_REPO=$(echo "$REPO_URL" | sed "s/https:\/\//https:\/\/$GH_PAT@/g") 40 | 41 | # Determine the default branch dynamically and clone only that branch 42 | DEFAULT_BRANCH=$(curl -H "Authorization: token $GH_PAT" -s "https://api.github.com/repos/$REPO" | jq -r '.default_branch') 43 | 44 | echo "Cloning $REPO (default branch: $DEFAULT_BRANCH)..." 45 | git clone --branch "$DEFAULT_BRANCH" --single-branch "$AUTHENTICATED_REPO" "$(basename $REPO)-$DEFAULT_BRANCH" || echo "Failed to clone $REPO." 46 | done 47 | 48 | # Run cloc to analyze lines of code, excluding non-source code files 49 | echo "Calculating lines of code..." 50 | mkdir -p ../output 51 | cloc . --exclude-ext=json,html,css,svg,md,py,ps1,scss --json > ../output/cloc-output.json 52 | 53 | # Commit and push the updated cloc-output.json and README.md to the current branch 54 | - name: Commit and Push Output 55 | env: 56 | GH_PAT: ${{ secrets.GH_PAT }} 57 | run: | 58 | git config --global user.name "github-actions[bot]" 59 | git config --global user.email "github-actions[bot]@users.noreply.github.com" 60 | 61 | # Format and update README 62 | TOTAL_LINES=$(jq '.SUM.code // 0' output/cloc-output.json) 63 | JS_LINES=$(jq '.JavaScript.code // 0' output/cloc-output.json) 64 | TS_LINES=$(jq '.TypeScript.code // 0' output/cloc-output.json) 65 | JSX_LINES=$(jq '.JSX.code // 0' output/cloc-output.json) 66 | CSHARP_LINES=$(jq '."C#".code // 0' output/cloc-output.json) 67 | VUE_LINES=$(jq '."Vuejs Component".code // 0' output/cloc-output.json) 68 | PHP_LINES=$(jq '.PHP.code // 0' output/cloc-output.json) 69 | OTHER_LINES=$((TOTAL_LINES - JS_LINES - TS_LINES - JSX_LINES - PHP_LINES - CSHARP_LINES - VUE_LINES)) 70 | 71 | # Function to format numbers with commas (ensuring proper locale settings) 72 | format_number() { 73 | export LC_ALL="en_US.UTF-8" 74 | printf "%'d\n" $1 75 | } 76 | 77 | FORMATTED_TOTAL=$(format_number $TOTAL_LINES) 78 | FORMATTED_JS=$(format_number $JS_LINES) 79 | FORMATTED_TS=$(format_number $TS_LINES) 80 | FORMATTED_JSX=$(format_number $JSX_LINES) 81 | FORMATTED_CSHARP=$(format_number $CSHARP_LINES) 82 | FORMATTED_VUE=$(format_number $VUE_LINES) 83 | FORMATTED_PHP=$(format_number $PHP_LINES) 84 | FORMATTED_OTHER=$(format_number $OTHER_LINES) 85 | 86 | CODE_BLOCK="\`\`\` 87 | [ LANGUAGES BREAKDOWN ] 88 | 89 | JavaScript --> $FORMATTED_JS lines 90 | TypeScript --> $FORMATTED_TS lines 91 | JSX --> $FORMATTED_JSX lines 92 | Vue.js --> $FORMATTED_VUE lines 93 | PHP --> $FORMATTED_PHP lines 94 | C# --> $FORMATTED_CSHARP lines 95 | Other --> $FORMATTED_OTHER lines 96 | 97 | [ TOTAL LINES OF CODE: $FORMATTED_TOTAL ] 98 | \`\`\`" 99 | 100 | # Update README.md by replacing the section between predefined comment markers 101 | echo "$CODE_BLOCK" > temp_block.txt 102 | sed -i '//,//{ 103 | //!d 104 | //r temp_block.txt 105 | }' README.md 106 | 107 | rm temp_block.txt 108 | 109 | git add output/cloc-output.json README.md 110 | git commit -m "chore: update README and cloc-output.json with latest code stats" || echo "No changes to commit" 111 | git push origin HEAD 112 | -------------------------------------------------------------------------------- /output/cloc-output.json: -------------------------------------------------------------------------------- 1 | {"header" : { 2 | "cloc_url" : "github.com/AlDanial/cloc", 3 | "cloc_version" : "1.98", 4 | "elapsed_seconds" : 5.15685415267944, 5 | "n_files" : 6314, 6 | "n_lines" : 717329, 7 | "files_per_second" : 1224.38987279082, 8 | "lines_per_second" : 139102.05306607}, 9 | "JavaScript" :{ 10 | "nFiles": 3547, 11 | "blank": 68634, 12 | "comment": 99398, 13 | "code": 376043}, 14 | "TypeScript" :{ 15 | "nFiles": 2302, 16 | "blank": 12537, 17 | "comment": 3239, 18 | "code": 109269}, 19 | "JSX" :{ 20 | "nFiles": 171, 21 | "blank": 1923, 22 | "comment": 71, 23 | "code": 20312}, 24 | "PHP" :{ 25 | "nFiles": 77, 26 | "blank": 798, 27 | "comment": 3607, 28 | "code": 5248}, 29 | "Sass" :{ 30 | "nFiles": 32, 31 | "blank": 361, 32 | "comment": 239, 33 | "code": 4706}, 34 | "XML" :{ 35 | "nFiles": 34, 36 | "blank": 265, 37 | "comment": 29, 38 | "code": 3357}, 39 | "YAML" :{ 40 | "nFiles": 41, 41 | "blank": 192, 42 | "comment": 172, 43 | "code": 1502}, 44 | "Text" :{ 45 | "nFiles": 19, 46 | "blank": 181, 47 | "comment": 0, 48 | "code": 1413}, 49 | "JSON" :{ 50 | "nFiles": 1, 51 | "blank": 0, 52 | "comment": 0, 53 | "code": 823}, 54 | "diff" :{ 55 | "nFiles": 1, 56 | "blank": 3, 57 | "comment": 13, 58 | "code": 588}, 59 | "Windows Module Definition" :{ 60 | "nFiles": 5, 61 | "blank": 88, 62 | "comment": 0, 63 | "code": 437}, 64 | "Bourne Shell" :{ 65 | "nFiles": 12, 66 | "blank": 54, 67 | "comment": 53, 68 | "code": 283}, 69 | "make" :{ 70 | "nFiles": 22, 71 | "blank": 90, 72 | "comment": 22, 73 | "code": 219}, 74 | "DOS Batch" :{ 75 | "nFiles": 8, 76 | "blank": 48, 77 | "comment": 4, 78 | "code": 180}, 79 | "Gradle" :{ 80 | "nFiles": 10, 81 | "blank": 27, 82 | "comment": 7, 83 | "code": 171}, 84 | "Bourne Again Shell" :{ 85 | "nFiles": 2, 86 | "blank": 21, 87 | "comment": 21, 88 | "code": 141}, 89 | "CoffeeScript" :{ 90 | "nFiles": 7, 91 | "blank": 23, 92 | "comment": 11, 93 | "code": 92}, 94 | "INI" :{ 95 | "nFiles": 5, 96 | "blank": 17, 97 | "comment": 0, 98 | "code": 72}, 99 | "Swift" :{ 100 | "nFiles": 1, 101 | "blank": 19, 102 | "comment": 13, 103 | "code": 52}, 104 | "Java" :{ 105 | "nFiles": 4, 106 | "blank": 19, 107 | "comment": 17, 108 | "code": 50}, 109 | "Properties" :{ 110 | "nFiles": 6, 111 | "blank": 10, 112 | "comment": 31, 113 | "code": 21}, 114 | "zsh" :{ 115 | "nFiles": 1, 116 | "blank": 0, 117 | "comment": 0, 118 | "code": 15}, 119 | "EJS" :{ 120 | "nFiles": 2, 121 | "blank": 0, 122 | "comment": 0, 123 | "code": 14}, 124 | "Dockerfile" :{ 125 | "nFiles": 1, 126 | "blank": 9, 127 | "comment": 10, 128 | "code": 10}, 129 | "Ruby" :{ 130 | "nFiles": 1, 131 | "blank": 0, 132 | "comment": 0, 133 | "code": 8}, 134 | "Lisp" :{ 135 | "nFiles": 1, 136 | "blank": 0, 137 | "comment": 0, 138 | "code": 6}, 139 | "ProGuard" :{ 140 | "nFiles": 1, 141 | "blank": 3, 142 | "comment": 18, 143 | "code": 0}, 144 | "SUM": { 145 | "blank": 85322, 146 | "comment": 106975, 147 | "code": 525032, 148 | "nFiles": 6314} } 149 | --------------------------------------------------------------------------------