cfengine 0.16.3__tar.gz → 0.16.5__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 (159) hide show
  1. {cfengine-0.16.3 → cfengine-0.16.5}/.github/workflows/lint-policy-in-other-repos.yml +7 -4
  2. {cfengine-0.16.3 → cfengine-0.16.5}/.github/workflows/update-syntax-description.yml +1 -1
  3. {cfengine-0.16.3 → cfengine-0.16.5}/PKG-INFO +19 -3
  4. {cfengine-0.16.3 → cfengine-0.16.5}/README.md +18 -2
  5. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine.egg-info/PKG-INFO +19 -3
  6. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine.egg-info/SOURCES.txt +2 -0
  7. cfengine-0.16.5/src/cfengine.egg-info/scm_file_list.json +153 -0
  8. cfengine-0.16.5/src/cfengine.egg-info/scm_version.json +8 -0
  9. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/commands.py +3 -74
  10. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/dev.py +7 -5
  11. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/docs.py +19 -12
  12. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/format.py +97 -6
  13. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/011_macros.expected.cf +91 -0
  14. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/011_macros.input.cf +56 -0
  15. {cfengine-0.16.3 → cfengine-0.16.5}/uv.lock +9 -9
  16. {cfengine-0.16.3 → cfengine-0.16.5}/.github/dependabot.yml +0 -0
  17. {cfengine-0.16.3 → cfengine-0.16.5}/.github/workflows/make-check.yml +0 -0
  18. {cfengine-0.16.3 → cfengine-0.16.5}/.github/workflows/pypi-publish.yml +0 -0
  19. {cfengine-0.16.3 → cfengine-0.16.5}/.github/workflows/stale.yml +0 -0
  20. {cfengine-0.16.3 → cfengine-0.16.5}/.gitignore +0 -0
  21. {cfengine-0.16.3 → cfengine-0.16.5}/.python-version +0 -0
  22. {cfengine-0.16.3 → cfengine-0.16.5}/CLAUDE.md +0 -0
  23. {cfengine-0.16.3 → cfengine-0.16.5}/HACKING.md +0 -0
  24. {cfengine-0.16.3 → cfengine-0.16.5}/LICENSE +0 -0
  25. {cfengine-0.16.3 → cfengine-0.16.5}/Makefile +0 -0
  26. {cfengine-0.16.3 → cfengine-0.16.5}/ci/01-install.sh +0 -0
  27. {cfengine-0.16.3 → cfengine-0.16.5}/ci/02-safe-tests.sh +0 -0
  28. {cfengine-0.16.3 → cfengine-0.16.5}/ci/03-unsafe-tests.sh +0 -0
  29. {cfengine-0.16.3 → cfengine-0.16.5}/pyproject.toml +0 -0
  30. {cfengine-0.16.3 → cfengine-0.16.5}/setup.cfg +0 -0
  31. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine.egg-info/dependency_links.txt +0 -0
  32. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine.egg-info/entry_points.txt +0 -0
  33. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine.egg-info/requires.txt +0 -0
  34. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine.egg-info/top_level.txt +0 -0
  35. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/__init__.py +0 -0
  36. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/__main__.py +0 -0
  37. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/changelog.py +0 -0
  38. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/deptool-README.md +0 -0
  39. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/deptool.py +0 -0
  40. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/lint.py +0 -0
  41. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/lint_csv.py +0 -0
  42. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/lint_yml.py +0 -0
  43. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/main.py +0 -0
  44. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/masterfiles/__init__.py +0 -0
  45. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/masterfiles/analyze.py +0 -0
  46. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/masterfiles/check_download_matches_git.py +0 -0
  47. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/masterfiles/download.py +0 -0
  48. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/masterfiles/generate_git_tags.py +0 -0
  49. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/masterfiles/generate_release_information.py +0 -0
  50. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/masterfiles/generate_vcf_download.py +0 -0
  51. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/masterfiles/generate_vcf_git_checkout.py +0 -0
  52. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/paths.py +0 -0
  53. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/profile.py +0 -0
  54. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/shell.py +0 -0
  55. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/syntax-description.json +0 -0
  56. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/syntax_tree.py +0 -0
  57. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/up.py +0 -0
  58. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/utils.py +0 -0
  59. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/validate.py +0 -0
  60. {cfengine-0.16.3 → cfengine-0.16.5}/src/cfengine_cli/version.py +0 -0
  61. {cfengine-0.16.3 → cfengine-0.16.5}/tests/README.md +0 -0
  62. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/001_hello_world.expected.cf +0 -0
  63. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/001_hello_world.input.cf +0 -0
  64. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/002_basics.expected.cf +0 -0
  65. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/002_basics.input.cf +0 -0
  66. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/003_wrapping.expected.cf +0 -0
  67. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/003_wrapping.input.cf +0 -0
  68. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/004_comments.expected.cf +0 -0
  69. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/004_comments.input.cf +0 -0
  70. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/005_bundle_comments.expected.cf +0 -0
  71. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/005_bundle_comments.input.cf +0 -0
  72. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/006_remove_empty_comments.expected.cf +0 -0
  73. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/006_remove_empty_comments.input.cf +0 -0
  74. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/007_class_guarded_empty_lines.expected.cf +0 -0
  75. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/007_class_guarded_empty_lines.input.cf +0 -0
  76. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/008_long_string.expected.cf +0 -0
  77. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/008_long_string.input.cf +0 -0
  78. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/009_single_line.expected.cf +0 -0
  79. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/009_single_line.input.cf +0 -0
  80. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/010_stakeholder.expected.cf +0 -0
  81. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/010_stakeholder.input.cf +0 -0
  82. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/011_promises.expected.cf +0 -0
  83. {cfengine-0.16.3 → cfengine-0.16.5}/tests/format/011_promises.input.cf +0 -0
  84. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/001_hello_world.cf +0 -0
  85. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/002_deprecations.expected.txt +0 -0
  86. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/002_deprecations.x.cf +0 -0
  87. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/003_name_lowercase.cf +0 -0
  88. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/003_name_lowercase.expected.txt +0 -0
  89. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/003_name_lowercase.x.cf +0 -0
  90. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/004_block_type.cf +0 -0
  91. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/004_block_type.expected.txt +0 -0
  92. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/004_block_type.x.cf +0 -0
  93. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/005_syntax_error.cf +0 -0
  94. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/005_syntax_error.expected.txt +0 -0
  95. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/005_syntax_error.x.cf +0 -0
  96. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/006_empty_file.expected.txt +0 -0
  97. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/006_empty_file.x.cf +0 -0
  98. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/007_namespace.cf +0 -0
  99. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/007_namespace.expected.txt +0 -0
  100. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/007_namespace.x.cf +0 -0
  101. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/008_bundle_shadows_function.cf +0 -0
  102. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/008_bundle_shadows_function.expected.txt +0 -0
  103. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/008_bundle_shadows_function.x.cf +0 -0
  104. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/009_unknown_function_inside_vars.cf +0 -0
  105. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/009_unknown_function_inside_vars.expected.txt +0 -0
  106. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/009_unknown_function_inside_vars.x.cf +0 -0
  107. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/010_mutually_exclusive_types_vars.cf +0 -0
  108. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/010_mutually_exclusive_types_vars.expected.txt +0 -0
  109. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/010_mutually_exclusive_types_vars.x.cf +0 -0
  110. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/011_invalid_attributes.expected.txt +0 -0
  111. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/011_invalid_attributes.x.cf +0 -0
  112. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/012_function_call_arg_count.cf +0 -0
  113. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/012_function_call_arg_count.expected.txt +0 -0
  114. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/012_function_call_arg_count.x.cf +0 -0
  115. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/013_num_args_body.expected.txt +0 -0
  116. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/013_num_args_body.x.cf +0 -0
  117. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/013_num_args_bundle.expected.txt +0 -0
  118. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/013_num_args_bundle.x.cf +0 -0
  119. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/013_num_args_bundle_body.cf +0 -0
  120. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/014_variadic_func_arg_count.cf +0 -0
  121. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/014_variadic_func_arg_count.expected.txt +0 -0
  122. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/014_variadic_func_arg_count.x.cf +0 -0
  123. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/015_macro_multi_def_bundle.cf +0 -0
  124. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/015_macro_multi_def_bundle.expected.txt +0 -0
  125. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/015_macro_multi_def_bundle.x.cf +0 -0
  126. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/016_half_promises.cf +0 -0
  127. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/016_half_promises.expected.txt +0 -0
  128. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/016_half_promises.x.cf +0 -0
  129. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/017_implies_body.cf +0 -0
  130. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/017_implies_body.expected.txt +0 -0
  131. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/017_implies_body.x.cf +0 -0
  132. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/018_nested_calls.cf +0 -0
  133. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/018_nested_calls.expected.txt +0 -0
  134. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/018_nested_calls.x.cf +0 -0
  135. {cfengine-0.16.3 → cfengine-0.16.5}/tests/lint/019_bundle_name_expansion.cf +0 -0
  136. {cfengine-0.16.3 → cfengine-0.16.5}/tests/run-format-tests.sh +0 -0
  137. {cfengine-0.16.3 → cfengine-0.16.5}/tests/run-lint-tests.sh +0 -0
  138. {cfengine-0.16.3 → cfengine-0.16.5}/tests/run-shell-tests.sh +0 -0
  139. {cfengine-0.16.3 → cfengine-0.16.5}/tests/run-up-validate-tests.sh +0 -0
  140. {cfengine-0.16.3 → cfengine-0.16.5}/tests/shell/001-help.sh +0 -0
  141. {cfengine-0.16.3 → cfengine-0.16.5}/tests/shell/002-version.sh +0 -0
  142. {cfengine-0.16.3 → cfengine-0.16.5}/tests/shell/003-format.sh +0 -0
  143. {cfengine-0.16.3 → cfengine-0.16.5}/tests/shell/004-format-check.sh +0 -0
  144. {cfengine-0.16.3 → cfengine-0.16.5}/tests/shell/005-lint.sh +0 -0
  145. {cfengine-0.16.3 → cfengine-0.16.5}/tests/unit/__init__.py +0 -0
  146. {cfengine-0.16.3 → cfengine-0.16.5}/tests/unit/test_changelog.py +0 -0
  147. {cfengine-0.16.3 → cfengine-0.16.5}/tests/unit/test_deps.py +0 -0
  148. {cfengine-0.16.3 → cfengine-0.16.5}/tests/unit/test_format.py +0 -0
  149. {cfengine-0.16.3 → cfengine-0.16.5}/tests/unit/test_lint_csv.py +0 -0
  150. {cfengine-0.16.3 → cfengine-0.16.5}/tests/unit/test_lint_yml.py +0 -0
  151. {cfengine-0.16.3 → cfengine-0.16.5}/tests/unit/test_paths.py +0 -0
  152. {cfengine-0.16.3 → cfengine-0.16.5}/tests/unit/test_utils.py +0 -0
  153. {cfengine-0.16.3 → cfengine-0.16.5}/tests/unit/test_version.py +0 -0
  154. {cfengine-0.16.3 → cfengine-0.16.5}/tests/up-validate/001_complex_config.yaml +0 -0
  155. {cfengine-0.16.3 → cfengine-0.16.5}/tests/up-validate/002_version.x.yaml +0 -0
  156. {cfengine-0.16.3 → cfengine-0.16.5}/tests/up-validate/003_box.x.yaml +0 -0
  157. {cfengine-0.16.3 → cfengine-0.16.5}/tests/up-validate/004_count.x.yaml +0 -0
  158. {cfengine-0.16.3 → cfengine-0.16.5}/tests/up-validate/mocks/cloud_config.json +0 -0
  159. {cfengine-0.16.3 → cfengine-0.16.5}/tests/up-validate/mocks/vagrant +0 -0
@@ -44,12 +44,15 @@ jobs:
44
44
  python -m pip install --upgrade pip
45
45
  python -m pip install uv
46
46
  make install
47
- - name: Run cfengine lint
47
+ - name: Check other repos with cfengine format and cfengine lint
48
48
  working-directory: cfengine-cli
49
49
  run: |
50
- uv run cfengine lint --strict no ../masterfiles
51
- uv run cfengine lint --strict no ../modules
52
- uv run cfengine lint --strict no ../documentation
50
+ # Linting should work with no errors on all the other repos:
51
+ uv run cfengine lint --strict no ../masterfiles ../documentation ../modules
52
+ # Formatting should work with no errors on all the other repos:
53
+ uv run cfengine format ../masterfiles ../documentation ../modules
54
+ # Linting should still work after formatting:
55
+ uv run cfengine lint --strict no ../masterfiles ../documentation ../modules
53
56
  # TODO: Add core when ready
54
57
  # TODO: Do all of them together, with strict, when ready;
55
58
  # uv run cfengine lint ../core ../masterfiles ../documentation ../modules
@@ -35,7 +35,7 @@ jobs:
35
35
  - name: Install cfengine
36
36
  run: |
37
37
  cf-remote --version master download --edition community ubuntu24 amd64 hub
38
- sudo dpkg -i ~/.cfengine/cf-remote/packages/cfengine-community*.deb
38
+ sudo dpkg -i ~/.cache/cfengine/cf-remote/packages/cfengine-community*.deb
39
39
  - name: Extract new syntax-description
40
40
  run: |
41
41
  (
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cfengine
3
- Version: 0.16.3
3
+ Version: 0.16.5
4
4
  Summary: Human-oriented CLI for interacting with CFEngine policy language, projects, and other CFEngine tools
5
5
  License: GNU GENERAL PUBLIC LICENSE
6
6
  Version 3, 29 June 2007
@@ -803,12 +803,28 @@ groups:
803
803
  spawn: ubuntu
804
804
  ```
805
805
 
806
- up will spawn the necessary VMs and install cfengine using cf-remote
806
+ `up` will spawn the necessary VMs and install cfengine using cf-remote.
807
807
 
808
- ```
808
+ ```bash
809
809
  cfengine up config.yaml
810
810
  ```
811
811
 
812
+ To validate the config, one can run:
813
+
814
+ ```bash
815
+ cfengine up --validate config.yaml
816
+ ```
817
+
818
+ Every time `up` is run, it will do all the necessary (spawn, destroy, install, uninstall) to reconciliate the current state with the input configuration.
819
+ After a successful run, the applied configuration is saved.
820
+ On subsequent runs, CFEngine compares the previously applied configuration with the current configuration and applies only the necessary changes.
821
+
822
+ To ignore the previously saved configuration and apply the current configuration as-is, run:
823
+
824
+ ```bash
825
+ cfengine up --reset config.yaml
826
+ ```
827
+
812
828
  ## Supported platforms and versions
813
829
 
814
830
  This tool will only support a limited number of platforms, it is not intended to run everywhere CFEngine runs.
@@ -99,12 +99,28 @@ groups:
99
99
  spawn: ubuntu
100
100
  ```
101
101
 
102
- up will spawn the necessary VMs and install cfengine using cf-remote
102
+ `up` will spawn the necessary VMs and install cfengine using cf-remote.
103
103
 
104
- ```
104
+ ```bash
105
105
  cfengine up config.yaml
106
106
  ```
107
107
 
108
+ To validate the config, one can run:
109
+
110
+ ```bash
111
+ cfengine up --validate config.yaml
112
+ ```
113
+
114
+ Every time `up` is run, it will do all the necessary (spawn, destroy, install, uninstall) to reconciliate the current state with the input configuration.
115
+ After a successful run, the applied configuration is saved.
116
+ On subsequent runs, CFEngine compares the previously applied configuration with the current configuration and applies only the necessary changes.
117
+
118
+ To ignore the previously saved configuration and apply the current configuration as-is, run:
119
+
120
+ ```bash
121
+ cfengine up --reset config.yaml
122
+ ```
123
+
108
124
  ## Supported platforms and versions
109
125
 
110
126
  This tool will only support a limited number of platforms, it is not intended to run everywhere CFEngine runs.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cfengine
3
- Version: 0.16.3
3
+ Version: 0.16.5
4
4
  Summary: Human-oriented CLI for interacting with CFEngine policy language, projects, and other CFEngine tools
5
5
  License: GNU GENERAL PUBLIC LICENSE
6
6
  Version 3, 29 June 2007
@@ -803,12 +803,28 @@ groups:
803
803
  spawn: ubuntu
804
804
  ```
805
805
 
806
- up will spawn the necessary VMs and install cfengine using cf-remote
806
+ `up` will spawn the necessary VMs and install cfengine using cf-remote.
807
807
 
808
- ```
808
+ ```bash
809
809
  cfengine up config.yaml
810
810
  ```
811
811
 
812
+ To validate the config, one can run:
813
+
814
+ ```bash
815
+ cfengine up --validate config.yaml
816
+ ```
817
+
818
+ Every time `up` is run, it will do all the necessary (spawn, destroy, install, uninstall) to reconciliate the current state with the input configuration.
819
+ After a successful run, the applied configuration is saved.
820
+ On subsequent runs, CFEngine compares the previously applied configuration with the current configuration and applies only the necessary changes.
821
+
822
+ To ignore the previously saved configuration and apply the current configuration as-is, run:
823
+
824
+ ```bash
825
+ cfengine up --reset config.yaml
826
+ ```
827
+
812
828
  ## Supported platforms and versions
813
829
 
814
830
  This tool will only support a limited number of platforms, it is not intended to run everywhere CFEngine runs.
@@ -21,6 +21,8 @@ src/cfengine.egg-info/SOURCES.txt
21
21
  src/cfengine.egg-info/dependency_links.txt
22
22
  src/cfengine.egg-info/entry_points.txt
23
23
  src/cfengine.egg-info/requires.txt
24
+ src/cfengine.egg-info/scm_file_list.json
25
+ src/cfengine.egg-info/scm_version.json
24
26
  src/cfengine.egg-info/top_level.txt
25
27
  src/cfengine_cli/__init__.py
26
28
  src/cfengine_cli/__main__.py
@@ -0,0 +1,153 @@
1
+ {
2
+ "files": [
3
+ "README.md",
4
+ "uv.lock",
5
+ "Makefile",
6
+ ".python-version",
7
+ "LICENSE",
8
+ "pyproject.toml",
9
+ "HACKING.md",
10
+ "CLAUDE.md",
11
+ ".gitignore",
12
+ "src/cfengine_cli/changelog.py",
13
+ "src/cfengine_cli/shell.py",
14
+ "src/cfengine_cli/__init__.py",
15
+ "src/cfengine_cli/version.py",
16
+ "src/cfengine_cli/lint_yml.py",
17
+ "src/cfengine_cli/profile.py",
18
+ "src/cfengine_cli/deptool-README.md",
19
+ "src/cfengine_cli/utils.py",
20
+ "src/cfengine_cli/format.py",
21
+ "src/cfengine_cli/syntax-description.json",
22
+ "src/cfengine_cli/docs.py",
23
+ "src/cfengine_cli/up.py",
24
+ "src/cfengine_cli/lint_csv.py",
25
+ "src/cfengine_cli/syntax_tree.py",
26
+ "src/cfengine_cli/main.py",
27
+ "src/cfengine_cli/deptool.py",
28
+ "src/cfengine_cli/lint.py",
29
+ "src/cfengine_cli/commands.py",
30
+ "src/cfengine_cli/validate.py",
31
+ "src/cfengine_cli/__main__.py",
32
+ "src/cfengine_cli/paths.py",
33
+ "src/cfengine_cli/dev.py",
34
+ "src/cfengine_cli/masterfiles/check_download_matches_git.py",
35
+ "src/cfengine_cli/masterfiles/generate_vcf_git_checkout.py",
36
+ "src/cfengine_cli/masterfiles/__init__.py",
37
+ "src/cfengine_cli/masterfiles/download.py",
38
+ "src/cfengine_cli/masterfiles/analyze.py",
39
+ "src/cfengine_cli/masterfiles/generate_release_information.py",
40
+ "src/cfengine_cli/masterfiles/generate_vcf_download.py",
41
+ "src/cfengine_cli/masterfiles/generate_git_tags.py",
42
+ "ci/01-install.sh",
43
+ "ci/02-safe-tests.sh",
44
+ "ci/03-unsafe-tests.sh",
45
+ "tests/README.md",
46
+ "tests/run-shell-tests.sh",
47
+ "tests/run-up-validate-tests.sh",
48
+ "tests/run-lint-tests.sh",
49
+ "tests/run-format-tests.sh",
50
+ "tests/lint/015_macro_multi_def_bundle.cf",
51
+ "tests/lint/010_mutually_exclusive_types_vars.cf",
52
+ "tests/lint/010_mutually_exclusive_types_vars.x.cf",
53
+ "tests/lint/013_num_args_body.expected.txt",
54
+ "tests/lint/007_namespace.x.cf",
55
+ "tests/lint/018_nested_calls.expected.txt",
56
+ "tests/lint/010_mutually_exclusive_types_vars.expected.txt",
57
+ "tests/lint/016_half_promises.expected.txt",
58
+ "tests/lint/002_deprecations.x.cf",
59
+ "tests/lint/009_unknown_function_inside_vars.cf",
60
+ "tests/lint/008_bundle_shadows_function.expected.txt",
61
+ "tests/lint/013_num_args_bundle.x.cf",
62
+ "tests/lint/017_implies_body.cf",
63
+ "tests/lint/017_implies_body.expected.txt",
64
+ "tests/lint/008_bundle_shadows_function.cf",
65
+ "tests/lint/003_name_lowercase.x.cf",
66
+ "tests/lint/009_unknown_function_inside_vars.x.cf",
67
+ "tests/lint/004_block_type.x.cf",
68
+ "tests/lint/011_invalid_attributes.expected.txt",
69
+ "tests/lint/005_syntax_error.cf",
70
+ "tests/lint/013_num_args_bundle_body.cf",
71
+ "tests/lint/003_name_lowercase.expected.txt",
72
+ "tests/lint/002_deprecations.expected.txt",
73
+ "tests/lint/013_num_args_bundle.expected.txt",
74
+ "tests/lint/012_function_call_arg_count.expected.txt",
75
+ "tests/lint/004_block_type.cf",
76
+ "tests/lint/005_syntax_error.x.cf",
77
+ "tests/lint/015_macro_multi_def_bundle.expected.txt",
78
+ "tests/lint/006_empty_file.expected.txt",
79
+ "tests/lint/014_variadic_func_arg_count.x.cf",
80
+ "tests/lint/004_block_type.expected.txt",
81
+ "tests/lint/016_half_promises.cf",
82
+ "tests/lint/018_nested_calls.x.cf",
83
+ "tests/lint/011_invalid_attributes.x.cf",
84
+ "tests/lint/018_nested_calls.cf",
85
+ "tests/lint/014_variadic_func_arg_count.cf",
86
+ "tests/lint/017_implies_body.x.cf",
87
+ "tests/lint/012_function_call_arg_count.x.cf",
88
+ "tests/lint/007_namespace.expected.txt",
89
+ "tests/lint/015_macro_multi_def_bundle.x.cf",
90
+ "tests/lint/019_bundle_name_expansion.cf",
91
+ "tests/lint/001_hello_world.cf",
92
+ "tests/lint/009_unknown_function_inside_vars.expected.txt",
93
+ "tests/lint/007_namespace.cf",
94
+ "tests/lint/014_variadic_func_arg_count.expected.txt",
95
+ "tests/lint/012_function_call_arg_count.cf",
96
+ "tests/lint/008_bundle_shadows_function.x.cf",
97
+ "tests/lint/013_num_args_body.x.cf",
98
+ "tests/lint/003_name_lowercase.cf",
99
+ "tests/lint/005_syntax_error.expected.txt",
100
+ "tests/lint/006_empty_file.x.cf",
101
+ "tests/lint/016_half_promises.x.cf",
102
+ "tests/shell/001-help.sh",
103
+ "tests/shell/002-version.sh",
104
+ "tests/shell/004-format-check.sh",
105
+ "tests/shell/005-lint.sh",
106
+ "tests/shell/003-format.sh",
107
+ "tests/up-validate/002_version.x.yaml",
108
+ "tests/up-validate/004_count.x.yaml",
109
+ "tests/up-validate/001_complex_config.yaml",
110
+ "tests/up-validate/003_box.x.yaml",
111
+ "tests/up-validate/mocks/vagrant",
112
+ "tests/up-validate/mocks/cloud_config.json",
113
+ "tests/format/003_wrapping.input.cf",
114
+ "tests/format/011_macros.expected.cf",
115
+ "tests/format/009_single_line.input.cf",
116
+ "tests/format/009_single_line.expected.cf",
117
+ "tests/format/010_stakeholder.input.cf",
118
+ "tests/format/007_class_guarded_empty_lines.expected.cf",
119
+ "tests/format/004_comments.expected.cf",
120
+ "tests/format/001_hello_world.input.cf",
121
+ "tests/format/011_promises.expected.cf",
122
+ "tests/format/006_remove_empty_comments.input.cf",
123
+ "tests/format/005_bundle_comments.expected.cf",
124
+ "tests/format/002_basics.expected.cf",
125
+ "tests/format/005_bundle_comments.input.cf",
126
+ "tests/format/006_remove_empty_comments.expected.cf",
127
+ "tests/format/007_class_guarded_empty_lines.input.cf",
128
+ "tests/format/003_wrapping.expected.cf",
129
+ "tests/format/010_stakeholder.expected.cf",
130
+ "tests/format/004_comments.input.cf",
131
+ "tests/format/001_hello_world.expected.cf",
132
+ "tests/format/011_promises.input.cf",
133
+ "tests/format/008_long_string.expected.cf",
134
+ "tests/format/008_long_string.input.cf",
135
+ "tests/format/002_basics.input.cf",
136
+ "tests/format/011_macros.input.cf",
137
+ "tests/unit/__init__.py",
138
+ "tests/unit/test_version.py",
139
+ "tests/unit/test_deps.py",
140
+ "tests/unit/test_utils.py",
141
+ "tests/unit/test_format.py",
142
+ "tests/unit/test_changelog.py",
143
+ "tests/unit/test_lint_csv.py",
144
+ "tests/unit/test_lint_yml.py",
145
+ "tests/unit/test_paths.py",
146
+ ".github/dependabot.yml",
147
+ ".github/workflows/lint-policy-in-other-repos.yml",
148
+ ".github/workflows/pypi-publish.yml",
149
+ ".github/workflows/make-check.yml",
150
+ ".github/workflows/stale.yml",
151
+ ".github/workflows/update-syntax-description.yml"
152
+ ]
153
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.16.5",
3
+ "distance": 0,
4
+ "node": "g19fe73398e8d85aedf2c458c53db1e2b66cc607c",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-06-23"
8
+ }
@@ -1,19 +1,14 @@
1
- import sys
2
1
  import os
3
2
  import re
4
3
  import json
5
4
  import yaml
6
5
  from cfengine_cli.profile import profile_cfengine, generate_callstack
7
6
  from cfengine_cli.dev import dispatch_dev_subcommand
8
- from cfengine_cli.lint import lint_args, PolicySyntaxError
7
+ from cfengine_cli.lint import lint_args
9
8
  from cfengine_cli.shell import user_command
10
9
  from cfengine_cli.paths import bin
11
10
  from cfengine_cli.version import cfengine_cli_version_string
12
- from cfengine_cli.format import (
13
- format_policy_file,
14
- format_json_file,
15
- format_policy_fin_fout,
16
- )
11
+ from cfengine_cli.format import format_paths
17
12
  from cfengine_cli.utils import UserError
18
13
  from cfengine_cli.up import validate_config
19
14
  from cfbs.commands import build_command
@@ -51,74 +46,8 @@ def deploy() -> int:
51
46
  return r
52
47
 
53
48
 
54
- def _format_filename(filename: str, line_length: int, check: bool) -> int:
55
- """Format a single file.
56
-
57
- Raises PolicySyntaxError for .cf files with syntax errors."""
58
- if filename.endswith(".json"):
59
- return format_json_file(filename, check)
60
- if filename.endswith(".cf"):
61
- return format_policy_file(filename, line_length, check)
62
- raise UserError(f"Unrecognized file format: {filename}")
63
-
64
-
65
- def _format_dirname(directory: str, line_length: int, check: bool) -> int:
66
- ret = 0
67
- for root, dirs, files in os.walk(directory):
68
- # Don't recurse into hidden folders
69
- dirs[:] = [d for d in dirs if not d.startswith(".")]
70
- for name in sorted(files):
71
- if name.startswith("."):
72
- continue # Hidden files are ignored by default
73
- if (
74
- name.endswith(".x.cf")
75
- or name.endswith(".input.cf")
76
- or name.endswith(".output.cf")
77
- or name.endswith(".expected.cf")
78
- ):
79
- continue # Test files skipped during directory traversal
80
- if name.endswith(
81
- (".input.json", ".jqinput.json", ".x.json", ".expected.json")
82
- ):
83
- continue # Test files skipped during directory traversal
84
- filepath = os.path.join(root, name)
85
- if name.endswith(".json") or name.endswith(".cf"):
86
- ret |= _format_filename(filepath, line_length, check)
87
- return ret
88
-
89
-
90
49
  def format(names, line_length, check) -> int:
91
- try:
92
- return _format_inner(names, line_length, check)
93
- except PolicySyntaxError as e:
94
- print(f"Error: {e}")
95
- return 1
96
-
97
-
98
- def _format_inner(names, line_length, check) -> int:
99
- if not names:
100
- return _format_dirname(".", line_length, check)
101
- if len(names) == 1 and names[0] == "-":
102
- # Special case, format policy file from stdin to stdout
103
- return format_policy_fin_fout(sys.stdin, sys.stdout, line_length, check)
104
-
105
- ret = 0
106
- for name in names:
107
- if name == "-":
108
- raise UserError(
109
- "The - argument has a special meaning and cannot be combined with other paths"
110
- )
111
- if not os.path.exists(name):
112
- raise UserError(f"{name} does not exist")
113
- if os.path.isfile(name):
114
- ret |= _format_filename(name, line_length, check)
115
- continue
116
- if os.path.isdir(name):
117
- ret |= _format_dirname(name, line_length, check)
118
- continue
119
- if check:
120
- return ret
121
- return 0
50
+ return format_paths(names, line_length, check)
122
51
 
123
52
 
124
53
  def _lint(files, strict) -> int:
@@ -8,7 +8,7 @@ from cfengine_cli.deptool import (
8
8
  update_dependency_tables as _update_dependency_tables,
9
9
  print_release_dependency_tables,
10
10
  )
11
- from cfengine_cli.docs import update_docs, check_docs
11
+ from cfengine_cli import docs
12
12
  from cfengine_cli.syntax_tree import syntax_tree
13
13
 
14
14
 
@@ -47,14 +47,16 @@ def print_dependency_tables(args) -> int:
47
47
  return print_release_dependency_tables(args.versions)
48
48
 
49
49
 
50
- def format_docs(files) -> int:
50
+ def format_docs() -> int:
51
51
  _expect_repo("documentation")
52
- return update_docs(files)
52
+ ret = docs.format_docs()
53
+ return ret
53
54
 
54
55
 
55
56
  def lint_docs() -> int:
56
57
  _expect_repo("documentation")
57
- return check_docs()
58
+ ret = docs.lint_docs()
59
+ return ret
58
60
 
59
61
 
60
62
  def generate_release_information(
@@ -71,7 +73,7 @@ def dispatch_dev_subcommand(subcommand, args) -> int:
71
73
  if subcommand == "print-dependency-tables":
72
74
  return print_dependency_tables(args)
73
75
  if subcommand == "format-docs":
74
- return format_docs(args.files)
76
+ return format_docs()
75
77
  if subcommand == "lint-docs":
76
78
  return lint_docs()
77
79
  if subcommand == "syntax-tree":
@@ -16,7 +16,7 @@ import markdown_it
16
16
  from cfbs.pretty import pretty_file
17
17
  from cfbs.utils import find
18
18
 
19
- from cfengine_cli.format import format_policy_file
19
+ from cfengine_cli.format import format_policy_file, format_paths
20
20
  from cfengine_cli.lint import lint_args, lint_policy_file_snippet
21
21
  from cfengine_cli.utils import UserError
22
22
 
@@ -420,12 +420,11 @@ def _run_prettier(path) -> bool:
420
420
  return formatted
421
421
 
422
422
 
423
- def _update_docs_single_arg(path):
423
+ def _format_docs_single_arg(path):
424
424
  if not os.path.exists(path):
425
425
  raise UserError(f"The specified file/folder '{path}' does not exist")
426
426
  if not os.path.isfile(path) and not os.path.isdir(path):
427
427
  raise UserError(f"Argument '{path}' is not a file or a folder")
428
-
429
428
  formatted = False
430
429
  if os.path.isdir(path) or path.endswith(".py"):
431
430
  if _run_black(path):
@@ -443,13 +442,16 @@ def _update_docs_single_arg(path):
443
442
  replace=True,
444
443
  cleanup=True,
445
444
  )
446
-
445
+ if os.path.isdir(path) or path.endswith(".cf"):
446
+ r = format_paths([path], line_length=80, check=False)
447
+ if r != 0:
448
+ formatted = True
447
449
  return formatted
448
450
 
449
451
 
450
- def update_docs(paths) -> int:
452
+ def format_docs() -> int:
451
453
  """
452
- Iterate through entire docs repo, or specified subfolders / files, autoformatting as much as possible:
454
+ Iterate through entire docs repo (CWD) autoformatting as much as possible:
453
455
  - python code with black
454
456
  - markdown files with prettier
455
457
  - code blocks inside markdown files are formatted for the formats supported by prettier
@@ -458,23 +460,27 @@ def update_docs(paths) -> int:
458
460
  Run by the command:
459
461
  cfengine dev format-docs
460
462
  """
461
- if not paths:
462
- _update_docs_single_arg(".")
463
- return 0
464
- for path in paths:
465
- _update_docs_single_arg(path)
463
+ _format_docs_single_arg(".")
466
464
  return 0
467
465
 
468
466
 
469
- def check_docs() -> int:
467
+ def lint_docs() -> int:
470
468
  """
471
469
  Run checks / tests on docs.
472
470
 
473
471
  Run by the command:
474
472
  cfengine dev lint-docs"""
473
+
474
+ # cfengine lint
475
475
  r = lint_args(["."], strict=False)
476
476
  if r != 0:
477
+ print(f"Failure - {r} errors while running cfengine lint .")
477
478
  return r
479
+
480
+ # cfengine format --check
481
+ r = format_paths(["."], line_length=80, check=True)
482
+
483
+ # Extract code blocks from markdown files and run checks on them:
478
484
  _process_markdown_code_blocks(
479
485
  path=".",
480
486
  languages=["json", "cf3"],
@@ -485,4 +491,5 @@ def check_docs() -> int:
485
491
  replace=False,
486
492
  cleanup=True,
487
493
  )
494
+ print("Success - all files look correct")
488
495
  return 0
@@ -3,11 +3,14 @@ from __future__ import annotations
3
3
  from typing import IO
4
4
 
5
5
  import json
6
+ import os
7
+ import sys
6
8
 
7
9
  import tree_sitter_cfengine as tscfengine
8
10
  from tree_sitter import Language, Parser, Node
9
11
  from cfbs.pretty import pretty_file, pretty_check_file
10
- from cfengine_cli.lint import check_policy_syntax
12
+ from cfengine_cli.lint import check_policy_syntax, PolicySyntaxError
13
+ from cfengine_cli.utils import UserError
11
14
 
12
15
  # Node types that increase indentation by 2 when entered
13
16
  INDENTED_TYPES = {
@@ -233,6 +236,11 @@ def split_generic_list(
233
236
  """Split list elements into one-per-line strings, each pre-indented."""
234
237
  has_macros = _contains_macro(middle)
235
238
  elements: list[str] = []
239
+ # Indices of the lines that end a top-level value element. For a nested
240
+ # element, like a function call inside an slist, this is only the final line,
241
+ # outside the function call, so we do not add trailing commas into function
242
+ # call argument lists.
243
+ value_end_indices: list[int] = []
236
244
  for element in middle:
237
245
  if elements and element.type == ",":
238
246
  elements[-1] = elements[-1] + ","
@@ -240,6 +248,9 @@ def split_generic_list(
240
248
  if element.type == "macro":
241
249
  elements.append(text(element))
242
250
  continue
251
+ if element.type == "comment":
252
+ elements.append(" " * indent + text(element))
253
+ continue
243
254
  line = " " * indent + stringify_single_line_node(element)
244
255
  # Strict < reserves 1 char for the comma appended after this check
245
256
  if len(line) < line_length:
@@ -248,14 +259,18 @@ def split_generic_list(
248
259
  lines = split_generic_value(element, indent, line_length)
249
260
  elements.append(" " * indent + lines[0])
250
261
  elements.extend(lines[1:])
262
+ value_end_indices.append(len(elements) - 1)
251
263
 
252
- # Adjust trailing commas: with macros, fix every non-macro element
253
- # (one per branch); without, fix only the last non-comment element.
264
+ # Set trailing commas
254
265
  if has_macros:
255
- for i, e in enumerate(elements):
256
- if not e.lstrip().startswith(("@", "#")):
257
- elements[i] = _set_trailing_comma(e, trailing_comma)
266
+ # With macros: Use the indices we counted earlier to ensure there
267
+ # are trailing commas at the right places (before macro, inside macro,
268
+ # after macros), but NOT inside the nested function calls which may
269
+ # be multi line
270
+ for i in value_end_indices:
271
+ elements[i] = _set_trailing_comma(elements[i], trailing_comma)
258
272
  else:
273
+ # No macros: Just ensure the last non-comment element has a trailing comma
259
274
  for i in reversed(range(len(elements))):
260
275
  if not elements[i].lstrip().startswith("#"):
261
276
  elements[i] = _set_trailing_comma(elements[i], trailing_comma)
@@ -985,3 +1000,79 @@ def format_policy_fin_fout(
985
1000
  new_data = fmt.buffer + "\n"
986
1001
  fout.write(new_data)
987
1002
  return 0
1003
+
1004
+
1005
+ def _format_filename(filename: str, line_length: int, check: bool) -> int:
1006
+ """Format a single file.
1007
+
1008
+ Raises PolicySyntaxError for .cf files with syntax errors."""
1009
+ if filename.endswith(".json"):
1010
+ return format_json_file(filename, check)
1011
+ if filename.endswith(".cf"):
1012
+ return format_policy_file(filename, line_length, check)
1013
+ raise UserError(f"Unrecognized file format: {filename}")
1014
+
1015
+
1016
+ def _format_dirname(directory: str, line_length: int, check: bool) -> int:
1017
+ ret = 0
1018
+ for root, dirs, files in os.walk(directory):
1019
+ # Don't recurse into hidden folders
1020
+ dirs[:] = [d for d in dirs if not d.startswith(".")]
1021
+ for name in sorted(files):
1022
+ if name.startswith("."):
1023
+ continue # Hidden files are ignored by default
1024
+ if (
1025
+ name.endswith(".x.cf")
1026
+ or name.endswith(".input.cf")
1027
+ or name.endswith(".output.cf")
1028
+ or name.endswith(".expected.cf")
1029
+ ):
1030
+ continue # Test files skipped during directory traversal
1031
+ if name.endswith(
1032
+ (".input.json", ".jqinput.json", ".x.json", ".expected.json")
1033
+ ):
1034
+ continue # Test files skipped during directory traversal
1035
+ filepath = os.path.join(root, name)
1036
+ if name.endswith(".json") or name.endswith(".cf"):
1037
+ ret |= _format_filename(filepath, line_length, check)
1038
+ return ret
1039
+
1040
+
1041
+ def _format_paths_inner(names, line_length, check) -> int:
1042
+ if not names:
1043
+ return _format_dirname(".", line_length, check)
1044
+ if len(names) == 1 and names[0] == "-":
1045
+ # Special case, format policy file from stdin to stdout
1046
+ return format_policy_fin_fout(sys.stdin, sys.stdout, line_length, check)
1047
+
1048
+ ret = 0
1049
+ for name in names:
1050
+ if name == "-":
1051
+ raise UserError(
1052
+ "The - argument has a special meaning and cannot be combined with other paths"
1053
+ )
1054
+ if not os.path.exists(name):
1055
+ raise UserError(f"{name} does not exist")
1056
+ if os.path.isfile(name):
1057
+ ret |= _format_filename(name, line_length, check)
1058
+ continue
1059
+ if os.path.isdir(name):
1060
+ ret |= _format_dirname(name, line_length, check)
1061
+ continue
1062
+ if check:
1063
+ return ret
1064
+ return 0
1065
+
1066
+
1067
+ def format_paths(names, line_length, check) -> int:
1068
+ """Format the given paths (files and/or directories), or stdin.
1069
+
1070
+ With no names, formats the current directory. A single "-" formats
1071
+ policy from stdin to stdout. Returns 0 on success (or no reformat
1072
+ needed), 1 when reformatting is needed in check mode or a policy file
1073
+ has syntax errors."""
1074
+ try:
1075
+ return _format_paths_inner(names, line_length, check)
1076
+ except PolicySyntaxError as e:
1077
+ print(f"Error: {e}")
1078
+ return 1