https://github.com/duolingo/pulldozer
CLI tool for batch editing multiple repos :tractor:
https://github.com/duolingo/pulldozer
bash cli codemod git github pr
Last synced: 18 days ago
JSON representation
CLI tool for batch editing multiple repos :tractor:
- Host: GitHub
- URL: https://github.com/duolingo/pulldozer
- Owner: duolingo
- License: apache-2.0
- Created: 2019-04-07T05:27:15.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2024-12-10T17:04:55.000Z (11 months ago)
- Last Synced: 2025-06-06T12:05:54.375Z (5 months ago)
- Topics: bash, cli, codemod, git, github, pr
- Language: Shell
- Homepage:
- Size: 200 KB
- Stars: 51
- Watchers: 7
- Forks: 5
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Pulldozer
Pulldozer is a simple CLI tool for batch editing multiple GitHub repos.
You give Pulldozer a transformation script and it spits out pull requests. [Duolingo](https://www.duolingo.com/) has used Pulldozer to create well over 9000 PRs to date!
## Usage
Clone this repo onto any Unix machine that has [`curl`](https://formulae.brew.sh/formula/curl) and [`jq`](https://formulae.brew.sh/formula/jq). Set your `GITHUB_TOKEN` environment variable to an [access token](https://github.com/settings/tokens) with `repo` scope and [SSO enabled](https://docs.github.com/en/github/authenticating-to-github/authorizing-a-personal-access-token-for-use-with-saml-single-sign-on). To perform a batch edit (a.k.a. _codemod_):
1. Create a script file that defines a `COMMIT_MESSAGE` string, `transform` function, and `REPOS` list.
Click here to see a Shell example
```sh
COMMIT_MESSAGE='Fix "langauge" typos'
transform() {
# Write your transformation logic inside this function. GitHub org name
# and repo name are passed into this `transform` function as vars $1 and
# $2, respectively.
echo "Hello world from $1/$2" > README.md
# Pulldozer provides a `replace_all` helper function for replacing text
# across all repo files. It's basically glorified sed.
replace_all 'langauge' 'language'
# Advanced `replace_all` example: regex, capture grouping, multi-line
# matching, and file path filtering
replace_all '(\nprotobuf==)\S+' '\13.19.4' 'requirements\.(in|txt)$'
}
REPOS='
artnc/dotfiles
duolingo/halflife-regression
duolingo/rtl-viewpager
'
# Optional but recommended: Markdown to include in pull request descriptions
DESCRIPTION='[Correct spelling](https://en.wiktionary.org/wiki/language)'
# Optional: Markdown that will be posted as a comment on PRs
COMMENT='This is a pull request comment'
# Optional: Declare this variable to enable GitHub auto-merge on PRs
AUTOMERGE=1
```
By default, Pulldozer will interpret your script as POSIX shell. If you want to use Bash instead, just run `bash ./pulldozer` instead of `./pulldozer` during step 2 below.
Click here to see a JavaScript example
```js
const COMMIT_MESSAGE = 'Fix "langauge" typos';
// Write your transformation logic inside this function. GitHub org name and
// repo name are passed in as parameters.
const transform = async (org, repo) => {
const { writeFile } = require("fs").promises;
await writeFile("README.md", `Hello world from ${org}/${repo}`);
// Pulldozer provides a `replace_all` helper function (no need to import)
// for replacing text across all repo files. This helper is easier than
// traversing the repo yourself, and it also respects .gitignore.
await replace_all("langauge", "language");
// Advanced `replace_all` example: regex, capture grouping, multiline
// matching, editing only a subset of repo files (optional third param)
await replace_all(
/(\nprotobuf==)\S+/,
"$13.19.4",
/requirements\.(in|txt)$/,
);
};
const REPOS = [
"artnc/dotfiles",
"duolingo/halflife-regression",
"duolingo/rtl-viewpager",
];
// Optional but recommended: Markdown to include in pull request descriptions
const DESCRIPTION =
"[Correct spelling](https://en.wiktionary.org/wiki/language)";
// Optional: Markdown that will be posted as a comment on PRs
const COMMENT = "This is a pull request comment";
// Optional: Declare this variable to enable GitHub auto-merge on PRs
const AUTOMERGE = 1;
```
Click here to see a Python example
```py
COMMIT_MESSAGE = 'Fix "langauge" typos'
# Write your transformation logic inside this function. GitHub org name and
# repo name are passed in as parameters.
def transform(org, repo):
with open("README.md", "w") as f:
f.write(f"Hello world from {org}/{repo}")
# Pulldozer provides a `replace_all` helper function (no need to import)
# for replacing text across all repo files. This helper is easier than
# using `os.walk` and `re.sub`, and it also respects .gitignore.
replace_all("langauge", "language")
# Advanced `replace_all` example: regex, capture grouping, multiline
# matching, editing only a subset of repo files (optional third param)
replace_all(r"(\nprotobuf==)\S+", r"\13.19.4", r"requirements\.(in|txt)$")
REPOS = [
"artnc/dotfiles",
"duolingo/halflife-regression",
"duolingo/rtl-viewpager",
]
# Optional but recommended: Markdown to include in pull request descriptions
DESCRIPTION = "[Correct spelling](https://en.wiktionary.org/wiki/language)"
# Optional: Markdown that will be posted as a comment on PRs
COMMENT = "This is a pull request comment"
# Optional: Declare this variable to enable GitHub auto-merge on PRs
AUTOMERGE = 1
```
1. Run `./pulldozer /path/to/your/script`. Pulldozer will generate a preview diff and ask for confirmation before creating PRs.
## Demo video

_Duolingo is hiring! Apply at https://www.duolingo.com/careers_