diff options
-rw-r--r-- | INTEGRATION-MODES.md | 4 | ||||
-rw-r--r-- | debputy.pod | 27 | ||||
-rw-r--r-- | src/debputy/filesystem_scan.py | 21 | ||||
-rw-r--r-- | src/debputy/installations.py | 5 | ||||
-rw-r--r-- | src/debputy/lsp/lsp_debian_control_reference_data.py | 33 | ||||
-rw-r--r-- | src/debputy/lsp/lsp_reference_keyword.py | 20 | ||||
-rw-r--r-- | src/debputy/plugin/debputy/build_system_rules.py | 5 |
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) |