From c693ac3d387a5f999d967ee45d75e16819158910 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Thu, 30 Jul 2020 21:10:16 +0200 Subject: [PATCH 01/21] Fewer line breaks in PR comment --- .github/workflows/branch.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/branch.yml b/.github/workflows/branch.yml index b0dd5cc25a..d9b8623bec 100644 --- a/.github/workflows/branch.yml +++ b/.github/workflows/branch.yml @@ -24,9 +24,7 @@ jobs: message: | Hi @${{ github.event.pull_request.user.login }}, - It looks like this pull-request is has been made against the ${{github.event.pull_request.head.repo.full_name}} `master` branch. - The `master` branch on nf-core repositories should always contain code from the latest release. - Beacuse of this, PRs to `master` are only allowed if they come from the ${{github.event.pull_request.head.repo.full_name}} `dev` branch. + It looks like this pull-request is has been made against the ${{github.event.pull_request.head.repo.full_name}} `master` branch. The `master` branch on nf-core repositories should always contain code from the latest release. Beacuse of this, PRs to `master` are only allowed if they come from the ${{github.event.pull_request.head.repo.full_name}} `dev` branch. You do not need to close this PR, you can change the target branch to `dev` by clicking the _"Edit"_ button at the top of this page. From b4284c08ee1775bb5157a36f439eaa1d9794a22a Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Thu, 30 Jul 2020 21:13:07 +0200 Subject: [PATCH 02/21] Changelog updates --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69c9a9b2ad..2c45c9b96e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,9 +14,12 @@ Apologies for the inconvenience. Patch release to fix the automatic template synchronisation, which failed in the v1.10 release. * Improved logging: `nf-core --log-file log.txt` now saves a verbose log to disk. -* GitHub actions sync now uploads verbose log as an artifact. -* Sync - fixed several minor bugs, improved logging. +* nf-core/tools GitHub Actions pipeline sync now uploads verbose log as an artifact. +* Sync - fixed several minor bugs, made logging less verbose. * Python Rich library updated to `>=4.2.1` +* Hopefully fix git config for pipeline sync so that commit comes from @nf-core-bot +* Fix sync auto-PR text indentation so that it doesn't all show as code +* Added explicit flag `--show-passed` for `nf-core lint` instead of taking logging verbosity ## [v1.10 - Copper Camel](https://github.com/nf-core/tools/releases/tag/1.10) - [2020-07-30] From 4ea80de6765e26aefed07a5e4cbbf9329f0aa8f4 Mon Sep 17 00:00:00 2001 From: phue Date: Fri, 31 Jul 2020 01:08:35 +0200 Subject: [PATCH 03/21] update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c45c9b96e..c799255f67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Apologies for the inconvenience. * Fix syntax error in `/push_dockerhub.yml` GitHub Action workflow * Change `params.readPaths` -> `params.input_paths` in `test_full.config` * Check results when posting the lint results as a GitHub comment +* Change `params.readPaths` -> `params.input_paths` in `test_full.config` ## [v1.10.1 - Copper Camel _(patch)_](https://github.com/nf-core/tools/releases/tag/1.10.1) - [2020-07-30] From 7996a379f75266a68eb59ac11c8d63fe41f3cdef Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 00:15:19 +0200 Subject: [PATCH 04/21] First attempt at a dynamic matrix for sync --- .github/workflows/sync_test.yml | 78 +++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .github/workflows/sync_test.yml diff --git a/.github/workflows/sync_test.yml b/.github/workflows/sync_test.yml new file mode 100644 index 0000000000..8cc921b717 --- /dev/null +++ b/.github/workflows/sync_test.yml @@ -0,0 +1,78 @@ +name: Sync matrix test +on: [push, pull_request] + +jobs: + get-pipelines: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - id: set-matrix + run: | + curl -O https://nf-co.re/pipeline_names.json + echo "::set-output name=matrix::$(cat pipeline_names.json)" + + sync: + needs: get-pipelines + runs-on: ubuntu-latest + strategy: + matrix: ${{fromJson(needs.get-pipelines.outputs.matrix)}} + fail-fast: false + steps: + + - uses: actions/checkout@v2 + name: Check out nf-core/tools + + - uses: actions/checkout@v2 + name: Check out nf-core/${{ matrix.pipeline }} + with: + repository: nf-core/${{ matrix.pipeline }} + ref: dev + token: ${{ secrets.nf_core_bot_auth_token }} + path: nf-core/${{ matrix.pipeline }} + + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + + - name: Install python dependencies + run: | + python -m pip install --upgrade pip + pip install . + + - name: Install Nextflow + run: | + mkdir /tmp/nextflow + cd /tmp/nextflow + wget -qO- get.nextflow.io | bash + sudo ln -s /tmp/nextflow/nextflow /usr/local/bin/nextflow + +#### FOR TESTING + - name: Run linting + run: | + nf-core --log-file sync_log_${{ matrix.pipeline }}.txt \ + lint nf-core/${{ matrix.pipeline }} + +#### THE REAL THING +# - name: Run synchronisation +# if: github.repository == 'nf-core/tools' +# env: +# AUTH_TOKEN: ${{ secrets.nf_core_bot_auth_token }} +# run: | +# git config --global user.email "core@nf-co.re" +# git config --global user.name "nf-core-bot" +# nf-core --log-file sync_log.txt sync nf-core/${{ matrix.pipeline }} \ +# --from-branch dev \ +# --pull-request \ +# --username nf-core-bot \ +# --repository nf-core/${{ matrix.pipeline }} \ +# --auth-token ${{ secrets.nf_core_bot_auth_token }} +######### + + - name: Upload sync log file artifact + if: ${{ always() }} + uses: actions/upload-artifact@v2 + with: + name: sync_log_${{ matrix.pipeline }} + path: sync_log_${{ matrix.pipeline }}.txt From 9de0255e29d7cbe4ca686a94cefeaa5b312ad913 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 01:09:42 +0200 Subject: [PATCH 05/21] Stable new matrix sync workflow --- .github/workflows/sync.yml | 40 ++++++++++++++--- .github/workflows/sync_test.yml | 78 --------------------------------- 2 files changed, 34 insertions(+), 84 deletions(-) delete mode 100644 .github/workflows/sync_test.yml diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 256a33570d..9019a20e73 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -4,13 +4,34 @@ on: types: [published] jobs: - sync-all: - name: Sync all pipelines + get-pipelines: runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - id: set-matrix + run: | + curl -O https://nf-co.re/pipeline_names.json + echo "::set-output name=matrix::$(cat pipeline_names.json)" + sync: + needs: get-pipelines + runs-on: ubuntu-latest + strategy: + matrix: ${{fromJson(needs.get-pipelines.outputs.matrix)}} + fail-fast: false steps: + - uses: actions/checkout@v2 - name: Check out source-code repository + name: Check out nf-core/tools + + - uses: actions/checkout@v2 + name: Check out nf-core/${{ matrix.pipeline }} + with: + repository: nf-core/${{ matrix.pipeline }} + ref: dev + token: ${{ secrets.nf_core_bot_auth_token }} + path: nf-core/${{ matrix.pipeline }} - name: Set up Python 3.8 uses: actions/setup-python@v1 @@ -36,10 +57,17 @@ jobs: run: | git config --global user.email "core@nf-co.re" git config --global user.name "nf-core-bot" - nf-core --log-file sync_log.txt sync --all --username nf-core-bot --auth-token $AUTH_TOKEN + nf-core --log-file sync_log.txt sync nf-core/${{ matrix.pipeline }} \ + --from-branch dev \ + --pull-request \ + --username nf-core-bot \ + --repository nf-core/${{ matrix.pipeline }} \ + --auth-token ${{ secrets.nf_core_bot_auth_token }} + - name: Upload sync log file artifact + if: ${{ always() }} uses: actions/upload-artifact@v2 with: - name: sync-log-file - path: sync_log.txt + name: sync_log_${{ matrix.pipeline }} + path: sync_log_${{ matrix.pipeline }}.txt diff --git a/.github/workflows/sync_test.yml b/.github/workflows/sync_test.yml deleted file mode 100644 index 8cc921b717..0000000000 --- a/.github/workflows/sync_test.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: Sync matrix test -on: [push, pull_request] - -jobs: - get-pipelines: - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} - steps: - - id: set-matrix - run: | - curl -O https://nf-co.re/pipeline_names.json - echo "::set-output name=matrix::$(cat pipeline_names.json)" - - sync: - needs: get-pipelines - runs-on: ubuntu-latest - strategy: - matrix: ${{fromJson(needs.get-pipelines.outputs.matrix)}} - fail-fast: false - steps: - - - uses: actions/checkout@v2 - name: Check out nf-core/tools - - - uses: actions/checkout@v2 - name: Check out nf-core/${{ matrix.pipeline }} - with: - repository: nf-core/${{ matrix.pipeline }} - ref: dev - token: ${{ secrets.nf_core_bot_auth_token }} - path: nf-core/${{ matrix.pipeline }} - - - name: Set up Python 3.8 - uses: actions/setup-python@v1 - with: - python-version: 3.8 - - - name: Install python dependencies - run: | - python -m pip install --upgrade pip - pip install . - - - name: Install Nextflow - run: | - mkdir /tmp/nextflow - cd /tmp/nextflow - wget -qO- get.nextflow.io | bash - sudo ln -s /tmp/nextflow/nextflow /usr/local/bin/nextflow - -#### FOR TESTING - - name: Run linting - run: | - nf-core --log-file sync_log_${{ matrix.pipeline }}.txt \ - lint nf-core/${{ matrix.pipeline }} - -#### THE REAL THING -# - name: Run synchronisation -# if: github.repository == 'nf-core/tools' -# env: -# AUTH_TOKEN: ${{ secrets.nf_core_bot_auth_token }} -# run: | -# git config --global user.email "core@nf-co.re" -# git config --global user.name "nf-core-bot" -# nf-core --log-file sync_log.txt sync nf-core/${{ matrix.pipeline }} \ -# --from-branch dev \ -# --pull-request \ -# --username nf-core-bot \ -# --repository nf-core/${{ matrix.pipeline }} \ -# --auth-token ${{ secrets.nf_core_bot_auth_token }} -######### - - - name: Upload sync log file artifact - if: ${{ always() }} - uses: actions/upload-artifact@v2 - with: - name: sync_log_${{ matrix.pipeline }} - path: sync_log_${{ matrix.pipeline }}.txt From d17af2b0fa0fe9118460b85c8e2834605b056bf9 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 09:14:11 +0200 Subject: [PATCH 06/21] Sync - strip out --all flag --- nf_core/__main__.py | 36 ++++++++---------------- nf_core/sync.py | 68 +-------------------------------------------- 2 files changed, 13 insertions(+), 91 deletions(-) diff --git a/nf_core/__main__.py b/nf_core/__main__.py index a52b18a101..3b27daf971 100755 --- a/nf_core/__main__.py +++ b/nf_core/__main__.py @@ -550,14 +550,13 @@ def bump_version(pipeline_dir, new_version, nextflow): @nf_core_cli.command("sync", help_priority=10) -@click.argument("pipeline_dir", type=click.Path(exists=True), nargs=-1, metavar="") +@click.argument("pipeline_dir", required=True, type=click.Path(exists=True), metavar="") @click.option("-b", "--from-branch", type=str, help="The git branch to use to fetch workflow vars.") @click.option("-p", "--pull-request", is_flag=True, default=False, help="Make a GitHub pull-request with the changes.") -@click.option("-u", "--username", type=str, help="GitHub username for the PR.") -@click.option("-r", "--repository", type=str, help="GitHub repository name for the PR.") -@click.option("-a", "--auth-token", type=str, help="GitHub API personal access token.") -@click.option("--all", is_flag=True, default=False, help="Sync template for all nf-core pipelines.") -def sync(pipeline_dir, from_branch, pull_request, username, repository, auth_token, all): +@click.option("-r", "--repository", type=str, help="GitHub PR: target repository.") +@click.option("-u", "--username", type=str, help="GitHub PR: auth username.") +@click.option("-a", "--auth-token", type=str, help="GitHub PR: API personal access token.") +def sync(pipeline_dir, from_branch, pull_request, repository, username, auth_token): """ Sync a pipeline TEMPLATE branch with the nf-core template. @@ -571,24 +570,13 @@ def sync(pipeline_dir, from_branch, pull_request, username, repository, auth_tok new release of nf-core/tools (and the included template) is made. """ - # Pull and sync all nf-core pipelines - if all: - nf_core.sync.sync_all_pipelines(username, auth_token) - else: - # Manually check for the required parameter - if not pipeline_dir or len(pipeline_dir) != 1: - log.error("Either use --all or specify one ") - sys.exit(1) - else: - pipeline_dir = pipeline_dir[0] - - # Sync the given pipeline dir - sync_obj = nf_core.sync.PipelineSync(pipeline_dir, from_branch, pull_request) - try: - sync_obj.sync() - except (nf_core.sync.SyncException, nf_core.sync.PullRequestException) as e: - log.error(e) - sys.exit(1) + # Sync the given pipeline dir + sync_obj = nf_core.sync.PipelineSync(pipeline_dir, from_branch, pull_request, repository, username, auth_token) + try: + sync_obj.sync() + except (nf_core.sync.SyncException, nf_core.sync.PullRequestException) as e: + log.error(e) + sys.exit(1) if __name__ == "__main__": diff --git a/nf_core/sync.py b/nf_core/sync.py index dc1fdeff56..a2d90e93cb 100644 --- a/nf_core/sync.py +++ b/nf_core/sync.py @@ -59,7 +59,7 @@ class PipelineSync(object): """ def __init__( - self, pipeline_dir, from_branch=None, make_pr=False, gh_username=None, gh_repo=None, gh_auth_token=None, + self, pipeline_dir, from_branch=None, make_pr=False, gh_repo=None, gh_username=None, gh_auth_token=None, ): """ Initialise syncing object """ @@ -334,69 +334,3 @@ def reset_target_dir(self): self.repo.git.checkout(self.original_branch) except git.exc.GitCommandError as e: raise SyncException("Could not reset to original branch `{}`:\n{}".format(self.from_branch, e)) - - -def sync_all_pipelines(gh_username=None, gh_auth_token=None): - """Sync all nf-core pipelines - """ - - # Get remote workflows - wfs = nf_core.list.Workflows() - wfs.get_remote_workflows() - - successful_syncs = [] - failed_syncs = [] - - # Set up a working directory - tmpdir = tempfile.mkdtemp() - - # Let's do some updating! - for wf in wfs.remote_workflows: - - log.info("-" * 30) - log.info("Syncing {}".format(wf.full_name)) - - # Make a local working directory - wf_local_path = os.path.join(tmpdir, wf.name) - os.mkdir(wf_local_path) - log.debug("Sync working directory: {}".format(wf_local_path)) - - # Clone the repo - wf_remote_url = "https://{}@github.com/nf-core/{}".format(gh_auth_token, wf.name) - repo = git.Repo.clone_from(wf_remote_url, wf_local_path) - assert repo - - # Only show error messages from pipeline creation - logging.getLogger("nf_core.create").setLevel(logging.ERROR) - - # Sync the repo - log.debug("Running template sync") - sync_obj = nf_core.sync.PipelineSync( - pipeline_dir=wf_local_path, - from_branch="dev", - make_pr=True, - gh_username=gh_username, - gh_auth_token=gh_auth_token, - ) - try: - sync_obj.sync() - except (SyncException, PullRequestException) as e: - log.error("Sync failed for {}:\n{}".format(wf.full_name, e)) - failed_syncs.append(wf.name) - except Exception as e: - log.error("Something went wrong when syncing {}:\n{}".format(wf.full_name, e)) - failed_syncs.append(wf.name) - else: - log.info("[green]Sync successful for {}".format(wf.full_name)) - successful_syncs.append(wf.name) - - # Clean up - log.debug("Removing work directory: {}".format(wf_local_path)) - shutil.rmtree(wf_local_path) - - if len(successful_syncs) > 0: - log.info("[green]Finished. Successfully synchronised {} pipelines".format(len(successful_syncs))) - - if len(failed_syncs) > 0: - failed_list = "\n - ".join(failed_syncs) - log.error("[red]Errors whilst synchronising {} pipelines:\n - {}".format(len(failed_syncs), failed_list)) From 1ce174671d3d46579f573ee5b86c695e33ed5157 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 09:32:08 +0200 Subject: [PATCH 07/21] Sync - handle existing PR --- CHANGELOG.md | 1 - nf_core/sync.py | 15 +++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c799255f67..2c45c9b96e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,6 @@ Apologies for the inconvenience. * Fix syntax error in `/push_dockerhub.yml` GitHub Action workflow * Change `params.readPaths` -> `params.input_paths` in `test_full.config` * Check results when posting the lint results as a GitHub comment -* Change `params.readPaths` -> `params.input_paths` in `test_full.config` ## [v1.10.1 - Copper Camel _(patch)_](https://github.com/nf-core/tools/releases/tag/1.10.1) - [2020-07-30] diff --git a/nf_core/sync.py b/nf_core/sync.py index a2d90e93cb..2b4586710d 100644 --- a/nf_core/sync.py +++ b/nf_core/sync.py @@ -317,13 +317,20 @@ def make_pull_request(self): self.gh_pr_returned_data = r.content returned_data_prettyprint = r.content - if r.status_code != 201: + # PR worked + if r.status_code == 201: + log.debug("GitHub API PR worked:\n{}".format(returned_data_prettyprint)) + log.info("GitHub PR created: {}".format(self.gh_pr_returned_data["html_url"])) + + # We already had a PR open + elif r.status_code == 422: + log.warn("GitHub PR is already open!") + + # Something went wrong + else: raise PullRequestException( "GitHub API returned code {}: \n{}".format(r.status_code, returned_data_prettyprint) ) - else: - log.debug("GitHub API PR worked:\n{}".format(returned_data_prettyprint)) - log.info("GitHub PR created: {}".format(self.gh_pr_returned_data["html_url"])) def reset_target_dir(self): """ From 729386f0adb361dfdede54bf91f9be50647eab4d Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 09:56:24 +0200 Subject: [PATCH 08/21] Sync - check for existing PRs and update See nf-core/tools#710 --- nf_core/sync.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/nf_core/sync.py b/nf_core/sync.py index 2b4586710d..034460e1e7 100644 --- a/nf_core/sync.py +++ b/nf_core/sync.py @@ -287,6 +287,7 @@ def make_pull_request(self): log.debug("Submitting a pull request via the GitHub API") + pr_title = "Important! Template update for nf-core/tools v{}".format(nf_core.__version__) pr_body_text = ( "A new release of the main template in nf-core/tools has just been released. " "This automated pull-request attempts to apply the relevant updates to this pipeline.\n\n" @@ -298,13 +299,85 @@ def make_pull_request(self): "please see the [nf-core/tools v{tag} release page](https://github.com/nf-core/tools/releases/tag/{tag})." ).format(tag=nf_core.__version__) + # Try to update an existing pull-request + if self.update_existing_pull_request(pr_title, pr_body_text) is False: + # None found - make a new pull-request + self.submit_pull_request(pr_title, pr_body_text) + + def update_existing_pull_request(self, pr_title, pr_body_text): + """ + List existing pull-requests between TEMPLATE and self.from_branch + + If one is found, attempt to update it with a new title and body text + If none are found, return False + """ + + # Look for existing pull-requests + r = requests.get( + url="https://api.github.com/repos/{}/{}/pulls?head=nf-core:TEMPLATE&base={}".format( + self.gh_username, self.gh_repo, self.from_branch + ), + auth=requests.auth.HTTPBasicAuth(self.gh_username, self.gh_auth_token), + ) + try: + r_json = json.loads(r.content) + r_pp = json.dumps(r_json, indent=4) + except: + r_json = r.content + r_pp = r.content + + # PR worked + if r.status_code == 200: + log.debug("GitHub API listing existing PRs:\n{}".format(r_pp)) + + # No open PRs + if len(r_json) == 0: + log.debug("No open PRs found between TEMPLATE and {}".format(self.from_branch)) + return False + + # Update existing PR + pr_update_api_url = r_json[0]["url"] + pr_content = {"title": pr_title, "body": pr_body_text} + + r = requests.patch( + url=pr_update_api_url, + data=json.dumps(pr_content), + auth=requests.auth.HTTPBasicAuth(self.gh_username, self.gh_auth_token), + ) + try: + r_json = json.loads(r.content) + r_pp = json.dumps(r_json, indent=4) + except: + r_json = r.content + r_pp = r.content + + # PR update worked + if r.status_code == 200: + log.debug("GitHub API PR-update worked:\n{}".format(r_pp)) + log.info("Updated GitHub PR: {}".format(r_json["html_url"])) + return True + # Something went wrong + else: + log.warn("Could not update PR {}: \n{}".format(r.status_code, r_pp)) + return False + + # Something went wrong + else: + log.warn("Could not list open PRs: \n{}".format(r.status_code, r_pp)) + return False + + def submit_pull_request(self, pr_title, pr_body_text): + """ + Create a new pull-request on GitHub + """ pr_content = { - "title": "Important! Template update for nf-core/tools v{}".format(nf_core.__version__), + "title": pr_title, "body": pr_body_text, "maintainer_can_modify": True, "head": "TEMPLATE", "base": self.from_branch, } + r = requests.post( url="https://api.github.com/repos/{}/{}/pulls".format(self.gh_username, self.gh_repo), data=json.dumps(pr_content), From 635888f3aada6863dbf01adb4a7f8b46deea910a Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 10:12:38 +0200 Subject: [PATCH 09/21] Spotted a typo in the template docs --- .../{{cookiecutter.name_noslash}}/.github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nf_core/pipeline-template/{{cookiecutter.name_noslash}}/.github/CONTRIBUTING.md b/nf_core/pipeline-template/{{cookiecutter.name_noslash}}/.github/CONTRIBUTING.md index bd292ca180..3836aa7637 100644 --- a/nf_core/pipeline-template/{{cookiecutter.name_noslash}}/.github/CONTRIBUTING.md +++ b/nf_core/pipeline-template/{{cookiecutter.name_noslash}}/.github/CONTRIBUTING.md @@ -46,7 +46,7 @@ These tests are run both with the latest available version of `Nextflow` and als ## Patch -: warning: Only in the unlikely and regretful event of a release happening with a bug. +:warning: Only in the unlikely and regretful event of a release happening with a bug. * On your own fork, make a new branch `patch` based on `upstream/master`. * Fix the bug, and bump version (X.Y.Z+1). From 93c141ab6bb929f6a0b70ec1bfba762ff6a11d48 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 10:16:26 +0200 Subject: [PATCH 10/21] Remove PR-create check for existing PR --- nf_core/sync.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/nf_core/sync.py b/nf_core/sync.py index 034460e1e7..5fafca2cbf 100644 --- a/nf_core/sync.py +++ b/nf_core/sync.py @@ -395,10 +395,6 @@ def submit_pull_request(self, pr_title, pr_body_text): log.debug("GitHub API PR worked:\n{}".format(returned_data_prettyprint)) log.info("GitHub PR created: {}".format(self.gh_pr_returned_data["html_url"])) - # We already had a PR open - elif r.status_code == 422: - log.warn("GitHub PR is already open!") - # Something went wrong else: raise PullRequestException( From f8843649b13b571d927165010d001393830fff09 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 10:22:25 +0200 Subject: [PATCH 11/21] Test sync - check that exceptions are being triggered --- tests/test_sync.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/tests/test_sync.py b/tests/test_sync.py index 01b56ac358..4bd3a61560 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -30,6 +30,7 @@ def test_inspect_sync_dir_notgit(self): psync = nf_core.sync.PipelineSync(tempfile.mkdtemp()) try: psync.inspect_sync_dir() + raise UserWarning("Should have hit an exception") except nf_core.sync.SyncException as e: assert "does not appear to be a git repository" in e.args[0] @@ -42,6 +43,7 @@ def test_inspect_sync_dir_dirty(self): psync = nf_core.sync.PipelineSync(self.pipeline_dir) try: psync.inspect_sync_dir() + raise UserWarning("Should have hit an exception") except nf_core.sync.SyncException as e: os.remove(test_fn) assert e.args[0].startswith("Uncommitted changes found in pipeline directory!") @@ -56,6 +58,7 @@ def test_get_wf_config_no_branch(self): try: psync.inspect_sync_dir() psync.get_wf_config() + raise UserWarning("Should have hit an exception") except nf_core.sync.SyncException as e: assert e.args[0] == "Branch `foo` not found!" @@ -82,6 +85,7 @@ def test_get_wf_config_missing_required_config(self): try: psync.inspect_sync_dir() psync.get_wf_config() + raise UserWarning("Should have hit an exception") except nf_core.sync.SyncException as e: # Check that we did actually get some config back assert psync.wf_config["params.outdir"] == "'./results'" @@ -163,6 +167,7 @@ def test_push_template_branch_error(self): # Try to push changes try: psync.push_template_branch() + raise UserWarning("Should have hit an exception") except nf_core.sync.PullRequestException as e: assert e.args[0].startswith("Could not push TEMPLATE branch") @@ -173,6 +178,7 @@ def test_make_pull_request_missing_username(self): psync.gh_repo = None try: psync.make_pull_request() + raise UserWarning("Should have hit an exception") except nf_core.sync.PullRequestException as e: assert e.args[0] == "Could not find GitHub username and repo name" @@ -184,6 +190,7 @@ def test_make_pull_request_missing_auth(self): psync.gh_auth_token = None try: psync.make_pull_request() + raise UserWarning("Should have hit an exception") except nf_core.sync.PullRequestException as e: assert e.args[0] == "No GitHub authentication token set - cannot make PR" @@ -204,22 +211,23 @@ def __init__(self, data, status_code): @mock.patch("requests.post", side_effect=mocked_requests_post) def test_make_pull_request_bad_response(self, mock_post): - """ Try making a PR without any auth """ + """ Try making a PR - successful response """ psync = nf_core.sync.PipelineSync(self.pipeline_dir) - psync.gh_username = "bad" + psync.gh_username = "good" psync.gh_repo = "response" psync.gh_auth_token = "test" - try: - psync.make_pull_request() - except nf_core.sync.PullRequestException as e: - assert e.args[0].startswith("GitHub API returned code 404:") + psync.make_pull_request() + assert psync.gh_pr_returned_data["html_url"] == "great_success" @mock.patch("requests.post", side_effect=mocked_requests_post) def test_make_pull_request_bad_response(self, mock_post): - """ Try making a PR without any auth """ + """ Try making a PR and getting a 404 error """ psync = nf_core.sync.PipelineSync(self.pipeline_dir) - psync.gh_username = "good" + psync.gh_username = "bad" psync.gh_repo = "response" psync.gh_auth_token = "test" - psync.make_pull_request() - assert psync.gh_pr_returned_data["html_url"] == "great_success" + try: + psync.make_pull_request() + raise UserWarning("Should have hit an exception") + except nf_core.sync.PullRequestException as e: + assert e.args[0].startswith("GitHub API returned code 404:") From 81e5c64055830cc0e8c41c4313ecc58db8a7b30e Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 10:45:10 +0200 Subject: [PATCH 12/21] Sync pytests - add more tests, update --- nf_core/sync.py | 12 ++++------ tests/test_sync.py | 60 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/nf_core/sync.py b/nf_core/sync.py index 5fafca2cbf..76f896f517 100644 --- a/nf_core/sync.py +++ b/nf_core/sync.py @@ -313,12 +313,10 @@ def update_existing_pull_request(self, pr_title, pr_body_text): """ # Look for existing pull-requests - r = requests.get( - url="https://api.github.com/repos/{}/{}/pulls?head=nf-core:TEMPLATE&base={}".format( - self.gh_username, self.gh_repo, self.from_branch - ), - auth=requests.auth.HTTPBasicAuth(self.gh_username, self.gh_auth_token), + list_prs_url = "https://api.github.com/repos/{}/{}/pulls?head=nf-core:TEMPLATE&base={}".format( + self.gh_username, self.gh_repo, self.from_branch ) + r = requests.get(url=list_prs_url, auth=requests.auth.HTTPBasicAuth(self.gh_username, self.gh_auth_token),) try: r_json = json.loads(r.content) r_pp = json.dumps(r_json, indent=4) @@ -358,12 +356,12 @@ def update_existing_pull_request(self, pr_title, pr_body_text): return True # Something went wrong else: - log.warn("Could not update PR {}: \n{}".format(r.status_code, r_pp)) + log.warn("Could not update PR ('{}'):\n{}\n{}".format(r.status_code, pr_update_api_url, r_pp)) return False # Something went wrong else: - log.warn("Could not list open PRs: \n{}".format(r.status_code, r_pp)) + log.warn("Could not list open PRs ('{}')\n{}\n{}".format(r.status_code, list_prs_url, r_pp)) return False def submit_pull_request(self, pr_title, pr_body_text): diff --git a/tests/test_sync.py b/tests/test_sync.py index 4bd3a61560..357c3a479e 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -194,7 +194,7 @@ def test_make_pull_request_missing_auth(self): except nf_core.sync.PullRequestException as e: assert e.args[0] == "No GitHub authentication token set - cannot make PR" - def mocked_requests_post(**kwargs): + def mocked_requests_get(**kwargs): """ Helper function to emulate POST requests responses from the web """ class MockResponse: @@ -202,28 +202,62 @@ def __init__(self, data, status_code): self.status_code = status_code self.content = json.dumps(data) - if kwargs["url"] == "https://api.github.com/repos/bad/response/pulls": - return MockResponse({}, 404) + url_template = "https://api.github.com/repos/{}/response/pulls?head=nf-core:TEMPLATE&base=None" + if kwargs["url"] == url_template.format("no_existing_pr"): + response_data = [] + return MockResponse(response_data, 200) + + if kwargs["url"] == url_template.format("existing_pr"): + response_data = [{"url": "url_to_update_pr"}] + return MockResponse(response_data, 200) + + return MockResponse({"get_url": kwargs["url"]}, 404) + + def mocked_requests_patch(**kwargs): + """ Helper function to emulate POST requests responses from the web """ + + class MockResponse: + def __init__(self, data, status_code): + self.status_code = status_code + self.content = json.dumps(data) + + if kwargs["url"] == "url_to_update_pr": + response_data = {"html_url": "great_success"} + return MockResponse(response_data, 200) + + return MockResponse({"patch_url": kwargs["url"]}, 404) - if kwargs["url"] == "https://api.github.com/repos/good/response/pulls": + def mocked_requests_post(**kwargs): + """ Helper function to emulate POST requests responses from the web """ + + class MockResponse: + def __init__(self, data, status_code): + self.status_code = status_code + self.content = json.dumps(data) + + if kwargs["url"] == "https://api.github.com/repos/no_existing_pr/response/pulls": response_data = {"html_url": "great_success"} return MockResponse(response_data, 201) + return MockResponse({"post_url": kwargs["url"]}, 404) + + @mock.patch("requests.get", side_effect=mocked_requests_get) @mock.patch("requests.post", side_effect=mocked_requests_post) - def test_make_pull_request_bad_response(self, mock_post): + def test_make_pull_request_success(self, mock_get, mock_post): """ Try making a PR - successful response """ psync = nf_core.sync.PipelineSync(self.pipeline_dir) - psync.gh_username = "good" + psync.gh_username = "no_existing_pr" psync.gh_repo = "response" psync.gh_auth_token = "test" psync.make_pull_request() assert psync.gh_pr_returned_data["html_url"] == "great_success" + @mock.patch("requests.get", side_effect=mocked_requests_get) @mock.patch("requests.post", side_effect=mocked_requests_post) - def test_make_pull_request_bad_response(self, mock_post): + def test_make_pull_request_bad_response(self, mock_get, mock_post): """ Try making a PR and getting a 404 error """ psync = nf_core.sync.PipelineSync(self.pipeline_dir) - psync.gh_username = "bad" + psync.gh_username = "bad_url" psync.gh_repo = "response" psync.gh_auth_token = "test" try: @@ -231,3 +265,13 @@ def test_make_pull_request_bad_response(self, mock_post): raise UserWarning("Should have hit an exception") except nf_core.sync.PullRequestException as e: assert e.args[0].startswith("GitHub API returned code 404:") + + @mock.patch("requests.get", side_effect=mocked_requests_get) + @mock.patch("requests.patch", side_effect=mocked_requests_patch) + def test_update_existing_pull_request(self, mock_get, mock_patch): + """ Try discovering a PR and updating it """ + psync = nf_core.sync.PipelineSync(self.pipeline_dir) + psync.gh_username = "existing_pr" + psync.gh_repo = "response" + psync.gh_auth_token = "test" + assert psync.update_existing_pull_request("title", "body") is True From 40ad562e1d1703a9bbb028c64f38df60f925e0b7 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 10:58:29 +0200 Subject: [PATCH 13/21] Sync - use env variable for auth token instead of cli argument --- .github/workflows/sync.yml | 5 ++--- nf_core/__main__.py | 3 +-- nf_core/sync.py | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 9019a20e73..5baa78de7c 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -53,7 +53,7 @@ jobs: - name: Run synchronisation if: github.repository == 'nf-core/tools' env: - AUTH_TOKEN: ${{ secrets.nf_core_bot_auth_token }} + GITHUB_AUTH_TOKEN: ${{ secrets.nf_core_bot_auth_token }} run: | git config --global user.email "core@nf-co.re" git config --global user.name "nf-core-bot" @@ -61,8 +61,7 @@ jobs: --from-branch dev \ --pull-request \ --username nf-core-bot \ - --repository nf-core/${{ matrix.pipeline }} \ - --auth-token ${{ secrets.nf_core_bot_auth_token }} + --repository nf-core/${{ matrix.pipeline }} - name: Upload sync log file artifact diff --git a/nf_core/__main__.py b/nf_core/__main__.py index 3b27daf971..4abea69788 100755 --- a/nf_core/__main__.py +++ b/nf_core/__main__.py @@ -555,7 +555,6 @@ def bump_version(pipeline_dir, new_version, nextflow): @click.option("-p", "--pull-request", is_flag=True, default=False, help="Make a GitHub pull-request with the changes.") @click.option("-r", "--repository", type=str, help="GitHub PR: target repository.") @click.option("-u", "--username", type=str, help="GitHub PR: auth username.") -@click.option("-a", "--auth-token", type=str, help="GitHub PR: API personal access token.") def sync(pipeline_dir, from_branch, pull_request, repository, username, auth_token): """ Sync a pipeline TEMPLATE branch with the nf-core template. @@ -571,7 +570,7 @@ def sync(pipeline_dir, from_branch, pull_request, repository, username, auth_tok """ # Sync the given pipeline dir - sync_obj = nf_core.sync.PipelineSync(pipeline_dir, from_branch, pull_request, repository, username, auth_token) + sync_obj = nf_core.sync.PipelineSync(pipeline_dir, from_branch, pull_request, repository, username) try: sync_obj.sync() except (nf_core.sync.SyncException, nf_core.sync.PullRequestException) as e: diff --git a/nf_core/sync.py b/nf_core/sync.py index 76f896f517..eac1db484f 100644 --- a/nf_core/sync.py +++ b/nf_core/sync.py @@ -44,7 +44,6 @@ class PipelineSync(object): make_pr (bool): Set this to `True` to create a GitHub pull-request with the changes gh_username (str): GitHub username gh_repo (str): GitHub repository name - gh_auth_token (str): Authorisation token used to make PR with GitHub API Attributes: pipeline_dir (str): Path to target pipeline directory @@ -55,11 +54,10 @@ class PipelineSync(object): required_config_vars (list): List of nextflow variables required to make template pipeline gh_username (str): GitHub username gh_repo (str): GitHub repository name - gh_auth_token (str): Authorisation token used to make PR with GitHub API """ def __init__( - self, pipeline_dir, from_branch=None, make_pr=False, gh_repo=None, gh_username=None, gh_auth_token=None, + self, pipeline_dir, from_branch=None, make_pr=False, gh_repo=None, gh_username=None, ): """ Initialise syncing object """ @@ -73,7 +71,6 @@ def __init__( self.gh_username = gh_username self.gh_repo = gh_repo - self.gh_auth_token = gh_auth_token def sync(self): """ Find workflow attributes, create a new template pipeline on TEMPLATE @@ -276,14 +273,14 @@ def make_pull_request(self): # If we've been asked to make a PR, check that we have the credentials try: - assert self.gh_auth_token is not None + assert os.environ.get("GITHUB_AUTH_TOKEN", "") != "" except AssertionError: log.info( "Make a PR at the following URL:\n https://github.com/{}/{}/compare/{}...TEMPLATE".format( self.gh_username, self.gh_repo, self.original_branch ) ) - raise PullRequestException("No GitHub authentication token set - cannot make PR") + raise PullRequestException("Environment variable GITHUB_AUTH_TOKEN not set - cannot make PR") log.debug("Submitting a pull request via the GitHub API") @@ -311,12 +308,14 @@ def update_existing_pull_request(self, pr_title, pr_body_text): If one is found, attempt to update it with a new title and body text If none are found, return False """ - + assert os.environ.get("GITHUB_AUTH_TOKEN", "") != "" # Look for existing pull-requests list_prs_url = "https://api.github.com/repos/{}/{}/pulls?head=nf-core:TEMPLATE&base={}".format( self.gh_username, self.gh_repo, self.from_branch ) - r = requests.get(url=list_prs_url, auth=requests.auth.HTTPBasicAuth(self.gh_username, self.gh_auth_token),) + r = requests.get( + url=list_prs_url, auth=requests.auth.HTTPBasicAuth(self.gh_username, os.environ.get("GITHUB_AUTH_TOKEN")), + ) try: r_json = json.loads(r.content) r_pp = json.dumps(r_json, indent=4) @@ -340,7 +339,7 @@ def update_existing_pull_request(self, pr_title, pr_body_text): r = requests.patch( url=pr_update_api_url, data=json.dumps(pr_content), - auth=requests.auth.HTTPBasicAuth(self.gh_username, self.gh_auth_token), + auth=requests.auth.HTTPBasicAuth(self.gh_username, os.environ.get("GITHUB_AUTH_TOKEN")), ) try: r_json = json.loads(r.content) @@ -368,6 +367,7 @@ def submit_pull_request(self, pr_title, pr_body_text): """ Create a new pull-request on GitHub """ + assert os.environ.get("GITHUB_AUTH_TOKEN", "") != "" pr_content = { "title": pr_title, "body": pr_body_text, @@ -379,7 +379,7 @@ def submit_pull_request(self, pr_title, pr_body_text): r = requests.post( url="https://api.github.com/repos/{}/{}/pulls".format(self.gh_username, self.gh_repo), data=json.dumps(pr_content), - auth=requests.auth.HTTPBasicAuth(self.gh_username, self.gh_auth_token), + auth=requests.auth.HTTPBasicAuth(self.gh_username, os.environ.get("GITHUB_AUTH_TOKEN")), ) try: self.gh_pr_returned_data = json.loads(r.content) From 240f9da0ed3d0c160dab695faf1379bdcc392b6a Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 11:14:17 +0200 Subject: [PATCH 14/21] Make lots of log message INFO again --- nf_core/__main__.py | 2 +- nf_core/sync.py | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/nf_core/__main__.py b/nf_core/__main__.py index 4abea69788..be9bdfaac9 100755 --- a/nf_core/__main__.py +++ b/nf_core/__main__.py @@ -555,7 +555,7 @@ def bump_version(pipeline_dir, new_version, nextflow): @click.option("-p", "--pull-request", is_flag=True, default=False, help="Make a GitHub pull-request with the changes.") @click.option("-r", "--repository", type=str, help="GitHub PR: target repository.") @click.option("-u", "--username", type=str, help="GitHub PR: auth username.") -def sync(pipeline_dir, from_branch, pull_request, repository, username, auth_token): +def sync(pipeline_dir, from_branch, pull_request, repository, username): """ Sync a pipeline TEMPLATE branch with the nf-core template. diff --git a/nf_core/sync.py b/nf_core/sync.py index eac1db484f..3e7a897659 100644 --- a/nf_core/sync.py +++ b/nf_core/sync.py @@ -76,11 +76,11 @@ def sync(self): """ Find workflow attributes, create a new template pipeline on TEMPLATE """ - log.debug("Pipeline directory: {}".format(self.pipeline_dir)) + log.info("Pipeline directory: {}".format(self.pipeline_dir)) if self.from_branch: - log.debug("Using branch `{}` to fetch workflow variables".format(self.from_branch)) + log.info("Using branch `{}` to fetch workflow variables".format(self.from_branch)) if self.make_pr: - log.debug("Will attempt to automatically create a pull request") + log.info("Will attempt to automatically create a pull request") self.inspect_sync_dir() self.get_wf_config() @@ -121,7 +121,7 @@ def inspect_sync_dir(self): # get current branch so we can switch back later self.original_branch = self.repo.active_branch.name - log.debug("Original pipeline repository branch is '{}'".format(self.original_branch)) + log.info("Original pipeline repository branch is '{}'".format(self.original_branch)) # Check to see if there are uncommitted changes on current branch if self.repo.is_dirty(untracked_files=True): @@ -136,7 +136,7 @@ def get_wf_config(self): # Try to check out target branch (eg. `origin/dev`) try: if self.from_branch and self.repo.active_branch.name != self.from_branch: - log.debug("Checking out workflow branch '{}'".format(self.from_branch)) + log.info("Checking out workflow branch '{}'".format(self.from_branch)) self.repo.git.checkout(self.from_branch) except git.exc.GitCommandError: raise SyncException("Branch `{}` not found!".format(self.from_branch)) @@ -197,7 +197,7 @@ def delete_template_branch_files(self): Delete all files in the TEMPLATE branch """ # Delete everything - log.debug("Deleting all files in TEMPLATE branch") + log.info("Deleting all files in TEMPLATE branch") for the_file in os.listdir(self.pipeline_dir): if the_file == ".git": continue @@ -215,7 +215,7 @@ def make_template_pipeline(self): """ Delete all files and make a fresh template using the workflow variables """ - log.debug("Making a new template pipeline using pipeline variables") + log.info("Making a new template pipeline using pipeline variables") # Only show error messages from pipeline creation logging.getLogger("nf_core.create").setLevel(logging.ERROR) @@ -242,7 +242,7 @@ def commit_template_changes(self): self.repo.git.add(A=True) self.repo.index.commit("Template update for nf-core/tools version {}".format(nf_core.__version__)) self.made_changes = True - log.debug("Committed changes to TEMPLATE branch") + log.info("Committed changes to TEMPLATE branch") except Exception as e: raise SyncException("Could not commit changes to TEMPLATE:\n{}".format(e)) return True @@ -252,7 +252,7 @@ def push_template_branch(self): and try to make a PR. If we don't have the auth token, try to figure out a URL for the PR and print this to the console. """ - log.debug("Pushing TEMPLATE branch to remote: '{}'".format(os.path.basename(self.pipeline_dir))) + log.info("Pushing TEMPLATE branch to remote: '{}'".format(os.path.basename(self.pipeline_dir))) try: self.repo.git.push() except git.exc.GitCommandError as e: @@ -275,14 +275,14 @@ def make_pull_request(self): try: assert os.environ.get("GITHUB_AUTH_TOKEN", "") != "" except AssertionError: - log.info( + raise PullRequestException( + "Environment variable GITHUB_AUTH_TOKEN not set - cannot make PR\n" "Make a PR at the following URL:\n https://github.com/{}/{}/compare/{}...TEMPLATE".format( self.gh_username, self.gh_repo, self.original_branch ) ) - raise PullRequestException("Environment variable GITHUB_AUTH_TOKEN not set - cannot make PR") - log.debug("Submitting a pull request via the GitHub API") + log.info("Submitting a pull request via the GitHub API") pr_title = "Important! Template update for nf-core/tools v{}".format(nf_core.__version__) pr_body_text = ( @@ -329,7 +329,7 @@ def update_existing_pull_request(self, pr_title, pr_body_text): # No open PRs if len(r_json) == 0: - log.debug("No open PRs found between TEMPLATE and {}".format(self.from_branch)) + log.info("No open PRs found between TEMPLATE and {}".format(self.from_branch)) return False # Update existing PR @@ -403,7 +403,7 @@ def reset_target_dir(self): """ Reset the target pipeline directory. Check out the original branch. """ - log.debug("Checking out original branch: '{}'".format(self.original_branch)) + log.info("Checking out original branch: '{}'".format(self.original_branch)) try: self.repo.git.checkout(self.original_branch) except git.exc.GitCommandError as e: From 75d7238af93c4246683931c572c900f6df18d71e Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 11:20:40 +0200 Subject: [PATCH 15/21] Sync pytests - update to work with env var auth --- tests/test_sync.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/test_sync.py b/tests/test_sync.py index 357c3a479e..d674bcad8d 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -187,12 +187,16 @@ def test_make_pull_request_missing_auth(self): psync = nf_core.sync.PipelineSync(self.pipeline_dir) psync.gh_username = "foo" psync.gh_repo = "bar" - psync.gh_auth_token = None + if "GITHUB_AUTH_TOKEN" in os.environ: + del os.environ["GITHUB_AUTH_TOKEN"] try: psync.make_pull_request() raise UserWarning("Should have hit an exception") except nf_core.sync.PullRequestException as e: - assert e.args[0] == "No GitHub authentication token set - cannot make PR" + assert e.args[0] == ( + "Environment variable GITHUB_AUTH_TOKEN not set - cannot make PR\n" + "Make a PR at the following URL:\n https://github.com/foo/bar/compare/None...TEMPLATE" + ) def mocked_requests_get(**kwargs): """ Helper function to emulate POST requests responses from the web """ @@ -248,7 +252,7 @@ def test_make_pull_request_success(self, mock_get, mock_post): psync = nf_core.sync.PipelineSync(self.pipeline_dir) psync.gh_username = "no_existing_pr" psync.gh_repo = "response" - psync.gh_auth_token = "test" + os.environ["GITHUB_AUTH_TOKEN"] = "test" psync.make_pull_request() assert psync.gh_pr_returned_data["html_url"] == "great_success" @@ -259,7 +263,7 @@ def test_make_pull_request_bad_response(self, mock_get, mock_post): psync = nf_core.sync.PipelineSync(self.pipeline_dir) psync.gh_username = "bad_url" psync.gh_repo = "response" - psync.gh_auth_token = "test" + os.environ["GITHUB_AUTH_TOKEN"] = "test" try: psync.make_pull_request() raise UserWarning("Should have hit an exception") @@ -273,5 +277,5 @@ def test_update_existing_pull_request(self, mock_get, mock_patch): psync = nf_core.sync.PipelineSync(self.pipeline_dir) psync.gh_username = "existing_pr" psync.gh_repo = "response" - psync.gh_auth_token = "test" + os.environ["GITHUB_AUTH_TOKEN"] = "test" assert psync.update_existing_pull_request("title", "body") is True From 777e115253ce2964a0c2e48a81c5f5d575702715 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 11:23:12 +0200 Subject: [PATCH 16/21] Sync log file - correct filename --- .github/workflows/sync.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 5baa78de7c..764649e5cc 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -57,7 +57,7 @@ jobs: run: | git config --global user.email "core@nf-co.re" git config --global user.name "nf-core-bot" - nf-core --log-file sync_log.txt sync nf-core/${{ matrix.pipeline }} \ + nf-core --log-file sync_log_${{ matrix.pipeline }}.txt sync nf-core/${{ matrix.pipeline }} \ --from-branch dev \ --pull-request \ --username nf-core-bot \ From 866a591fbf50f71166439874c8f014a81dcd0f97 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 11:24:47 +0200 Subject: [PATCH 17/21] Add comment to actions yaml for branch protection comments --- .../{{cookiecutter.name_noslash}}/.github/workflows/branch.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/nf_core/pipeline-template/{{cookiecutter.name_noslash}}/.github/workflows/branch.yml b/nf_core/pipeline-template/{{cookiecutter.name_noslash}}/.github/workflows/branch.yml index 94ec0a87ca..a561ad54f0 100644 --- a/nf_core/pipeline-template/{{cookiecutter.name_noslash}}/.github/workflows/branch.yml +++ b/nf_core/pipeline-template/{{cookiecutter.name_noslash}}/.github/workflows/branch.yml @@ -17,6 +17,7 @@ jobs: {% raw %} # If the above check failed, post a comment on the PR explaining the failure + # NOTE - this doesn't currently work if the PR is coming from a fork, due to limitations in GitHub actions secrets - name: Post PR comment if: failure() uses: mshick/add-pr-comment@v1 From 7aed3bc60fa4d7852de27990abf12aac3939ade2 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 11:39:17 +0200 Subject: [PATCH 18/21] Sync - don't be clever and try to figure out the remote repo name automatically --- nf_core/sync.py | 20 -------------------- tests/test_sync.py | 15 --------------- 2 files changed, 35 deletions(-) diff --git a/nf_core/sync.py b/nf_core/sync.py index 3e7a897659..8bc2e8c2ff 100644 --- a/nf_core/sync.py +++ b/nf_core/sync.py @@ -148,26 +148,6 @@ def get_wf_config(self): except git.exc.GitCommandError as e: log.error("Could not find active repo branch: ".format(e)) - # Figure out the GitHub username and repo name from the 'origin' remote if we can - try: - origin_url = self.repo.remotes.origin.url.rstrip(".git") - gh_origin_match = re.search(r"github\.com[:\/]([^\/]+)/([^\/]+)$", origin_url) - if gh_origin_match: - self.gh_username = gh_origin_match.group(1) - self.gh_repo = gh_origin_match.group(2) - else: - raise AttributeError - except AttributeError as e: - log.debug( - "Could not find repository URL for remote called 'origin' from remote: {}".format(self.repo.remotes) - ) - else: - log.debug( - "Found username and repo from remote: {}, {} - {}".format( - self.gh_username, self.gh_repo, self.repo.remotes.origin.url - ) - ) - # Fetch workflow variables log.debug("Fetching workflow config variables") self.wf_config = nf_core.utils.fetch_wf_config(self.pipeline_dir) diff --git a/tests/test_sync.py b/tests/test_sync.py index d674bcad8d..089271aba7 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -62,21 +62,6 @@ def test_get_wf_config_no_branch(self): except nf_core.sync.SyncException as e: assert e.args[0] == "Branch `foo` not found!" - def test_get_wf_config_fetch_origin(self): - """ - Try getting the GitHub username and repo from the git origin - - Also checks the fetched config variables, should pass - """ - # Try to sync, check we halt with the right error - psync = nf_core.sync.PipelineSync(self.pipeline_dir) - psync.inspect_sync_dir() - # Add a remote to the git repo - psync.repo.create_remote("origin", "https://github.com/nf-core/demo.git") - psync.get_wf_config() - assert psync.gh_username == "nf-core" - assert psync.gh_repo == "demo" - def test_get_wf_config_missing_required_config(self): """ Try getting a workflow config, then make it miss a required config option """ # Try to sync, check we halt with the right error From fa834a35cd5b839371d47b0f312d187b509d6549 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 11:42:20 +0200 Subject: [PATCH 19/21] Fix the now-broken URLs for the GitHub API --- nf_core/sync.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nf_core/sync.py b/nf_core/sync.py index 8bc2e8c2ff..0f48eb5161 100644 --- a/nf_core/sync.py +++ b/nf_core/sync.py @@ -257,8 +257,8 @@ def make_pull_request(self): except AssertionError: raise PullRequestException( "Environment variable GITHUB_AUTH_TOKEN not set - cannot make PR\n" - "Make a PR at the following URL:\n https://github.com/{}/{}/compare/{}...TEMPLATE".format( - self.gh_username, self.gh_repo, self.original_branch + "Make a PR at the following URL:\n https://github.com/{}/compare/{}...TEMPLATE".format( + self.gh_repo, self.original_branch ) ) @@ -290,8 +290,8 @@ def update_existing_pull_request(self, pr_title, pr_body_text): """ assert os.environ.get("GITHUB_AUTH_TOKEN", "") != "" # Look for existing pull-requests - list_prs_url = "https://api.github.com/repos/{}/{}/pulls?head=nf-core:TEMPLATE&base={}".format( - self.gh_username, self.gh_repo, self.from_branch + list_prs_url = "https://api.github.com/repos/{}/pulls?head=nf-core:TEMPLATE&base={}".format( + self.gh_repo, self.from_branch ) r = requests.get( url=list_prs_url, auth=requests.auth.HTTPBasicAuth(self.gh_username, os.environ.get("GITHUB_AUTH_TOKEN")), @@ -357,7 +357,7 @@ def submit_pull_request(self, pr_title, pr_body_text): } r = requests.post( - url="https://api.github.com/repos/{}/{}/pulls".format(self.gh_username, self.gh_repo), + url="https://api.github.com/repos/{}/pulls".format(self.gh_repo), data=json.dumps(pr_content), auth=requests.auth.HTTPBasicAuth(self.gh_username, os.environ.get("GITHUB_AUTH_TOKEN")), ) From 0d2da051934a110e19e65e1d6fd0c93990237409 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 11:48:14 +0200 Subject: [PATCH 20/21] Changelog update --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c45c9b96e..2fcf3628f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ Apologies for the inconvenience. * Fix syntax error in `/push_dockerhub.yml` GitHub Action workflow * Change `params.readPaths` -> `params.input_paths` in `test_full.config` * Check results when posting the lint results as a GitHub comment + * This feature is unfortunately not possible when making PRs from forks outside of the nf-core organisation for now. +* More major refactoring of the automated pipeline sync + * New GitHub Actions matrix parallelisation of sync jobs across pipelines [[#673](https://github.com/nf-core/tools/issues/673)] + * Removed the `--all` behaviour from `nf-core sync` as we no longer need it + * Sync now uses a new list of pipelines on the website which does not include archived pipelines [[#712](https://github.com/nf-core/tools/issues/712)] + * When making a PR it checks if a PR already exists - if so it updates it [[#710](https://github.com/nf-core/tools/issues/710)] + * More tests and code refactoring for more stable code. Hopefully fixes 404 error [[#711](https://github.com/nf-core/tools/issues/711)] ## [v1.10.1 - Copper Camel _(patch)_](https://github.com/nf-core/tools/releases/tag/1.10.1) - [2020-07-30] From ee5c0109774a51de0094e973338bcc17df32f36e Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 31 Jul 2020 12:06:39 +0200 Subject: [PATCH 21/21] Update tests to use proper gh_repo style --- tests/test_sync.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_sync.py b/tests/test_sync.py index 089271aba7..c7726cfb7d 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -171,7 +171,7 @@ def test_make_pull_request_missing_auth(self): """ Try making a PR without any auth """ psync = nf_core.sync.PipelineSync(self.pipeline_dir) psync.gh_username = "foo" - psync.gh_repo = "bar" + psync.gh_repo = "foo/bar" if "GITHUB_AUTH_TOKEN" in os.environ: del os.environ["GITHUB_AUTH_TOKEN"] try: @@ -236,7 +236,7 @@ def test_make_pull_request_success(self, mock_get, mock_post): """ Try making a PR - successful response """ psync = nf_core.sync.PipelineSync(self.pipeline_dir) psync.gh_username = "no_existing_pr" - psync.gh_repo = "response" + psync.gh_repo = "no_existing_pr/response" os.environ["GITHUB_AUTH_TOKEN"] = "test" psync.make_pull_request() assert psync.gh_pr_returned_data["html_url"] == "great_success" @@ -247,7 +247,7 @@ def test_make_pull_request_bad_response(self, mock_get, mock_post): """ Try making a PR and getting a 404 error """ psync = nf_core.sync.PipelineSync(self.pipeline_dir) psync.gh_username = "bad_url" - psync.gh_repo = "response" + psync.gh_repo = "bad_url/response" os.environ["GITHUB_AUTH_TOKEN"] = "test" try: psync.make_pull_request() @@ -261,6 +261,6 @@ def test_update_existing_pull_request(self, mock_get, mock_patch): """ Try discovering a PR and updating it """ psync = nf_core.sync.PipelineSync(self.pipeline_dir) psync.gh_username = "existing_pr" - psync.gh_repo = "response" + psync.gh_repo = "existing_pr/response" os.environ["GITHUB_AUTH_TOKEN"] = "test" assert psync.update_existing_pull_request("title", "body") is True