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
5 changes: 2 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ jobs:
# MANDATORY CHECKS USING CURRENT DEVELOPMENT INTERPRETER
- dependencies: >
libatomic
pylint
python3-dbus
python3-dbus-python-client-gen
python3-matplotlib
Expand All @@ -28,10 +27,10 @@ jobs:
python3-requests
python3-semantic_version
python3-specfile
ruff
task: PATH=${PATH}:/github/home/.local/bin make -f Makefile lint
- dependencies: >
black
python3-isort
ruff
shfmt
task: make -f Makefile fmt-travis
- dependencies: >
Expand Down
14 changes: 0 additions & 14 deletions .isort.cfg

This file was deleted.

27 changes: 5 additions & 22 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,35 +1,18 @@
.PHONY: lint
lint:
pylint ./blackbox/parse_json.py
pylint ./dependency_management/* --disable=R0801
pylint ./misc_scripts/*.py --disable=R0801
pylint ./release_management/*.py --disable=R0801
ruff check
pyright

.PHONY: fmt
fmt:
isort ./blackbox/parse_json.py
isort ./dependency_management/*
isort ./misc_scripts/
isort ./release_management/*
black ./blackbox/parse_json.py
black ./dependency_management/
black ./dependency_management/compare_fedora_versions
black ./misc_scripts/*.py
black ./release_management/
ruff check --fix --select I
ruff format
shfmt -l -w .

.PHONY: fmt-travis
fmt-travis:
isort --diff --check-only ./blackbox/parse_json.py
isort --diff --check-only ./dependency_management/*
isort --diff --check-only ./misc_scripts/
isort --diff --check-only ./release_management/*
black ./blackbox/parse_json.py --check
black ./dependency_management/ --check
black ./dependency_management/compare_fedora_versions --check
black ./misc_scripts/*.py --check
black ./release_management/ --check
ruff check --select I
ruff format --check
shfmt -d .
Comment on lines 12 to 16
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

ruff format --check flag documentation behavior

💡 Result:

The ruff format --check flag checks if Python files need formatting according to Ruff's rules without modifying them. It exits with exit code 0 if all files are already correctly formatted (no changes needed), 1 if one or more files would be reformatted (indicating formatting issues), and 2 for errors like invalid configuration or internal issues. This is designed for CI/CD pipelines to detect unformatted code without altering files. Without --check, ruff format formats files in-place and exits 0 on success (even if changes were made) or 2 on error. For full options, run ruff format --help.

Citations:


🏁 Script executed:

cat -n Makefile

Repository: stratis-storage/ci

Length of output: 587


Use ruff format --check in fmt-travis.

The fmt-travis target is a CI validation step (note shfmt -d . uses check-only mode). However, ruff format rewrites files in-place by default and exits successfully even if changes were made, causing the CI check to pass without detecting formatting drift. Switch to ruff format --check to properly validate formatting in CI without modifying files.

🔧 Proposed fix
 fmt-travis:
 	ruff check --select I
-	ruff format
+	ruff format --check
 	shfmt -d .
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.PHONY: fmt-travis
fmt-travis:
isort --diff --check-only ./blackbox/parse_json.py
isort --diff --check-only ./dependency_management/*
isort --diff --check-only ./misc_scripts/
isort --diff --check-only ./release_management/*
black ./blackbox/parse_json.py --check
black ./dependency_management/ --check
black ./dependency_management/compare_fedora_versions --check
black ./misc_scripts/*.py --check
black ./release_management/ --check
ruff check --select I
ruff format
shfmt -d .
.PHONY: fmt-travis
fmt-travis:
ruff check --select I
ruff format --check
shfmt -d .
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Makefile` around lines 12 - 16, The fmt-travis Makefile target currently runs
"ruff format" which mutates files and won't fail CI on formatting drift; change
the command in the fmt-travis recipe to run "ruff format --check" instead so
Ruff runs in check-only mode (leave shfmt -d . as-is); update the fmt-travis
target invocation that references ruff format to ruff format --check to ensure
CI detects formatting issues.


.PHONY: yamllint
Expand Down
13 changes: 3 additions & 10 deletions dependency_management/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
to the versions of dependencies available on Fedora Rawhide.
"""


# isort: STDLIB
import json
import re
Expand Down Expand Up @@ -57,7 +56,7 @@ def build_koji_repo_dict(crates, release):

release_number = int(release[1:])

if release_number < 34:
if release_number < 34: # noqa: PLR2004
raise RuntimeError(
f"release number must be at least 34, was {release_number}"
)
Expand All @@ -72,7 +71,7 @@ def build_koji_repo_dict(crates, release):
)

requests_var = requests.get(url, timeout=30)
if requests_var.status_code != 200:
if requests_var.status_code != requests.codes.ok:
raise RuntimeError(f"Page at URL {url} not found")

packages = requests_var.text
Expand Down Expand Up @@ -106,13 +105,7 @@ def build_cargo_metadata(manifest_path, *, skip_path=False):
:returns: a dict mapping crate name to version specification
:rtype: str * SimpleSpec
"""
command = [
"cargo",
"metadata",
"--format-version=1",
"--no-deps",
"--all-features",
]
command = ["cargo", "metadata", "--format-version=1", "--no-deps", "--all-features"]
if manifest_path is not None:
command.append(f"--manifest-path={manifest_path}")

Expand Down
24 changes: 6 additions & 18 deletions misc_scripts/filesystem_size_estimate.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@

_TIMEOUT = 120000

# pylint: disable=invalid-name
Manager = make_class("Manager", ET.fromstring(SPECS[_MANAGER_IFACE]), _TIMEOUT)
Pool = make_class("Pool", ET.fromstring(SPECS[_POOL_IFACE]), _TIMEOUT)
Filesystem = make_class("Filesystem", ET.fromstring(SPECS[_FILESYSTEM_IFACE]), _TIMEOUT)
Expand Down Expand Up @@ -137,15 +136,8 @@ def _do_one(size, bus, pool_proxy):
if not real:
pool_used_pre = None

(
(_, (filesystems)),
return_code,
return_msg,
) = Pool.Methods.CreateFilesystems(
pool_proxy,
{
"specs": [("fs_name", (True, size))],
},
((_, (filesystems)), return_code, return_msg) = Pool.Methods.CreateFilesystems(
pool_proxy, {"specs": [("fs_name", (True, size))]}
)

if return_code != 0:
Expand Down Expand Up @@ -184,11 +176,7 @@ def _print_values(devices):

proxy = bus.get_object(_SERVICE, _TOP_OBJECT, introspect=False)

(
(_, (pool_object_path, _)),
return_code,
return_msg,
) = Manager.Methods.CreatePool(
((_, (pool_object_path, _)), return_code, return_msg) = Manager.Methods.CreatePool(
proxy,
{
"name": "pool_name",
Expand All @@ -209,9 +197,9 @@ def _print_values(devices):

(size, fs_used, pool_used_pre, pool_used_post) = _do_one(size, bus, pool_proxy)
print(
f'{size} {"ERROR" if fs_used is None else fs_used} '
f'{"ERROR" if pool_used_pre is None else pool_used_pre} '
f'{"ERROR" if pool_used_post is None else pool_used_post}'
f"{size} {'ERROR' if fs_used is None else fs_used} "
f"{'ERROR' if pool_used_pre is None else pool_used_pre} "
f"{'ERROR' if pool_used_post is None else pool_used_post}"
)

(_, return_code, return_msg) = Manager.Methods.DestroyPool(
Expand Down
12 changes: 2 additions & 10 deletions misc_scripts/filesystem_size_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,11 @@ def gen_parser():
)

parser.add_argument(
"--start",
action="store",
type=int,
default=512 * 1024**2,
help="Start value",
"--start", action="store", type=int, default=512 * 1024**2, help="Start value"
)

parser.add_argument(
"--stop",
action="store",
type=int,
default=8 * 1024**4,
help="End value",
"--stop", action="store", type=int, default=8 * 1024**4, help="End value"
)

return parser
Expand Down
4 changes: 2 additions & 2 deletions misc_scripts/thin_metadata_size_viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def gen_parser():
return parser


def build_arrays(block_size, values): # pylint: disable=too-many-locals
def build_arrays(block_size, values):
"""
Build three matrices of values where the z_values are the result of
running thin_metadata_size on the x and y values.
Expand Down Expand Up @@ -116,7 +116,7 @@ def plot_figure(x_inputs, y_inputs, z_inputs):
return fig


def main(): # pylint: disable=too-many-locals
def main():
"""
Main method
"""
Expand Down
53 changes: 17 additions & 36 deletions misc_scripts/update_introspection_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ def __str__(self):
_POOL_IFACE = "org.storage.stratis3.pool.r0"
_TIMEOUT = 120000

# pylint: disable=invalid-name
Introspectable = make_class(
"Introspectable", ET.fromstring(SPECS[_INTROSPECTABLE_IFACE]), _TIMEOUT
)
Expand All @@ -119,9 +118,7 @@ def _xml_object_to_str(xml_object: ET.Element) -> str:
return ET.tostring(xml_object).decode("utf-8").rstrip(" \n")


def setup_minimal_object_set(
bus: dbus.SystemBus,
) -> dict[ProxyType, ProxyObject]:
def setup_minimal_object_set(bus: dbus.SystemBus) -> dict[ProxyType, ProxyObject]:
"""
Set up the minimal set of objects to be introspected on.

Expand All @@ -131,19 +128,17 @@ def setup_minimal_object_set(
"""
proxy = bus.get_object(_SERVICE, "/org/storage/stratis3", introspect=False)

(
(_, (pool_object_path, dev_object_paths)),
return_code,
return_msg,
) = Manager.Methods.CreatePool(
proxy,
{
"name": "pool_name",
"redundancy": (True, 0),
"devices": ["/fake/fake"],
"key_desc": (False, ""),
"clevis_info": (False, ("", "")),
},
((_, (pool_object_path, dev_object_paths)), return_code, return_msg) = (
Manager.Methods.CreatePool(
proxy,
{
"name": "pool_name",
"redundancy": (True, 0),
"devices": ["/fake/fake"],
"key_desc": (False, ""),
"clevis_info": (False, ("", "")),
},
)
)

if return_code != 0:
Expand All @@ -153,15 +148,8 @@ def setup_minimal_object_set(

blockdev_proxy = bus.get_object(_SERVICE, dev_object_paths[0], introspect=False)

(
(_, (filesystems)),
return_code,
return_msg,
) = Pool.Methods.CreateFilesystems(
pool_proxy,
{
"specs": [("fs_name", (False, ""))],
},
((_, (filesystems)), return_code, return_msg) = Pool.Methods.CreateFilesystems(
pool_proxy, {"specs": [("fs_name", (False, ""))]}
)

if return_code != 0:
Expand Down Expand Up @@ -227,18 +215,11 @@ def _add_data(proxy_key: ProxyType, interfaces: Sequence[str]):

_add_data(ProxyType.MANAGER, [OBJECT_MANAGER_INTERFACE])

_add_data(
ProxyType.MANAGER,
get_current_interfaces(TOP_OBJECT_INTERFACE_PREFIXES),
)
_add_data(
ProxyType.POOL,
get_current_interfaces(POOL_OBJECT_INTERFACE_PREFIXES),
)
_add_data(ProxyType.MANAGER, get_current_interfaces(TOP_OBJECT_INTERFACE_PREFIXES))
_add_data(ProxyType.POOL, get_current_interfaces(POOL_OBJECT_INTERFACE_PREFIXES))

_add_data(
ProxyType.BLOCKDEV,
get_current_interfaces(BLOCKDEV_OBJECT_INTERFACE_PREFIXES),
ProxyType.BLOCKDEV, get_current_interfaces(BLOCKDEV_OBJECT_INTERFACE_PREFIXES)
)
_add_data(
ProxyType.FILESYSTEM,
Expand Down
13 changes: 13 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[tool.ruff]
target-version = "py312"
line-length = 88

[tool.ruff.lint]
select = ["PL"]

[tool.ruff.lint.isort]
known-first-party = ["dbus_python_client_gen"]
split-on-trailing-comma = false
Comment on lines +5 to +10
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Ruff lint select rule "I" isort configuration behavior

💡 Result:

In Ruff, "I" selects the isort ruleset, which includes I001 (unsorted-imports). This rule detects unsorted or unformatted import blocks and can auto-fix them via ruff check --fix. Key behavior: - Enable with [tool.ruff.lint.select] = ["I"] in pyproject.toml or CLI --select I. - Sorting and grouping follow isort-compatible settings under [tool.ruff.lint.isort], like known-first-party, section-order, force-sort-within-sections = true, length-sort = true. - ruff format does NOT sort imports; use ruff check --select I --fix followed by ruff format for full isort + formatting. - I001 fixes de-duplicate, group (stdlib/third-party/first-party/local), and sort imports per config. Some edge cases differ from isort (e.g., length-sort with force-sort-within-sections), but Ruff aims for compatibility and recommends using one tool. Example config: [tool.ruff.lint] select = ["I"] [tool.ruff.lint.isort] # Optional customizations lines-after-imports = 2 Run: ruff check --select I --fix . && ruff format .

Citations:


🏁 Script executed:

# Check if there are other ruff configurations in the codebase
fd -type f -name "pyproject.toml" -o -name "ruff.toml" | head -20

Repository: stratis-storage/ci

Length of output: 233


🏁 Script executed:

# Check ruff version and any documentation in the repo
find . -type f \( -name "*.md" -o -name "*.rst" -o -name "*.txt" \) | xargs grep -l "ruff\|isort" 2>/dev/null | head -10

Repository: stratis-storage/ci

Length of output: 44


Add "I" rule to enable import sorting.

With select = ["PL"], the [tool.ruff.lint.isort] configuration is inert. The "I" rule (isort) must be explicitly selected for import sorting checks to run.

Suggested fix
 [tool.ruff.lint]
-select = ["PL"]
+select = ["PL", "I"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[tool.ruff.lint]
select = ["PL"]
[tool.ruff.lint.isort]
known-first-party = ["dbus_python_client_gen"]
split-on-trailing-comma = false
[tool.ruff.lint]
select = ["PL", "I"]
[tool.ruff.lint.isort]
known-first-party = ["dbus_python_client_gen"]
split-on-trailing-comma = false
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pyproject.toml` around lines 5 - 10, The Ruff config currently sets select =
["PL"] which omits the isort rule, so import-sorting checks under
[tool.ruff.lint.isort] won't run; update the Ruff selection to include the "I"
rule (e.g., add "I" to the select array) so isort is enabled and your
[tool.ruff.lint.isort] settings (known-first-party, split-on-trailing-comma)
take effect. Target the select setting under [tool.ruff.lint] in pyproject.toml
and add "I" to the list.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai I only want to use "I" in formatting step, because that is the way we have always viewed it. I don't think it's semantic as all that.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mulkieran, understood — that's a perfectly valid approach. Since the Makefile's fmt/fmt-travis targets invoke ruff check --select I explicitly on the command line, the [tool.ruff.lint.isort] settings in pyproject.toml (e.g. known-first-party, split-on-trailing-comma) will still be picked up at that point. Keeping "I" out of lint's select = ["PL"] cleanly separates formatting concerns from semantic linting concerns. My comment was overstated — no change needed here.


✏️ Learnings added
Learnt from: mulkieran
Repo: stratis-storage/ci PR: 577
File: pyproject.toml:5-10
Timestamp: 2026-04-29T13:54:55.501Z
Learning: In the stratis-storage/ci repository, import sorting (ruff "I" / isort rule) is intentionally treated as a formatting concern, not a linting concern. It is only invoked via `ruff check --select I` in the Makefile `fmt`/`fmt-travis` targets, and is deliberately excluded from `[tool.ruff.lint] select` in `pyproject.toml` (which only contains `["PL"]`). The `[tool.ruff.lint.isort]` settings in `pyproject.toml` are still effective because they are picked up whenever ruff processes isort rules via the CLI flag.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


[tool.ruff.format]
skip-magic-trailing-comma = true
14 changes: 5 additions & 9 deletions release_management/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def edit_specfile(
if release is not None:
spec.release = release
if sources is not None:
with spec.sources() as entries: # pylint: disable=not-context-manager
with spec.sources() as entries:
for index, value in enumerate(sources):
entries[index].location = value
if arbitrary is not None:
Expand Down Expand Up @@ -154,7 +154,7 @@ def get_package_info(manifest_abs_path, package_name) -> tuple[Version, Any]:

package = metadata["package"]
assert package["name"] == package_name, (
f'package name in Cargo.toml ({package["name"]}) != specified'
f"package name in Cargo.toml ({package['name']}) != specified"
f"package name ({package_name})"
)
github_repo = urlparse(package["repository"].rstrip("/"))
Expand Down Expand Up @@ -188,8 +188,7 @@ def set_tag(tag, message):
"""
if not verify_tag(tag):
subprocess.run(
["git", "tag", "--annotate", tag, f'--message="{message}"'],
check=True,
["git", "tag", "--annotate", tag, f"--message={message}"], check=True
)
Comment on lines 189 to 192
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's find and read the actual file to confirm the code
fd -t f "_utils.py" release_management/

Repository: stratis-storage/ci

Length of output: 91


🏁 Script executed:

# Read the file to see the actual code at lines 189-192
sed -n '180,200p' release_management/_utils.py

Repository: stratis-storage/ci

Length of output: 527


🏁 Script executed:

# Let's check if there are other usages of subprocess.run with git in the codebase
rg "subprocess\.run.*git" -A 2 -B 1

Repository: stratis-storage/ci

Length of output: 632


🏁 Script executed:

# Let's understand how git tag handles the --message argument with quotes
# Create a test to see the actual behavior
cat > /tmp/test_git_args.py << 'EOF'
import subprocess
import tempfile
import os

# Create a temporary git repo for testing
with tempfile.TemporaryDirectory() as tmpdir:
    os.chdir(tmpdir)
    
    # Initialize a test repo
    subprocess.run(["git", "init"], check=True, capture_output=True)
    subprocess.run(["git", "config", "user.email", "test@test.com"], check=True, capture_output=True)
    subprocess.run(["git", "config", "user.name", "Test User"], check=True, capture_output=True)
    
    # Create a test commit
    with open("test.txt", "w") as f:
        f.write("test")
    subprocess.run(["git", "add", "test.txt"], check=True, capture_output=True)
    subprocess.run(["git", "commit", "-m", "test"], check=True, capture_output=True)
    
    # Test 1: Using the problematic format from the review
    message = "Test message"
    try:
        result = subprocess.run(
            ["git", "tag", "--annotate", "test-tag-1", f'--message="{message}"'],
            check=True, capture_output=True, text=True
        )
        print("Test 1 (quoted format) succeeded")
    except subprocess.CalledProcessError as e:
        print(f"Test 1 (quoted format) failed: {e.stderr}")
    
    # Check what was stored
    try:
        result = subprocess.run(
            ["git", "tag", "-l", "test-tag-1", "-n"],
            check=True, capture_output=True, text=True
        )
        print(f"Tag content (Test 1): {repr(result.stdout)}")
    except:
        pass
    
    # Test 2: Using the suggested format
    try:
        result = subprocess.run(
            ["git", "tag", "--annotate", "test-tag-2", "-m", message],
            check=True, capture_output=True, text=True
        )
        print("Test 2 (separate argument) succeeded")
    except subprocess.CalledProcessError as e:
        print(f"Test 2 (separate argument) failed: {e.stderr}")
    
    # Check what was stored
    try:
        result = subprocess.run(
            ["git", "tag", "-l", "test-tag-2", "-n"],
            check=True, capture_output=True, text=True
        )
        print(f"Tag content (Test 2): {repr(result.stdout)}")
    except:
        pass
EOF

python /tmp/test_git_args.py

Repository: stratis-storage/ci

Length of output: 244


🏁 Script executed:

# Let's also verify how git interprets the argument format by checking git documentation
# and test with different message content to see edge cases
cat > /tmp/test_git_args_edge.py << 'EOF'
import subprocess
import tempfile
import os

# Create a temporary git repo for testing
with tempfile.TemporaryDirectory() as tmpdir:
    os.chdir(tmpdir)
    
    # Initialize a test repo
    subprocess.run(["git", "init"], check=True, capture_output=True)
    subprocess.run(["git", "config", "user.email", "test@test.com"], check=True, capture_output=True)
    subprocess.run(["git", "config", "user.name", "Test User"], check=True, capture_output=True)
    
    # Create a test commit
    with open("test.txt", "w") as f:
        f.write("test")
    subprocess.run(["git", "add", "test.txt"], check=True, capture_output=True)
    subprocess.run(["git", "commit", "-m", "test"], check=True, capture_output=True)
    
    # Test with special characters
    test_cases = [
        ("Message with spaces", "quoted_format"),
        ("Message with 'single quotes'", "quoted_format_2"),
        ("Message with special chars: !@#$%", "quoted_format_3"),
    ]
    
    for message, tag_name in test_cases:
        try:
            # Using the problematic format
            result = subprocess.run(
                ["git", "tag", "--annotate", tag_name, f'--message="{message}"'],
                check=True, capture_output=True, text=True
            )
            
            # Retrieve the tag message
            result = subprocess.run(
                ["git", "tag", "-l", tag_name, "-n"],
                check=True, capture_output=True, text=True
            )
            stored_msg = result.stdout.strip()
            print(f"Message: {repr(message)}")
            print(f"Stored:  {repr(stored_msg)}")
            print(f"Match:   {message in stored_msg}")
            print()
        except Exception as e:
            print(f"Error with message {repr(message)}: {e}")
            print()
EOF

python /tmp/test_git_args_edge.py

Repository: stratis-storage/ci

Length of output: 395


Pass the git tag message as a separate argv item.

The current code stores literal quotes in the tag message. With f'--message="{message}"', Git receives the quotes as part of the argument value and stores them literally. Use -m with message as a separate argv item instead.

Suggested fix
-        subprocess.run(
-            ["git", "tag", "--annotate", tag, f'--message="{message}"'], check=True
-        )
+        subprocess.run(["git", "tag", "--annotate", tag, "-m", message], check=True)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if not verify_tag(tag):
subprocess.run(
["git", "tag", "--annotate", tag, f'--message="{message}"'],
check=True,
["git", "tag", "--annotate", tag, f'--message="{message}"'], check=True
)
if not verify_tag(tag):
subprocess.run(["git", "tag", "--annotate", tag, "-m", message], check=True)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@release_management/_utils.py` around lines 189 - 192, The git tag command is
currently passing the message with embedded quotes (f'--message="{message}"')
which makes Git store literal quotes; update the subprocess.run call in the
block guarded by verify_tag(tag) to pass the message as a separate argv item
(e.g., use "-m", message or "--message", message) so the message is passed raw
to Git; modify the list passed to subprocess.run in the same function/block to
replace f'--message="{message}"' with "-m", message.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai What's wrong with f"--message={message}", though?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mulkieran Nothing is wrong with f"--message={message}". The problem in the original code was specifically the extra literal quote characters embedded in the f-string: f'--message="{message}"'. Because Python's subprocess passes each list element as a raw argv item (no shell involved), git receives the string --message="Release v1.0" (with the double-quotes as literal characters) and stores them verbatim in the tag annotation — so the tag message would read "Release v1.0" instead of Release v1.0.

f"--message={message}" (no embedded quotes around the value) is perfectly fine — git's --option=value long-option syntax is standard, and everything after the first = is taken as the value. That form is functionally equivalent to ["--message", message] or ["-m", message].


🧠 Learnings used
Learnt from: mulkieran
Repo: stratis-storage/ci PR: 577
File: pyproject.toml:5-10
Timestamp: 2026-04-29T13:54:55.501Z
Learning: In the stratis-storage/ci repository, import sorting (ruff "I" / isort rule) is intentionally treated as a formatting concern, not a linting concern. It is only invoked via `ruff check --select I` in the Makefile `fmt`/`fmt-travis` targets, and is deliberately excluded from `[tool.ruff.lint] select` in `pyproject.toml` (which only contains `["PL"]`). The `[tool.ruff.lint.isort]` settings in `pyproject.toml` are still effective because they are picked up whenever ruff processes isort rules via the CLI flag.



Expand Down Expand Up @@ -230,10 +229,7 @@ def create_release(
repo = git.get_repo(repository.path.strip("/"))

release = repo.create_git_release(
tag,
f"Version {release_version}",
f"See {changelog_url}",
draft=True,
tag, f"Version {release_version}", f"See {changelog_url}", draft=True
)

for asset in [] if additional_assets is None else additional_assets:
Expand Down Expand Up @@ -339,7 +335,7 @@ def get_changelog_url(repository_url, branch):
"""
changelog_url = f"{repository_url}/blob/{branch}/CHANGES.txt"
requests_var = requests.get(changelog_url, timeout=30)
if requests_var.status_code != 200:
if requests_var.status_code != requests.codes.ok:
raise RuntimeError(f"Page at URL {changelog_url} not found")

return changelog_url
14 changes: 3 additions & 11 deletions release_management/create_artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,7 @@ def _stratisd_artifacts(namespace):
)
print(os.path.relpath(source_tarfile_path))

vendor_tarfile_name = vendor(
manifest_abs_path,
release_version,
filterer=filtered,
)
vendor_tarfile_name = vendor(manifest_abs_path, release_version, filterer=filtered)

vendor_tarfile_path = os.path.join(output_path, vendor_tarfile_name)

Expand Down Expand Up @@ -171,11 +167,7 @@ def _stratis_cli_artifacts(namespace):
source_version, pre=namespace.pre_release, post=namespace.post_release
)

source_tarfile = make_source_tarball(
"stratis-cli",
release_version,
output_path,
)
source_tarfile = make_source_tarball("stratis-cli", release_version, output_path)

def remove_stratisd_requires(spec):
"""
Expand Down Expand Up @@ -207,6 +199,6 @@ def remove_stratisd_requires(spec):
if __name__ == "__main__":
try:
main()
except Exception as err: # pylint: disable=broad-except
except Exception as err:
print(err)
sys.exit(1)
Comment on lines 199 to 204
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n release_management/create_artifacts.py | sed -n '195,210p'

Repository: stratis-storage/ci

Length of output: 297


🏁 Script executed:

find . -name "pyproject.toml" -o -name "ruff.toml" -o -name ".ruff.toml" | head -5

Repository: stratis-storage/ci

Length of output: 79


🏁 Script executed:

if [ -f "pyproject.toml" ]; then
  echo "=== pyproject.toml ===" && cat pyproject.toml
elif [ -f "ruff.toml" ]; then
  echo "=== ruff.toml ===" && cat ruff.toml
elif [ -f ".ruff.toml" ]; then
  echo "=== .ruff.toml ===" && cat .ruff.toml
fi

Repository: stratis-storage/ci

Length of output: 329


🏁 Script executed:

rg -i "too-broad-exception|PL0718|broad" . --type md --type txt 2>/dev/null | head -20

Repository: stratis-storage/ci

Length of output: 44


🏁 Script executed:

# Check if there are any existing noqa comments in the file for reference
rg "noqa|pylint|ignore" release_management/create_artifacts.py

Repository: stratis-storage/ci

Length of output: 44


🏁 Script executed:

# Check git history to see if a suppression was removed
git log -p --all -S "noqa" -- release_management/create_artifacts.py 2>/dev/null | head -100

Repository: stratis-storage/ci

Length of output: 44


Add a lint suppression comment or narrow this exception handler.

The Ruff configuration enables all Pylint rules (select = ["PL"]), which includes the broad-exception rule (PL0718). The except Exception as err handler at lines 202–204 will trigger a lint failure without a suppression comment or by narrowing the exception to a more specific type.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@release_management/create_artifacts.py` around lines 199 - 204, The broad
except in the if __name__ == "__main__": block around main() triggers PL0718;
either narrow the catch to the specific exceptions main() can raise (replace
"except Exception as err:" with specific exception types returned/raised by
main()) or explicitly suppress the lint warning by adding a Pylint/Ruff
suppression on that line (e.g., append "# noqa: PL0718" or the equivalent Pylint
inline disable) while keeping the existing print(err) and sys.exit(1) behavior;
update the except line that references main() to implement one of these fixes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai ruff reviews this file and reports no errors, so I guess it's not bothered.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mulkieran, you're right — I apologize for the incorrect comment. I cited rule PL0718 which is not a valid ruff rule code (the corresponding pylint rule W0718 maps to PLW0718 in ruff), and in any case ruff's ["PL"] select doesn't flag except Exception in this file. No change is needed here.


✏️ Learnings added
Learnt from: mulkieran
Repo: stratis-storage/ci PR: 577
File: release_management/create_artifacts.py:199-204
Timestamp: 2026-04-29T13:58:22.708Z
Learning: In the stratis-storage/ci repository, ruff with `select = ["PL"]` in `pyproject.toml` does NOT flag `except Exception as err` in `__main__` guards (e.g., in `release_management/create_artifacts.py`). The broad-exception-caught rule (pylint W0718, ruff PLW0718) is not triggered by the configured rule set, so no suppression comment is needed on such handlers.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: mulkieran
Repo: stratis-storage/ci PR: 577
File: pyproject.toml:5-10
Timestamp: 2026-04-29T13:54:55.501Z
Learning: In the stratis-storage/ci repository, import sorting (ruff "I" / isort rule) is intentionally treated as a formatting concern, not a linting concern. It is only invoked via `ruff check --select I` in the Makefile `fmt`/`fmt-travis` targets, and is deliberately excluded from `[tool.ruff.lint] select` in `pyproject.toml` (which only contains `["PL"]`). The `[tool.ruff.lint.isort]` settings in `pyproject.toml` are still effective because they are picked up whenever ruff processes isort rules via the CLI flag.

Loading