Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
173 commits
Select commit Hold shift + click to select a range
ae66e2e
Download pipeline with auth. GH API
jpfeuffer Jun 5, 2025
191f685
remove debug
jpfeuffer Jun 5, 2025
eff74f4
ruff linter
jpfeuffer Jun 5, 2025
dfaf3fe
good old indent on empty line
jpfeuffer Jun 5, 2025
9ab9642
read topdir from zip for rename
jpfeuffer Jun 5, 2025
bb094e2
ruff....
jpfeuffer Jun 5, 2025
b423285
add a cli flag to avoid rate limit when unauthenticated
jpfeuffer Jun 6, 2025
146632f
fix test
jpfeuffer Jun 6, 2025
651a0ca
fix test: Remove api-download param from test dict since it's a boole…
jpfeuffer Sep 18, 2025
592d621
add testing artifacts to gitignore
jpfeuffer Sep 18, 2025
e77b261
lint
jpfeuffer Sep 18, 2025
8fc05a5
update module and subworkflow template
nvnieuwk Sep 26, 2025
de4908f
[automated] Update CHANGELOG.md
nf-core-bot Sep 26, 2025
4d94180
Merge branch 'dev' into feat/topics
nvnieuwk Sep 29, 2025
c48f7ac
also emit versions
nvnieuwk Sep 29, 2025
b534065
Merge branch 'dev' into feat/topics
nvnieuwk Sep 30, 2025
62ea7f0
add main.nf linting for topics
nvnieuwk Sep 30, 2025
e490c68
Update modules lint --fix to work with eval
nvnieuwk Sep 30, 2025
5f1af90
Update nf_core/module-template/main.nf
nvnieuwk Oct 1, 2025
1c6f6c9
versions emit should now contain the tool name
nvnieuwk Oct 1, 2025
a749470
Merge branch 'feat/topics' of github.com:nvnieuwk/tools into feat/topics
nvnieuwk Oct 1, 2025
6cb96ee
linting fixes for new versions topics
nvnieuwk Oct 1, 2025
a4d8aac
Merge branch 'dev' into feat/topics
nvnieuwk Oct 8, 2025
5a31432
add topic handling to the main workflow
nvnieuwk Oct 8, 2025
0e726e1
pre-commit
nvnieuwk Oct 8, 2025
3cae307
Improve file ignores in workflow file enumeration
jmuhlich Oct 8, 2025
5c6c50d
Update CHANGELOG.md
jmuhlich Oct 9, 2025
249c8eb
add versions file as valid topic output + fix meta yaml template
nvnieuwk Oct 9, 2025
6b02333
update meta yaml versions output on module create
nvnieuwk Oct 9, 2025
f9f42a1
Merge branch 'dev' into feat/topics
nvnieuwk Oct 9, 2025
3095597
pre-commit
nvnieuwk Oct 9, 2025
0eea1b4
pre-commit
nvnieuwk Oct 9, 2025
1d49215
Address PR review comments: refactor download functionality
jpfeuffer Oct 10, 2025
09a8fb0
remove duplication
jpfeuffer Oct 10, 2025
7310e75
better code flow.
jpfeuffer Oct 10, 2025
9b8d397
fmt
jpfeuffer Oct 10, 2025
2d49346
sort versions output
nvnieuwk Oct 13, 2025
c7be4d7
try to add versions on module creation
nvnieuwk Oct 13, 2025
c113b81
Merge branch 'dev' into jpfeuffer-patch-1
JulianFlesch Oct 13, 2025
5e1928d
update topics structure + added a check for empty input and output
nvnieuwk Oct 14, 2025
53f356c
Merge branch 'dev' into feat/topics
nvnieuwk Oct 14, 2025
b150244
fix wrongly resolved merge conflict
nvnieuwk Oct 14, 2025
7c954d6
fix module create tests + pre-commit
nvnieuwk Oct 14, 2025
db8e598
Adds installing nf-core at state from local workspace
JulianFlesch Oct 15, 2025
fbe8f63
fix usage of versions string
nvnieuwk Oct 16, 2025
58d4ba7
Merge branch 'dev' into feat/topics
nvnieuwk Oct 16, 2025
6fb78b8
bump version to 3.5.0dev
mashehu Oct 16, 2025
6d6c996
Merge pull request #3839 from mashehu/bump-version-back
mashehu Oct 16, 2025
89a028d
Remove building nfcore/devcontainer:dev
JulianFlesch Oct 17, 2025
f3b23e8
Update .devcontainer/setup.sh
JulianFlesch Oct 19, 2025
aae7f41
Revert "Remove building nfcore/devcontainer:dev"
JulianFlesch Oct 19, 2025
bbdcc80
Merge branch 'issues/3837-impr-devcontainer' of github.com:JulianFles…
JulianFlesch Oct 20, 2025
3f2efdc
Merge branch 'dev' into issues/3837-impr-devcontainer
mashehu Oct 24, 2025
0833d10
Merge branch 'dev' into get-wf-files-ignores
mashehu Oct 24, 2025
8107706
Add test for utils.get_wf_files
jmuhlich Oct 24, 2025
06fc1a4
Remove manually changing devcontainer tags from Checklist
JulianFlesch Oct 27, 2025
331ca17
Fix missing ) and correct ID in release notification GA
ewels Oct 27, 2025
914ca61
Merge branch 'dev' into feat/topics
nvnieuwk Oct 28, 2025
93f8cc1
re-allow versions yaml in linting
nvnieuwk Oct 28, 2025
ae22b17
don't close open template update PRs
mashehu Oct 28, 2025
75e534e
update multiqc version to fix utils test (#3853)
mashehu Oct 28, 2025
43ef567
Merge branch 'dev' into fix-mastodon-toot
mashehu Oct 28, 2025
2b6b60c
Automatically sync the Rocrate and README content during pipelines li…
ningyuxin1999 Oct 28, 2025
24d2916
Merge pull request #3850 from ewels/fix-mastodon-toot
mashehu Oct 28, 2025
8d07eec
Merge branch 'dev' into keep-sync-pr-open
ningyuxin1999 Oct 28, 2025
d04574d
Merge branch 'dev' into issues/3837-impr-devcontainer
ningyuxin1999 Oct 28, 2025
31ae184
add topic linting check
nvnieuwk Oct 28, 2025
efd1176
fix review comments
nvnieuwk Oct 29, 2025
8ffc35f
Merge branch 'dev' into feat/topics
nvnieuwk Oct 29, 2025
4500ae5
fix linting
nvnieuwk Oct 29, 2025
5e39aa4
Merge branch 'feat/topics' of github.com:nvnieuwk/tools into feat/topics
nvnieuwk Oct 29, 2025
da4cb3f
Merge pull request #3779 from nvnieuwk/feat/topics
nvnieuwk Oct 29, 2025
2518bbf
Fix changelog for topics
nvnieuwk Oct 29, 2025
2ad9c9a
[automated] Update CHANGELOG.md
nf-core-bot Oct 29, 2025
d126c02
remove automated changelog addition
nvnieuwk Oct 29, 2025
348d115
Merge pull request #3856 from nf-core/nvnieuwk-patch-1
mashehu Oct 31, 2025
de08834
add optional link to blogpost to sync PR (#3852)
mashehu Oct 31, 2025
8b60a83
Update dependency textual-dev to v1.8.0
renovate[bot] Nov 1, 2025
549e9ad
Update pre-commit hook astral-sh/ruff-pre-commit to v0.14.3
renovate[bot] Nov 1, 2025
2b52b01
[automated] Update CHANGELOG.md
nf-core-bot Nov 1, 2025
0c46390
[automated] Update CHANGELOG.md
nf-core-bot Nov 1, 2025
c47c939
Merge pull request #3861 from nf-core/renovate/astral-sh-ruff-pre-com…
mashehu Nov 3, 2025
df0c150
Merge branch 'dev' into renovate/textual-dev-1.x
mashehu Nov 3, 2025
884c849
Merge pull request #3860 from nf-core/renovate/textual-dev-1.x
mashehu Nov 3, 2025
98ee5aa
Change GitHub Codespaces badge style
maxulysse Nov 4, 2025
e897920
[automated] Update CHANGELOG.md
nf-core-bot Nov 4, 2025
3a6c87b
add note about changed PR closing behaviour
mashehu Nov 4, 2025
40cca1f
Re-set aws.client.anonymous to fix pipeline submissions (irl) on AWS …
FriederikeHanssen Nov 4, 2025
fd704b7
Merge pull request #3851 from mashehu/keep-sync-pr-open
mashehu Nov 5, 2025
8b8aa46
Merge pull request #3869 from nf-core/maxulysse-patch-4
maxulysse Nov 5, 2025
b245817
ignore nf-core components during prettier linting
mashehu Oct 31, 2025
ce886ff
[automated] Update CHANGELOG.md
nf-core-bot Oct 31, 2025
7eb53cd
Add files_unchanged section to template_features.yml
mashehu Nov 5, 2025
7410a74
[automated] Update CHANGELOG.md
nf-core-bot Nov 5, 2025
abd7d45
Update CHANGELOG.md
mashehu Nov 5, 2025
47b7050
remove trailing comas from nextflow_schema.json
mirpedrol Nov 5, 2025
d9ff872
[automated] Update CHANGELOG.md
nf-core-bot Nov 5, 2025
564be56
Make bump version snapshot test more stable
rrahn Nov 3, 2025
e171533
Make test params file more stable
rrahn Nov 3, 2025
460fc46
Make test rocrate more stable
rrahn Nov 3, 2025
159b5ed
[automated] Update CHANGELOG.md
nf-core-bot Nov 5, 2025
e1983a4
Merge branch 'dev' into fix-nextflow-schema
mashehu Nov 5, 2025
df79d3f
Merge pull request #3874 from mirpedrol/fix-nextflow-schema
mirpedrol Nov 5, 2025
db8ee04
Update GitHub Actions (major) (#3849)
renovate[bot] Nov 5, 2025
2b2c951
Update python:3.14-slim Docker digest to 4ed3310 (#3862)
renovate[bot] Nov 5, 2025
0abee45
Merge pull request #3840 from JulianFlesch/issues/3837-impr-devcontainer
JulianFlesch Nov 5, 2025
8fe0b10
Read authentication status from gh_api
JulianFlesch Nov 6, 2025
bbd9f4d
Remove Redundat downloading of workflow files in favor of single requ…
JulianFlesch Nov 6, 2025
547b84a
Merge branch 'dev' into jpfeuffer-patch-1
JulianFlesch Nov 6, 2025
f457472
linting: update json schema store URL
mashehu Nov 6, 2025
659c624
[automated] Update CHANGELOG.md
nf-core-bot Nov 6, 2025
6300095
Merge branch 'dev' into jpfeuffer-patch-1
mashehu Nov 6, 2025
7599ad5
Remove the authenticated flag. Authentication will be checked automat…
JulianFlesch Nov 6, 2025
475f83b
Remove duplicated code section
JulianFlesch Nov 6, 2025
1b9d9b5
Merge branch 'jpfeuffer-patch-1' of github.com:nf-core/tools into jpf…
JulianFlesch Nov 6, 2025
21703d8
Fix download urls for tests that run with github auth. Adds an unauth…
JulianFlesch Nov 6, 2025
59053c8
Remove removed 'authenticated' param from test call
JulianFlesch Nov 6, 2025
beef14f
Fix test with with noauth by unsetting gh_api.auth
JulianFlesch Nov 6, 2025
e54481b
Fix typo
JulianFlesch Nov 6, 2025
8ff5da6
Merge pull request #3607 from nf-core/jpfeuffer-patch-1
JulianFlesch Nov 6, 2025
f0786c4
Update docker/setup-qemu-action digest to c7c5346 (#3875)
renovate[bot] Nov 6, 2025
c9d3b76
chore(deps): update python:3.14-slim docker digest to 9813eec (#3880)
renovate[bot] Nov 7, 2025
a4be08d
Preserve the value of self.modules_repo across nested calls
muffato Nov 7, 2025
fb80c7d
[automated] Update CHANGELOG.md
nf-core-bot Nov 7, 2025
9a0ba10
sync: Avoid deleting files ignored by git during `pipelines sync` (#3…
tgelafr-pfzr Nov 7, 2025
ea2e079
chore(deps): update pre-commit hook astral-sh/ruff-pre-commit to v0.14.4
renovate[bot] Nov 7, 2025
cc2f59f
[automated] Update CHANGELOG.md
nf-core-bot Nov 7, 2025
9da30b8
add missing setup steps to snapshot update action
mashehu Nov 7, 2025
fbae502
remove redundant if clause
mashehu Nov 7, 2025
e535731
[automated] Update CHANGELOG.md
nf-core-bot Nov 7, 2025
7dff5ee
Update dependency textual to v6.5.0 (#3859)
renovate[bot] Nov 7, 2025
8bce834
update multiqc to 1.32
mashehu Nov 6, 2025
b81aa25
[automated] Update CHANGELOG.md
nf-core-bot Nov 6, 2025
5ab6be3
update snapshots
mashehu Nov 7, 2025
c2151f1
fix sync test
mashehu Nov 7, 2025
d096856
[automated] Update CHANGELOG.md
nf-core-bot Nov 7, 2025
e101b4f
Add Changelog entry for PR 3607
JulianFlesch Nov 6, 2025
b4b6a73
fix syntax in dockerfile for devcontainer (#3887)
mashehu Nov 10, 2025
6caa62e
Added a test
muffato Nov 11, 2025
ac1b9cd
Change information on the create pipeline landing page
jfy133 Nov 11, 2025
18f4c01
[automated] Update CHANGELOG.md
nf-core-bot Nov 11, 2025
0049c6d
one more fix forsyntax in dockerfile for devcontainer
mashehu Nov 11, 2025
772b846
Update dependency textual to v6.6.0 (#3892)
renovate[bot] Nov 11, 2025
6cd0a50
chore(deps): update mcr.microsoft.com/devcontainers/miniconda docker …
renovate[bot] Nov 11, 2025
9c306ee
chore(deps): update mcr.microsoft.com/devcontainers/base:debian docke…
renovate[bot] Nov 11, 2025
ea63385
Fix pytest matrix setup (#3888)
rrahn Nov 12, 2025
ff03f25
add word boundary for input, output and topic linting
mashehu Nov 11, 2025
dae176a
[automated] Update CHANGELOG.md
nf-core-bot Nov 11, 2025
a596d23
Update tests/modules/lint/test_main_nf.py
mashehu Nov 12, 2025
e8b2f8e
Update pre-commit hook astral-sh/ruff-pre-commit to v0.14.5 (#3900)
renovate[bot] Nov 13, 2025
e3acb62
Merge pull request #3881 from nf-core/modules_repo_nested_crossorg_su…
muffato Nov 14, 2025
d52b8b3
Enable CI tests for pipelines/download
rrahn Nov 13, 2025
b9f76e0
Fix docker test
rrahn Nov 13, 2025
734a5e7
Ensure container out dir exists
rrahn Nov 13, 2025
bae5110
Fix singularity error
rrahn Nov 13, 2025
5b78790
add linting of topics
mirpedrol Nov 14, 2025
8cf07f5
[automated] Update CHANGELOG.md
nf-core-bot Nov 14, 2025
062725c
apply new topics structure to test fixtures
mashehu Nov 17, 2025
3619d80
Fix GH API rate limits. (#3895)
rrahn Nov 17, 2025
36a5c73
Merge pull request #3902 from mirpedrol/fix-eval-parsing
mirpedrol Nov 17, 2025
01acabf
Swich to Apptainer in pytest
rrahn Nov 18, 2025
ef733fa
Update actions/checkout digest to 93cb6ef (#3906)
renovate[bot] Nov 18, 2025
530a7a1
devcontainer: Set `moby: false` to fix trixie builds (#3904)
mashehu Nov 18, 2025
55b39f6
Merge branch 'dev' into get-wf-files-ignores
mashehu Nov 18, 2025
d823888
Fix LSP warnings on pipeline template (#3905)
dialvarezs Nov 18, 2025
85f2444
Merge branch 'dev' into update-pipeline-creation-info
mashehu Nov 18, 2025
9200da2
Merge pull request #3820 from jmuhlich/get-wf-files-ignores
mirpedrol Nov 18, 2025
a4ab3e9
devcontainer: downgrade to debian 12 and revert `moby:false` (#3907)
mashehu Nov 18, 2025
b4f5d50
Merge pull request #3898 from rrahn/fix/pipelines-download-tests
MatthiasZepper Nov 18, 2025
c7c62a2
update textual snapshot
mirpedrol Nov 18, 2025
b3ad622
modules lint: handle meta.ymls without topics field (#3909)
mashehu Nov 18, 2025
65dd582
Merge pull request #3891 from jfy133/update-pipeline-creation-info
mirpedrol Nov 18, 2025
c40d998
bump to 3.5.0 (#3884)
mashehu Nov 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
sync: Avoid deleting files ignored by git during pipelines sync (#3847
)

Co-authored-by: Tatiana Gelaf Romer <[email protected]>
Co-authored-by: nf-core-bot <[email protected]>
Co-authored-by: René Rahn <[email protected]>
  • Loading branch information
4 people authored Nov 7, 2025
commit 9a0ba106653fd0eb499190c9810b9b5a1669b61a
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- Update python:3.14-slim Docker digest to 4ed3310 ([#3862](https://github.com/nf-core/tools/pull/3862))
- Update dependency textual-dev to v1.8.0 ([#3860](https://github.com/nf-core/tools/pull/3860))
- Update pre-commit hook astral-sh/ruff-pre-commit to v0.14.3 ([#3861](https://github.com/nf-core/tools/pull/3861))
- Avoid deleting files ignored by git during `pipelines sync` ([#3847](https://github.com/nf-core/tools/pull/3847))
- remove trailing comas from nextflow_schema.json ([#3874](https://github.com/nf-core/tools/pull/3874))
- Make bump-version snapshot test more stable ([#3865](https://github.com/nf-core/tools/pull/3865))
- Update docker/setup-qemu-action digest to c7c5346 ([#3875](https://github.com/nf-core/tools/pull/3875))
Expand Down
81 changes: 65 additions & 16 deletions nf_core/pipelines/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import logging
import os
import re
import shutil
from pathlib import Path
from typing import Any

Expand Down Expand Up @@ -142,7 +141,7 @@ def sync(self) -> None:
self.inspect_sync_dir()
self.get_wf_config()
self.checkout_template_branch()
self.delete_template_branch_files()
self.delete_tracked_template_branch_files()
self.make_template_pipeline()
self.commit_template_changes()

Expand Down Expand Up @@ -195,9 +194,14 @@ def inspect_sync_dir(self):
# Check to see if there are uncommitted changes on current branch
if self.repo.is_dirty(untracked_files=True):
raise SyncExceptionError(
"Uncommitted changes found in pipeline directory!\nPlease commit these before running nf-core pipelines sync"
"Uncommitted changes found in pipeline directory!\n"
"Please commit these before running nf-core pipelines sync.\n"
"(Hint: .gitignored files are ignored.)"
)

# Track ignored files to avoid processing them
self.ignored_files = self._get_ignored_files()

def get_wf_config(self):
"""Check out the target branch if requested and fetch the nextflow config.
Check that we have the required config variables.
Expand Down Expand Up @@ -241,25 +245,51 @@ def checkout_template_branch(self):
except GitCommandError:
raise SyncExceptionError("Could not check out branch 'origin/TEMPLATE' or 'TEMPLATE'")

def delete_template_branch_files(self):
def delete_tracked_template_branch_files(self):
"""
Delete all files in the TEMPLATE branch
Delete all tracked files and subsequent empty directories in the TEMPLATE branch
"""
# Delete everything
log.info("Deleting all files in 'TEMPLATE' branch")
for the_file in os.listdir(self.pipeline_dir):
if the_file == ".git":
continue
file_path = os.path.join(self.pipeline_dir, the_file)
# Delete tracked files
log.info("Deleting tracked files in 'TEMPLATE' branch")
self._delete_tracked_files()
self._clean_up_empty_dirs()

def _delete_tracked_files(self):
"""
Delete all tracked files in the repository
"""
for the_file in self._get_tracked_files():
file_path = Path(self.pipeline_dir) / the_file
log.debug(f"Deleting {file_path}")
try:
if os.path.isfile(file_path):
os.unlink(file_path)
elif os.path.isdir(file_path):
shutil.rmtree(file_path)
file_path.unlink()
except Exception as e:
raise SyncExceptionError(e)

def _clean_up_empty_dirs(self):
"""
Delete empty directories in the repository

Walks the directory tree from the bottom up, deleting empty directories as it goes.
"""
# Track deleted child directories so we know they've been deleted when evaluating if the parent is empty
deleted = set()

for curr_dir, sub_dirs, files in os.walk(self.pipeline_dir, topdown=False):
# Don't delete the root directory (should never happen due to .git, but just in case)
if curr_dir == str(self.pipeline_dir):
continue

subdir_set = set(Path(curr_dir) / d for d in sub_dirs)
currdir_is_empty = (len(subdir_set - deleted) == 0) and (len(files) == 0)
if currdir_is_empty:
log.debug(f"Deleting empty directory {curr_dir}")
try:
Path(curr_dir).rmdir()
except Exception as e:
raise SyncExceptionError(e)
deleted.add(Path(curr_dir))

def make_template_pipeline(self):
"""
Delete all files and make a fresh template using the workflow variables
Expand Down Expand Up @@ -312,7 +342,10 @@ def commit_template_changes(self):
return False
# Commit changes
try:
self.repo.git.add(A=True)
newly_ignored_files = self._get_ignored_files()
# add and commit all files except self.ignored_files
# :! syntax to exclude files using git pathspec
self.repo.git.add([f":!{f}" for f in self.ignored_files if f not in newly_ignored_files], all=True)
self.repo.index.commit(f"Template update for nf-core/tools version {nf_core.__version__}")
self.made_changes = True
log.info("Committed changes to 'TEMPLATE' branch")
Expand Down Expand Up @@ -441,3 +474,19 @@ def reset_target_dir(self):
self.repo.git.checkout(self.original_branch)
except GitCommandError as e:
raise SyncExceptionError(f"Could not reset to original branch `{self.original_branch}`:\n{e}")

def _get_ignored_files(self) -> list[str]:
"""
Get a list of all files in the repo ignored by git.
"""
# -z separates with \0 and makes sure special characters are handled correctly
raw_ignored_files = self.repo.git.ls_files(z=True, ignored=True, others=True, exclude_standard=True)
return raw_ignored_files.split("\0")[:-1] if raw_ignored_files else []

def _get_tracked_files(self) -> list[str]:
"""
Get a list of all files in the repo tracked by git.
"""
# -z separates with \0 and makes sure special characters are handled correctly
raw_tracked_files = self.repo.git.ls_files(z=True)
return raw_tracked_files.split("\0")[:-1] if raw_tracked_files else []
191 changes: 182 additions & 9 deletions tests/pipelines/test_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ def json(self):
return self.data


def mocked_requests_get(url) -> MockResponse:
"""Helper function to emulate POST requests responses from the web"""
def mocked_requests_get(url, params=None, **kwargs) -> MockResponse:
"""Helper function to emulate GET request responses from the web"""

url_template = "https://api.github.com/repos/{}/response/"
if url == Path(url_template.format("no_existing_pr"), "pulls?head=TEMPLATE&base=None"):
Expand Down Expand Up @@ -120,6 +120,17 @@ def test_inspect_sync_dir_dirty(self):
finally:
os.remove(test_fn)

def test_inspect_sync_ignored_files(self):
"""
Try inspecting the repo for syncing with untracked changes that are ignored.
No assertions, we are checking that no exception is raised in the process.
"""
test_fn = Path(self.pipeline_dir) / "ignored.txt"
self._make_ignored_file(test_fn)

psync = nf_core.pipelines.sync.PipelineSync(self.pipeline_dir)
psync.inspect_sync_dir()

def test_get_wf_config_no_branch(self):
"""Try getting a workflow config when the branch doesn't exist"""
# Try to sync, check we halt with the right error
Expand Down Expand Up @@ -161,24 +172,134 @@ def test_checkout_template_branch_no_template(self):
psync.checkout_template_branch()
assert exc_info.value.args[0] == "Could not check out branch 'origin/TEMPLATE' or 'TEMPLATE'"

def test_delete_template_branch_files(self):
"""Confirm that we can delete all files in the TEMPLATE branch"""
def test_delete_tracked_template_branch_files(self):
"""Confirm that we can delete all tracked files in the TEMPLATE branch"""
psync = nf_core.pipelines.sync.PipelineSync(self.pipeline_dir)
psync.inspect_sync_dir()
psync.get_wf_config()
psync.checkout_template_branch()
psync.delete_tracked_template_branch_files()
top_level_ignored = self._get_top_level_ignored(psync)
assert set(os.listdir(self.pipeline_dir)) == set([".git"]).union(top_level_ignored)

def test_delete_tracked_template_branch_files_unlink_throws_error(self):
"""Test that SyncExceptionError is raised when os.unlink throws an exception"""
psync = nf_core.pipelines.sync.PipelineSync(self.pipeline_dir)
psync.inspect_sync_dir()
psync.get_wf_config()
psync.checkout_template_branch()

# Create a test file that would normally be deleted
test_file = Path(self.pipeline_dir) / "test_file.txt"
test_file.touch()

# Mock os.unlink to raise an exception
with mock.patch("os.unlink", side_effect=OSError("Permission denied")) as mock_unlink:
with pytest.raises(nf_core.pipelines.sync.SyncExceptionError) as exc_info:
psync.delete_tracked_template_branch_files()

# Verify the exception contains the original error
assert "Permission denied" in str(exc_info.value)

# Verify os.unlink was called
mock_unlink.assert_called()

def test_delete_tracked_template_branch_rmdir_throws_error(self):
"""Test that SyncExceptionError is raised when os.rmdir throws an exception"""
psync = nf_core.pipelines.sync.PipelineSync(self.pipeline_dir)
psync.inspect_sync_dir()
psync.get_wf_config()
psync.checkout_template_branch()

# Create an empty directory that would normally be deleted
empty_dir = Path(self.pipeline_dir) / "empty_test_dir"
empty_dir.mkdir()

# Mock os.rmdir to raise an exception
with mock.patch("os.rmdir", side_effect=OSError("Permission denied")) as mock_rmdir:
with pytest.raises(nf_core.pipelines.sync.SyncExceptionError) as exc_info:
psync.delete_tracked_template_branch_files()

# Verify the exception contains the original error
assert "Permission denied" in str(exc_info.value)

# Verify os.rmdir was called
mock_rmdir.assert_called()

def test_delete_staged_template_branch_files_ignored(self):
"""Confirm that files in .gitignore are not deleted by delete_staged_template_branch_files"""
psync = nf_core.pipelines.sync.PipelineSync(self.pipeline_dir)

ignored_file = Path(self.pipeline_dir) / "ignored.txt"
self._make_ignored_file(ignored_file)

psync.inspect_sync_dir()
psync.get_wf_config()
psync.checkout_template_branch()
psync.delete_tracked_template_branch_files()

# Ignored file should still exist
assert ignored_file.exists()

# .git directory should still exist
assert (Path(self.pipeline_dir) / ".git").exists()

def test_delete_staged_template_branch_files_ignored_nested_dir(self):
"""Confirm that deletion of ignored files respects directory structure"""
psync = nf_core.pipelines.sync.PipelineSync(self.pipeline_dir)
repo = git.Repo(self.pipeline_dir)

# Create this structure:
# dir
# ├── subdirA # (should be kept)
# │   └── subdirB # (should be kept)
# │   └── ignored.txt # add to .gitignore (should be kept)
# └── subdirC # (should be deleted)
# └── subdirD # (should be deleted)
# └── not_ignored.txt # commit this file (should be deleted)
parent_dir = Path(self.pipeline_dir) / "dir"
to_be_kept_dir = parent_dir / "subdirA" / "subdirB"
ignored_file = to_be_kept_dir / "ignored.txt"
to_be_deleted_dir = parent_dir / "subdirC" / "subdirD"
non_ignored_file = to_be_deleted_dir / "not_ignored.txt"

to_be_kept_dir.mkdir(parents=True)
to_be_deleted_dir.mkdir(parents=True)
non_ignored_file.touch()

repo.git.add(non_ignored_file)
repo.index.commit("Add non-ignored file")

self._make_ignored_file(ignored_file)

psync.inspect_sync_dir()
psync.get_wf_config()
psync.checkout_template_branch()
psync.delete_template_branch_files()
assert os.listdir(self.pipeline_dir) == [".git"]
psync.delete_tracked_template_branch_files()

# Ignored file and its parent directory should still exist
assert ignored_file.exists()
assert to_be_kept_dir.exists() # subdirB
assert to_be_kept_dir.parent.exists() # subdirA

# Non-ignored file and its parent directory should be deleted
assert not non_ignored_file.exists()
assert not to_be_deleted_dir.exists() # subdirD
assert not to_be_deleted_dir.parent.exists() # subdirC

# .git directory should still exist
assert (Path(self.pipeline_dir) / ".git").exists()

def test_create_template_pipeline(self):
"""Confirm that we can delete all files in the TEMPLATE branch"""
"""Confirm that we can create a new template pipeline in an empty directory"""
# First, delete all the files
psync = nf_core.pipelines.sync.PipelineSync(self.pipeline_dir)
psync.inspect_sync_dir()
psync.get_wf_config()
psync.checkout_template_branch()
psync.delete_template_branch_files()
assert os.listdir(self.pipeline_dir) == [".git"]
psync.delete_tracked_template_branch_files()
top_level_ignored = self._get_top_level_ignored(psync)
assert set(os.listdir(self.pipeline_dir)) == set([".git"]).union(top_level_ignored)
# Now create the new template
psync.make_template_pipeline()
assert "main.nf" in os.listdir(self.pipeline_dir)
Expand Down Expand Up @@ -211,6 +332,22 @@ def test_commit_template_changes_changes(self):
# Check that we don't have any uncommitted changes
assert psync.repo.is_dirty(untracked_files=True) is False

def test_commit_template_preserves_ignored(self):
"""Try to commit the TEMPLATE branch, but no changes were made"""
# Check out the TEMPLATE branch but skip making the new template etc.
psync = nf_core.pipelines.sync.PipelineSync(self.pipeline_dir)

ignored_file = Path(self.pipeline_dir) / "ignored.txt"

self._make_ignored_file(ignored_file)

psync.inspect_sync_dir()
psync.get_wf_config()
psync.checkout_template_branch()
psync.commit_template_changes()

assert ignored_file.exists()

def test_push_template_branch_error(self):
"""Try pushing the changes, but without a remote (should fail)"""
# Check out the TEMPLATE branch but skip making the new template etc.
Expand Down Expand Up @@ -365,3 +502,39 @@ def test_sync_no_github_token(self):
with self.assertRaises(nf_core.pipelines.sync.PullRequestExceptionError) as exc_info:
psync.sync()
self.assertIn("GITHUB_AUTH_TOKEN not set!", str(exc_info.exception))

def test_sync_preserves_ignored_files(self):
"""Test that sync preserves files and directories specified in .gitignore"""
with (
mock.patch("requests.get", side_effect=mocked_requests_get),
):
psync = nf_core.pipelines.sync.PipelineSync(self.pipeline_dir)

ignored_file = Path(self.pipeline_dir) / "ignored.txt"
self._make_ignored_file(ignored_file)

psync.made_changes = True

psync.sync()

self.assertTrue(ignored_file.exists())

def _make_ignored_file(self, file_path: Path):
"""Helper function to create an ignored file."""
if not self.pipeline_dir:
raise ValueError("Instantiate a pipeline before adding ignored files.")

file_path.touch()

gitignore_path = Path(self.pipeline_dir) / ".gitignore"
with open(gitignore_path, "a") as f:
f.write(f"{file_path.name}\n")

repo = git.Repo(self.pipeline_dir)
repo.git.add(".gitignore")
repo.index.commit("Add .gitignore")

def _get_top_level_ignored(self, psync: nf_core.pipelines.sync.PipelineSync) -> set[str]:
"""Helper function to get top-level part of relative directory of ignored files from psync.ignored_files."""
top_level_ignored = {Path(f).parts[0] for f in psync.ignored_files}
return top_level_ignored
Loading