{"id":23002552,"url":"https://github.com/jwodder/forall","last_synced_at":"2025-04-02T14:20:58.894Z","repository":{"id":267536904,"uuid":"901562872","full_name":"jwodder/forall","owner":"jwodder","description":"Operate on each project in a directory","archived":false,"fork":false,"pushed_at":"2025-04-01T06:55:08.000Z","size":238,"stargazers_count":1,"open_issues_count":12,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-01T07:38:13.630Z","etag":null,"topics":["batch-processing","git","repository-management","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/jwodder.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2024-12-10T22:25:43.000Z","updated_at":"2025-04-01T06:55:11.000Z","dependencies_parsed_at":null,"dependency_job_id":"6228ce1a-4c64-4e2e-b644-950234ba5ef4","html_url":"https://github.com/jwodder/forall","commit_stats":null,"previous_names":["jwodder/forall"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwodder%2Fforall","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwodder%2Fforall/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwodder%2Fforall/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwodder%2Fforall/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jwodder","download_url":"https://codeload.github.com/jwodder/forall/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246828477,"owners_count":20840474,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["batch-processing","git","repository-management","rust"],"created_at":"2024-12-15T07:11:22.330Z","updated_at":"2025-04-02T14:20:58.888Z","avatar_url":"https://github.com/jwodder.png","language":"Rust","readme":"[![Project Status: Concept – Minimal or no implementation has been done yet, or the repository is only intended to be a limited example, demo, or proof-of-concept.](https://www.repostatus.org/badges/latest/concept.svg)](https://www.repostatus.org/#concept)\n[![CI Status](https://github.com/jwodder/forall/actions/workflows/test.yml/badge.svg)](https://github.com/jwodder/forall/actions/workflows/test.yml)\n[![codecov.io](https://codecov.io/gh/jwodder/forall/branch/main/graph/badge.svg)](https://codecov.io/gh/jwodder/forall)\n[![Minimum Supported Rust Version](https://img.shields.io/badge/MSRV-1.81-orange)](https://www.rust-lang.org)\n[![MIT License](https://img.shields.io/github/license/jwodder/forall.svg)](https://opensource.org/licenses/MIT)\n\n[GitHub](https://github.com/jwodder/forall) | [Issues](https://github.com/jwodder/forall/issues) | [Changelog](https://github.com/jwodder/forall/blob/main/CHANGELOG.md)\n\n`forall` is my personal [Rust](https://www.rust-lang.org) program for\nperforming various operations on multiple local Git repositories at once.  It\ntraverses one or more directory trees looking for projects and runs a specified\ncommand on each of them, possibly after excluding certain projects.\n\nCurrently, only Git repositories containing Rust projects or\n`pyproject.toml`-based Python projects are supported.\n\nWhile this program may in theory be suitable for general use, I make no\nguarantees, nor do I intend to release it for general consumption.  Use at your\nown risk.\n\nUsage\n=====\n\n    forall [\u003cglobal options\u003e] \u003csubcommand\u003e ...\n\nGlobal Options\n--------------\n\nAll of the following options can be supplied either before or after the\nsubcommand.\n\n- `-D`, `--def-branch` — Only operate on projects currently on their default\n  branch (`main` or `master`)\n\n- `--no-def-branch` — Only operate on projects currently not on their default\n  branch\n\n- `--exclude \u003cname\u003e` — Do not operate on the given project.  This option can be\n  specified multiple times.\n\n- `-f \u003cshellcmd\u003e`, `--filter \u003cshellcmd\u003e` — Run `$SHELL -c \u003cshellcmd\u003e` with the\n  current working directory set to each project's directory and only operate on\n  those projects for which the command succeeds\n\n- `--has-github` — Only operate on projects that have GitHub remotes\n\n- `--no-github` — Only operate on projects that do not have GitHub remotes\n\n- `--has-stash` — Only operate on projects that have stashed changes\n\n- `--no-stash` — Only operate on projects that do not have stashed changes\n\n- `-L \u003clanguage\u003e`, `--language \u003clanguage\u003e` — Only operate on projects written\n  in the given language.  Possible options are \"Python\"/\"py\" and \"Rust\"/\"rs\"\n  (all case-insensitive).\n\n- `-k`, `--keep-going` — By default, if a subcommand fails or another error\n  occurs for a project, `forall` terminates immediately.  If `--keep-going` is\n  supplied, `forall` will instead continue with the remaining projects and will\n  print a list of all failures on exit.\n\n- `-q`, `--quiet` — Be less verbose; this option can be specified multiple\n  times.  See \"Logging\" below for more infomation.\n\n- `-R \u003cdirpath\u003e`, `--root \u003cdirpath\u003e` — Start traversing from `\u003cdirpath\u003e`.  This\n  option can be specified multiple times to traverse multiple directories.\n  [default: the current working directory]\n\n- `-v`, `--verbose` — Be more verbose.  See \"Logging\" below for more\n  information.\n\nProject Names\n-------------\n\nEach project is identified by a name, which is output when operating on the\nproject and accepted by the `--skip` option in order to not operate on a\nproject.  Project names are determined as follows:\n\n- For Python projects and non-workspace Rust projects, the name is the metadata\n  name of the sole package in the project.\n\n- For non-virtual Rust workspaces, the name is the metadata name of the root\n  package.\n\n- For virtual Rust workspaces, the project's `Cargo.toml` must set\n  `workspace.package.repository` to a GitHub repository URL, and the name of\n  this repository is used as the project name.\n\nLogging\n-------\n\n`forall` logs various messages to stdout and/or stderr during its operation.\nThe `-q`/`--quiet` and `-v`/`--verbose` options can be used to control which\nmessages are shown.  The following table indicates when each type of message is\nshown for each quiet/verbose level:\n\n| Message Type                      | `-qq` | `-q` |  —  | `-v` | Style  | Stream |\n| --------------------------------- | :---: | :--: | :-: | :--: | ------ | ------ |\n| Project names                     | ✓     | ✓    | ✓   | ✓    | Bold   | stdout |\n| Errors                            | ✓     | ✓    | ✓   | ✓    | Red    | stderr |\n| Lists of failures                 | ✓     | ✓    | ✓   | ✓    | Plain  | stdout |\n| `run` and `runpr` commands        | ✗     | ✗    | ✓   | ✓    | Cyan   | stderr |\n| `run` and `runpr` commands output | ✗     | ✓    | ✓   | ✓    | Plain  | stdout |\n| Operational commands              | ✗     | ✗    | ✓   | ✓    | Cyan   | stderr |\n| Operational commands output       | ✗     | ✗    | ✓   | ✓    | Plain  | stdout |\n| Filter commands                   | ✗     | ✗    | ✗   | ✓    | Cyan   | stderr |\n| Filter commands output            | ✗     | ✗    | ✗   | ✗    | —      | —      |\n| HTTP requests                     | ✗     | ✗    | ✗   | ✓    | Cyan   | stderr |\n| Messages about skipped projects   | ✗     | ✗    | ✗   | ✓    | Yellow | stderr |\n| Other informative messages        | ✗     | ✗    | ✓   | ✓    | Yellow | stderr |\n\nNotes:\n\n- If both `--quiet` and `--verbose` are specified on the command line, the\n  `--verbose` negates one instance of `--quiet`.\n\n- Passing more than two `--quiet` options (after applying `--verbose` negation)\n  is equivalent to passing just two.\n\n- \"Errors\" includes captured output from failed commands whose output would\n  otherwise be suppressed.\n\n- The \"commands\" message types are messages showing each executed command along\n  with its arguments and working directory.\n\n- \"`run` and `runpr` commands\" are commands passed to the `run` and `runpr`\n  subcommands for execution.\n\n- \"Operational commands\" are miscellaneous commands run by `forall`, such as\n  `git commit` for `runpr` or `pre-commit autoupdate` for `pre-update`.\n\n- \"Filter commands\" are commands run in order to determine whether to operate\n  on a project.\n\n`forall list`\n-------------\n\n    forall [\u003cglobal options\u003e] list [\u003coptions\u003e]\n\nPrint the name of each project in the directory tree in sorted order.\n\n### Options\n\n- `-J`, `--json` — Instead of printing the name of each project, print\n  newline-delimited JSON objects describing each project.  Each object contains\n  the following fields:\n\n    - `name` — project name\n    - `dirpath` — path to the directory in which the project is located\n    - `language` — the project's language (`\"Python\"` or `\"Rust\"`)\n    - `ghrepo` — the project's remote GitHub repository in `{owner}/{name}`\n      format, or `null` if it does not have a GitHub remote\n    - `on_default_branch` — `true` if the Git repository is currently on the\n      default branch (`main` or `master`), `false` otherwise\n    - `is_workspace` — `true` iff the project is a Rust workspace\n    - `is_virtual_workspace` — `true` iff the project is a Rust virtual\n      workspace\n\n`forall clean`\n-------------\n\n    forall [\u003cglobal options\u003e] clean\n\nRun `git clean -dXf` on each project that needs it\n\n`forall cloc`\n-------------\n\n    forall [\u003cglobal options\u003e] cloc\n\nUse [`cloc`](https://github.com/AlDanial/cloc/) to count the number of\neffective lines in each project, and output a simple table of the results.\n\n`forall gc`\n-----------\n\n    forall [\u003cglobal options\u003e] gc\n\nRun `git gc` on each project\n\n`forall pre-update`\n-------------------\n\n    forall [\u003cglobal options\u003e] pre-update\n\nRun `pre-commit autoupdate` on all projects with `.pre-commit-config.yaml`\nfiles.  `pre-commit run -a` is then run to apply any new formatting, followed\nby a second `pre-commit run -a` to ensure that linting is still successful.\nAny \u0026 all changes are then committed.\n\n`forall pull`\n-------------\n\n    forall [\u003cglobal options\u003e] pull\n\nRun `git pull` on each project that has a GitHub remote\n\n`forall push`\n-------------\n\n    forall [\u003cglobal options\u003e] push\n\nRun `git push` on each project that has a GitHub remote and for which `HEAD` is\nahead of `@{upstream}`\n\n`forall rsclean`\n----------------\n\n    forall [\u003cglobal options\u003e] rsclean\n\nRun `cargo clean` on each Rust project that contains a `target/` directory\n\n`forall run`\n------------\n\n    forall [\u003cglobal options\u003e] run [\u003coptions\u003e] \u003ccommand\u003e [\u003cargs\u003e ...]\n\nRun the given command on each project.\n\nThe command is run with the current working directory set to each respective\nproject's directory.\n\n### Options\n\n- `--script` — Treat the command as a path to a script file.  The path is\n  canonicalized, and it is run via `perl` for its shebang handling; thus, the\n  script need not be executable, but it does need to have an appropriate\n  shebang.\n\n- `--shell` — Run the command with `$SHELL -c \u003ccommand\u003e \u003cargs\u003e`\n\n- `-s`, `--stash` — Stash any uncommitted changes before running the command\n\n`forall run-pr`\n---------------\n\n    forall [\u003cglobal options\u003e] run-pr [\u003coptions\u003e] \u003ccommand\u003e [\u003cargs\u003e ...]\n\nRun the given command on each project and submit the changes as a GitHub pull\nrequest.\n\nSpecifically, for each project that has a non-archived GitHub remote:\n\n- Any uncommitted changes are stashed.\n\n- A new branch is created (starting from the default branch) and checked out.\n\n- The command is run on the new branch, with the current working directory set\n  to the project's directory.\n    - The command should not perform any Git commits itself at this time.\n\n- `git add .` is run.  If there are no staged changes afterwards, then the\n  project's default branch is checked out, the PR branch is deleted, and no\n  further steps are taken.\n\n- `git commit` is run, and the branch is pushed to the `origin` remote.\n\n- A pull request is created in the corresponding GitHub repository, and the URL\n  of the PR is output.\n\nThis command requires a GitHub access token to have been either set via the\n`GH_TOKEN` or `GITHUB_TOKEN` environment variable or else saved with\n[`gh`](https://github.com/cli/cli) in order to interact with the GitHub REST\nAPI.\n\n### Options\n\n- `-b \u003cNAME\u003e`, `--branch \u003cNAME\u003e` — Set the name for the new branch from which\n  the pull request is created.  Defaults to `forall-runpr-%Y%m%d%H%M%S`.\n\n- `-B \u003cFILE\u003e`, `--pr-body-file \u003cFILE\u003e` — Path to a file containing the body to\n  use for the pull requests.  If not specified, the PRs will have empty bodies.\n\n- `-l \u003cNAME\u003e`, `--label \u003cNAME\u003e` — Apply the given label to the new pull\n  requests.  If the label does not already exist in a repository, it is\n  created.  This option can be specified multiple times.\n\n- `-m \u003cTEXT\u003e`, `--message \u003cTEXT\u003e` — The commit message to use.  This option is\n  required.\n\n- `--script` — Treat the command as a path to a script file.  The path is\n  canonicalized, and it is run via `perl` for its shebang handling; thus, the\n  script need not be executable, but it does need to have an appropriate\n  shebang.\n\n- `--shell` — Run the command with `$SHELL -c \u003ccommand\u003e \u003cargs\u003e`\n\n- `--soft-label \u003cNAME\u003e` — Apply the given label to the new pull requests.  If\n  the label does not already exist in a repository, the label is not applied.\n  This option can be specified multiple times.\n\n- `-T \u003cTEXT\u003e`, `--pr-title \u003cTEXT\u003e` — The title to give the pull requests.\n  Defaults to the commit message with `[skip ci]` and similar strings removed.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjwodder%2Fforall","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjwodder%2Fforall","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjwodder%2Fforall/lists"}