From dcfc816d1943a7a1e7d8ebf112c168ee7b9b26c7 Mon Sep 17 00:00:00 2001 From: Kevin Aude Date: Fri, 12 Jan 2024 17:21:22 +0100 Subject: [PATCH 1/5] feat: allow for pruning of older PRs * UPDATE main template sync script to support the option to delete older PRs targeting the same branch if a newer one is created * UPDATE README to reflect the change --- README.md | 42 ++++++++++++++++++++++++------------------ action.yml | 4 ++++ src/sync_template.sh | 25 ++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 02ecd97c..b8e18890 100644 --- a/README.md +++ b/README.md @@ -232,24 +232,25 @@ jobs: ### Configuration parameters -| Variable | Description | Required | `[Default]` | -|----|----|----|----| -| github_token | Token for the repo. Can be passed in using `$\{{ secrets.GITHUB_TOKEN }}` | `true` | | -| source_repo_path | Repository path of the template | `true` | | -| upstream_branch | The target branch | `false` | `` | -| source_repo_ssh_private_key | `[optional]` private ssh key for the source repository. [see](#private-template-repository)| `false` | | -| pr_branch_name_prefix | `[optional]` the prefix of branches created by this action | `false` | `chore/template_sync` | -| pr_title | `[optional]` the title of PRs opened by this action. Must be already created. | `false` | `upstream merge template repository` | -| pr_labels | `[optional]` comma separated list. [pull request labels][pr-labels]. Must be already created. | `false` | | -| pr_reviewers | `[optional]` comma separated list of pull request reviewers. | `false` | | -| pr_commit_msg | `[optional]` commit message in the created pull request | `false` | `chore(template): merge template changes :up:` | -| hostname | `[optional]` the hostname of the repository | `false` | `github.com` | -| is_dry_run | `[optional]` set to `true` if you do not want to push the changes and not want to create a PR | `false` | | -| is_allow_hooks | `[optional]` set to `true` if you want to enable lifecycle hooks. Use this with caution! | `false` | `false` | -| is_not_source_github | `[optional]` set to `true` if the source git provider is not GitHub | `false` | `false` | -| git_user_name | `[optional]` set the committer git user.name | `false` | `${GITHUB_ACTOR}` | -| git_user_email | `[optional]` set the committer git user.email | `false` | `github-action@actions-template-sync.noreply.${SOURCE_REPO_HOSTNAME}` | -| git_remote_pull_params |`[optional]` set remote pull parameters | `false` | `--allow-unrelated-histories --squash --strategy=recursive -X theirs` | +| Variable | Description | Required | `[Default]` | +|-----------------------------|---------------------------------------------------------------------------------------------------------------|----------|-----------------------------------------------------------------------| +| github_token | Token for the repo. Can be passed in using `$\{{ secrets.GITHUB_TOKEN }}` | `true` | | +| source_repo_path | Repository path of the template | `true` | | +| upstream_branch | The target branch | `false` | `` | +| source_repo_ssh_private_key | `[optional]` private ssh key for the source repository. [see](#private-template-repository) | `false` | | +| pr_branch_name_prefix | `[optional]` the prefix of branches created by this action | `false` | `chore/template_sync` | +| pr_title | `[optional]` the title of PRs opened by this action. Must be already created. | `false` | `upstream merge template repository` | +| pr_labels | `[optional]` comma separated list. [pull request labels][pr-labels]. Must be already created. | `false` | | +| pr_reviewers | `[optional]` comma separated list of pull request reviewers. | `false` | | +| pr_commit_msg | `[optional]` commit message in the created pull request | `false` | `chore(template): merge template changes :up:` | +| hostname | `[optional]` the hostname of the repository | `false` | `github.com` | +| is_dry_run | `[optional]` set to `true` if you do not want to push the changes and not want to create a PR | `false` | | +| is_allow_hooks | `[optional]` set to `true` if you want to enable lifecycle hooks. Use this with caution! | `false` | `false` | +| is_pr_cleanup | `[optional]` set to `true` if you want to cleanup older PRs targeting the same branch. Use this with caution! | `false` | `false` | +| is_not_source_github | `[optional]` set to `true` if the source git provider is not GitHub | `false` | `false` | +| git_user_name | `[optional]` set the committer git user.name | `false` | `${GITHUB_ACTOR}` | +| git_user_email | `[optional]` set the committer git user.email | `false` | `github-action@actions-template-sync.noreply.${SOURCE_REPO_HOSTNAME}` | +| git_remote_pull_params | `[optional]` set remote pull parameters | `false` | `--allow-unrelated-histories --squash --strategy=recursive -X theirs` | ### Docker @@ -303,6 +304,7 @@ The following hooks are supported (please check [docs/ARCHITECTURE.md](docs/ARCH * `install` is executed after the container has started and after reading and setting up the environment. * `prepull` is executed before the code is pulled from the source repository * `prepush` is executed before the push is executed, right after the commit +* `precleanup` is executed before older PRs targeting the same branch are closed * `prepr` is executed before the PR is done **Remark** The underlying OS is defined by an Alpine container. @@ -324,6 +326,10 @@ hooks: commands: - echo 'hi, we are within the prepush phase' - echo 'maybe you want to add further changes and commits' + precleanup: + commands: + - echo 'hi, we are within the precleanup phase' + - echo 'maybe you want to interact with older PRs before they are closed' prepr: commands: - echo 'hi, we are within the prepr phase' diff --git a/action.yml b/action.yml index a4a23b5d..3a637093 100644 --- a/action.yml +++ b/action.yml @@ -36,6 +36,9 @@ inputs: is_allow_hooks: description: "[optional] set to true if you want to allow hooks. Use this functionality with caution!" default: "false" + is_pr_cleanup: + description: "[optional] set to true if you want to cleanup older PRs targeting the same branch." + default: "false" is_not_source_github: description: "[optional] set to true if the source repository is not a github related repository. Useful e.q. if the source is GitLab" default: "false" @@ -61,6 +64,7 @@ runs: HOSTNAME: ${{ inputs.hostname }} IS_DRY_RUN: ${{ inputs.is_dry_run }} IS_ALLOW_HOOKS: ${{ inputs.is_allow_hooks }} + IS_PR_CLEANUP: ${{ inputs.is_pr_cleanup}} IS_NOT_SOURCE_GITHUB: ${{ inputs.is_not_source_github }} GIT_USER_NAME: ${{ inputs.git_user_name }} GIT_USER_EMAIL: ${{ inputs.git_user_email }} diff --git a/src/sync_template.sh b/src/sync_template.sh index 8408ecb2..f298a2cc 100644 --- a/src/sync_template.sh +++ b/src/sync_template.sh @@ -119,10 +119,33 @@ git commit -m "${PR_COMMIT_MSG}" echo "::endgroup::" +cleanup_older_prs () { + cmd_from_yml_file "precleanup" + + if [ "$IS_DRY_RUN" != "true" ]; then + echo "::group::cleanup older PRs" + gh pr list \ + --base "${UPSTREAM_BRANCH}" \ + --state open \ + --label "${PR_LABELS}" \ + --json number \ + --template '{{range .}}{{printf "%v" .number}}{{"\n"}}{{end}}' | xargs -L1 gh pr close + echo "::endgroup::" + else + warn "dry_run option is set to off. Skipping older prs cleanup" + fi +} + +if [ "$IS_PR_CLEANUP" != "false" ]; then + cleanup_older_prs +else + warn "is_pr_cleanup option is set to off. Skipping older prs cleanup" +fi + push_and_create_pr () { cmd_from_yml_file "prepush" - if [ "$IS_DRY_RUN" != "true" ]; then + if [ "$IS_DRY_RUN" != "true" ]; then echo "::group::push changes and create PR" debug "push changes" git push --set-upstream origin "${NEW_BRANCH}" From 84337bf3ae88f5be8647e48035a40f69f17aebc3 Mon Sep 17 00:00:00 2001 From: Kevin Aude Date: Tue, 23 Jan 2024 10:11:40 +0100 Subject: [PATCH 2/5] feat: automatic label creation * UPDATE main template sync script to support the automatic creation of labels and skip PR cleanup if no labels are set * UPDATE README in order to reflect these changes and explain the reasoning behind it --- README.md | 16 +++++-- action.yml | 1 + src/sync_template.sh | 111 ++++++++++++++++++++++++++++++------------- 3 files changed, 90 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 9ec4fff3..11f45ad2 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} source_repo_path: upstream_branch: # defaults to main - pr_labels: ,[,...] # optional, no default + pr_labels: ,[,...] # defaults to template_sync ``` You will receive a pull request within your repository if there are some changes available in the template. @@ -139,7 +139,7 @@ jobs: github_token: ${{ steps.generate_token.outputs.token }} source_repo_path: upstream_branch: # defaults to main - pr_labels: ,[,...] # optional, no default + pr_labels: ,[,...] # defaults to template_sync ``` #### 2. Using SSH @@ -175,7 +175,7 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} source_repo_path: ${{ secrets.SOURCE_REPO_PATH }} # , should be within secrets upstream_branch: ${{ secrets.TARGET_BRANCH }} # # defaults to main - pr_labels: ,[,...] # optional, no default + pr_labels: ,[,...] # defaults to template_sync source_repo_ssh_private_key: ${{ secrets.SOURCE_REPO_SSH_PRIVATE_KEY }} # contains the private ssh key of the private repository ``` @@ -240,7 +240,7 @@ jobs: | source_repo_ssh_private_key | `[optional]` private ssh key for the source repository. [see](#private-template-repository) | `false` | | | pr_branch_name_prefix | `[optional]` the prefix of branches created by this action | `false` | `chore/template_sync` | | pr_title | `[optional]` the title of PRs opened by this action. Must be already created. | `false` | `upstream merge template repository` | -| pr_labels | `[optional]` comma separated list. [pull request labels][pr-labels]. Must be already created. | `false` | | +| pr_labels | `[optional]` comma separated list. [pull request labels][pr-labels]. | `false` | `sync_template` | | pr_reviewers | `[optional]` comma separated list of pull request reviewers. | `false` | | | pr_commit_msg | `[optional]` commit message in the created pull request | `false` | `chore(template): merge template changes :up:` | | hostname | `[optional]` the hostname of the repository | `false` | `github.com` | @@ -340,6 +340,14 @@ hooks: - echo 'hi, we are within the prepr phase' - echo 'maybe you want to change the code a bit and do another push before creating the pr' ``` +## Labels creation +By default, generated PRs will be labeled with the `template_sync` label. +If that label doesn't exist in your repository, it will be created automatically unless you specify your own existing labels. +Associating a label with the generated PRs helps keeping track of them and allows for features like automatic PR cleanup. + +## Pull request cleanup +Depending on your way of working, you may end up with multiple pull requests related to template syncing pointing to the same branch. +If you want to avoid this situation, you can instruct this action to clean up older PRs pointing to the same branch (search based on labels). ## Troubleshooting diff --git a/action.yml b/action.yml index 3a637093..760b5786 100644 --- a/action.yml +++ b/action.yml @@ -23,6 +23,7 @@ inputs: default: "upstream merge template repository" pr_labels: description: "[optional] comma separated list of pull request labels" + default: "template_sync" pr_reviewers: description: "[optional] comma separated list of pull request reviewers" pr_commit_msg: diff --git a/src/sync_template.sh b/src/sync_template.sh index 97bafc31..75c8b023 100644 --- a/src/sync_template.sh +++ b/src/sync_template.sh @@ -66,6 +66,7 @@ echo "::endgroup::" cmd_from_yml_file "prepull" echo "::group::Pull template" + debug "create new branch from default branch with name ${NEW_BRANCH}" git checkout -b "${NEW_BRANCH}" debug "pull changes from template" @@ -97,6 +98,7 @@ fi cmd_from_yml_file "precommit" echo "::group::commit changes" + git add . # we are checking the ignore file if it exists or is empty @@ -122,48 +124,89 @@ git commit -m "${PR_COMMIT_MSG}" echo "::endgroup::" cleanup_older_prs () { - cmd_from_yml_file "precleanup" - - if [ "$IS_DRY_RUN" != "true" ]; then - echo "::group::cleanup older PRs" - gh pr list \ - --base "${UPSTREAM_BRANCH}" \ - --state open \ - --label "${PR_LABELS}" \ - --json number \ - --template '{{range .}}{{printf "%v" .number}}{{"\n"}}{{end}}' | xargs -L1 gh pr close - echo "::endgroup::" + gh pr list \ + --base "${UPSTREAM_BRANCH}" \ + --state open \ + --label "${PR_LABELS}" \ + --json number \ + --template '{{range .}}{{printf "%v" .number}}{{"\n"}}{{end}}' | xargs -L1 gh pr close +} +echo "::group::cleanup older PRs" + +if [ "$IS_DRY_RUN" != "true" ]; then + if [ "$IS_PR_CLEANUP" != "false" ]; then + if [[ -z "${PR_LABELS}" ]]; then + cmd_from_yml_file "precleanup" + cleanup_older_prs + else + warn "env var 'PR_LABELS' is empty. Skipping older prs cleanup" + fi else - warn "dry_run option is set to off. Skipping older prs cleanup" + warn "is_pr_cleanup option is set to off. Skipping older prs cleanup" fi -} - -if [ "$IS_PR_CLEANUP" != "false" ]; then - cleanup_older_prs else - warn "is_pr_cleanup option is set to off. Skipping older prs cleanup" + warn "dry_run option is set to off. Skipping older prs cleanup" fi -push_and_create_pr () { - cmd_from_yml_file "prepush" +echo "::endgroup::" + + +maybe_create_labels () { +all_labels=${PR_LABELS//,/$'\n'} +for label in $all_labels +do + search_result=$(gh label list \ + --search "${label}" \ + --limit 1 \ + --json name \ + --template '{{range .}}{{printf "%v" .name}}{{"\n"}}{{end}}') + + if [ "${search_result}" = "${label}" ]; then + info "label '${label}' was found in the repository" + else + gh label create "${label}" + info "label '${label}' was missing and has been created" + fi +done +} + +echo "::group::check for missing labels" +if [[ -z "${PR_LABELS}" ]]; then + info "env var 'PR_LABELS' is empty. Skipping labels check" +else if [ "$IS_DRY_RUN" != "true" ]; then - echo "::group::push changes and create PR" - debug "push changes" - git push --set-upstream origin "${NEW_BRANCH}" - - cmd_from_yml_file "prepr" - - gh pr create \ - --title "${PR_TITLE}" \ - --body "Merge ${SOURCE_REPO_PATH} ${NEW_TEMPLATE_GIT_HASH}" \ - --base "${UPSTREAM_BRANCH}" \ - --label "${PR_LABELS}" \ - --reviewer "${PR_REVIEWERS}" - echo "::endgroup::" + maybe_create_labels else - warn "dry_run option is set to off. Skipping push changes and skip create pr" + warn "dry_run option is set to off. Skipping labels check" fi +fi + +echo "::endgroup::" + +push () { + debug "push changes" + git push --set-upstream origin "${NEW_BRANCH}" +} + +create_pr () { + gh pr create \ + --title "${PR_TITLE}" \ + --body "Merge ${SOURCE_REPO_PATH} ${NEW_TEMPLATE_GIT_HASH}" \ + --base "${UPSTREAM_BRANCH}" \ + --label "${PR_LABELS}" \ + --reviewer "${PR_REVIEWERS}" } -push_and_create_pr +echo "::group::push changes and create PR" + +if [ "$IS_DRY_RUN" != "true" ]; then + cmd_from_yml_file "prepush" + push + cmd_from_yml_file "prepr" + create_pr +else + warn "dry_run option is set to off. Skipping push changes and skip create pr" +fi + +echo "::endgroup::" From 50f36acf612157c0bec45877ac918a303cd83cd5 Mon Sep 17 00:00:00 2001 From: Andy Augustin Date: Tue, 23 Jan 2024 20:36:44 +0100 Subject: [PATCH 3/5] docs: :memo: (#438) add warning about prune old pull requests --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 11f45ad2..41832389 100644 --- a/README.md +++ b/README.md @@ -340,14 +340,18 @@ hooks: - echo 'hi, we are within the prepr phase' - echo 'maybe you want to change the code a bit and do another push before creating the pr' ``` + ## Labels creation + By default, generated PRs will be labeled with the `template_sync` label. If that label doesn't exist in your repository, it will be created automatically unless you specify your own existing labels. Associating a label with the generated PRs helps keeping track of them and allows for features like automatic PR cleanup. ## Pull request cleanup + Depending on your way of working, you may end up with multiple pull requests related to template syncing pointing to the same branch. -If you want to avoid this situation, you can instruct this action to clean up older PRs pointing to the same branch (search based on labels). +If you want to avoid this situation, you can instruct this action to clean up older PRs pointing to the same branch (search based on labels defined with the `pr_labels` config parameter). +:warning: this feature will close all pull requests with labels configured with `pr_labels` config parameter. ## Troubleshooting From e6c2eebc1af5a91de1e8d1374383a491c67c0620 Mon Sep 17 00:00:00 2001 From: Andy Augustin Date: Wed, 24 Jan 2024 21:37:58 +0100 Subject: [PATCH 4/5] style: :rotating_light: (#437) doc lint issue --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41832389..26b6a6e9 100644 --- a/README.md +++ b/README.md @@ -350,7 +350,7 @@ Associating a label with the generated PRs helps keeping track of them and allow ## Pull request cleanup Depending on your way of working, you may end up with multiple pull requests related to template syncing pointing to the same branch. -If you want to avoid this situation, you can instruct this action to clean up older PRs pointing to the same branch (search based on labels defined with the `pr_labels` config parameter). +If you want to avoid this situation, you can instruct this action to clean up older PRs (search based on labels defined with the `pr_labels` config parameter). :warning: this feature will close all pull requests with labels configured with `pr_labels` config parameter. ## Troubleshooting From 20fab00070cfeb3e4411575d8c5b9948a98f32e2 Mon Sep 17 00:00:00 2001 From: Kevin Aude Date: Thu, 25 Jan 2024 10:53:03 +0100 Subject: [PATCH 5/5] fix: label emptiness check for PRs cleanup --- src/sync_template.sh | 46 +++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/sync_template.sh b/src/sync_template.sh index 75c8b023..cb819987 100644 --- a/src/sync_template.sh +++ b/src/sync_template.sh @@ -124,22 +124,28 @@ git commit -m "${PR_COMMIT_MSG}" echo "::endgroup::" cleanup_older_prs () { - gh pr list \ + older_prs=$(gh pr list \ --base "${UPSTREAM_BRANCH}" \ --state open \ --label "${PR_LABELS}" \ --json number \ - --template '{{range .}}{{printf "%v" .number}}{{"\n"}}{{end}}' | xargs -L1 gh pr close + --template '{{range .}}{{printf "%v" .number}}{{"\n"}}{{end}}') + + for older_pr in $older_prs + do + gh pr close "$older_pr" + debug "Closed PR #${older_pr}" + done } echo "::group::cleanup older PRs" if [ "$IS_DRY_RUN" != "true" ]; then if [ "$IS_PR_CLEANUP" != "false" ]; then if [[ -z "${PR_LABELS}" ]]; then + warn "env var 'PR_LABELS' is empty. Skipping older prs cleanup" + else cmd_from_yml_file "precleanup" cleanup_older_prs - else - warn "env var 'PR_LABELS' is empty. Skipping older prs cleanup" fi else warn "is_pr_cleanup option is set to off. Skipping older prs cleanup" @@ -152,22 +158,22 @@ echo "::endgroup::" maybe_create_labels () { -all_labels=${PR_LABELS//,/$'\n'} -for label in $all_labels -do - search_result=$(gh label list \ - --search "${label}" \ - --limit 1 \ - --json name \ - --template '{{range .}}{{printf "%v" .name}}{{"\n"}}{{end}}') - - if [ "${search_result}" = "${label}" ]; then - info "label '${label}' was found in the repository" - else - gh label create "${label}" - info "label '${label}' was missing and has been created" - fi -done + all_labels=${PR_LABELS//,/$'\n'} + for label in $all_labels + do + search_result=$(gh label list \ + --search "${label}" \ + --limit 1 \ + --json name \ + --template '{{range .}}{{printf "%v" .name}}{{"\n"}}{{end}}') + + if [ "${search_result}" = "${label}" ]; then + info "label '${label}' was found in the repository" + else + gh label create "${label}" + info "label '${label}' was missing and has been created" + fi + done } echo "::group::check for missing labels"