cfengine 0.12.0__tar.gz → 0.12.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. {cfengine-0.12.0 → cfengine-0.12.2}/PKG-INFO +18 -1
  2. {cfengine-0.12.0 → cfengine-0.12.2}/README.md +17 -0
  3. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine.egg-info/PKG-INFO +18 -1
  4. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/lint.py +65 -17
  5. {cfengine-0.12.0 → cfengine-0.12.2}/.github/dependabot.yml +0 -0
  6. {cfengine-0.12.0 → cfengine-0.12.2}/.github/workflows/format.yml +0 -0
  7. {cfengine-0.12.0 → cfengine-0.12.2}/.github/workflows/lint.yml +0 -0
  8. {cfengine-0.12.0 → cfengine-0.12.2}/.github/workflows/pypi-publish.yml +0 -0
  9. {cfengine-0.12.0 → cfengine-0.12.2}/.github/workflows/test.yml +0 -0
  10. {cfengine-0.12.0 → cfengine-0.12.2}/.gitignore +0 -0
  11. {cfengine-0.12.0 → cfengine-0.12.2}/.python-version +0 -0
  12. {cfengine-0.12.0 → cfengine-0.12.2}/HACKING.md +0 -0
  13. {cfengine-0.12.0 → cfengine-0.12.2}/LICENSE +0 -0
  14. {cfengine-0.12.0 → cfengine-0.12.2}/ci/01-install.sh +0 -0
  15. {cfengine-0.12.0 → cfengine-0.12.2}/ci/02-safe-tests.sh +0 -0
  16. {cfengine-0.12.0 → cfengine-0.12.2}/ci/03-unsafe-tests.sh +0 -0
  17. {cfengine-0.12.0 → cfengine-0.12.2}/pyproject.toml +0 -0
  18. {cfengine-0.12.0 → cfengine-0.12.2}/setup.cfg +0 -0
  19. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine.egg-info/SOURCES.txt +0 -0
  20. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine.egg-info/dependency_links.txt +0 -0
  21. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine.egg-info/entry_points.txt +0 -0
  22. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine.egg-info/requires.txt +0 -0
  23. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine.egg-info/top_level.txt +0 -0
  24. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/__init__.py +0 -0
  25. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/__main__.py +0 -0
  26. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/commands.py +0 -0
  27. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/deptool-README.md +0 -0
  28. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/deptool.py +0 -0
  29. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/dev.py +0 -0
  30. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/docs.py +0 -0
  31. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/format.py +0 -0
  32. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/main.py +0 -0
  33. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/masterfiles/__init__.py +0 -0
  34. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/masterfiles/analyze.py +0 -0
  35. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/masterfiles/check_download_matches_git.py +0 -0
  36. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/masterfiles/download.py +0 -0
  37. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/masterfiles/generate_git_tags.py +0 -0
  38. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/masterfiles/generate_release_information.py +0 -0
  39. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/masterfiles/generate_vcf_download.py +0 -0
  40. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/masterfiles/generate_vcf_git_checkout.py +0 -0
  41. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/paths.py +0 -0
  42. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/policy_language.py +0 -0
  43. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/profile.py +0 -0
  44. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/shell.py +0 -0
  45. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/utils.py +0 -0
  46. {cfengine-0.12.0 → cfengine-0.12.2}/src/cfengine_cli/version.py +0 -0
  47. {cfengine-0.12.0 → cfengine-0.12.2}/tests/__init__.py +0 -0
  48. {cfengine-0.12.0 → cfengine-0.12.2}/tests/format/001_hello_world.expected.cf +0 -0
  49. {cfengine-0.12.0 → cfengine-0.12.2}/tests/format/001_hello_world.input.cf +0 -0
  50. {cfengine-0.12.0 → cfengine-0.12.2}/tests/format/002_basics.expected.cf +0 -0
  51. {cfengine-0.12.0 → cfengine-0.12.2}/tests/format/002_basics.input.cf +0 -0
  52. {cfengine-0.12.0 → cfengine-0.12.2}/tests/format/003_wrapping.expected.cf +0 -0
  53. {cfengine-0.12.0 → cfengine-0.12.2}/tests/format/003_wrapping.input.cf +0 -0
  54. {cfengine-0.12.0 → cfengine-0.12.2}/tests/format/004_comments.expected.cf +0 -0
  55. {cfengine-0.12.0 → cfengine-0.12.2}/tests/format/004_comments.input.cf +0 -0
  56. {cfengine-0.12.0 → cfengine-0.12.2}/tests/run-format-tests.sh +0 -0
  57. {cfengine-0.12.0 → cfengine-0.12.2}/tests/run-shell-tests.sh +0 -0
  58. {cfengine-0.12.0 → cfengine-0.12.2}/tests/shell/001-help.sh +0 -0
  59. {cfengine-0.12.0 → cfengine-0.12.2}/tests/shell/002-version.sh +0 -0
  60. {cfengine-0.12.0 → cfengine-0.12.2}/tests/shell/003-format.sh +0 -0
  61. {cfengine-0.12.0 → cfengine-0.12.2}/tests/test_deps.py +0 -0
  62. {cfengine-0.12.0 → cfengine-0.12.2}/tests/test_paths.py +0 -0
  63. {cfengine-0.12.0 → cfengine-0.12.2}/tests/test_utils.py +0 -0
  64. {cfengine-0.12.0 → cfengine-0.12.2}/tests/test_version.py +0 -0
  65. {cfengine-0.12.0 → cfengine-0.12.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cfengine
3
- Version: 0.12.0
3
+ Version: 0.12.2
4
4
  Summary: Human-oriented CLI for interacting with CFEngine tools
5
5
  License: GNU GENERAL PUBLIC LICENSE
6
6
  Version 3, 29 June 2007
@@ -749,6 +749,23 @@ cfengine format
749
749
  cfengine lint
750
750
  ```
751
751
 
752
+ You can also specify filenames or folders;
753
+
754
+ ```bash
755
+ cfengine lint main.cf
756
+ ```
757
+
758
+ When it finds a mistake, it points out where the problem is like this;
759
+
760
+ ```
761
+ "Hello, CFEngine"
762
+ ifvarclass => "cfengine";
763
+ ^--------^
764
+ Deprecation: Use 'if' instead of 'ifvarclass' at main.cf:5:7
765
+ FAIL: main.cf (1 errors)
766
+ Failure, 1 errors in total.
767
+ ```
768
+
752
769
  Note that since we use a different parser than `cf-agent` / `cf-promises`, they are not 100% in sync.
753
770
  `cf-agent` could point out something as a syntax error, while `cfengine lint` does not and vice versa.
754
771
  We aim to make the tree-sitter parser (used in this tool) more strict in general, so that when `cfengine lint` is happy with your policy, `cf-agent` will also accept it.
@@ -47,6 +47,23 @@ cfengine format
47
47
  cfengine lint
48
48
  ```
49
49
 
50
+ You can also specify filenames or folders;
51
+
52
+ ```bash
53
+ cfengine lint main.cf
54
+ ```
55
+
56
+ When it finds a mistake, it points out where the problem is like this;
57
+
58
+ ```
59
+ "Hello, CFEngine"
60
+ ifvarclass => "cfengine";
61
+ ^--------^
62
+ Deprecation: Use 'if' instead of 'ifvarclass' at main.cf:5:7
63
+ FAIL: main.cf (1 errors)
64
+ Failure, 1 errors in total.
65
+ ```
66
+
50
67
  Note that since we use a different parser than `cf-agent` / `cf-promises`, they are not 100% in sync.
51
68
  `cf-agent` could point out something as a syntax error, while `cfengine lint` does not and vice versa.
52
69
  We aim to make the tree-sitter parser (used in this tool) more strict in general, so that when `cfengine lint` is happy with your policy, `cf-agent` will also accept it.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cfengine
3
- Version: 0.12.0
3
+ Version: 0.12.2
4
4
  Summary: Human-oriented CLI for interacting with CFEngine tools
5
5
  License: GNU GENERAL PUBLIC LICENSE
6
6
  Version 3, 29 June 2007
@@ -749,6 +749,23 @@ cfengine format
749
749
  cfengine lint
750
750
  ```
751
751
 
752
+ You can also specify filenames or folders;
753
+
754
+ ```bash
755
+ cfengine lint main.cf
756
+ ```
757
+
758
+ When it finds a mistake, it points out where the problem is like this;
759
+
760
+ ```
761
+ "Hello, CFEngine"
762
+ ifvarclass => "cfengine";
763
+ ^--------^
764
+ Deprecation: Use 'if' instead of 'ifvarclass' at main.cf:5:7
765
+ FAIL: main.cf (1 errors)
766
+ Failure, 1 errors in total.
767
+ ```
768
+
752
769
  Note that since we use a different parser than `cf-agent` / `cf-promises`, they are not 100% in sync.
753
770
  `cf-agent` could point out something as a syntax error, while `cfengine lint` does not and vice versa.
754
771
  We aim to make the tree-sitter parser (used in this tool) more strict in general, so that when `cfengine lint` is happy with your policy, `cf-agent` will also accept it.
@@ -14,6 +14,7 @@ import os
14
14
  import json
15
15
  import itertools
16
16
  import tree_sitter_cfengine as tscfengine
17
+ from dataclasses import dataclass
17
18
  from tree_sitter import Language, Parser
18
19
  from cfbs.validate import validate_config
19
20
  from cfbs.cfbs_config import CFBSConfig
@@ -26,6 +27,39 @@ from cfengine_cli.policy_language import (
26
27
  )
27
28
 
28
29
 
30
+ @dataclass
31
+ class _State:
32
+ block_type: str | None = None # "bundle" | "body" | "promise" | None
33
+ promise_type: str | None = None # "vars" | "files" | "classes" | ... | None
34
+ attribute_name: str | None = None # "if" | "string" | "slist" | ... | None
35
+
36
+ def update(self, node) -> "_State":
37
+ """Updates and returns the state that should apply to the children of `node`."""
38
+ if node.type == "bundle_block":
39
+ return _State(block_type="bundle")
40
+ if node.type == "body_block":
41
+ return _State(block_type="body")
42
+ if node.type == "promise_block":
43
+ return _State(block_type="promise")
44
+ if node.type == "bundle_section":
45
+ for child in node.children:
46
+ if child.type == "promise_guard":
47
+ return _State(
48
+ block_type=self.block_type,
49
+ promise_type=_text(child)[:-1], # strip trailing ':'
50
+ )
51
+ return _State(block_type=self.block_type)
52
+ if node.type == "attribute":
53
+ for child in node.children:
54
+ if child.type == "attribute_name":
55
+ return _State(
56
+ block_type=self.block_type,
57
+ promise_type=self.promise_type,
58
+ attribute_name=_text(child),
59
+ )
60
+ return self
61
+
62
+
29
63
  def lint_cfbs_json(filename) -> int:
30
64
  assert os.path.isfile(filename)
31
65
  assert filename.endswith("cfbs.json")
@@ -93,16 +127,9 @@ def _find_node_type(filename, lines, node, node_type):
93
127
  return matches
94
128
 
95
129
 
96
- def _find_nodes(filename, lines, node):
97
- matches = []
98
- visitor = lambda x: matches.append(x)
99
- _walk_generic(filename, lines, node, visitor)
100
- return matches
101
-
102
-
103
- def _single_node_checks(filename, lines, node, user_definition, strict):
104
- """Things which can be checked by only looking at one node,
105
- not needing to recurse into children."""
130
+ def _node_checks(filename, lines, node, user_definition, strict, state: _State):
131
+ """Checks we run on each node in the syntax tree,
132
+ utilizes state for checks which require context."""
106
133
  line = node.range.start_point[0] + 1
107
134
  column = node.range.start_point[1] + 1
108
135
  if node.type == "attribute_name" and _text(node) == "ifvarclass":
@@ -133,7 +160,6 @@ def _single_node_checks(filename, lines, node, user_definition, strict):
133
160
  f"Error: Undefined promise type '{promise_type}' at {filename}:{line}:{column}"
134
161
  )
135
162
  return 1
136
-
137
163
  if node.type == "bundle_block_name":
138
164
  if _text(node) != _text(node).lower():
139
165
  _highlight_range(node, lines)
@@ -156,6 +182,16 @@ def _single_node_checks(filename, lines, node, user_definition, strict):
156
182
  )
157
183
  return 1
158
184
  if node.type == "calling_identifier":
185
+ if (
186
+ strict
187
+ and _text(node) in user_definition.get("all_bundle_names", set())
188
+ and state.promise_type in user_definition.get("custom_promise_types", set())
189
+ ):
190
+ _highlight_range(node, lines)
191
+ print(
192
+ f"Error: Call to bundle '{_text(node)}' inside custom promise: '{state.promise_type}' at {filename}:{line}:{column}"
193
+ )
194
+ return 1
159
195
  if strict and (
160
196
  _text(node)
161
197
  not in BUILTIN_FUNCTIONS.union(
@@ -171,6 +207,22 @@ def _single_node_checks(filename, lines, node, user_definition, strict):
171
207
  return 0
172
208
 
173
209
 
210
+ def _stateful_walk(
211
+ filename, lines, node, user_definition, strict, state: _State | None = None
212
+ ) -> int:
213
+ if state is None:
214
+ state = _State()
215
+
216
+ errors = _node_checks(filename, lines, node, user_definition, strict, state)
217
+
218
+ child_state = state.update(node)
219
+ for child in node.children:
220
+ errors += _stateful_walk(
221
+ filename, lines, child, user_definition, strict, child_state
222
+ )
223
+ return errors
224
+
225
+
174
226
  def _walk(filename, lines, node, user_definition=None, strict=True) -> int:
175
227
  if user_definition is None:
176
228
  user_definition = {}
@@ -187,11 +239,7 @@ def _walk(filename, lines, node, user_definition=None, strict=True) -> int:
187
239
  line = node.range.start_point[0] + 1
188
240
  column = node.range.start_point[1] + 1
189
241
 
190
- errors = 0
191
- for node in _find_nodes(filename, lines, node):
192
- errors += _single_node_checks(filename, lines, node, user_definition, strict)
193
-
194
- return errors
242
+ return _stateful_walk(filename, lines, node, user_definition, strict)
195
243
 
196
244
 
197
245
  def _parse_user_definition(filename, lines, root_node):
@@ -348,4 +396,4 @@ def lint_single_arg(arg, strict=True):
348
396
  return lint_folder(arg, strict)
349
397
  assert os.path.isfile(arg)
350
398
 
351
- return lint_single_file(arg, strict)
399
+ return lint_single_file(arg, strict=strict)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes