summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--INTEGRATION-MODES.md4
-rw-r--r--debputy.pod27
-rw-r--r--src/debputy/filesystem_scan.py21
-rw-r--r--src/debputy/installations.py5
-rw-r--r--src/debputy/lsp/lsp_debian_control_reference_data.py33
-rw-r--r--src/debputy/lsp/lsp_reference_keyword.py20
-rw-r--r--src/debputy/plugin/debputy/build_system_rules.py5
7 files changed, 97 insertions, 18 deletions
diff --git a/INTEGRATION-MODES.md b/INTEGRATION-MODES.md
index 35e56a1..1094e67 100644
--- a/INTEGRATION-MODES.md
+++ b/INTEGRATION-MODES.md
@@ -48,7 +48,7 @@ Pros:
which is not possible with `debhelper`.
* You get maximum compatibility with existing `dh` add-ons.
* Migration is generally possible with minimal or small changes.
- * It is accessible in `bullseye-backports`
+ * It is accessible in `bookworm-backports`
Cons:
@@ -89,7 +89,7 @@ Pros:
* You can use the most features of `debputy`. Only the build and environment related ones are
not accessible.
- * It is accessible in `bullseye-backports`
+ * It is accessible in `bookworm-backports`
Cons:
diff --git a/debputy.pod b/debputy.pod
index 3ce9b90..7d08813 100644
--- a/debputy.pod
+++ b/debputy.pod
@@ -136,9 +136,9 @@ For packages that does not have the B<X-Style> field, B<debputy> will result to
can derive a style that all parties would agree too (or the team style for packaging teams), then that
style will be used.
-At the time of introduction, the B<black> style is similar to that of B<wrap-and-sort -astkb>, since
-that was one of the most common style according to L<https://bugs.debian.org/895570>, But the style is
-expected to evolve over time and the two styles may diverge over time.
+The B<black> style started as similar to that of B<wrap-and-sort -ast>, since that was one of the most
+common styles according to L<https://bugs.debian.org/895570>, but the style is expected to evolve over
+time and the two styles may diverge over time.
The command accepts the following options:
@@ -208,6 +208,12 @@ files. This command is useful for CI or for when you cannot use the language ser
the same diagnostics as B<debputy lsp server> would but without requiring an LSP capable editor as intermediate.
The output is only intended for human consumption. Machine readable is not a goal at this time.
+The B<debputy lint> command is a form of static analysis and will not load nor execute code from the
+code it is scanning. It is a security bug for B<debputy lint> to violate this principle directly
+or indirectly. Therefore, B<debputy> can only provide diagnostics from libraries and tools that takes
+this principle seriously. It also means that B<debputy> will likely have false-positives for self-hosting
+code, since it is not allowed to load the self-hosted code.
+
Note that at the time of writing, the B<debputy.manifest> file is only B<partially> supported. If you
have F<debian/debputy.manifest>, please also run B<debputy check-manifest> to get more thorough checks
for that file for now. The B<lint> command will inform you about this issue in the output if a
@@ -335,6 +341,21 @@ B<debputy lsp server> will suffice (using the stdio transport). See B<debputy ls
for other integration options such as TCP (B<--tcp>) or websocket (B<--ws>) plus related supporting
options.
+The B<debputy lsp server> command provides several features including a form of static analysis in the
+form of "as-you-type" diagnostics. For the diagnostics, is B<debputy lsp server> not allowed load nor
+execute code from the code it is scanning. It is a security bug for B<debputy lsp server> to violate
+this principle directly or indirectly. Therefore, B<debputy> can only provide diagnostics from libraries
+and tools that takes this principle seriously. It also means that B<debputy> will likely have
+false-positives for self-hosting code, since it is not allowed to load the self-hosted code.
+
+This security principle also applies to hover docs, completion suggestions and other trivial code editing
+or viewing features. However, it is not universal, since certain LSP features are deliberately designed
+to run the code you are viewing. As an example, B<debputy lsp server> can provide a "code lens" (LSP term)
+for building the current package. On activation of the code lens, B<debputy> will trigger code from the
+package to be run and that is expected. The critical points here are that the user most explicitly
+trigger the feature and it must use terms commonly associated with running code such as B<build>,
+B<run> or B<execute> (non-exhaustive list).
+
If you need to debug an issue with the language server, the TCP integration (B<--tcp>) can be
quite helpful. In this mode, you run B<debputy lsp server --tcp> in a terminal before starting your
editor. This means you have direct and unfiltered access to the B<debputy> command and its output.
diff --git a/src/debputy/filesystem_scan.py b/src/debputy/filesystem_scan.py
index 7b20040..29d37bd 100644
--- a/src/debputy/filesystem_scan.py
+++ b/src/debputy/filesystem_scan.py
@@ -57,6 +57,7 @@ from debputy.util import (
escape_shell,
assume_not_none,
_normalize_path,
+ _debug_log,
)
BY_BASENAME = operator.attrgetter("name")
@@ -668,7 +669,7 @@ class FSPath(VirtualPathBase, ABC):
@mode.setter
def mode(self, new_mode: int) -> None:
self._rw_check()
- min_bit = 0o500 if self.is_dir else 0o400
+ min_bit = 0o700 if self.is_dir else 0o400
if (new_mode & min_bit) != min_bit:
omode = oct(new_mode)[2:]
omin = oct(min_bit)[2:]
@@ -680,6 +681,22 @@ class FSPath(VirtualPathBase, ABC):
)
self._mode = new_mode
+ def _ensure_min_mode(self) -> None:
+ min_bit = 0o700 if self.is_dir else 0o600
+ if self.has_fs_path and (self.mode & 0o600) != 0o600:
+ try:
+ fs_path = self.fs_path
+ except TestPathWithNonExistentFSPathError:
+ pass
+ else:
+ st = os.stat(fs_path)
+ new_fs_mode = stat.S_IMODE(st.st_mode) | min_bit
+ _debug_log(
+ f"Applying chmod {oct(min_bit)[2:]} {fs_path} ({self.path}) to avoid problems down the line"
+ )
+ os.chmod(fs_path, new_fs_mode)
+ self.mode |= min_bit
+
@property
def mtime(self) -> float:
mtime = self._mtime
@@ -1243,6 +1260,7 @@ class VirtualDirectoryFSPath(VirtualPathWithReference):
)
self._reference_path = reference_path
assert reference_path is None or reference_path.is_dir
+ self._ensure_min_mode()
@property
def is_dir(self) -> bool:
@@ -1326,6 +1344,7 @@ class FSBackedFilePath(VirtualPathWithReference):
assert (
not replaceable_inline or "debputy/scratch-dir/" in fs_path
), f"{fs_path} should not be inline-replaceable -- {self.path}"
+ self._ensure_min_mode()
@property
def is_dir(self) -> bool:
diff --git a/src/debputy/installations.py b/src/debputy/installations.py
index 806a964..1ed89ca 100644
--- a/src/debputy/installations.py
+++ b/src/debputy/installations.py
@@ -1021,6 +1021,7 @@ class PPFInstallRule(InstallRule):
"_into",
)
+ # noinspection PyMissingConstructor
def __init__(
self,
into: BinaryPackage,
@@ -1146,8 +1147,8 @@ class DiscardRule(InstallRule):
if s.search_dir.fs_path in matches
)
if len(limit_to) != len(search_dirs):
- matches.difference(s.search_dir.fs_path for s in search_dirs)
- paths = ":".join(matches)
+ m = matches.difference(s.search_dir.fs_path for s in search_dirs)
+ paths = ":".join(m)
_error(
f"The discard rule defined at {self._definition_source} mentions the following"
f" search directories that were not known to debputy: {paths}."
diff --git a/src/debputy/lsp/lsp_debian_control_reference_data.py b/src/debputy/lsp/lsp_debian_control_reference_data.py
index 8f8fb10..85a04a3 100644
--- a/src/debputy/lsp/lsp_debian_control_reference_data.py
+++ b/src/debputy/lsp/lsp_debian_control_reference_data.py
@@ -49,6 +49,8 @@ from debputy.lsp.lsp_reference_keyword import (
ALL_PUBLIC_NAMED_STYLES,
Keyword,
allowed_values,
+ format_comp_item_synopsis_doc,
+ UsageHint,
)
from debputy.lsp.quickfixes import (
propose_correct_text_quick_fix,
@@ -300,7 +302,8 @@ ALL_SECTIONS = allowed_values(
ALL_PRIORITIES = allowed_values(
Keyword(
"required",
- synopsis_doc="[RARE]: Package is Essential or an Essential package needs it (and is not a library)",
+ usage_hint="rare",
+ synopsis_doc="Package is Essential or an Essential package needs it (and is not a library)",
hover_text=textwrap.dedent(
"""\
The package is necessary for the proper functioning of the system (read: dpkg needs it).
@@ -314,7 +317,8 @@ ALL_PRIORITIES = allowed_values(
),
Keyword(
"important",
- synopsis_doc="[RARE]: Bare minimum of commonly-expected and necessary tools",
+ usage_hint="rare",
+ synopsis_doc="Bare minimum of commonly-expected and necessary tools",
hover_text=textwrap.dedent(
"""\
The *important* packages are a bare minimum of commonly-expected and necessary tools.
@@ -328,7 +332,8 @@ ALL_PRIORITIES = allowed_values(
),
Keyword(
"standard",
- synopsis_doc="[RARE]: If your distribution installer would install this by default (not for libraries)",
+ usage_hint="rare",
+ synopsis_doc="If your distribution installer would install this by default (not for libraries)",
hover_text=textwrap.dedent(
"""\
These packages provide a reasonable small but not too limited character-mode system. This is
@@ -1496,6 +1501,7 @@ class Deb822KnownField:
)
# One-line description for space-constrained docs (such as completion docs)
synopsis_doc: Optional[str] = None
+ usage_hint: Optional[UsageHint] = None
hover_text: Optional[str] = None
spellcheck_value: bool = False
is_stanza_name: bool = False
@@ -1555,7 +1561,11 @@ class Deb822KnownField:
insert_text=complete_as,
deprecated=is_deprecated,
tags=tags,
- detail=self.synopsis_doc,
+ detail=format_comp_item_synopsis_doc(
+ self.usage_hint,
+ self.synopsis_doc,
+ is_deprecated,
+ ),
documentation=doc,
)
@@ -1681,7 +1691,13 @@ class Deb822KnownField:
keyword.value,
insert_text=keyword.value,
sort_text=keyword.sort_text,
- detail=keyword.synopsis_doc,
+ detail=format_comp_item_synopsis_doc(
+ keyword.usage_hint,
+ keyword.synopsis_doc,
+ keyword.is_deprecated,
+ ),
+ deprecated=keyword.is_deprecated,
+ tags=[CompletionItemTag.Deprecated] if keyword.is_deprecated else None,
documentation=(
MarkupContent(value=keyword.hover_text, kind=markdown_kind)
if keyword.hover_text
@@ -2504,7 +2520,7 @@ SOURCE_FIELDS = _fields(
deprecated_with_no_replacement=True,
default_value="no",
known_values=allowed_values("yes", "no"),
- synopsis_doc="**Obsolete**: Old ACL mechanism for Debian Maintainers",
+ synopsis_doc="Old ACL mechanism for Debian Maintainers",
hover_text=textwrap.dedent(
"""\
Obsolete field
@@ -2753,7 +2769,7 @@ SOURCE_FIELDS = _fields(
"X-Python-Version",
FieldValueClass.COMMA_SEPARATED_LIST,
replaced_by="X-Python3-Version",
- synopsis_doc="**Obsolete**: Supported Python2 versions (`dh-python` specific)",
+ synopsis_doc="Supported Python2 versions (`dh-python` specific)",
hover_text=textwrap.dedent(
"""\
Obsolete field for declaring the supported Python2 versions
@@ -3457,7 +3473,8 @@ BINARY_FIELDS = _fields(
"allowed",
# Never show `allowed` first, it is the absolute least likely candidate.
sort_text="zzzz-allowed",
- synopsis_doc="[RARE]: Consumer decides whether it is `same` and `foreign`",
+ usage_hint="rare",
+ synopsis_doc="Consumer decides whether it is `same` and `foreign`",
hover_text=textwrap.dedent(
"""\
**Advanced and very rare value**. This value is exceedingly rare to the point that less
diff --git a/src/debputy/lsp/lsp_reference_keyword.py b/src/debputy/lsp/lsp_reference_keyword.py
index 4046aaa..0e16ac1 100644
--- a/src/debputy/lsp/lsp_reference_keyword.py
+++ b/src/debputy/lsp/lsp_reference_keyword.py
@@ -1,10 +1,23 @@
import dataclasses
import textwrap
-from typing import Optional, Union, Mapping, Sequence, Callable, Iterable
+from typing import Optional, Union, Mapping, Sequence, Callable, Iterable, Literal
from debputy.lsp.vendoring._deb822_repro import Deb822ParagraphElement
+UsageHint = Literal["rare",]
+
+
+def format_comp_item_synopsis_doc(
+ usage_hint: Optional[UsageHint], synopsis_doc: str, is_deprecated: bool
+) -> str:
+ if is_deprecated:
+ return f"[OBSOLETE]: {synopsis_doc}"
+ if usage_hint is not None:
+ return f"[{usage_hint.upper()}]: {synopsis_doc}"
+ return synopsis_doc
+
+
@dataclasses.dataclass(slots=True, frozen=True)
class Keyword:
value: str
@@ -14,6 +27,7 @@ class Keyword:
replaced_by: Optional[str] = None
is_exclusive: bool = False
sort_text: Optional[str] = None
+ usage_hint: Optional[UsageHint] = None
can_complete_keyword_in_stanza: Optional[
Callable[[Iterable[Deb822ParagraphElement]], bool]
] = None
@@ -22,6 +36,10 @@ class Keyword:
value in `Architecture` of `debian/control` cannot be used with any other architecture.
"""
+ @property
+ def is_deprecated(self) -> bool:
+ return self.is_obsolete or self.replaced_by is not None
+
def is_keyword_valid_completion_in_stanza(
self,
stanza_parts: Sequence[Deb822ParagraphElement],
diff --git a/src/debputy/plugin/debputy/build_system_rules.py b/src/debputy/plugin/debputy/build_system_rules.py
index 043b8fa..6d70d23 100644
--- a/src/debputy/plugin/debputy/build_system_rules.py
+++ b/src/debputy/plugin/debputy/build_system_rules.py
@@ -670,7 +670,10 @@ class PerlBuildBuildSystemRule(StepBasedBuildSystemRule):
) -> None:
perl_config_data, cross_env_mod = self._perl_cross_build_env(context)
configure_env = EnvironmentModification(
- replacements=(("PERL_MM_USE_DEFAULT", "1"),)
+ replacements=(
+ ("PERL_MM_USE_DEFAULT", "1"),
+ ("PKG_CONFIG", context.cross_tool("pkg-config")),
+ )
)
if cross_env_mod is not None:
configure_env = configure_env.combine(cross_env_mod)