{"id":13589946,"url":"https://github.com/palantir/bulldozer","last_synced_at":"2025-10-23T18:38:41.910Z","repository":{"id":25423467,"uuid":"102513583","full_name":"palantir/bulldozer","owner":"palantir","description":"GitHub Pull Request Auto-Merge Bot","archived":false,"fork":false,"pushed_at":"2025-05-01T23:33:57.000Z","size":12215,"stargazers_count":760,"open_issues_count":61,"forks_count":101,"subscribers_count":233,"default_branch":"develop","last_synced_at":"2025-05-02T00:23:53.613Z","etag":null,"topics":["bot","github-app","github-bot","golang","merge","octo-correct-managed","prs"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/palantir.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}},"created_at":"2017-09-05T17:58:09.000Z","updated_at":"2025-05-01T23:34:00.000Z","dependencies_parsed_at":"2023-02-18T11:46:34.727Z","dependency_job_id":"b8b2a095-d84c-4e56-a0f2-4a1a4e382752","html_url":"https://github.com/palantir/bulldozer","commit_stats":{"total_commits":339,"total_committers":33,"mean_commits":"10.272727272727273","dds":0.3893805309734514,"last_synced_commit":"e4ac61f180c45f3725f56b2329c9f801f7114ae2"},"previous_names":[],"tags_count":45,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palantir%2Fbulldozer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palantir%2Fbulldozer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palantir%2Fbulldozer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palantir%2Fbulldozer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/palantir","download_url":"https://codeload.github.com/palantir/bulldozer/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254259370,"owners_count":22040819,"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":["bot","github-app","github-bot","golang","merge","octo-correct-managed","prs"],"created_at":"2024-08-01T16:00:36.625Z","updated_at":"2025-10-23T18:38:41.881Z","avatar_url":"https://github.com/palantir.png","language":"Go","funding_links":[],"categories":["golang","Go","Autonomous Repo Management"],"sub_categories":["Auto-Merge Strategies"],"readme":"\u003cp align=\"right\"\u003e\n\u003ca href=\"https://autorelease.general.dmz.palantir.tech/palantir/bulldozer\"\u003e\u003cimg src=\"https://img.shields.io/badge/Perform%20an-Autorelease-success.svg\" alt=\"Autorelease\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# bulldozer\n\n[![Docker Pulls](https://img.shields.io/docker/pulls/palantirtechnologies/bulldozer.svg)](https://hub.docker.com/r/palantirtechnologies/bulldozer/)\n\n`bulldozer` is a [GitHub App](https://developer.github.com/apps/) that\nautomatically merges pull requests (PRs) when (and only when) all required\nstatus checks are successful and required reviews are provided.\n\nAdditionally, `bulldozer` can:\n\n- Only merge pull requests that match certain conditions, like having a\n  specific label or comment\n- Ignore pull requests that match certain conditions, like having a specific\n  label or comment\n- Automatically keep pull request branches up-to-date by merging in the target\n  branch\n- Wait for additional status checks that are not required by GitHub\n\nBulldozer might be useful if you:\n\n- Have CI builds that take longer than the normal review process. It will merge\n  reviewed PRs as soon as the tests pass so you don't have to watch the pull\n  request or remember to merge it later.\n- Combine it with [policy-bot](https://github.com/palantir/policy-bot) to\n  automatically merge certain types of pre-approved or automated changes.\n- Want to give contributors more control over when they can merge PRs without\n  granting them write access to the repository.\n- Have a lot of active development that makes it difficult to merge a pull\n  request while it is up-to-date with the target branch.\n\n## Contents\n\n* [Behavior](#behavior)\n* [Configuration](#configuration)\n  + [bulldozer.yml Specification](#bulldozeryml-specification)\n* [FAQ](#faq)\n* [Deployment](#deployment)\n* [Development](#development)\n* [Contributing](#contributing)\n* [License](#license)\n\n## Behavior\n\n`bulldozer` will only merge pull requests that GitHub allows non-admin\ncollaborators to merge. This means that all branch protection settings,\nincluding required status checks and required reviews, are respected. It also\nmeans that you _must_ enable branch protection to prevent `bulldozer` from\nimmediately merging every pull request.\n\nOnly pull requests matching the trigger conditions (or _not_ matching\nignore conditions) are considered for merging. `bulldozer` is event-driven,\nwhich means it will usually merge a pull request within a few seconds of the\npull request satisfying all preconditions.\n\n## Configuration\n\nThe behavior of the bot is configured by a `.bulldozer.yml` file at the root of\nthe repository. The file name and location are configurable when running your\nown instance of the server.\n\n- The file is read from the most recent commit on the _target_ branch of each\n  pull request.\n\n- The file may contain a reference to a configuration in a different\n  repository (see [Remote Configuration](#remote-configuration).)\n\n- If the file does not exist in the repository, `bulldozer` tries to load a\n  shared `bulldozer.yml` file at the root of the `.github` repository in the\n  same organization. You can change this path and repository name when running\n  your own instance of the server.\n\n- You can also define a global default configuration when running your own\n  instance of the server. This is used if the repository or the shared\n  organization repository do not define any configuration.\n\n- If configuration does not exist in the repository or in the shared\n  organization repository and the server does not have a default configuration,\n  `bulldozer` does not act on the pull request. This means it is safe to enable\n  `bulldozer` on all repositories in an organization.\n\n### bulldozer.yml Specification\n\nThe `.bulldozer.yml` file supports the following keys.\n\n```yaml\n# \"version\" is the configuration version, currently \"1\".\nversion: 1\n\n# \"merge\" defines how and when pull requests are merged. If the section is\n# missing, bulldozer will consider all pull requests and use default settings.\nmerge:\n  # \"trigger\" defines the set of pull requests considered by bulldozer. If\n  # the section is missing, bulldozer considers all pull requests not excluded\n  # by the ignore conditions.\n  trigger:\n    # Pull requests with any of these labels (case-insensitive) are added to\n    # the trigger.\n    labels: [\"merge when ready\"]\n\n    # Pull requests where the body or any comment contains any of these\n    # substrings are added to the trigger.\n    comment_substrings: [\"==MERGE_WHEN_READY==\"]\n\n    # Pull requests where any comment matches one of these exact strings are\n    # added to the trigger.\n    comments: [\"Please merge this pull request!\"]\n\n    # Pull requests where the body contains any of these substrings are added\n    # to the trigger.\n    pr_body_substrings: [\"==MERGE_WHEN_READY==\"]\n\n    # Pull requests targeting any of these branches are added to the trigger.\n    branches: [\"develop\"]\n\n    # Pull requests targeting branches matching any of these regular expressions are added to the trigger.\n    branch_patterns: [\"feature/.*\"]\n\n    # Pull requests with auto merge enabled are added to the trigger.\n    auto_merge: true\n\n  # \"ignore\" defines the set of pull request ignored by bulldozer. If the\n  # section is missing, bulldozer considers all pull requests. It takes the\n  # same keys as the \"trigger\" section.\n  ignore:\n    labels: [\"do not merge\"]\n    comment_substrings: [\"==DO_NOT_MERGE==\"]\n\n  # \"method\" defines the merge method. The available options are \"merge\",\n  # \"rebase\", \"squash\", and \"ff-only\".\n  method: squash\n\n  ##### branch_method has been DEPRECATED in favor of merge_method #####\n  # \n  # Allows the merge method that is used when auto-merging a PR to be different\n  # target branch. The keys of the hash are the target branch name, and the values are the merge method that\n  # will be used for PRs targeting that branch. The valid values are the same as for the \"method\" key.\n  # Note: If the target branch does not match any of the specified keys, the \"method\" key is used instead.\n  branch_method:\n    develop: squash\n    master: merge\n  ##### branch_method has been DEPRECATED in favor of merge_method #####\n\n  # Allows the merge method that is used when auto-merging a PR to be different\n  # based on trigger criteria. The first method where ALL triggers match will\n  # be used. Otherwise, the method specified previously in \"merge.method\" will \n  # be used.\n  # - ALL trigger criteria must match, unlike merge/trigger where ANY match \n  # will trigger bulldozer.\n  # - This will override any branch_method logic if one of the methods is\n  # triggered\n  # - If no trigger criteria is provided the method is ignored\n  merge_method:\n    # \"method\" defines the merge method. The available options are \"merge\",\n    # \"rebase\", \"squash\", and \"ff-only\".\n    - method: squash\n      trigger:\n        # All methods from merge/trigger are supported. Additionally, the\n        # following additional methods are provided:\n\n        # Pull requests which a number of commits less than or equal to this value are added to the trigger.\n        max_commits: 3\n\n  # \"options\" defines additional options for the individual merge methods.\n  options:\n    # \"squash\" options are only used when the merge method is \"squash\"\n    squash:\n      # \"title\" defines how the title of the commit message is created when\n      # generating a squash commit. The options are \"pull_request_title\",\n      # \"first_commit_title\", and \"github_default_title\". The default is\n      # \"pull_request_title\".\n      title: \"pull_request_title\"\n\n      # \"body\" defines how the body of the commit message is created when\n      # generating a squash commit. The options are \"pull_request_body\",\n      # \"summarize_commits\", and \"empty_body\". The default is \"empty_body\".\n      body: \"empty_body\"\n\n      # If \"body\" is \"pull_request_body\", then the commit message will be the\n      # part of the pull request body surrounded by \"message_delimiter\"\n      # strings. This is disabled (empty string) by default.\n      message_delimiter: ==COMMIT_MSG==\n\n  # \"required_statuses\" is a list of additional status contexts that must pass\n  # before bulldozer can merge a pull request. This is useful if you want to\n  # require extra testing for automated merges, but not for manual merges.\n  required_statuses:\n    - \"ci/circleci: ete-tests\"\n\n  # If true, bulldozer will delete branches after their pull requests merge.\n  delete_after_merge: true\n\n  # If true, bulldozer will merge pull requests with no required checks. This\n  # helps to protect against merging branches which inadvertently do not have\n  # required status checks.\n  allow_merge_with_no_checks: false\n\n# \"update\" defines how and when to update pull request branches. Unlike with\n# merges, if this section is missing, bulldozer will not update any pull requests.\nupdate:\n  # \"trigger\" defines the set of pull requests that should be updated by\n  # bulldozer. It accepts the same keys as the trigger in the \"merge\" block.\n  trigger:\n    labels: [\"WIP\", \"Update Me\"]\n\n  # \"ignore\" defines the set of pull requests that should not be updated by\n  # bulldozer. It accepts the same keys as the ignore in the \"merge\" block.\n  ignore:\n    labels: [\"Do Not Update\"]\n\n  # If true, bulldozer will ignore updating draft pull requests, unless they\n  # explicitly match a configured trigger condition.\n  ignore_drafts: false\n\n  # \"required_statuses\" is a list of additional status contexts that must pass\n  # before bulldozer will update a pull request, unless the pull request\n  # explicitly matches a configured trigger condition. This is useful if you want\n  # to require certain statuses to pass before automated updates are made.\n  required_statuses:\n    - \"policy-bot: develop\"\n```\n\n#### Remote Configuration\n\nYou can use a remote configuration by specifying a repository and an optional\npath and Git reference. Place the following in the repository's\n`.bulldozer.yml` file instead of the normal configuration:\n\n```yaml\n# The remote repository to read the configuration file from. This is required,\n# and must be in \"org/repo-name\" form. Must be a public repository.\nremote: org/repo-name\n\n# The path to the configuration file in the remote repository. If not set,\n# uses the default configuration path.\npath: path/to/bulldozer.yml\n\n# The branch (or tag, or commit hash) that should be used on the remote\n# repository. If not set, uses the default branch of the repository.\nref: main\n```\n\nThe remote file must contain `bulldozer` configuration and cannot be another\nremote reference. However, the organization-level default configuration may be\na remote reference.\n\n## FAQ\n\n#### Can I specify both `ignore` and `trigger`?\n\nYes. If both `ignore` and `trigger` are specified, bulldozer will attempt to match\non both. In cases where both match, `ignore` will take precedence.\n\n#### Can I specify the body of the commit when using the `squash` strategy?\n\nYes. When the merge strategy is `squash`, you can set additional options under the\n`options.squash` property, including how to render the commit body.\n\n```yaml\nmerge:\n  method: squash\n  options:\n    squash:\n      body: summarize_commits # or `pull_request_body`, `empty_body`\n```\n\nYou can also define part of pull request body to pick as a commit message when\n`body` is `pull_request_body`.\n\n```yaml\nmerge:\n  method: squash\n  options:\n    squash:\n      body: pull_request_body\n      message_delimiter: ==COMMIT_MSG==\n```\n\nAnything that's contained between two `==COMMIT_MSG==` strings will become the\ncommit message instead of whole pull request body.\n\n#### What if I don't want to put config files into each repo?\n\nYou can add default repository configuration in your bulldozer config file.\n\nIt will be used only when your repo config file does not exist.\n\n```yaml\noptions:\n  default_repository_config:\n    ignore:\n      labels: [\"do not merge\"] # or any other available config.\n```\n\n#### Bulldozer isn't merging my commit when it should, what could be happening?\n\nBulldozer will attempt to merge a branch whenever it passes the trigger/ignore\ncriteria. GitHub may prevent it from merging a branch in certain conditions, some of\nwhich are to be expected, and others that may be caused by mis-configuring Bulldozer.\n\n* Required status checks have not passed\n* Review requirements are not satisfied\n* The merge strategy configured in `.bulldozer.yml` is not allowed by your\n  repository settings\n* Branch protection rules are preventing `bulldozer[bot]` from [pushing to the\n  branch][push restrictions]. Github apps can be added to the list of restricted\n  push users, so you can allow Bulldozer specifically for your repo.\n* The branch has no required checks and `allow_merge_with_no_checks` is set to\n  the default value (`false`).\n\n[push restrictions]: https://help.github.com/articles/about-branch-restrictions/\n[a workaround]: #can-bulldozer-work-with-push-restrictions-on-branches\n\n#### Bulldozer isn't updating my branch when it should, what could be happening?\n\nWhen using the branch update functionality, Bulldozer only performs an update\n_after_ updates are enabled when:\n* A label is added\n* The pull request is opened\n* The target branch is updated\n\nFor example:\n\n1. User A opens a pull request targetting `develop`\n2. User B pushes a commit to `develop`\n3. User A adds an `update me` comment to the first pull request\n4. User C pushes a commit to `develop`\n5. Bulldozer updates the pull request with the commits from Users B and C\n\nNote that the update does _not_ happen when the `update me` comment is added,\neven though there is a new commit on `develop` that is not part of the pull\nrequest.\n\n#### Can Bulldozer work with push restrictions on branches?\n\nAs mentioned above, as of Github ~2.19.x, GitHub Apps _can_ be added to the list of users associated\nwith [push restrictions][]. If you don't want to do this, or if you're running\nan older version of Github that doesn't support this behaviour, you may work\naround this:\n\n1. Use another app like [policy-bot](https://github.com/palantir/policy-bot) to\n   implement _approval_ restrictions as required status checks instead of using\n   push restrictions. This effectively limits who can push to a branch by\n   requiring changes to go through the pull request process and be approved.\n\n2. Configure Bulldozer to use a personal access token for a regular user to\n   perform merges in this case. The token must have the `repo` scope and the\n   user must be allowed to push to the branch. In the server configuration\n   file, set:\n\n   ```yaml\n   options:\n     push_restriction_user_token: \u003ctoken-value\u003e\n   ```\n\n   The token is _only_ used if the target branch has push restrictions enabled.\n   All other merges are performed as the normal GitHub App user.\n\n## Deployment\n\nbulldozer is easy to deploy in your own environment as it has no dependencies\nother than GitHub. It is also safe to run multiple instances of the server,\nmaking it a good fit for container schedulers like Nomad or Kubernetes.\n\nWe provide both a Docker container and a binary distribution of the server:\n\n- Binaries: https://github.com/palantir/bulldozer/releases\n- Docker Images: https://hub.docker.com/r/palantirtechnologies/bulldozer/\n\nA sample configuration file is provided at `config/bulldozer.example.yml`.\nCertain values may also be set by environment variables; these are noted in the\ncomments in the sample configuration file. By default, the environment\nvariables for server values are prefixed with `BULLDOZER_` (e.g.\n`BULLDOZER_PORT`). For configuring options the prefix is \n`BULLDOZER_OPTIONS` (e.g. `BULLDOZER_OPTIONS_APP_NAME`)\nThis prefix can be overridden by setting the\n`BULLDOZER_ENV_PREFIX` environment variable.\n\n### GitHub App Configuration\n\nTo configure Bulldozer as a GitHub App, these general options are required:\n\n- **Webhook URL**: `http(s)://\u003cyour-bulldozer-domain\u003e/api/github/hook`\n- **Webhook secret**: A random string that matches the value of the\n  `github.app.webhook_secret` property in the server configuration\n\nThe app requires these permissions:\n\n| Permission | Access | Reason |\n| ---------- | ------ | ------ |\n| Repository administration | Read-only | Determine required status checks |\n| Checks | Read-only | Read checks for ref |\n| Repository contents | Read \u0026 write | Read configuration, perform merges |\n| Issues | Read \u0026 write | Read comments, close linked issues |\n| Repository metadata | Read-only | Basic repository data |\n| Pull requests | Read \u0026 write | Merge and close pull requests |\n| Commit status | Read-only | Evaluate pull request status |\n\nThe app should be subscribed to these events:\n\n* Check run\n* Commit comment\n* Pull request\n* Status\n* Push\n* Issue comment\n* Pull request review\n* Pull request review comment\n\n### Operations\n\nbulldozer uses [go-baseapp](https://github.com/palantir/go-baseapp) and\n[go-githubapp](https://github.com/palantir/go-githubapp), both of which emit\nstandard metrics and structured log keys. Please see those projects for\ndetails.\n\n### Example Files\n\nExample `.bulldozer.yml` files can be found in [`config/examples`](https://github.com/palantir/bulldozer/tree/develop/config/examples)\n\n### Migrating: Version 0.4.X to 1.X\n\nThe server configuration for bulldozer allows you to specify `configuration_v0_path`, which is a list of paths\nto check for `0.4.X` style bulldozer configuration. When a `1.X` style configuration file does not appear\nat the configured path, bulldozer will attempt to read from the paths configured by `configuration_v0_path`,\nconverting the legacy configuration into an equivalent `v1` configuration internally.\n\nThe upgrade process is therefore to deploy the latest version of bulldozer with both `configuration_path` and\n`configuration_v0_path` configured, and to enable the bulldozer GitHub App on all organizations where it was\npreviously installed.\n\n### Reducing Update Commit and CI Pressure\n\nThe Bulldozer \"Update\" feature is useful when keeping branches up-to-date,  but \ncan cause lots of pressure on CI build systems and Github when there are many pull requests\nopen on a single repository.\n\nTo reduce pressure on CI systems and Github, the update feature can be disabled at the\nserver level by specifying the following server option:\n\n```yaml\noptions:\n  disable_update_feature: true\n```\n\n\n## Development\n\nTo develop `bulldozer`, you will need a [Go installation](https://golang.org/doc/install).\n\n**Run style checks and tests**\n\n    ./godelw verify\n\n**Running the server locally**\n\n    # copy and edit the server config\n    cp config/bulldozer.example.yml config/bulldozer.yml\n\n    ./godelw run bulldozer server\n\n- `config/bulldozer.yml` is used as the default configuration file\n- The server is available at `http://localhost:8080/`\n\n**Running the server via Docker**\n\n    # copy and edit the server config\n    cp config/bulldozer.example.yml config/bulldozer.yml\n\n    # build the docker image\n    ./godelw docker build --verbose\n\n    docker run --rm -v \"$(pwd)/config:/secrets/\" -p 8080:8080 palantirtechnologies/bulldozer:latest\n\n- This mounts the `config` directory (which should contain the `bulldozer.yml` configuration file) at the expected location\n- The server is available at `http://localhost:8080/`\n\n## Contributing\n\nContributions and issues are welcome. For new features or large contributions,\nwe prefer discussing the proposed change on a GitHub issue prior to a PR.\n\n## License\n\nThis application is made available under the [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpalantir%2Fbulldozer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpalantir%2Fbulldozer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpalantir%2Fbulldozer/lists"}