cfengine 0.23.3__tar.gz → 0.24.1__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.
- {cfengine-0.23.3 → cfengine-0.24.1}/.github/workflows/coverage.yml +1 -1
- {cfengine-0.23.3 → cfengine-0.24.1}/.github/workflows/lint-policy-in-other-repos.yml +1 -1
- {cfengine-0.23.3 → cfengine-0.24.1}/.github/workflows/make-check.yml +1 -1
- {cfengine-0.23.3 → cfengine-0.24.1}/.github/workflows/pypi-publish.yml +1 -1
- {cfengine-0.23.3 → cfengine-0.24.1}/.github/workflows/renovate.yml +2 -1
- {cfengine-0.23.3 → cfengine-0.24.1}/.github/workflows/update-syntax-description.yml +1 -1
- {cfengine-0.23.3 → cfengine-0.24.1}/CHEATSHEET.md +2 -2
- {cfengine-0.23.3 → cfengine-0.24.1}/CLAUDE.md +1 -1
- {cfengine-0.23.3 → cfengine-0.24.1}/HACKING.md +3 -3
- {cfengine-0.23.3 → cfengine-0.24.1}/Makefile +1 -1
- {cfengine-0.23.3 → cfengine-0.24.1}/PKG-INFO +5 -7
- {cfengine-0.23.3 → cfengine-0.24.1}/pyproject.toml +8 -10
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine.egg-info/PKG-INFO +5 -7
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine.egg-info/SOURCES.txt +2 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine.egg-info/requires.txt +2 -2
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine.egg-info/scm_file_list.json +2 -0
- cfengine-0.24.1/src/cfengine.egg-info/scm_version.json +8 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/cfengine_wrapper/arg_parse.py +31 -9
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/cfengine_wrapper/cfengine_commands.py +35 -2
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/cfengine_wrapper/cfengine_objects.py +3 -2
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/cfengine_wrapper/cfengine_utils.py +41 -3
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/changelog.py +99 -19
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/commands.py +6 -3
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/deptool-README.md +2 -2
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/deptool.py +10 -18
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/format.py +2 -2
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/initialize_project.py +33 -5
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/lint.py +5 -5
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/main.py +35 -4
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/masterfiles/download.py +2 -2
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/syntax-description.json +81 -1
- cfengine-0.24.1/src/cfengine_cli/utils.py +28 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/002_basics.expected.cf +2 -2
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/002_basics.input.cf +2 -2
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/011_promises.expected.cf +1 -1
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/011_promises.input.cf +1 -1
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/run-format-tests.sh +1 -1
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/run-lint-tests.sh +1 -1
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/run-shell-tests.sh +1 -1
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/shell/003-format.sh +1 -1
- cfengine-0.24.1/tests/unit/test_cfengine_wrapper.py +95 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/unit/test_changelog.py +3 -1
- cfengine-0.24.1/tests/unit/test_initialize_project.py +11 -0
- cfengine-0.24.1/uv.lock +804 -0
- cfengine-0.23.3/src/cfengine.egg-info/scm_version.json +0 -8
- cfengine-0.23.3/src/cfengine_cli/utils.py +0 -9
- cfengine-0.23.3/uv.lock +0 -829
- {cfengine-0.23.3 → cfengine-0.24.1}/.coveragerc +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/.github/renovate-config.js +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/.github/workflows/stale.yml +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/.gitignore +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/.python-version +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/LICENSE +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/README.md +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/ci/01-install.sh +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/ci/02-safe-tests.sh +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/ci/03-unsafe-tests.sh +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/renovate.json5 +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/setup.cfg +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine.egg-info/dependency_links.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine.egg-info/entry_points.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine.egg-info/top_level.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/__init__.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/__main__.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/cfengine_wrapper/__init__.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/container.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/dev.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/docker/test-agent/Dockerfile +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/docs.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/lint_csv.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/lint_yml.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/masterfiles/__init__.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/masterfiles/analyze.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/masterfiles/check_download_matches_git.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/masterfiles/generate_git_tags.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/masterfiles/generate_release_information.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/masterfiles/generate_vcf_download.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/masterfiles/generate_vcf_git_checkout.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/paths.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/profile.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/shell.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/syntax_tree.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/up.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/validate.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/src/cfengine_cli/version.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/README.md +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/001_hello_world.expected.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/001_hello_world.input.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/003_wrapping.expected.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/003_wrapping.input.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/004_comments.expected.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/004_comments.input.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/005_bundle_comments.expected.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/005_bundle_comments.input.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/006_remove_empty_comments.expected.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/006_remove_empty_comments.input.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/007_class_guarded_empty_lines.expected.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/007_class_guarded_empty_lines.input.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/008_long_string.expected.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/008_long_string.input.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/009_single_line.expected.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/009_single_line.input.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/010_stakeholder.expected.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/010_stakeholder.input.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/011_macros.expected.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/011_macros.input.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/012_sameline_comments.expected.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/format/012_sameline_comments.input.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/001_hello_world.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/002_deprecations.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/002_deprecations.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/003_name_lowercase.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/003_name_lowercase.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/003_name_lowercase.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/004_block_type.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/004_block_type.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/004_block_type.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/005_syntax_error.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/005_syntax_error.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/005_syntax_error.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/006_empty_file.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/006_empty_file.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/007_namespace.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/007_namespace.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/007_namespace.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/008_bundle_shadows_function.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/008_bundle_shadows_function.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/008_bundle_shadows_function.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/009_unknown_function_inside_vars.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/009_unknown_function_inside_vars.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/009_unknown_function_inside_vars.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/010_mutually_exclusive_types_vars.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/010_mutually_exclusive_types_vars.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/010_mutually_exclusive_types_vars.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/011_invalid_attributes.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/011_invalid_attributes.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/012_function_call_arg_count.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/012_function_call_arg_count.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/012_function_call_arg_count.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/013_num_args_body.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/013_num_args_body.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/013_num_args_bundle.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/013_num_args_bundle.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/013_num_args_bundle_body.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/014_variadic_func_arg_count.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/014_variadic_func_arg_count.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/014_variadic_func_arg_count.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/015_macro_multi_def_bundle.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/015_macro_multi_def_bundle.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/015_macro_multi_def_bundle.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/016_half_promises.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/016_half_promises.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/016_half_promises.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/017_implies_body.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/017_implies_body.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/017_implies_body.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/018_nested_calls.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/018_nested_calls.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/018_nested_calls.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/019_bundle_name_expansion.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/020_missing_braces.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/020_missing_braces.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/020_missing_semicolon.expected.txt +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/020_missing_semicolon.x.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/lint/021_reactor_strtotime_silence.cf +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/run-up-validate-tests.sh +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/shell/001-help.sh +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/shell/002-version.sh +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/shell/004-format-check.sh +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/shell/005-lint.sh +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/shell/006-test-fail.sh +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/shell/007-test-container.sh +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/shell/008-cfbs-index.sh +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/unit/__init__.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/unit/test_deps.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/unit/test_format.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/unit/test_lint_csv.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/unit/test_lint_yml.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/unit/test_paths.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/unit/test_utils.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/unit/test_version.py +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/up-validate/001_complex_config.yaml +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/up-validate/002_version.x.yaml +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/up-validate/003_box.x.yaml +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/up-validate/004_count.x.yaml +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/up-validate/mocks/cloud_config.json +0 -0
- {cfengine-0.23.3 → cfengine-0.24.1}/tests/up-validate/mocks/vagrant +0 -0
|
@@ -7,12 +7,13 @@ on:
|
|
|
7
7
|
|
|
8
8
|
jobs:
|
|
9
9
|
renovate:
|
|
10
|
+
if: contains(fromJSON('["cfengine","mendersoftware","NorthernTechHQ"]'), github.repository_owner)
|
|
10
11
|
runs-on: ubuntu-latest
|
|
11
12
|
steps:
|
|
12
13
|
- name: Checkout
|
|
13
14
|
uses: actions/checkout@v7.0.1
|
|
14
15
|
- name: Self-hosted Renovate
|
|
15
|
-
uses: renovatebot/github-action@v46.3.
|
|
16
|
+
uses: renovatebot/github-action@v46.3.12
|
|
16
17
|
with:
|
|
17
18
|
configurationFile: .github/renovate-config.js
|
|
18
19
|
token: ${{ secrets.RENOVATE_TOKEN }}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CFEngine CLI cheatsheet
|
|
2
2
|
|
|
3
|
-
Quick reference for `cfengine COMMANDS
|
|
3
|
+
Quick reference for `cfengine COMMANDS`.
|
|
4
4
|
For the full command list, run `cfengine help` or `cfengine COMMAND --help`.
|
|
5
5
|
|
|
6
6
|
## Managing hosts
|
|
@@ -86,7 +86,7 @@ cfengine init --policy-set
|
|
|
86
86
|
# Initialize an example build project for working on a custom promise type (python)
|
|
87
87
|
cfengine init --promise-type
|
|
88
88
|
|
|
89
|
-
# Initialize an
|
|
89
|
+
# Initialize an example module project for build.cfengine.com (or internal use)
|
|
90
90
|
cfengine init --policy-module
|
|
91
91
|
|
|
92
92
|
# Initialize an example policy module that takes input data
|
|
@@ -48,4 +48,4 @@ We have different suites:
|
|
|
48
48
|
- Unit tests in `tests/unit` test individual python functions.
|
|
49
49
|
- Formatting tests in `tests/format` test the formatter (`cfengine format`).
|
|
50
50
|
- Linting tests in `tests/lint` test the linter.
|
|
51
|
-
- Shell tests in `tests/shell`
|
|
51
|
+
- Shell tests in `tests/shell` test various subcommands and the tool as a whole in an end-to-end fashion.
|
|
@@ -16,7 +16,7 @@ If your editor does not do this automatically, you can run these tools from the
|
|
|
16
16
|
make format
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
## Installing from source
|
|
19
|
+
## Installing from source
|
|
20
20
|
|
|
21
21
|
For developers working on CFEngine CLI, you can install it globally using pipx:
|
|
22
22
|
|
|
@@ -53,7 +53,7 @@ uv run bash tests/run-shell-tests.sh
|
|
|
53
53
|
|
|
54
54
|
## Releasing new versions
|
|
55
55
|
|
|
56
|
-
Releases are [automated using a GH Action](https://github.com/cfengine/cfengine-cli/blob/main/.github/workflows/pypi-publish.yml)
|
|
56
|
+
Releases are [automated using a GH Action](https://github.com/cfengine/cfengine-cli/blob/main/.github/workflows/pypi-publish.yml).
|
|
57
57
|
We create tags and releases through the GitHub UI.
|
|
58
58
|
Go to the releases section in GitHub:
|
|
59
59
|
|
|
@@ -107,7 +107,7 @@ Go through the prompts:
|
|
|
107
107
|
https://github.com/cfengine/cfengine-cli/releases/tag/0.2.0
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
Then check that everything went well
|
|
110
|
+
Then check that everything went well:
|
|
111
111
|
|
|
112
112
|
https://github.com/cfengine/cfengine-cli/actions
|
|
113
113
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cfengine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.24.1
|
|
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
|
|
@@ -685,22 +685,20 @@ Classifier: Topic :: Security
|
|
|
685
685
|
Classifier: Topic :: Software Development :: Build Tools
|
|
686
686
|
Classifier: Topic :: System :: Distributed Computing
|
|
687
687
|
Classifier: Topic :: System :: Systems Administration
|
|
688
|
-
Classifier: Programming Language :: Python :: 3
|
|
689
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
690
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
691
688
|
Classifier: Programming Language :: Python :: 3.11
|
|
692
689
|
Classifier: Programming Language :: Python :: 3.12
|
|
693
690
|
Classifier: Programming Language :: Python :: 3.13
|
|
691
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
694
692
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
695
|
-
Requires-Python: >=3.
|
|
693
|
+
Requires-Python: >=3.11
|
|
696
694
|
Description-Content-Type: text/markdown
|
|
697
|
-
Requires-Dist: cf-remote>=0.9.
|
|
695
|
+
Requires-Dist: cf-remote>=0.9.8
|
|
698
696
|
Requires-Dist: cfbs>=5.8.1
|
|
699
697
|
Requires-Dist: tree-sitter-cfengine>=1.1.12
|
|
700
698
|
Requires-Dist: tree-sitter>=0.26.0
|
|
701
699
|
Requires-Dist: markdown-it-py>=4.2.0
|
|
702
700
|
Requires-Dist: pyyaml>=6.0.3
|
|
703
|
-
Requires-Dist: pydantic>=2.
|
|
701
|
+
Requires-Dist: pydantic>=2.14.3
|
|
704
702
|
|
|
705
703
|
# CFEngine command line interface (CLI)
|
|
706
704
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=84.
|
|
2
|
+
requires = ["setuptools>=84.2.1", "setuptools-scm>=10.3.4"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
@@ -8,15 +8,15 @@ dynamic = ["version"]
|
|
|
8
8
|
description = "Human-oriented CLI for interacting with CFEngine policy language, projects, and other CFEngine tools"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {file = "LICENSE"}
|
|
11
|
-
requires-python = ">=3.
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
12
|
dependencies = [
|
|
13
|
-
"cf-remote>=0.9.
|
|
13
|
+
"cf-remote>=0.9.8",
|
|
14
14
|
"cfbs>=5.8.1",
|
|
15
15
|
"tree-sitter-cfengine>=1.1.12",
|
|
16
16
|
"tree-sitter>=0.26.0",
|
|
17
17
|
"markdown-it-py>=4.2.0",
|
|
18
18
|
"pyyaml>=6.0.3",
|
|
19
|
-
"pydantic>=2.
|
|
19
|
+
"pydantic>=2.14.3",
|
|
20
20
|
]
|
|
21
21
|
classifiers = [
|
|
22
22
|
"Development Status :: 3 - Alpha",
|
|
@@ -27,12 +27,10 @@ classifiers = [
|
|
|
27
27
|
"Topic :: Software Development :: Build Tools",
|
|
28
28
|
"Topic :: System :: Distributed Computing",
|
|
29
29
|
"Topic :: System :: Systems Administration",
|
|
30
|
-
"Programming Language :: Python :: 3",
|
|
31
|
-
"Programming Language :: Python :: 3.9",
|
|
32
|
-
"Programming Language :: Python :: 3.10",
|
|
33
30
|
"Programming Language :: Python :: 3.11",
|
|
34
31
|
"Programming Language :: Python :: 3.12",
|
|
35
32
|
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Programming Language :: Python :: 3.14",
|
|
36
34
|
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
|
37
35
|
]
|
|
38
36
|
|
|
@@ -54,7 +52,7 @@ venv = ".venv"
|
|
|
54
52
|
|
|
55
53
|
[dependency-groups]
|
|
56
54
|
dev = [
|
|
57
|
-
"flake8>=7.
|
|
58
|
-
"pytest>=9.
|
|
59
|
-
"coverage
|
|
55
|
+
"flake8>=7.5.0",
|
|
56
|
+
"pytest>=9.2.0",
|
|
57
|
+
"coverage",
|
|
60
58
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cfengine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.24.1
|
|
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
|
|
@@ -685,22 +685,20 @@ Classifier: Topic :: Security
|
|
|
685
685
|
Classifier: Topic :: Software Development :: Build Tools
|
|
686
686
|
Classifier: Topic :: System :: Distributed Computing
|
|
687
687
|
Classifier: Topic :: System :: Systems Administration
|
|
688
|
-
Classifier: Programming Language :: Python :: 3
|
|
689
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
690
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
691
688
|
Classifier: Programming Language :: Python :: 3.11
|
|
692
689
|
Classifier: Programming Language :: Python :: 3.12
|
|
693
690
|
Classifier: Programming Language :: Python :: 3.13
|
|
691
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
694
692
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
695
|
-
Requires-Python: >=3.
|
|
693
|
+
Requires-Python: >=3.11
|
|
696
694
|
Description-Content-Type: text/markdown
|
|
697
|
-
Requires-Dist: cf-remote>=0.9.
|
|
695
|
+
Requires-Dist: cf-remote>=0.9.8
|
|
698
696
|
Requires-Dist: cfbs>=5.8.1
|
|
699
697
|
Requires-Dist: tree-sitter-cfengine>=1.1.12
|
|
700
698
|
Requires-Dist: tree-sitter>=0.26.0
|
|
701
699
|
Requires-Dist: markdown-it-py>=4.2.0
|
|
702
700
|
Requires-Dist: pyyaml>=6.0.3
|
|
703
|
-
Requires-Dist: pydantic>=2.
|
|
701
|
+
Requires-Dist: pydantic>=2.14.3
|
|
704
702
|
|
|
705
703
|
# CFEngine command line interface (CLI)
|
|
706
704
|
|
|
@@ -164,9 +164,11 @@ tests/shell/006-test-fail.sh
|
|
|
164
164
|
tests/shell/007-test-container.sh
|
|
165
165
|
tests/shell/008-cfbs-index.sh
|
|
166
166
|
tests/unit/__init__.py
|
|
167
|
+
tests/unit/test_cfengine_wrapper.py
|
|
167
168
|
tests/unit/test_changelog.py
|
|
168
169
|
tests/unit/test_deps.py
|
|
169
170
|
tests/unit/test_format.py
|
|
171
|
+
tests/unit/test_initialize_project.py
|
|
170
172
|
tests/unit/test_lint_csv.py
|
|
171
173
|
tests/unit/test_lint_yml.py
|
|
172
174
|
tests/unit/test_paths.py
|
|
@@ -157,9 +157,11 @@
|
|
|
157
157
|
"tests/shell/007-test-container.sh",
|
|
158
158
|
"tests/shell/008-cfbs-index.sh",
|
|
159
159
|
"tests/unit/__init__.py",
|
|
160
|
+
"tests/unit/test_cfengine_wrapper.py",
|
|
160
161
|
"tests/unit/test_changelog.py",
|
|
161
162
|
"tests/unit/test_deps.py",
|
|
162
163
|
"tests/unit/test_format.py",
|
|
164
|
+
"tests/unit/test_initialize_project.py",
|
|
163
165
|
"tests/unit/test_lint_csv.py",
|
|
164
166
|
"tests/unit/test_lint_yml.py",
|
|
165
167
|
"tests/unit/test_paths.py",
|
|
@@ -8,6 +8,8 @@ from cf_remote.args import (
|
|
|
8
8
|
add_spawn_args,
|
|
9
9
|
add_destroy_args,
|
|
10
10
|
add_connect_args,
|
|
11
|
+
add_list_args,
|
|
12
|
+
add_download_args,
|
|
11
13
|
)
|
|
12
14
|
|
|
13
15
|
|
|
@@ -20,6 +22,15 @@ def _add_index_arg(parser: argparse.ArgumentParser):
|
|
|
20
22
|
)
|
|
21
23
|
|
|
22
24
|
|
|
25
|
+
def _add_version_arg(parser: argparse.ArgumentParser):
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"--version",
|
|
28
|
+
"-V",
|
|
29
|
+
help="Specify version",
|
|
30
|
+
type=str,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
23
34
|
def parse_wrapper_args(subp: argparse._SubParsersAction):
|
|
24
35
|
update_parser = subp.add_parser(
|
|
25
36
|
"update",
|
|
@@ -39,7 +50,7 @@ def parse_wrapper_args(subp: argparse._SubParsersAction):
|
|
|
39
50
|
remove_parser.add_argument(
|
|
40
51
|
"module",
|
|
41
52
|
nargs="+",
|
|
42
|
-
help="Module(s)
|
|
53
|
+
help="Module(s) to remove",
|
|
43
54
|
)
|
|
44
55
|
|
|
45
56
|
add_parser = subp.add_parser(
|
|
@@ -50,7 +61,7 @@ def parse_wrapper_args(subp: argparse._SubParsersAction):
|
|
|
50
61
|
add_parser.add_argument(
|
|
51
62
|
"module",
|
|
52
63
|
nargs="+",
|
|
53
|
-
help="Module(s)
|
|
64
|
+
help="Module(s) to add",
|
|
54
65
|
)
|
|
55
66
|
_add_index_arg(add_parser)
|
|
56
67
|
search_parser = subp.add_parser(
|
|
@@ -61,7 +72,7 @@ def parse_wrapper_args(subp: argparse._SubParsersAction):
|
|
|
61
72
|
search_parser.add_argument(
|
|
62
73
|
"module",
|
|
63
74
|
nargs="+",
|
|
64
|
-
help="Module(s)
|
|
75
|
+
help="Module(s) to look up",
|
|
65
76
|
)
|
|
66
77
|
_add_index_arg(search_parser)
|
|
67
78
|
|
|
@@ -153,14 +164,25 @@ def parse_wrapper_args(subp: argparse._SubParsersAction):
|
|
|
153
164
|
help="Install CFEngine on the given hosts",
|
|
154
165
|
description="A wrapper around the cf-remote `install` function",
|
|
155
166
|
)
|
|
156
|
-
install_parser
|
|
157
|
-
"--version",
|
|
158
|
-
"-V",
|
|
159
|
-
help="Specify version",
|
|
160
|
-
type=str,
|
|
161
|
-
)
|
|
167
|
+
_add_version_arg(install_parser)
|
|
162
168
|
add_install_args(install_parser)
|
|
163
169
|
|
|
170
|
+
list_parser = subp.add_parser(
|
|
171
|
+
"list",
|
|
172
|
+
help="List CFEngine packages available for download",
|
|
173
|
+
description="A wrapper around the cf-remote `list` function",
|
|
174
|
+
)
|
|
175
|
+
_add_version_arg(list_parser)
|
|
176
|
+
add_list_args(list_parser)
|
|
177
|
+
|
|
178
|
+
download_parser = subp.add_parser(
|
|
179
|
+
"download",
|
|
180
|
+
help="Download CFEngine packages",
|
|
181
|
+
description="A wrapper around the cf-remote `download` function",
|
|
182
|
+
)
|
|
183
|
+
_add_version_arg(download_parser)
|
|
184
|
+
add_download_args(download_parser)
|
|
185
|
+
|
|
164
186
|
uninstall_parser = subp.add_parser(
|
|
165
187
|
"uninstall",
|
|
166
188
|
help="Uninstall CFEngine on the given hosts",
|
|
@@ -31,6 +31,7 @@ from cfengine_cli.cfengine_wrapper.cfengine_objects import (
|
|
|
31
31
|
)
|
|
32
32
|
from cfengine_cli.cfengine_wrapper.cfengine_utils import (
|
|
33
33
|
extract_agent_file,
|
|
34
|
+
has_known_hubs,
|
|
34
35
|
prompt_two_options,
|
|
35
36
|
prompt_yes_no,
|
|
36
37
|
require_executable,
|
|
@@ -197,7 +198,7 @@ def report(
|
|
|
197
198
|
for agent in clients:
|
|
198
199
|
rc = _refresh_agent(agent)
|
|
199
200
|
if rc != 0:
|
|
200
|
-
log.error(f"Refresh failed on {agent.label}
|
|
201
|
+
log.error(f"Refresh failed on {agent.label}")
|
|
201
202
|
errors += 1
|
|
202
203
|
|
|
203
204
|
for hub in hubs:
|
|
@@ -206,7 +207,7 @@ def report(
|
|
|
206
207
|
client_ips = [client.location.split("@", 1)[1] for client in clients]
|
|
207
208
|
rc = _query_hub_delta(hub.hub, client_ips)
|
|
208
209
|
if rc != 0:
|
|
209
|
-
log.error(f"Hub refresh failed on {hub.label}
|
|
210
|
+
log.error(f"Hub refresh failed on {hub.label}")
|
|
210
211
|
errors += 1
|
|
211
212
|
|
|
212
213
|
if errors > 0:
|
|
@@ -219,9 +220,37 @@ def setup_code(target: str | None = None) -> int:
|
|
|
219
220
|
return hub.run("--new-setup-code")
|
|
220
221
|
|
|
221
222
|
|
|
223
|
+
def _policy_changed_since_build() -> bool:
|
|
224
|
+
"""True if there is no build, or project files changed since the last one."""
|
|
225
|
+
tarball = os.path.join("out", "masterfiles.tgz")
|
|
226
|
+
if not os.path.isfile(tarball):
|
|
227
|
+
return True
|
|
228
|
+
built_at = os.path.getmtime(tarball)
|
|
229
|
+
for root, dirs, files in os.walk("."):
|
|
230
|
+
# Skip build output and hidden entries (.git, editor state, ...)
|
|
231
|
+
dirs[:] = [d for d in dirs if d[0] != "." and (root, d) != (".", "out")]
|
|
232
|
+
for name in files:
|
|
233
|
+
path = os.path.join(root, name)
|
|
234
|
+
if name[0] != "." and os.path.getmtime(path) > built_at:
|
|
235
|
+
return True
|
|
236
|
+
return False
|
|
237
|
+
|
|
238
|
+
|
|
222
239
|
def run(*args, target: str | None = None) -> int:
|
|
223
240
|
agent = require_executable("cf-agent", target)
|
|
224
241
|
if not args:
|
|
242
|
+
if (
|
|
243
|
+
is_cfbs_repo()
|
|
244
|
+
and has_known_hubs()
|
|
245
|
+
and _policy_changed_since_build()
|
|
246
|
+
and prompt_yes_no(
|
|
247
|
+
"The policy set has changed since it was last built. "
|
|
248
|
+
"Build and deploy it before running?"
|
|
249
|
+
)
|
|
250
|
+
):
|
|
251
|
+
rc = build_command() or deploy_command(None, None)
|
|
252
|
+
if rc != 0:
|
|
253
|
+
return rc
|
|
225
254
|
return agent.run("-KIf update.cf", "-KI")
|
|
226
255
|
|
|
227
256
|
resolved = []
|
|
@@ -249,6 +278,10 @@ def build(hub=None, non_interactive=False) -> int:
|
|
|
249
278
|
rc = build_command()
|
|
250
279
|
if rc != 0:
|
|
251
280
|
return rc
|
|
281
|
+
if not hub and not has_known_hubs():
|
|
282
|
+
# Nothing to deploy to, so don't ask. Mirrors the check cf-remote's
|
|
283
|
+
# deploy does (it errors with "No hub to deploy to" in this case).
|
|
284
|
+
return 0
|
|
252
285
|
if prompt_yes_no(
|
|
253
286
|
"Deploy the built policy set now?",
|
|
254
287
|
default=True,
|
|
@@ -72,12 +72,13 @@ class Executable:
|
|
|
72
72
|
result = subprocess.run(args)
|
|
73
73
|
return result.returncode
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
# Include stderr, so cf-promises errors (e.g. duplicate bundle) are shown
|
|
76
|
+
full_command = f"{self.path} {command} 2>&1"
|
|
76
77
|
log.info(f"Executing command {full_command} on {self.location}")
|
|
77
78
|
output = run_command(self.location, full_command, sudo=True)
|
|
78
79
|
if (
|
|
79
80
|
output is None
|
|
80
|
-
): # TODO: Test this, I've had some policy failing but returning output instead
|
|
81
|
+
): # TODO: Test this, I've had some policy failing but returning output instead of error-code (I think)
|
|
81
82
|
# Error already logged in run_command
|
|
82
83
|
return 1
|
|
83
84
|
if output:
|
|
@@ -90,6 +90,11 @@ def _known_hosts(role_filter=None) -> Iterator[tuple[str, list[str]]]:
|
|
|
90
90
|
yield host_id, aliases
|
|
91
91
|
|
|
92
92
|
|
|
93
|
+
def has_known_hubs() -> bool:
|
|
94
|
+
"""True if cf-remote knows about any saved/spawned hub (without connecting)."""
|
|
95
|
+
return any(True for _ in _known_hosts("hub"))
|
|
96
|
+
|
|
97
|
+
|
|
93
98
|
@lru_cache(maxsize=None)
|
|
94
99
|
def _host_info(host: str):
|
|
95
100
|
try:
|
|
@@ -106,7 +111,17 @@ def _hosts_with_info(role_filter=None):
|
|
|
106
111
|
yield host, aliases, data
|
|
107
112
|
|
|
108
113
|
|
|
109
|
-
_Id
|
|
114
|
+
class _Id(namedtuple("_Id", "location aliases")):
|
|
115
|
+
@property
|
|
116
|
+
def label(self) -> str:
|
|
117
|
+
if self.aliases and self.location != "localhost":
|
|
118
|
+
return f"{self.aliases[0]} ({self.location})"
|
|
119
|
+
return self.location
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def known_hub_locations() -> list[str]:
|
|
123
|
+
""" "user@ip" of every hub cf-remote knows about (without connecting)."""
|
|
124
|
+
return [host for host, _ in _known_hosts("hub")]
|
|
110
125
|
|
|
111
126
|
|
|
112
127
|
def _identities(binary_name: str) -> Iterator[_Id]:
|
|
@@ -148,11 +163,13 @@ def _find_all_paired() -> list[Installation]:
|
|
|
148
163
|
|
|
149
164
|
|
|
150
165
|
def _prompt_choice(candidates, description):
|
|
166
|
+
"""Numbered prompt; pressing Enter picks the first candidate."""
|
|
151
167
|
print(f"Multiple installations of {description} found:")
|
|
152
168
|
for i, c in enumerate(candidates, 1):
|
|
153
169
|
print(f" {i}) {c.label}")
|
|
154
170
|
while True:
|
|
155
|
-
choice = input(f"Select which to use [1-{len(candidates)}]: ")
|
|
171
|
+
choice = input(f"Select which to use [1-{len(candidates)}, default 1]: ")
|
|
172
|
+
choice = choice.strip() or "1"
|
|
156
173
|
if choice.isdigit() and 1 <= int(choice) <= len(candidates):
|
|
157
174
|
return candidates[int(choice) - 1]
|
|
158
175
|
print("Invalid selection, try again.")
|
|
@@ -211,6 +228,27 @@ def _select(candidates, description, target: str | None = None):
|
|
|
211
228
|
return _prompt_choice(candidates, description)
|
|
212
229
|
|
|
213
230
|
|
|
231
|
+
def _select_without_connecting(name: str) -> Executable:
|
|
232
|
+
"""Only connects to the chosen host. Hubs first, so Enter picks a hub."""
|
|
233
|
+
hubs = set(known_hub_locations())
|
|
234
|
+
remote = [i for i in _identities(name) if i.location != "localhost"]
|
|
235
|
+
local = [_Id("localhost", ["localhost", "local"])] if _find_local_path(name) else []
|
|
236
|
+
ordered = (
|
|
237
|
+
[i for i in remote if i.location in hubs]
|
|
238
|
+
+ local
|
|
239
|
+
+ [i for i in remote if i.location not in hubs]
|
|
240
|
+
)
|
|
241
|
+
if not ordered:
|
|
242
|
+
raise UserError(
|
|
243
|
+
f"Could not find {name} locally or on any configured remote host."
|
|
244
|
+
)
|
|
245
|
+
ident = ordered[0] if len(ordered) == 1 else _prompt_choice(ordered, name)
|
|
246
|
+
chosen = _resolve(name, ident)
|
|
247
|
+
if chosen is None:
|
|
248
|
+
raise UserError(f"{ident.label} is unreachable or lacks {name}.")
|
|
249
|
+
return chosen
|
|
250
|
+
|
|
251
|
+
|
|
214
252
|
def require_executable(name: str, target: str | None = None) -> Executable:
|
|
215
253
|
if isinstance(target, list):
|
|
216
254
|
if len(target) > 1:
|
|
@@ -239,7 +277,7 @@ def require_executable(name: str, target: str | None = None) -> Executable:
|
|
|
239
277
|
candidates[0] if len(candidates) == 1 else _prompt_choice(candidates, name)
|
|
240
278
|
)
|
|
241
279
|
else:
|
|
242
|
-
chosen =
|
|
280
|
+
chosen = _select_without_connecting(name)
|
|
243
281
|
|
|
244
282
|
log.info(
|
|
245
283
|
f"Using {'local' if chosen.is_local else 'remote'} installation of {name} ({chosen.label})"
|