Skip to content

Commit

Permalink
fix: improve error message from snapd pack (#5188)
Browse files Browse the repository at this point in the history
Signed-off-by: Sergio Schvezov <[email protected]>
Co-authored-by: Imani Pelton <[email protected]>
  • Loading branch information
sergiusens and bepri authored Dec 20, 2024
1 parent 0dd97ef commit a9fc440
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 9 deletions.
9 changes: 9 additions & 0 deletions docs/reference/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ Changelog

For a complete list of commits, check out the `X.Y.Z`_ release on GitHub.

8.6.0 (2025-Jan-20)
-------------------

Core
====

* Improved error messaging when unable to pack.


8.5.1 (2024-Dec-17)
-------------------

Expand Down
2 changes: 1 addition & 1 deletion requirements-devel.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ craft-archives==2.0.1
# via
# craft-application
# snapcraft (setup.py)
craft-cli==2.12.0
craft-cli==2.13.0
# via
# craft-application
# snapcraft (setup.py)
Expand Down
2 changes: 1 addition & 1 deletion requirements-docs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ craft-archives==2.0.1
# via
# craft-application
# snapcraft (setup.py)
craft-cli==2.12.0
craft-cli==2.13.0
# via
# craft-application
# snapcraft (setup.py)
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ craft-archives==2.0.1
# via
# craft-application
# snapcraft (setup.py)
craft-cli==2.12.0
craft-cli==2.13.0
# via
# craft-application
# snapcraft (setup.py)
Expand Down
23 changes: 23 additions & 0 deletions snapcraft/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

"""Snapcraft error definitions."""

import subprocess
from typing import Optional

from craft_cli import CraftError
Expand Down Expand Up @@ -180,3 +181,25 @@ class SnapcraftAssertionError(SnapcraftError):
Not to be confused with Python's built-in AssertionError.
"""


class SnapPackError(SnapcraftError):
"""Snapd packing error."""

def _get_error_string_from_stderr(self, stderr: str | None) -> str | None:
if stderr is None:
return "snapd did not report an error"

error_lines = (err for err in stderr.splitlines() if err.startswith("error: "))
clean_error_lines = [err[len("error: ") :] for err in error_lines]
# There shall only be one
try:
return clean_error_lines[-1]
except IndexError:
return "snapd did not report an error"

def __init__(self, call_error: subprocess.CalledProcessError) -> None:
super().__init__(
message="Snapd failed to pack",
details=self._get_error_string_from_stderr(call_error.stderr),
)
7 changes: 1 addition & 6 deletions snapcraft/pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,17 +122,12 @@ def _pack(
command, capture_output=True, check=True, universal_newlines=True
)
except subprocess.CalledProcessError as err:
msg = str(err)
details = None
if err.stderr:
details = err.stderr.strip()
raise errors.SnapcraftError(msg, details=details) from err
raise errors.SnapPackError(err) from err

return Path(str(proc.stdout).partition(":")[2].strip()).name


def _retry_with_newer_snapd(func):

@wraps(func)
def retry_with_edge_snapd(
directory: Path, output_dir: Path, compression: Optional[str] = None
Expand Down
86 changes: 86 additions & 0 deletions tests/unit/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

"""Snapcraft error tests."""

import subprocess
import textwrap

import pytest

from snapcraft import errors
Expand All @@ -40,3 +43,86 @@ def test_maintenance_base_fallback():
assert str(error) == "'unknown-base' is not supported on this version of Snapcraft."
assert error.resolution is None
assert error.docs_url == "https://snapcraft.io/docs/base-snaps"


def test_snap_pack_error():
error = errors.SnapPackError(
call_error=subprocess.CalledProcessError(
returncode=1,
cmd=["snap", "pack"],
stderr=textwrap.dedent(
"""\
2024/12/20 11:25:37.751687 container.go:411: in snap "my-snap-name": path "this/file/does/not" does not exist
2024/12/20 11:25:37.751693 container.go:411: in snap "my-snap-name": path "this/file/does" does not exist
2024/12/20 11:25:37.751697 container.go:411: in snap "my-snap-name": path "this/file" does not exist
2024/12/20 11:25:37.751700 container.go:411: in snap "my-snap-name": path "this" does not exist
error: referenced command not found in snap
"""
),
)
)

assert str(error) == "Snapd failed to pack"
assert error.details == "referenced command not found in snap"
assert error.resolution is None
assert error.docs_url is None


def test_snap_pack_error_keep_last_error():
error = errors.SnapPackError(
call_error=subprocess.CalledProcessError(
returncode=1,
cmd=["snap", "pack"],
stderr=textwrap.dedent(
"""\
2024/12/20 11:25:37.751687 container.go:411: in snap "my-snap-name": path "this/file/does/not" does not exist
2024/12/20 11:25:37.751693 container.go:411: in snap "my-snap-name": path "this/file/does" does not exist
error: something happened
2024/12/20 11:25:37.751697 container.go:411: in snap "my-snap-name": path "this/file" does not exist
2024/12/20 11:25:37.751700 container.go:411: in snap "my-snap-name": path "this" does not exist
error: referenced command not found in snap
"""
),
)
)

assert str(error) == "Snapd failed to pack"
assert error.details == "referenced command not found in snap"
assert error.resolution is None
assert error.docs_url is None


def test_snap_pack_error_no_error_lines():
error = errors.SnapPackError(
call_error=subprocess.CalledProcessError(
returncode=1,
cmd=["snap", "pack"],
stderr=textwrap.dedent(
"""\
2024/12/20 11:25:37.751687 container.go:411: in snap "my-snap-name": path "this/file/does/not" does not exist
2024/12/20 11:25:37.751693 container.go:411: in snap "my-snap-name": path "this/file/does" does not exist
2024/12/20 11:25:37.751697 container.go:411: in snap "my-snap-name": path "this/file" does not exist
2024/12/20 11:25:37.751700 container.go:411: in snap "my-snap-name": path "this" does not exist
"""
),
)
)

assert str(error) == "Snapd failed to pack"
assert error.details == "snapd did not report an error"
assert error.resolution is None
assert error.docs_url is None


def test_snap_pack_error_no_stderr():
error = errors.SnapPackError(
call_error=subprocess.CalledProcessError(
returncode=1,
cmd=["snap", "pack"],
)
)

assert str(error) == "Snapd failed to pack"
assert error.details == "snapd did not report an error"
assert error.resolution is None
assert error.docs_url is None

0 comments on commit a9fc440

Please sign in to comment.