Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Keep workflow files LF-terminated regardless of the checkout platform's
# core.autocrlf setting. Windows runners otherwise check these out with
# CRLF, and GitHub Actions embeds a `run:` block's raw bytes verbatim into
# the generated shell script -- Cygwin's bash (unlike Git Bash) rejects a
# CRLF-terminated line like "set +e\r" with "invalid option".
.github/workflows/*.yml text eol=lf
.github/workflows/*.yaml text eol=lf
2 changes: 2 additions & 0 deletions .github/workflows/run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ jobs:
- run: dfetch update
- run: dfetch update
- run: dfetch update-patch
- run: dfetch replay-patches
- run: dfetch format-patch
- run: dfetch report -t sbom
- run: dfetch remove test-repo
Expand Down Expand Up @@ -211,6 +212,7 @@ jobs:
- run: dfetch update
- run: dfetch update
- run: dfetch update-patch
- run: dfetch replay-patches
- run: dfetch format-patch
- run: dfetch report -t sbom
- run: dfetch remove test-repo
Expand Down
6 changes: 5 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
Release 0.15.0 (unreleased)
==============================

* Add ``replay-patches`` command to step through patch contributions interactively (#1290)

Release 0.14.4 (released 2026-08-28)
====================================

Expand All @@ -17,7 +22,6 @@ Release 0.14.2 (released 2026-06-21)

Release 0.14.1 (released 2026-06-19)
====================================

* Implement C-043: add ``pip-audit`` OSV gate to the release workflow
* Add CRA Compliance: OSCAL 1.2.2 Component Definition
* Fix ``dfetch import`` mangling the namespace of a generic VCS URL whose path contains ``.git`` (#1268)
Expand Down
2 changes: 2 additions & 0 deletions dfetch/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import dfetch.commands.import_
import dfetch.commands.init
import dfetch.commands.remove
import dfetch.commands.replay_patches
import dfetch.commands.report
import dfetch.commands.update
import dfetch.commands.update_patch
Expand Down Expand Up @@ -56,6 +57,7 @@ def create_parser() -> argparse.ArgumentParser:
dfetch.commands.remove.Remove.create_menu(subparsers)
dfetch.commands.report.Report.create_menu(subparsers)
dfetch.commands.update.Update.create_menu(subparsers)
dfetch.commands.replay_patches.ReplayPatches.create_menu(subparsers)
dfetch.commands.update_patch.UpdatePatch.create_menu(subparsers)
dfetch.commands.validate.Validate.create_menu(subparsers)

Expand Down
26 changes: 26 additions & 0 deletions dfetch/commands/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@
import sys
from abc import ABC, abstractmethod
from argparse import ArgumentParser # pylint: disable=unused-import
from collections.abc import Callable
from typing import TYPE_CHECKING, TypeVar

from dfetch.log import get_logger
from dfetch.manifest.project import ProjectEntry
from dfetch.project.superproject import SuperProject
from dfetch.util.util import in_directory

if TYPE_CHECKING and sys.version_info >= (3, 10):
from typing import TypeAlias

Expand All @@ -18,6 +24,8 @@
argparse._SubParsersAction # pyright: ignore[reportPrivateUsage] #pylint: disable=protected-access
)

_command_logger = get_logger(__name__)


def pascal_to_kebab(name: str) -> str:
"""Insert a dash before each uppercase letter (except the first) and lowercase everything."""
Expand Down Expand Up @@ -60,6 +68,24 @@ def __call__(self, args: argparse.Namespace) -> None:
NotImplementedError: This is an abstract method that should be implemented by a subclass.
"""

@staticmethod
def _iter_projects(
superproject: SuperProject,
project_names: list[str],
process: Callable[[ProjectEntry], None],
) -> None:
"""Iterate over selected projects, log per-project errors, re-raise if any failed."""
had_errors = False
with in_directory(superproject.root_directory):
for project in superproject.manifest.selected_projects(project_names):
try:
process(project)
except RuntimeError as exc:
_command_logger.print_error_line(project.name, str(exc))
had_errors = True
if had_errors:
raise RuntimeError()

@staticmethod
def parser(
subparsers: SubparserActionType,
Expand Down
Loading
Loading