The latest release of cfbs
(4.4.0 released April 4th, 2025) introduces the analyze
command.
Last time I used this (Show notes: The agent is in - Episode 47 - Preview of cfbs analyze) I had installed it from a git clone, now I want to go back to regular install
pipx uninstall cfbs
pipx install cfbs
uninstalled cfbs! ✨ 🌟 ✨
installed package cfbs 4.4.0, installed using Python 3.12.3
These apps are now globally available
- cfbs
Now, cfbs help
should have our new cfbs analyze option:
cfbs help
usage: cfbs [-h] [--loglevel LOGLEVEL] [-M] [--version] [--force]
[--non-interactive] [--index INDEX] [--check]
[--checksum CHECKSUM] [--keep-order] [--git {yes,no}]
[--git-user-name GIT_USER_NAME] [--git-user-email GIT_USER_EMAIL]
[--git-commit-message GIT_COMMIT_MESSAGE] [--ignore-versions-json]
[--omit-download] [--check-against-git] [--from MINIMUM_VERSION]
[--to-json [TO_JSON]] [--reference-version REFERENCE_VERSION]
[--masterfiles-dir MASTERFILES_DIR]
[--ignored-path-components [IGNORED_PATH_COMPONENTS ...]]
[--offline] [--masterfiles MASTERFILES]
[cmd] [args ...]
CFEngine Build System.
positional arguments:
cmd The command to perform (pretty, init, status, search,
add, remove, clean, update, validate, download, build,
install, help, info, show, analyse, analyze, input,
set-input, get-input, generate-release-information)
args Command arguments
options:
-h, --help show this help message and exit
--loglevel LOGLEVEL, -l LOGLEVEL
Set log level for more/less detailed output
-M, --manual Print manual page
--version, -V Print version number
--force Force rebuild / redownload
--non-interactive Don't prompt, use defaults (only for testing)
--index INDEX Specify alternate index
--check Check if file(s) would be reformatted
--checksum CHECKSUM Expected checksum of the downloaded file
--keep-order Keep order of items in the JSON in 'cfbs pretty'
--git {yes,no} Override git option in cfbs.json
--git-user-name GIT_USER_NAME
Specify git user name
--git-user-email GIT_USER_EMAIL
Specify git user email
--git-commit-message GIT_COMMIT_MESSAGE
Specify git commit message
--ignore-versions-json
Ignore versions.json. Necessary in case of a custom
index or testing changes to the default index.
--omit-download Use existing masterfiles instead of downloading in
'cfbs generate-release-information'
--check-against-git Check whether masterfiles from cfengine.com and
github.com match in 'cfbs generate-release-
information'
--from MINIMUM_VERSION
Specify minimum version in 'cfbs generate-release-
information'
--to-json [TO_JSON] Output 'cfbs analyze' results to a JSON file;
optionally specify the JSON's filename
--reference-version REFERENCE_VERSION
Specify version to compare against for 'cfbs analyze'
--masterfiles-dir MASTERFILES_DIR
If the path given to 'cfbs analyze' contains a
masterfiles subdirectory, specify the subdirectory's
name
--ignored-path-components [IGNORED_PATH_COMPONENTS ...]
Specify path components which should be ignored during
'cfbs analyze' (the components should be passed
separately, delimited by spaces)
--offline Do not connect to the Internet to download the latest
version of MPF release information during 'cfbs
analyze'
--masterfiles MASTERFILES
Add masterfiles on cfbs init choose between
Let’s grab oldest version of the Masterfiles Policy Framework that cf-remote
knows about and test it out.
OLDEST_MPF_IN_CFREMOTE="$(cf-remote list | head -n1 | awk '{print $NF}')"
URL_OLDEST_MPF_IN_CFREMOTE="$(cf-remote --version ${OLDEST_MPF_IN_CFREMOTE} list masterfiles | tail -n1)"
TARBALL_FILENAME_OLDEST_MPF_IN_CFREMOTE="$(basename ${URL_OLDEST_MPF_IN_CFREMOTE})"
cf-remote --version 3.21.0 download masterfiles --output-dir .
tar --no-same-permissions --no-overwrite-dir -zxf ${TARBALL_FILENAME_OLDEST_MPF_IN_CFREMOTE} 2> /dev/null
mv masterfiles "masterfiles-${OLDEST_MPF_IN_CFREMOTE}"
Available releases: master, 3.25.0, 3.24.x, 3.24.1, 3.24.0, 3.21.x, 3.21.6, 3.21.5, 3.21.4, 3.21.3, 3.21.2, 3.21.1, 3.21.0
Using 3.21.0 LTS:
Downloading package: '/home/nickanderson/.cfengine/cf-remote/packages/cfengine-masterfiles-3.21.0-1.pkg.tar.gz'
Copied to '/tmp/cfengine-masterfiles-3.21.0-1.pkg.tar.gz' (Checksum OK).
If we analyze it:
cfbs analyze "./masterfiles-${OLDEST_MPF_IN_CFREMOTE}"
We can see that it appears to be completely un-modified, as expected.
Policy set path: ./masterfiles-3.21.0
Reference version: 3.21.0
No files are missing from the version.
No files of the version are modified.
No files are from a different version.
No files are not from any version.
Now, let’s make some representative modifications that are typically found in policy sets:
Let’s remove one of the vendored files:
# Trigger Files missing from the version:
rm -f "./masterfiles-${OLDEST_MPF_IN_CFREMOTE}/services/autorun/hello.cf"
Let’s customize a vendored policy file:
# Trigger Files from the version but with modifications:
echo 'bundle agent mpf_main
{
reports:
"Customized services/main.cf";
}
bundle agent hello_world
{
reports:
"Hello World!";
}' > "./masterfiles-${OLDEST_MPF_IN_CFREMOTE}/services/main.cf"
Let’s introduce a new file:
# Trigger Files not from any version (with both custom content and path):
echo '{
"variables": {
"default:def.bundlesequence_end": [ "hello_world" ]
}
}' > "./masterfiles-${OLDEST_MPF_IN_CFREMOTE}/def.json"
And, let’s introduce a file (but with different content) that would have been present in an older release of the MPF:
# Files from a different version, with modifications:
mkdir -p "./masterfiles-${OLDEST_MPF_IN_CFREMOTE}/lib/3.7/"
echo "# Oops, we neglected to clean up this file from an old MPF version. " > "./masterfiles-${OLDEST_MPF_IN_CFREMOTE}/lib/3.7/services.cf"
Now let’s analyze it:
cfbs analyze "./masterfiles-${OLDEST_MPF_IN_CFREMOTE}"
Looking at the result we see the files indicated in the appropriate section.
For lib/3.7/services.cf
notice that 4 different versions of the MPF were known to contain this file (‘3.7.8’, ‘3.7.7’, ‘3.7.6’, ‘3.7.5’), and it highlights the fact that the file still differed from what was included with those releases.
Policy set path: ./masterfiles-3.21.0
Reference version: 3.21.0
Files missing from the version:
└── services/autorun/hello.cf
Files from the version but with modifications:
└── services/main.cf
Files from a different version, with modifications:
└── ('lib/3.7/services.cf', ['3.7.8', '3.7.7', '3.7.6', '3.7.5'])
Files not from any version (with both custom content and path):
└── def.json
We aim to add additional functionality to this tooling, but we would love to hear your feedback on GitHub discussions or our mailing list.