LIBDNF5-ACTIONS(8)
๐ฆ NAME ๐ฆ
Okay bestie, this is libdnf5-actions โ the Actions Plugin page from the dnf5 manual. It's giving backstage-pass energy for callback hooks, and we're gonna get into all of it. ๐
โจ DESCRIPTION โจ
This plugin lets you define actions that get executed through libdnf5 callback hooks. Each action is hooked to one specific callback. Actions for goal_resolved, pre_transaction, and post_transaction callbacks may define a glob-like filtering rule on the package NEVRA or package files, plus whether the package is incoming or outgoing. No cap.
๐ CONFIGURATION ๐
The plugin does not extend the standard configuration. However, it reads actions files.
The actions files are read from the <libdnf5_plugins_config_dir>/actions.d/ directory. Only files with a .actions extension are read. Action files are read in lexical order (sorted by filename) by comparing character values, ignoring locale. Slay.
๐ธ ACTIONS FILE FORMAT ๐ธ
Empty lines and lines that start with a # character (comment line) are ignored.
Each non-comment line defines an action and consists of five items separated by colons: callback_name:package_filter:direction:options:command.
๐ callback_name ๐
pre_base_setuppost_base_setuprepos_configured(added in version 1.0.0)repos_loaded(added in version 1.0.0)pre_add_cmdline_packages(added in version 1.0.0)post_add_cmdline_packages(added in version 1.0.0)goal_resolved(added in version 1.3.0)pre_transactionpost_transaction
๐ฆ package_filter ๐ฆ
A glob-like filtering rule applied on the package NEVRA (also in the shortened forms) or package files.
An empty filter means executing the command once with no information about the package. The "*" filter means executing the command for each package in the transaction that matches the direction filter. The filter can be non-empty only for goal_resolved, pre_transaction, and post_transaction callbacks.
โญ direction โญ
Filters packages by their direction (coming into the system/going out of the system) in a transaction. If empty, the filter is not applied. The filter can only be non-empty when combined with a non-empty package_filter.
inโ packages coming to the system (downgrade, install, reinstall, upgrade)outโ packages going out of the system (upgraded, downgraded, reinstalled, removed, replaced/obsoleted)
๐ฅ options ๐ฅ
Options are separated by spaces. A space within an option can be written using escaping.
enabled=<value>โ the<value>specifies when the action is enabled (added in version 0.3.0)1โ action is always enabledhost-onlyโ the action is only enabled for operations on the hostinstallroot-onlyโ the action is only enabled for operations in the alternativeinstallroot
mode=<value>โ the<value>specifies the action plugin communication mode (added in version 1.2.0)plainโ the original and default communication mode. Data is only passed to the process via arguments when it is started. The process can send data to the actions plugin by writing to standard output.jsonโ this mode establishes a bidirectional communication channel between the actions plugin and the process, using a request-response model. The process writes requests to standard output and reads responses from standard input. All communication is handled in JSON format (added in version 1.2.0).
raise_error=<value>โ the<value>specifies how action process errors are handled. What happens if the action process did not start, ended with a non-zero exit code, ended abnormally (received a signal), or an error occurred during communication (syntax error, communication interrupt, failed to process the output line in plain communication mode). If the option is not present,raise_error=0for backward compatibility (added in version 1.4.0).0โ the errors are logged1โ an exception is thrown
๐ command ๐
Any executable file with arguments.
Arguments are separated by spaces. A space within an argument can be written using escaping. Escaping can also be used to prevent substitution and to pass special characters: \a, \b, \f, \n, \r, \t, \v. Unescaping of arguments is done after substitution.
The following variables in the command will be substituted:
${pid}โ process ID${plugin.version}โ version of the actions plugin (added in version 0.3.0)${conf.<option_name>}โ option from base configuration${conf.<repoid_pattern>.<option_name>[=<value_pattern>]}โ list ofrepoid.option=valuepairs (added in version 1.1.0)${var.<variable_name>}โ variable${tmp.<actions_plugin_variable_name>}โ variable exists only in actions plugin context${pkg.<package_attribute_name>}โ value of the package attribute${pkg.name}โ package name${pkg.arch}โ package arch${pkg.version}โ package version${pkg.release}โ package release${pkg.epoch}โ package epoch${pkg.na}โ package name.architecture${pkg.evr}โ package epoch-version-release${pkg.nevra}โ package nevra (0 epoch is hidden)${pkg.full_nevra}โ package full nevra (epoch is always present) name-epoch:version-release.arch${pkg.repo_id}โ package repository id${pkg.license}โ package license${pkg.location}โ package relative path/location from repodata${pkg.vendor}โ package vendor${pkg.action}โ action performed on the package:Iโ newly installed packageUโ package installed as an upgradeDโ package installed as a downgradeRโ package used for reinstallationEโ erased (removed) package from the systemOโ replaced package (was obsoleted/upgraded/downgraded/reinstalled/removed)?โ package with changed installation reason
The command will be evaluated for each package that matched the package_filter and the direction. However, after variable substitution, any duplicate commands will be removed and each command will only be executed once per transaction. The commands are executed in sequence. There is no parallelism. Argument substitution is performed after the previous command has completed. This allows the substitution to use the results of the previous commands. The order of execution of the commands follows the order in the action files, but may differ from the order of packages in the transaction. In other words, when you define several action lines for the same package_filter and direction these lines will be executed in the order they were defined in the action file when the package_filter and direction matches a package. However, the order of when a particular package_filter is invoked depends on the position of the corresponding package in the transaction.
The repoid.option=value pairs in the list are separated by the , character. The , character in the value is replaced by the escape sequence "\x2C". If value_pattern is used, only pairs with the matching value are listed. The repoid_pattern and value_pattern can contain globs.
๐ฌ ACTION PROCESS STANDARD OUTPUT FORMAT IN PLAIN COMMUNICATION MODE ๐ฌ
The standard output of each executed action (command) is captured and processed. Each line of output can change the value of a base configuration option, the value of a configuration option in matching repositories, or a variable. It can also set or unset one actions plugin variable. The value of this variable is available for the following commands using the ${tmp.<actions_plugin_variable_name>} substitution.
Actions should change the repositories configuration in the repos_configured hook. At this point, the repositories configuration is loaded but not yet applied.
Since version 1.4.0, the output line can write a message to the logger, throw a stop exception and an error exception.
๐ Output line format ๐
tmp.<actions_plugin_variable_name>=<value>โ sets the value of action plugins variable<actions_plugin_variable_name>tmp.<actions_plugin_variable_name>โ removes the action plugins variable if it existsconf.<option_name>=<value>โ sets the value of option<option_name>in the base configurationconf.<repoid_pattern>.<option_name>=<value>โ sets the value of option<option_name>in the matching repositories (added in version 1.1.0)var.<variable_name>=<value>โ sets value of the variable<variable_name>stop=<message>โ throws a stop exception with<message>(added in version 1.4.0)error=<message>โ the error<message>is logged or throws an error exception with<message>ifraise_error=1(added in version 1.4.0)log.<level>=<message>โ writes<message>to the logger with priority<level>(added in version 1.4.0)- Levels:
CRITICAL,ERROR,WARNING,NOTICE,INFO,DEBUG,TRACE
- Levels:
๐ง MESSAGES IN JSON COMMUNICATION MODE ๐ง
The "json" communication mode establishes a bidirectional communication channel between the actions plugin and the process, using a request-response model. The process writes requests to standard output and reads responses from the actions plugin from standard input. All communication is handled in JSON format.
Format of a request message from the process:
{"op":"<operation>", "domain":"<domain>", "args":{}}
Format of a response:
{"op":"reply", "requested_op":"<operation_from_request>", "domain":"<domain_from_request>",
"status":"OK", "return":{}}
Format of a response with an error message:
{"op":"reply", "requested_op":"<operation_from_request>", "domain":"<domain_from_request>",
"status":"ERROR", "message":"<error message>"}
๐ Supported <operation> values ๐
getโ request to get valuessetโ request to set a valuenewโ creates a new repository configurationlogโ writes a message to the logger (added in version 1.4.0)stopโ throws a stop exception with a message โ (added in version1.4.0)errorโ the error message gets logged, or it throws an error exception with a message ifraise_error=1(added in version1.4.0)
๐
Description of the get operation ๐
bestie, get is not one single thing โ it's defined separately for a whole bunch of <domain>s, and you gotta pick the right one:
confโ a request to read the value of a configuration (global configuration or repository configuration)varsโ a request to read the value of variablesactions_varsโ a request to read the value of action plugin variables โ these bad boys exist only in the actions plugin contextactions_attrsโ lets you get the process ID and the version of the actions pluginpackagesโ a request to list available and installed packagestrans_packagesโ a request to list packages in the transactioncmdline_packages_pathsโ a request to list command-line packages
๐ Reading a configuration value ๐
Request format, literally copy-paste this energy:
{"op":"get", "domain":"conf", "args":{"key":"<name>"}}
{"op":"get", "domain":"conf", "args":{"key":"<repo_id>.<name>"}}
Response format:
{"op":"reply", "requested_op":"get", "domain":"conf", "status":"OK",
"return":{"keys_val":[{"key":"<name>", "value":"<value>"}]}}
{"op":"reply", "requested_op":"get", "domain":"conf", "status":"OK",
"return":{"keys_val":[{"key":"<repo_id>.<name>", "value":"<value>"}]}}
{"op":"reply", "requested_op":"get", "domain":"conf", "status":"ERROR",
"message":"<error message>"}
Description, iykyk:
<name>โ the name of a global configuration option, or a repository configuration option if<repo_id>is specified<repo_id>โ the repository ID; in a request it can contain globs, in which case the value of the configuration option is read for all matching repositories<value>โ the read value of the configuration option<error message>โ error message
โจ the way that<repo_id>works? when you use it, the number ofkeys_valitems in the response depends on the number of matching repositories ๐
Example:
{"op":"get", "domain":"conf", "args":{"key":"countme"}}
{"op":"reply", "requested_op":"get", "domain":"conf", "status":"OK",
"return":{"keys_val":[{"key":"countme", "value":"0"}]}}
{"op": "get", "domain": "conf", "args": {"key": "*.enabled"}}
{"op": "reply", "requested_op": "get", "domain": "conf", "status": "OK",
"return": {
"keys_val": [{"key": "dnf-ci-fedora.enabled", "value": "1"},
{"key": "dnf-ci-fedora-updates.enabled", "value": "1"},
{"key": "dnf-ci-thirdparty.enabled", "value": "0"},
{"key": "test-repo.enabled", "value": "0"}]}}
๐ฆ Reading a variable value ๐ฆ
Request format:
{"op":"get", "domain":"vars", "args":{"name":"<name>"}}
Response format:
{"op":"reply", "requested_op":"get", "domain":"vars", "status":"OK",
"return":{"vars":[{"name":"<name>", "value":"<value>"}]}}
Description:
<name>โ the name of the variable; in a request it can contain globs, in which case the value of all matching variables will be read<value>โ the read value of the variable
the number of vars items in the response depends on the number of matching variables, no cap ๐ซถ
Example:
{"op":"get", "domain":"vars", "args":{"name":"test_var*"}}
{"op":"reply", "requested_op":"get", "domain":"vars", "status":"OK",
"return":{"vars":[{"name":"test_var1", "value":"value1"}]}}
{"op":"get", "domain":"vars", "args":{"name":"nonexist_var"}}
{"op":"reply", "requested_op":"get", "domain":"vars", "status":"OK",
"return":{"vars":[]}}
๐ธ Reading an action variable value ๐ธ
Request format:
{"op":"get", "domain":"actions_vars", "args":{"name":"<name>"}}
Response format:
{"op":"reply", "requested_op":"get", "domain":"actions_vars", "status":"OK",
"return":{"actions_vars":[{"name":"<name>", "value":"<value>"}]}}
Description:
<name>โ the name of the action variable; in a request it can contain globs, in which case the value of all matching action variables will be read<value>โ the read value of the action variable
the number of actions_vars items in the response depends on the number of matching action variables ๐
Example:
{"op":"get", "domain":"actions_vars", "args":{"name":"test_actions_var*"}}
{"op":"reply", "requested_op":"get", "domain":"actions_vars", "status":"OK",
"return":{"actions_vars":[{"name":"test_actions_var1", "value":"value1"}]}}
{"op":"get", "domain":"actions_vars", "args":{"name":"nonexist_var"}}
{"op":"reply", "requested_op":"get", "domain":"actions_vars", "status":"OK",
"return":{"actions_vars":[]}}
๐ง Reading an action attribute value ๐ง
Request format:
{"op":"get", "domain":"actions_attrs", "args":{"key":"<name>"}}
Response format:
{"op":"reply", "requested_op":"get", "domain":"actions_attrs", "status":"OK",
"return":{"actions_attrs":[{"key":"<name>", "value":"<value>"}]}}
Description:
<name>โ the name of the action attribute; in a request it can contain globs, in which case the value of all matching actions plugin attributes will be read. Currently, two attributes are supported:pidandversion.pidโ the process ID of the actions pluginversionโ the version of the actions plugin, a string in the formatMAJOR.MINOR.MICRO
<value>โ the read value of the action attribute
the number of actions_attrs items in the response depends on the number of matching action attributes ๐
Example:
{"op":"get", "domain":"actions_attrs", "args":{"key":"*"}}
{"op":"reply", "requested_op":"get", "domain":"actions_attrs", "status":"OK",
"return":{
"actions_attrs":[{"key":"pid", "value":"523"},
{"key":"version", "value":"1.4.0"}]}}
{"op":"get", "domain":"actions_attrs", "args":{"key":"nonexist_attribute"}}
{"op":"reply", "requested_op":"get", "domain":"actions_attrs", "status":"OK",
"return":{"actions_attrs":[]}}
๐ Getting available, installed, and transaction packages ๐
okay so quick vibe check on the domains here: the packages domain is what you use to get available and installed packages. The trans_packages domain is what you use to get packages in the transaction. And heads up โ the trans_packages domain can only be used in goal_resolved, pre_transaction, and post_transaction callbacks. no exceptions, bestie ๐
Request format:
{"op":"get", "domain":"packages", "args":{"output":["<pkg_attr>"]}}
{"op":"get", "domain":"packages", "args":{"output":["<pkg_attr>"],
"params":[{"key":"<param_name>"}]}}
{"op":"get", "domain":"packages", "args":{"output":["<pkg_attr>"],
"filters":[{"key":"<filter_name>", "value":"<value>", "operator":"<cmp_operator>"}]}}
{"op":"get", "domain":"packages", "args":{"output":["<pkg_attr>"],
"params":[{"key":"<param_name>"}],
"filters":[{"key":"<filter_name>", "value":"<value>", "operator":"<cmp_operator>"}]}}
{"op":"get", "domain":"trans_packages", "args":{"output":["<pkg_attr>"]}}
{"op":"get", "domain":"trans_packages", "args":{"output":["<pkg_attr>"],
"params":[{"key":"<param_name>"}]}}
{"op":"get", "domain":"trans_packages", "args":{"output":["<pkg_attr>"],
"filters":[{"key":"<filter_name>", "value":"<value>", "operator":"<cmp_operator>"}]}}
{"op":"get", "domain":"trans_packages", "args":{"output":["<pkg_attr>"],
"params":[{"key":"<param_name>"}],
"filters":[{"key":"<filter_name>", "value":"<value>", "operator":"<cmp_operator>"}]}}
Response format:
{"op":"reply", "requested_op":"get", "domain":"packages", "status":"OK",
"return":{"packages":[{"<pkg_attr>":"<attr_value>"}]}}
{"op":"reply", "requested_op":"get", "domain":"trans_packages", "status":"OK",
"return":{"trans_packages":[{"<pkg_attr>":"<attr_value>"}]}}
{"op":"reply", "requested_op":"get", "domain":"packages", "status":"ERROR",
"message":"<error message>"}
{"op":"reply", "requested_op":"get", "domain":"trans_packages", "status":"ERROR",
"message":"<error message>"}
Description:
<pkg_attr>โ package attribute to return; the array can contain multiple attributes
General attributes ๐
nameโ package namearchโ package archversionโ package versionreleaseโ package releaseepochโ package epochnaโ package name.architectureevrโ package epoch-version-releasenevraโ package nevra (0 epoch is hidden)full_nevraโ package full nevra (epoch is always present)name-epoch:version-release.archdownload_sizeโ filesize of packageinstall_sizeโ size the package should occupy after installing on diskrepo_idโ package repository idlicenseโ package licenselocationโ package relative path/location from repodatavendorโ package vendor
Attributes usable only with the trans_packages domain ๐
actionโ action performed on the packageIโ newly installed packageUโ package installed as an upgradeDโ package installed as a downgradeRโ package used for reinstallationEโ erased (removed) package from the systemOโ replaced package (was obsoleted/upgraded/downgraded/reinstalled/removed)?โ package with changed installation reason
directionโ package direction in transactionINโ packages coming to the system (downgrade, install, reinstall, upgrade)OUTโ packages going out of the system (upgraded, downgraded, reinstalled, removed, replaced/obsoleted)
<param_name>โ one ofIGNORE_EXCLUDES,IGNORE_MODULAR_EXCLUDES,IGNORE_REGULAR_EXCLUDES,IGNORE_REGULAR_CONFIG_EXCLUDES,IGNORE_REGULAR_USER_EXCLUDES<filter_name>โ name of package attribute to filter; the array can contain multiple attributes
General attributes โญ
nameโ filter packages by name, uses<value>and<cmp_operator>archโ filter packages by arch, uses<value>and<cmp_operator>versionโ filter packages by version, uses<value>and<cmp_operator>releaseโ filter packages by release, uses<value>and<cmp_operator>epochโ filter packages by epoch, uses<value>and<cmp_operator>nevraโ filter packages by nevra (0 epoch is hidden), uses<value>and<cmp_operator>repo_idโ filter packages by repository id, uses<value>and<cmp_operator>availableโ filter available packagesinstalledโ filter installed packagesuserinstalledโ filter user installed packagesinstallonlyโ filter installonly packagesdescriptionโ filter packages by description, uses<value>and<cmp_operator>
fileโ filters packages by the files they contain; uses<value>and<cmp_operator>๐๏ธupgradableโ filters installed packages that actually have upgrades available ๐ผupgradesโ filters available packages that count as upgrades to installed packages โฌ๏ธdowngradableโ filters installed packages that have downgrades available ๐ฝdowngradesโ filters available packages that count as downgrades to installed packages โฌ๏ธ
Attributes usable only with the trans_packages domain โจ
directionโ filters packages by their direction in a transaction; uses<value>; here's the supported tea โ:INโ packages coming to the system (downgrade, install, reinstall, upgrade) ๐ฅOUTโ packages going out of the system (upgraded, downgraded, reinstalled, removed, replaced/obsoleted) ๐ค
<value>โ a value used by some filters; the type of comparison depends on<cmp_operator>๐ซ<cmp_operator>โ the operator the filter uses to evaluate a match against<value>; if you don't specify it,EQis assumed (the default, no cap) ๐<attr_value>โ the value of the package attribute ๐ท๏ธ<error message>โ error message, aka the "oop" text ๐ญ
The number of packages and trans_packages items in the response depends on the number of matching packages. Like, it flexes based on how much matched ๐
Example:
{"op":"get", "domain":"packages",
"args":{
"params":[{"key":"IGNORE_EXCLUDES"}],
"filters":[{"key":"name", "value":"lame*", "operator":"GLOB"}],
"output":["nevra"]}}
{"op":"reply", "requested_op":"get", "domain":"packages", "status":"OK", "return":{"packages":[
{ "nevra":"lame-3.100-5.fc29.src" }, { "nevra":"lame-3.100-5.fc29.x86_64" },
{ "nevra":"lame-libs-3.100-5.fc29.x86_64" }, { "nevra":"lame-3.100-4.fc29.src" },
{ "nevra":"lame-3.100-4.fc29.x86_64" }, { "nevra":"lame-libs-3.100-4.fc29.x86_64"}]}}
{"op":"get", "domain":"packages",
"args":{
"params":[{"key":"UNKNOWN"}],
"filters":[{"key":"name", "value":"lame*", "operator":"GLOB"}],
"output":["nevra"]}}
{"op":"reply", "requested_op":"get", "domain":"packages", "status":"ERROR",
"message":"Bad key \"UNKNOWN\" for params"}
{"op":"get", "domain":"trans_packages",
"args":{
"filters":[{"key":"direction", "value":"IN"}, {"key":"arch", "value":"x86_64"}],
"output":["action", "name", "version", "repo_id"]}}
{"op":"reply", "requested_op":"get", "domain":"trans_packages", "status":"OK",
"return":{
"trans_packages":[
{"action":"I", "name":"glibc", "version":"2.28", "repo_id":"dnf-ci-fedora-updates"},
{"action":"I", "name":"glibc-all-langpacks", "version":"2.28", "repo_id":"dnf-ci-fedora-updates"},
{"action":"I", "name":"glibc-common", "version":"2.28", "repo_id":"dnf-ci-fedora-updates"},
{"action":"I", "name":"filesystem", "version":"3.9", "repo_id":"dnf-ci-fedora"}]}}
๐ธ Getting paths of command-line specified packages ๐ธ
Okay so this one grabs the paths of the packages you literally typed on the command line ๐ซถ
Request format:
{"op":"get", "domain":"cmdline_packages_paths", "args":{}}
{"op":"get", "domain":"cmdline_packages_paths", "args":{
"filters":[{"key":"path", "value":"<value>"}]}}
{"op":"get", "domain":"cmdline_packages_paths", "args":{
"filters":[{"key":"path", "value":"<value>", "operator":"<cmp_operator>"}]}}
Response format:
{"op":"reply", "requested_op":"get", "domain":"cmdline_packages_paths", "status":"OK",
"return":{"cmdline_packages_paths":["<pkg_path>"]}}
Description:
<value>โ if you specify it, only paths matching the given pattern come back; the comparison type depends on<cmp_operator>๐ฏ<cmp_operator>โ the operator that decides whether the path matches the pattern in<value>; if you don't specify it,EQis assumed ๐ซ<pkg_path>โ the path to the package you specified on the command line ๐
The number of cmdline_packages_paths items in the response depends on the number of matching package paths on the command line. It's giving "however many matched, that's how many you get" ๐
Example:
{"op":"get", "domain":"cmdline_packages_paths",
"args":{"filters":[{"key":"path", "value":"/local/*", "operator":"GLOB"}]}}
{"op":"reply", "requested_op":"get", "domain":"cmdline_packages_paths", "status":"OK",
"return":{"cmdline_packages_paths":["/local/packageB.rpm", "/local/packageC.rpm"]}}
๐ฆ Supported compare operators ๐ฆ
Some get operations support <cmp_operator>. The actions plugin supports these compare operators, bestie โ memorize the vibe โ
:
EQโ exact equality of arguments ๐ฏIEQโ string equality, case-insensitive comparison ๐GTโ greater than ๐ผGTEโ greater than or equal to ๐LTโ less than ๐ฝLTEโ less than or equal to ๐CONTAINSโ contains a substring ๐งฉICONTAINSโ contains a substring, case-insensitive search ๐STARTSWITHโ starts with a substring ๐ISTARTSWITHโ starts with a substring, case-insensitive comparison ๐ซENDSWITHโ ends with a substring ๐IENDSWITHโ ends with a substring, case-insensitive comparison ๐ฌREGEXโ matches a regular expression ๐งIREGEXโ matches a regular expression, case-insensitive evaluation ๐ชGLOBโ matches a glob pattern โญIGLOBโ matches a glob pattern, case-insensitive evaluation ๐
You can invert the meaning of any operator by slapping the NOT_ prefix on it. Examples: NOT_EQ, NOT_GT. Flip the whole vibe, periodt ๐
๐ Description of the set operation ๐
The set operation is defined for several <domain>s:
confโ request to set a configuration value (global or repository configuration) โ๏ธvarsโ request to set a variable value ๐งฎactions_varsโ request to set an action plugin variable โ these variables exist only in the actions plugin context, iykyk ๐
๐ Setting a configuration value ๐
Request format:
{"op":"set", "domain":"conf", "args":{"key":"<name>", "value":"<value>"}}
{"op":"set", "domain":"conf", "args":{"key":"<repo_id>.<name>", "value":"<value>"}}
Response format:
{"op":"reply", "requested_op":"set", "domain":"conf", "status":"OK",
"return":{"keys_val":[{"key":"<name>", "value":"<value>"}]}}
{"op":"reply", "requested_op":"set", "domain":"conf", "status":"OK",
"return":{"keys_val":[{"key":"<repo_id>.<name>", "value":"<value>"}]}}
{"op":"reply", "requested_op":"set", "domain":"conf", "status":"ERROR",
"message":"<error message>"}
Description:
<name>โ the name of a global configuration option, or a repository configuration option if<repo_id>is specified ๐ท๏ธ<repo_id>โ the repository ID; in a request it can contain globs, and in that case the value of the configuration option gets set for all matching repositories ๐<value>โ in the request, the value you want to set; in the response, the actual value after it got set โจ<error message>โ error message ๐ญ
When you use <repo_id>, the number of keys_val items in the response depends on the number of matching repositories. More repos = more keys, simple math ๐
Example:
{"op":"set", "domain":"conf", "args":{"key":"countme", "value":"1"}}
{"op":"reply", "requested_op":"set", "domain":"conf", "status":"OK",
"return":{"keys_val":[{"key":"countme", "value":"1"}]}}
{"op":"set", "domain":"conf", "args":{"key":"dnf-ci-fedora*.enabled", "value":"1"}}
{"op":"reply", "requested_op":"set", "domain":"conf", "status":"OK",
"return":{
"keys_val":[{"key":"dnf-ci-fedora.enabled", "value":"1"},
{"key":"dnf-ci-fedora-updates.enabled", "value":"1"}]}}
๐ง Setting a variable value ๐ง
Request format:
{"op":"set", "domain":"vars", "args":{"name":"<name>", "value":"<value>"}}
{"op":"set", "domain":"vars", "args":{"name":"<name>"}}
Response format:
{"op":"reply", "requested_op":"set", "domain":"vars", "status":"OK",
"return":{"vars":[{"name":"<name>", "value":"<value>"}]}}
{"op":"reply", "requested_op":"set", "domain":"vars", "status":"OK",
"return":{"vars":[{"name":"<name>"}]}}
{"op":"reply", "requested_op":"set", "domain":"vars", "status":"ERROR",
"message":"<error message>"}
Description:
<name>โ the name of the variable ๐ท๏ธ<value>โ in the request, the value to be set; if you don't specify it, that means the variable should be removed. In the response, the actual value after being set; if it's not specified, the variable didn't exist or it got removed ๐<error message>โ error message ๐ญ
Example:
{"op":"set", "domain":"vars", "args":{"name":"test_var1", "value":"value1"}}
{"op":"reply", "requested_op":"set", "domain":"vars", "status":"OK",
"return":{"vars":[{"name":"test_var1", "value":"value1"}]}}
๐ Setting an action variable value ๐
Request format:
{"op":"set", "domain":"actions_vars", "args":{"name":"<name>", "value":"<value>"}}
{"op":"set", "domain":"actions_vars", "args":{"name":"<name>"}}
Response format:
{"op":"reply", "requested_op":"set", "domain":"actions_vars", "status":"OK",
"return":{"vars":[{"name":"<name>", "value":"<value>"}]}}
{"op":"reply", "requested_op":"set", "domain":"actions_vars", "status":"OK",
"return":{"vars":[{"name":"<name>"}]}}
{"op":"reply", "requested_op":"set", "domain":"actions_vars", "status":"ERROR",
"message":"<error message>"}
Description:
<name>โ the name of the action variable ๐ท๏ธ<value>โ in the request, the value to be set; if you don't specify it, that means the variable should be removed. In the response, the actual value after being set; if it's not specified, the variable didn't exist or it got removed ๐ซง<error message>โ error message ๐ญ
Example:
{"op":"set", "domain":"actions_vars", "args":{"name":"test_actions_var1", "value":"value1"}}
{"op":"reply", "requested_op":"set", "domain":"actions_vars", "status":"OK",
"return":{"actions_vars":[{"name":"test_actions_var1", "value":"value1"}]}}
๐ฌ Description of the new operation ๐ฌ
The new operation creates a new repository configuration. Heads up though: it can be used only in the repos_configured callback โ that's the one and only place it works, no cap โ ๏ธ
Request format:
{"op":"new", "domain":"repoconf",
"args":{"keys_val":[{"key":"repo_id", "value":"<repo_id>"},
{"key":"<repo_opt>", "value":"<opt_value>"}]}
Response format:
{"op":"reply", "requested_op":"new", "domain":"repoconf", "status":"OK",
"return":{"keys_val":[{"key":"repo_id", "value":"<repo_id>"},
{"key":"<repo_opt>", "value":"<opt_value>"}]}}
{"op":"reply", "requested_op":"new", "domain":"repoconf", "status":"ERROR",
"message":"<error_message>"}
Description:
<repo_id>โ repository ID ๐<repo_opt>โ any existing repository configuration option โ๏ธ<opt_value>โ the value to be set for the configuration option โจ
The keys_val field has to have at least one element โ setting repo_id is mandatory, no cap. Every other repository configuration option is optional. If the enabled option isn't there, the repository is disabled by default ๐
. And the response comes back with the same keys as the request, plus their actual values.
Example:
{"op":"new", "domain":"repoconf",
"args":{
"keys_val":[{"key":"repo_id", "value":"test-repo"},
{"key":"name", "value":"Test repository"},
{"key":"enabled", "value":"false"},
{"key":"baseurl", "value":"https://xyz.com/rpm"}]}}
{"op":"reply", "requested_op":"new", "domain":"repoconf", "status":"OK",
"return":{
"keys_val":[{"key":"repo_id", "value":"test-repo"},
{"key":"name", "value":"Test repository"},
{"key":"enabled", "value":"0"},
{"key":"baseurl", "value":"https://xyz.com/rpm"}]}}
๐ชต Description of the log operation
The log operation writes a message to the logger, bestie. That's literally its whole job.
Request format:
{"op":"log", "args":{"level":"<level>", "message":"<message>"}}
Response format:
{"op":"reply", "requested_op":"log", "domain":"log", "status":"OK"}
{"op":"reply", "requested_op":"log", "domain":"log", "status":"ERROR",
"message":"Unknown log level '<level_from_request>'"}
Description:
<level>โCRITICAL,ERROR,WARNING,NOTICE,INFO,DEBUG,TRACE<message>โ text to be written to the log<level_from_request>โ the level from the request, if it contains an unsupported value
Example:
{"op":"log", "args":{"level":"WARNING", "message":"My warning message"}}
{"op":"reply", "requested_op":"log", "domain":"log", "status":"OK"}
๐ Description of the stop operation
The stop operation throws a stop exception with a message. It's giving "we're done here" energy.
Request format:
{"op":"stop", "args":{"message":"<message>"}}
Description:
<message>โ text to be included in the stop exception
After this request, the application is expected to terminate. The actions plugin does not send a response but closes the communication channel โ pipes connected to the standard output and input of the process.
Example:
{"op":"stop", "args":{"message":"I want to stop the task"}}
๐ Description of the error operation
The error operation logs the error message or throws an error exception with a message if raise_error=1.
Request format:
{"op":"error", "args":{"message":"<message>"}}
Response format:
{"op":"reply", "requested_op":"error", "domain":"error", "status":"OK"}
Description:
<message>โ text written to the log or included in the error exception ifraise_error=1
If raise_error=1, the application is expected to terminate. The actions plugin does not send a response but closes the communication channel โ pipes connected to the standard output and input of the process.
Example:
{"op":"error", "args":{"message":"Error in action process 1"}}
{"op":"reply", "requested_op":"error", "domain":"error", "status":"OK"}
๐ AN EXAMPLE ACTIONS FILE: ๐
# Prints header with process id
pre_base_setup::::/usr/bin/sh -c echo\ -------------------------------------\ >>/tmp/actions-trans.log
pre_base_setup::::/usr/bin/sh -c date\ >>/tmp/actions-trans.log
pre_base_setup::::/usr/bin/sh -c echo\ libdnf5\ pre_base_setup\ was\ called.\ Process\ ID\ =\ '${pid}'.\ >>/tmp/actions-trans.log
pre_base_setup:::enabled=installroot-only:/usr/bin/sh -c echo\ run\ in\ alternative\ "installroot":\ installroot\ =\ '${conf.installroot}'\ >>/tmp/actions-trans.log
# Prints the value of the configuration option "defaultyes".
pre_base_setup::::/usr/bin/sh -c echo\ pre_base_setup:\ conf.defaultyes=${conf.defaultyes}\ >>/tmp/actions.log
# Prints a message that the "post_base_setup" callback was called.
post_base_setup::::/usr/bin/sh -c echo\ libdnf5\ post_base_setup\ was\ called.\ >>/tmp/actions-trans.log
# Executes the "add_new_repo" application with json communication.
# This application, for instance, can add new repository configurations.
repos_configured:::mode=json:/usr/local/bin/add_new_repo
# Prints a list of configured repositories with their enable state.
repos_configured::::/usr/bin/sh -c echo\ Repositories:\ ${conf.*.enabled}\ >>/tmp/repos.log
# Prints a list of repositories that use the http protocol in baseurl.
repos_configured::::/usr/bin/sh -c echo\ "${conf.*.baseurl=*http://*}"\ >>/tmp/baseurl_http.log
# Disables all repositories whose id starts with "rpmfusion".
repos_configured::::/usr/bin/sh -c echo\ conf.rpmfusion*.enabled=0
# Executes the "check_transaction" application with json communication, terminating if the application encounters an error.
# This application, for instance, can check for forbidden packages within a transaction and send a stop message.
pre_transaction:::mode=json raise_error=1:/usr/local/bin/check_transaction
# Prints the information about the start of the transaction.
# Since package_filter is empty, it executes the commands once.
pre_transaction::::/usr/bin/sh -c echo\ Transaction\ start.\ Packages\ in\ transaction:\ >>/tmp/actions-trans.log
# Logs all packages (package action, full_nevra, repo id) in transaction into a file.
# Uses the shell command "echo" and redirection to a file.
pre_transaction:*:::/usr/bin/sh -c echo\ '${pkg.action}'\ '${pkg.full_nevra}'\ '${pkg.repo_id}'\ >>/tmp/actions-trans.log
# Prints the date and time and information about the end of the transaction.
# Since package_filter is empty, it executes the commands once.
post_transaction::::/usr/bin/sh -c date\ >>/tmp/actions-trans.log
post_transaction::::/usr/bin/sh -c echo\ Transaction\ end.\ Repositories\ used\ in\ the\ transaction:\ >>/tmp/actions-trans.log
# Logs all the repositories from which packages were used in the transaction to install on the system.
# Each repository will be listed only once, even if multiple packages from the same repository were used.
# The same command (after variables substitution) is executed only once per transaction.
post_transaction:*:in::/usr/bin/sh -c echo\ '${pkg.repo_id}'\ >>/tmp/actions-trans.log
# ==============================================================================================
# The next two actions emulate the DNF4 snapper plugin. It uses the "snapper" command-line program.
# Creates a snapshot description and saves it to the "tmp.snapper_descr" variable.
pre_transaction::::/usr/bin/sh -c echo\ "tmp.snapper_descr=$(ps\ -o\ command\ --no-headers\ -p\ '${pid}')"
# Creates pre snapshot before the transaction and stores the snapshot number in the "tmp.snapper_pre_number" variable.
pre_transaction::::/usr/bin/sh -c echo\ "tmp.snapper_pre_number=$(snapper\ create\ -t\ pre\ -p\ -d\ '${tmp.snapper_descr}')"
# If the variable "tmp.snapper_pre_number" exists, it creates post snapshot after the transaction and removes the used variables.
post_transaction::::/usr/bin/sh -c [\ -n\ "${tmp.snapper_pre_number}"\ ]\ &&\ snapper\ create\ -t\ post\ --pre-number\ "${tmp.snapper_pre_number}"\ -d\ "${tmp.snapper_descr}"\ ;\ echo\ tmp.snapper_pre_number\ ;\ echo\ tmp.snapper_descr
๐ Author
See AUTHORS.md in dnf5 source distribution.
๐ Copyright
Contributors to the dnf5 project.
๐ And that's the whole page, bestie โ source/version LIBDNF5-ACTIONS(8), dated Oct 17, 2026. Periodt. ๐
๐ฆ where this page comes from
- arch rolling: dnf5 5.4.6.0-2 ยท GPL-2.0-or-later
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms