cfengine 0.16.2__tar.gz → 0.16.4__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 (157) hide show
  1. {cfengine-0.16.2 → cfengine-0.16.4}/PKG-INFO +19 -3
  2. {cfengine-0.16.2 → cfengine-0.16.4}/README.md +18 -2
  3. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine.egg-info/PKG-INFO +19 -3
  4. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/commands.py +3 -74
  5. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/dev.py +7 -5
  6. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/docs.py +20 -12
  7. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/format.py +88 -1
  8. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/004_comments.expected.cf +13 -0
  9. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/004_comments.input.cf +11 -0
  10. {cfengine-0.16.2 → cfengine-0.16.4}/uv.lock +9 -9
  11. {cfengine-0.16.2 → cfengine-0.16.4}/.github/dependabot.yml +0 -0
  12. {cfengine-0.16.2 → cfengine-0.16.4}/.github/workflows/lint-policy-in-other-repos.yml +0 -0
  13. {cfengine-0.16.2 → cfengine-0.16.4}/.github/workflows/make-check.yml +0 -0
  14. {cfengine-0.16.2 → cfengine-0.16.4}/.github/workflows/pypi-publish.yml +0 -0
  15. {cfengine-0.16.2 → cfengine-0.16.4}/.github/workflows/stale.yml +0 -0
  16. {cfengine-0.16.2 → cfengine-0.16.4}/.github/workflows/update-syntax-description.yml +0 -0
  17. {cfengine-0.16.2 → cfengine-0.16.4}/.gitignore +0 -0
  18. {cfengine-0.16.2 → cfengine-0.16.4}/.python-version +0 -0
  19. {cfengine-0.16.2 → cfengine-0.16.4}/CLAUDE.md +0 -0
  20. {cfengine-0.16.2 → cfengine-0.16.4}/HACKING.md +0 -0
  21. {cfengine-0.16.2 → cfengine-0.16.4}/LICENSE +0 -0
  22. {cfengine-0.16.2 → cfengine-0.16.4}/Makefile +0 -0
  23. {cfengine-0.16.2 → cfengine-0.16.4}/ci/01-install.sh +0 -0
  24. {cfengine-0.16.2 → cfengine-0.16.4}/ci/02-safe-tests.sh +0 -0
  25. {cfengine-0.16.2 → cfengine-0.16.4}/ci/03-unsafe-tests.sh +0 -0
  26. {cfengine-0.16.2 → cfengine-0.16.4}/pyproject.toml +0 -0
  27. {cfengine-0.16.2 → cfengine-0.16.4}/setup.cfg +0 -0
  28. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine.egg-info/SOURCES.txt +0 -0
  29. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine.egg-info/dependency_links.txt +0 -0
  30. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine.egg-info/entry_points.txt +0 -0
  31. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine.egg-info/requires.txt +0 -0
  32. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine.egg-info/top_level.txt +0 -0
  33. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/__init__.py +0 -0
  34. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/__main__.py +0 -0
  35. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/changelog.py +0 -0
  36. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/deptool-README.md +0 -0
  37. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/deptool.py +0 -0
  38. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/lint.py +0 -0
  39. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/lint_csv.py +0 -0
  40. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/lint_yml.py +0 -0
  41. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/main.py +0 -0
  42. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/masterfiles/__init__.py +0 -0
  43. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/masterfiles/analyze.py +0 -0
  44. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/masterfiles/check_download_matches_git.py +0 -0
  45. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/masterfiles/download.py +0 -0
  46. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/masterfiles/generate_git_tags.py +0 -0
  47. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/masterfiles/generate_release_information.py +0 -0
  48. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/masterfiles/generate_vcf_download.py +0 -0
  49. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/masterfiles/generate_vcf_git_checkout.py +0 -0
  50. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/paths.py +0 -0
  51. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/profile.py +0 -0
  52. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/shell.py +0 -0
  53. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/syntax-description.json +0 -0
  54. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/syntax_tree.py +0 -0
  55. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/up.py +0 -0
  56. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/utils.py +0 -0
  57. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/validate.py +0 -0
  58. {cfengine-0.16.2 → cfengine-0.16.4}/src/cfengine_cli/version.py +0 -0
  59. {cfengine-0.16.2 → cfengine-0.16.4}/tests/README.md +0 -0
  60. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/001_hello_world.expected.cf +0 -0
  61. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/001_hello_world.input.cf +0 -0
  62. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/002_basics.expected.cf +0 -0
  63. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/002_basics.input.cf +0 -0
  64. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/003_wrapping.expected.cf +0 -0
  65. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/003_wrapping.input.cf +0 -0
  66. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/005_bundle_comments.expected.cf +0 -0
  67. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/005_bundle_comments.input.cf +0 -0
  68. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/006_remove_empty_comments.expected.cf +0 -0
  69. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/006_remove_empty_comments.input.cf +0 -0
  70. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/007_class_guarded_empty_lines.expected.cf +0 -0
  71. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/007_class_guarded_empty_lines.input.cf +0 -0
  72. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/008_long_string.expected.cf +0 -0
  73. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/008_long_string.input.cf +0 -0
  74. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/009_single_line.expected.cf +0 -0
  75. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/009_single_line.input.cf +0 -0
  76. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/010_stakeholder.expected.cf +0 -0
  77. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/010_stakeholder.input.cf +0 -0
  78. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/011_macros.expected.cf +0 -0
  79. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/011_macros.input.cf +0 -0
  80. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/011_promises.expected.cf +0 -0
  81. {cfengine-0.16.2 → cfengine-0.16.4}/tests/format/011_promises.input.cf +0 -0
  82. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/001_hello_world.cf +0 -0
  83. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/002_deprecations.expected.txt +0 -0
  84. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/002_deprecations.x.cf +0 -0
  85. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/003_name_lowercase.cf +0 -0
  86. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/003_name_lowercase.expected.txt +0 -0
  87. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/003_name_lowercase.x.cf +0 -0
  88. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/004_block_type.cf +0 -0
  89. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/004_block_type.expected.txt +0 -0
  90. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/004_block_type.x.cf +0 -0
  91. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/005_syntax_error.cf +0 -0
  92. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/005_syntax_error.expected.txt +0 -0
  93. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/005_syntax_error.x.cf +0 -0
  94. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/006_empty_file.expected.txt +0 -0
  95. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/006_empty_file.x.cf +0 -0
  96. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/007_namespace.cf +0 -0
  97. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/007_namespace.expected.txt +0 -0
  98. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/007_namespace.x.cf +0 -0
  99. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/008_bundle_shadows_function.cf +0 -0
  100. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/008_bundle_shadows_function.expected.txt +0 -0
  101. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/008_bundle_shadows_function.x.cf +0 -0
  102. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/009_unknown_function_inside_vars.cf +0 -0
  103. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/009_unknown_function_inside_vars.expected.txt +0 -0
  104. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/009_unknown_function_inside_vars.x.cf +0 -0
  105. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/010_mutually_exclusive_types_vars.cf +0 -0
  106. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/010_mutually_exclusive_types_vars.expected.txt +0 -0
  107. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/010_mutually_exclusive_types_vars.x.cf +0 -0
  108. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/011_invalid_attributes.expected.txt +0 -0
  109. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/011_invalid_attributes.x.cf +0 -0
  110. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/012_function_call_arg_count.cf +0 -0
  111. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/012_function_call_arg_count.expected.txt +0 -0
  112. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/012_function_call_arg_count.x.cf +0 -0
  113. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/013_num_args_body.expected.txt +0 -0
  114. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/013_num_args_body.x.cf +0 -0
  115. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/013_num_args_bundle.expected.txt +0 -0
  116. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/013_num_args_bundle.x.cf +0 -0
  117. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/013_num_args_bundle_body.cf +0 -0
  118. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/014_variadic_func_arg_count.cf +0 -0
  119. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/014_variadic_func_arg_count.expected.txt +0 -0
  120. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/014_variadic_func_arg_count.x.cf +0 -0
  121. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/015_macro_multi_def_bundle.cf +0 -0
  122. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/015_macro_multi_def_bundle.expected.txt +0 -0
  123. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/015_macro_multi_def_bundle.x.cf +0 -0
  124. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/016_half_promises.cf +0 -0
  125. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/016_half_promises.expected.txt +0 -0
  126. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/016_half_promises.x.cf +0 -0
  127. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/017_implies_body.cf +0 -0
  128. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/017_implies_body.expected.txt +0 -0
  129. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/017_implies_body.x.cf +0 -0
  130. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/018_nested_calls.cf +0 -0
  131. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/018_nested_calls.expected.txt +0 -0
  132. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/018_nested_calls.x.cf +0 -0
  133. {cfengine-0.16.2 → cfengine-0.16.4}/tests/lint/019_bundle_name_expansion.cf +0 -0
  134. {cfengine-0.16.2 → cfengine-0.16.4}/tests/run-format-tests.sh +0 -0
  135. {cfengine-0.16.2 → cfengine-0.16.4}/tests/run-lint-tests.sh +0 -0
  136. {cfengine-0.16.2 → cfengine-0.16.4}/tests/run-shell-tests.sh +0 -0
  137. {cfengine-0.16.2 → cfengine-0.16.4}/tests/run-up-validate-tests.sh +0 -0
  138. {cfengine-0.16.2 → cfengine-0.16.4}/tests/shell/001-help.sh +0 -0
  139. {cfengine-0.16.2 → cfengine-0.16.4}/tests/shell/002-version.sh +0 -0
  140. {cfengine-0.16.2 → cfengine-0.16.4}/tests/shell/003-format.sh +0 -0
  141. {cfengine-0.16.2 → cfengine-0.16.4}/tests/shell/004-format-check.sh +0 -0
  142. {cfengine-0.16.2 → cfengine-0.16.4}/tests/shell/005-lint.sh +0 -0
  143. {cfengine-0.16.2 → cfengine-0.16.4}/tests/unit/__init__.py +0 -0
  144. {cfengine-0.16.2 → cfengine-0.16.4}/tests/unit/test_changelog.py +0 -0
  145. {cfengine-0.16.2 → cfengine-0.16.4}/tests/unit/test_deps.py +0 -0
  146. {cfengine-0.16.2 → cfengine-0.16.4}/tests/unit/test_format.py +0 -0
  147. {cfengine-0.16.2 → cfengine-0.16.4}/tests/unit/test_lint_csv.py +0 -0
  148. {cfengine-0.16.2 → cfengine-0.16.4}/tests/unit/test_lint_yml.py +0 -0
  149. {cfengine-0.16.2 → cfengine-0.16.4}/tests/unit/test_paths.py +0 -0
  150. {cfengine-0.16.2 → cfengine-0.16.4}/tests/unit/test_utils.py +0 -0
  151. {cfengine-0.16.2 → cfengine-0.16.4}/tests/unit/test_version.py +0 -0
  152. {cfengine-0.16.2 → cfengine-0.16.4}/tests/up-validate/001_complex_config.yaml +0 -0
  153. {cfengine-0.16.2 → cfengine-0.16.4}/tests/up-validate/002_version.x.yaml +0 -0
  154. {cfengine-0.16.2 → cfengine-0.16.4}/tests/up-validate/003_box.x.yaml +0 -0
  155. {cfengine-0.16.2 → cfengine-0.16.4}/tests/up-validate/004_count.x.yaml +0 -0
  156. {cfengine-0.16.2 → cfengine-0.16.4}/tests/up-validate/mocks/cloud_config.json +0 -0
  157. {cfengine-0.16.2 → cfengine-0.16.4}/tests/up-validate/mocks/vagrant +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cfengine
3
- Version: 0.16.2
3
+ Version: 0.16.4
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.2
3
+ Version: 0.16.4
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.
@@ -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
 
@@ -279,6 +279,7 @@ def _process_markdown_code_blocks(
279
279
  for cb in parsed_markdowns["files"][origin_path]["code-blocks"][i:]:
280
280
  cb["first_line"] += offset
281
281
  cb["last_line"] += offset
282
+ offset = 0
282
283
 
283
284
  language = _translate_language(code_block["language"])
284
285
  snippet_number = i + 1
@@ -419,12 +420,11 @@ def _run_prettier(path) -> bool:
419
420
  return formatted
420
421
 
421
422
 
422
- def _update_docs_single_arg(path):
423
+ def _format_docs_single_arg(path):
423
424
  if not os.path.exists(path):
424
425
  raise UserError(f"The specified file/folder '{path}' does not exist")
425
426
  if not os.path.isfile(path) and not os.path.isdir(path):
426
427
  raise UserError(f"Argument '{path}' is not a file or a folder")
427
-
428
428
  formatted = False
429
429
  if os.path.isdir(path) or path.endswith(".py"):
430
430
  if _run_black(path):
@@ -442,13 +442,16 @@ def _update_docs_single_arg(path):
442
442
  replace=True,
443
443
  cleanup=True,
444
444
  )
445
-
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
446
449
  return formatted
447
450
 
448
451
 
449
- def update_docs(paths) -> int:
452
+ def format_docs() -> int:
450
453
  """
451
- 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:
452
455
  - python code with black
453
456
  - markdown files with prettier
454
457
  - code blocks inside markdown files are formatted for the formats supported by prettier
@@ -457,23 +460,27 @@ def update_docs(paths) -> int:
457
460
  Run by the command:
458
461
  cfengine dev format-docs
459
462
  """
460
- if not paths:
461
- _update_docs_single_arg(".")
462
- return 0
463
- for path in paths:
464
- _update_docs_single_arg(path)
463
+ _format_docs_single_arg(".")
465
464
  return 0
466
465
 
467
466
 
468
- def check_docs() -> int:
467
+ def lint_docs() -> int:
469
468
  """
470
469
  Run checks / tests on docs.
471
470
 
472
471
  Run by the command:
473
472
  cfengine dev lint-docs"""
473
+
474
+ # cfengine lint
474
475
  r = lint_args(["."], strict=False)
475
476
  if r != 0:
477
+ print(f"Failure - {r} errors while running cfengine lint .")
476
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:
477
484
  _process_markdown_code_blocks(
478
485
  path=".",
479
486
  languages=["json", "cf3"],
@@ -484,4 +491,5 @@ def check_docs() -> int:
484
491
  replace=False,
485
492
  cleanup=True,
486
493
  )
494
+ print("Success - all files look correct")
487
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 = {
@@ -822,6 +825,10 @@ def _comment_indent(node: Node, indent: int) -> int:
822
825
  # content at the end of the previous sibling subtree, so that comments
823
826
  # appended after a class-guarded block visually belong to that block.
824
827
  if nearest is None:
828
+ # A comment between a promiser and its terminating ';' is a promise
829
+ # continuation — indent it past the promiser.
830
+ if node.parent and node.parent.type in {"promise", "half_promise"}:
831
+ return indent + 2
825
832
  prev = _skip_comments(node.prev_named_sibling, "prev")
826
833
  if prev and prev.type in INDENTED_TYPES:
827
834
  return _trailing_comment_indent(prev, indent)
@@ -905,6 +912,10 @@ def _autoformat(
905
912
  if node.type in {",", ";"}:
906
913
  if previous and previous.type == "macro":
907
914
  fmt.print(node, indent + 2)
915
+ elif previous and previous.type == "comment":
916
+ # A '#' comment runs to end of line, so a following ';'/',' can't
917
+ # share that line — put it on its own continuation line.
918
+ fmt.print(node, indent + 2)
908
919
  else:
909
920
  fmt.print_same_line(node)
910
921
  elif node.type == "comment":
@@ -977,3 +988,79 @@ def format_policy_fin_fout(
977
988
  new_data = fmt.buffer + "\n"
978
989
  fout.write(new_data)
979
990
  return 0
991
+
992
+
993
+ def _format_filename(filename: str, line_length: int, check: bool) -> int:
994
+ """Format a single file.
995
+
996
+ Raises PolicySyntaxError for .cf files with syntax errors."""
997
+ if filename.endswith(".json"):
998
+ return format_json_file(filename, check)
999
+ if filename.endswith(".cf"):
1000
+ return format_policy_file(filename, line_length, check)
1001
+ raise UserError(f"Unrecognized file format: {filename}")
1002
+
1003
+
1004
+ def _format_dirname(directory: str, line_length: int, check: bool) -> int:
1005
+ ret = 0
1006
+ for root, dirs, files in os.walk(directory):
1007
+ # Don't recurse into hidden folders
1008
+ dirs[:] = [d for d in dirs if not d.startswith(".")]
1009
+ for name in sorted(files):
1010
+ if name.startswith("."):
1011
+ continue # Hidden files are ignored by default
1012
+ if (
1013
+ name.endswith(".x.cf")
1014
+ or name.endswith(".input.cf")
1015
+ or name.endswith(".output.cf")
1016
+ or name.endswith(".expected.cf")
1017
+ ):
1018
+ continue # Test files skipped during directory traversal
1019
+ if name.endswith(
1020
+ (".input.json", ".jqinput.json", ".x.json", ".expected.json")
1021
+ ):
1022
+ continue # Test files skipped during directory traversal
1023
+ filepath = os.path.join(root, name)
1024
+ if name.endswith(".json") or name.endswith(".cf"):
1025
+ ret |= _format_filename(filepath, line_length, check)
1026
+ return ret
1027
+
1028
+
1029
+ def _format_paths_inner(names, line_length, check) -> int:
1030
+ if not names:
1031
+ return _format_dirname(".", line_length, check)
1032
+ if len(names) == 1 and names[0] == "-":
1033
+ # Special case, format policy file from stdin to stdout
1034
+ return format_policy_fin_fout(sys.stdin, sys.stdout, line_length, check)
1035
+
1036
+ ret = 0
1037
+ for name in names:
1038
+ if name == "-":
1039
+ raise UserError(
1040
+ "The - argument has a special meaning and cannot be combined with other paths"
1041
+ )
1042
+ if not os.path.exists(name):
1043
+ raise UserError(f"{name} does not exist")
1044
+ if os.path.isfile(name):
1045
+ ret |= _format_filename(name, line_length, check)
1046
+ continue
1047
+ if os.path.isdir(name):
1048
+ ret |= _format_dirname(name, line_length, check)
1049
+ continue
1050
+ if check:
1051
+ return ret
1052
+ return 0
1053
+
1054
+
1055
+ def format_paths(names, line_length, check) -> int:
1056
+ """Format the given paths (files and/or directories), or stdin.
1057
+
1058
+ With no names, formats the current directory. A single "-" formats
1059
+ policy from stdin to stdout. Returns 0 on success (or no reformat
1060
+ needed), 1 when reformatting is needed in check mode or a policy file
1061
+ has syntax errors."""
1062
+ try:
1063
+ return _format_paths_inner(names, line_length, check)
1064
+ except PolicySyntaxError as e:
1065
+ print(f"Error: {e}")
1066
+ return 1
@@ -151,3 +151,16 @@ bundle agent stigs
151
151
  vars:
152
152
  "foo" string => "bar";
153
153
  }
154
+
155
+ bundle agent example_comment_before_semicolon
156
+ {
157
+ files:
158
+ "affected object"
159
+ # ...details....
160
+ ;
161
+
162
+ processes:
163
+ "affected object"
164
+ # ...details....
165
+ ;
166
+ }
@@ -143,3 +143,14 @@ bundle agent stigs
143
143
  vars:
144
144
  "foo" string => "bar";
145
145
  }
146
+ bundle agent example_comment_before_semicolon
147
+ {
148
+ files:
149
+ "affected object"
150
+ # ...details....
151
+ ;
152
+ processes:
153
+ "affected object"
154
+ # ...details....
155
+ ;
156
+ }
@@ -25,11 +25,11 @@ wheels = [
25
25
 
26
26
  [[package]]
27
27
  name = "certifi"
28
- version = "2026.5.20"
28
+ version = "2026.6.17"
29
29
  source = { registry = "https://pypi.org/simple" }
30
- sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" }
30
+ sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" }
31
31
  wheels = [
32
- { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" },
32
+ { url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" },
33
33
  ]
34
34
 
35
35
  [[package]]
@@ -46,11 +46,11 @@ wheels = [
46
46
 
47
47
  [[package]]
48
48
  name = "cfbs"
49
- version = "5.6.1"
49
+ version = "5.6.2"
50
50
  source = { registry = "https://pypi.org/simple" }
51
- sdist = { url = "https://files.pythonhosted.org/packages/0d/ab/60bc20d8aae03a6ee288cdac7a9cb35778eeed974b452895e7a6d3ff5dd9/cfbs-5.6.1.tar.gz", hash = "sha256:9e50371add952f05e2f4591d6a6280d3aca5ce60ad9449910c86a20f20768781", size = 89383, upload-time = "2026-06-12T17:21:45.984Z" }
51
+ sdist = { url = "https://files.pythonhosted.org/packages/b2/46/bda65e6ee28e7bb8fa25b1b6a2ee489c9a2d58caa9c72ba2141af975309e/cfbs-5.6.2.tar.gz", hash = "sha256:25bfa7872d33e0ba78c9c794a485448216bba52ebe356e3db7e7559f22ec1001", size = 89632, upload-time = "2026-06-18T17:29:17.823Z" }
52
52
  wheels = [
53
- { url = "https://files.pythonhosted.org/packages/df/a0/a5df7ad2e2373b6c1cc8140de229da9450348bf563ae7609b13ed422544e/cfbs-5.6.1-py3-none-any.whl", hash = "sha256:8897f17f2544ccb741f95526bf35399fe7d2de504a261d7ad8f0881b7e097bb7", size = 85254, upload-time = "2026-06-12T17:21:44.63Z" },
53
+ { url = "https://files.pythonhosted.org/packages/04/30/829753a0febc5c789b6b7bcb4020314bac5aaa0346a3954390a1b377e44b/cfbs-5.6.2-py3-none-any.whl", hash = "sha256:0e24989244545c754c4165773399370ab2d1965281994e13ada3709fde77bae0", size = 85533, upload-time = "2026-06-18T17:29:16.689Z" },
54
54
  ]
55
55
 
56
56
  [[package]]
@@ -455,7 +455,7 @@ wheels = [
455
455
 
456
456
  [[package]]
457
457
  name = "pytest"
458
- version = "9.0.3"
458
+ version = "9.1.1"
459
459
  source = { registry = "https://pypi.org/simple" }
460
460
  dependencies = [
461
461
  { name = "colorama", marker = "sys_platform == 'win32'" },
@@ -466,9 +466,9 @@ dependencies = [
466
466
  { name = "pygments" },
467
467
  { name = "tomli", marker = "python_full_version < '3.11'" },
468
468
  ]
469
- sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
469
+ sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
470
470
  wheels = [
471
- { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
471
+ { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
472
472
  ]
473
473
 
474
474
  [[package]]
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