A resource flag is an inferred flag definition from an operation resource requirement (aka dependency). Consider this example:
# guild.yml test: requires: - file: foo.txt flag-name: file
Guild supports this command:
guild run test file=bar.txt
file: foo.txt is considered a resource flag in this case.
This document captures our thinking about this functionality, it’s implementation, and how it might be refactored or otherwise improved.
This proposal is implemented and awaiting release
The set of commits related to this functionality in late 2022 and early 2023 reflects the complexity of this feature. This includes code spread across numerous modules (e.g.
op_cmd) in support of this functionality. The set of tests associated this functionality is similarly complex and distributed across many files.
We can simplify the code and clarify the intended behavior by shifting the problem away from special handling of resource defs toward relying on explicit flag definitions.
plugins.config_flags implements the
config-flags plugin, which supports simplified Guild files in support of Guild’s “config file as flag source” feature. It generates config that would otherwise be onerous to a user.
In the example above, a
resource-flags plugin would ensure that the operation was explicitly configured with the applicable flag def for the specified resource. Given such a well formed operation spec, Guild’s core would no longer need to support “resource flags” as special cases. This would address the code complexity problem at the core of this proposal.
The current implementation supports a flexible scheme for specifying resource sources as flag assignments. When configuring a resource source using a flag assignment, a user may specify the flag name part using one of:
- full URI
- partial URI (e.g. the file path, operation name, etc.)
- source URI path
- source name
- source flag name
The flexibility is there to support the range of configuration stage a user might go through (e.g. starting with a bare-bones spec, then adding a name for UI/messages, then adding a name for flag assignments).
Guild assumes that any and all sources that support configuration can be re-configured with flag assignments using this scheme. This may be the wrong assumption from the user’s point of view but the assumption goes unnoticed because Guild does not present this scheme to users in flag help (i.e. what’s shown in
guild run <op> --help-op, etc.)
This drives a non-trivial source-to-flag-def scheme. Below is a series of mappings from user-defined op def to plugin-transformed op def.
In this case the user only specifies the source URI.
test: requires: - file: foo.txt
Plugin-generated flag def:
test: flags: file:foo.txt: default: foo.txt alias: foo.txt
The relationship between the required source and the flag is determined by the plugin-generated
flag-name attribute, which corresponds to generated flag
Note this leads to the surprising interface of using the file path as the flag name to assign a different path:
guild run test foo.txt=bar.txt
This is not desirable and should not be supported.
file source should only be promoted as a flag def if it explicitly defines a
flag-name attribute. This is an unambiguous configuration indicating the the user intends to expose the file path to modification via flags.
name attribute is not a sufficient indicator that the user wants to expose the file path via flags.
name is used to improve readability of user-facing messages relating to the source.
The case for
operation sources is different. Consider the mapping below.
test: requires: - operation: upstream
Plugin generated flag def:
test: flags: operation:upstream: alias: upstream type: string arg-skip: yes
The flag def for an
operation source sets the type to
string. This ensures that run IDs that can be converted to numbers (e.g.
1e3) are preserved as string values.
arg-skip is true to avoid passing the flag as an argument to an underlying CLI (i.e. when
In this case, the run interface is less surprising.
guild run test operation:upstream=abcd1234
Using the flag alias:
guild run test upstream=abcd1234
Operation dependencies are commonly specified by users and so any
operation source should be assumed to be flag configurable.
In the case where an operation should not be flag configurable (e.g. the selection criteria are fixed and cannot be changed by the user) an explicit
flag-name with a false value indicates that the source should not be promoted as a flag (see examples below).
test: requires: - file: foo.txt name: foo
As outlined above, this not a sufficient indicator of user intent to expose a
file source as a flag def.
operation source would use
name in this case as the flag name.
test: requires: - operation: test name: test-run
Plugin generated flag def:
test: flags: test-run: type: string arg-skip: yes
Flag named resource
flag-name is specified, the user has provided an unambiguous queue that the source should be a flag def.
test: requires: - file: foo.txt flag-name: file
Plugin generated flag def:
test: flags: file: default: foo.txt type: string arg-skip: yes
The same process is applicable to
operation sources: the source
flag-name attribute is used for the flag name.
Guild would support a source attribute that explicit disabled any implicit flag creation for a source. Two options come to mind:
flag-name is the likely candidate as it’s otherwise used to explicitly indicate that a source should be exposed as a flag. If this value were explicitly
no (false), Guild assumes that the user has disabled flag-creation for the source.
A user can provide explicit configuration for a generated resource flag by defining the applicable flag def in config.
Consider this example, where the user provides a description for a resource flag and forces the flag to be passed to a CLI:
test: flags: operation:upstream: description: Run ID for the upstream operation arg-skip: no arg-name: upstream requires: - operation: upstream
operation source was named, either with
flag-name, the user would use the corresponding value for the explicit flag name.
test: flags: upstream: description: Run ID for the upstream operation arg-skip: no arg-name: upstream requires: - operation: upstream name: upstream
The current behavior (as released in 0.8.3 or 0.9, whichever goes out) works as intended. Any unintended features (or broken functionality/missed tests) can be fixed by targeting the applicable code.
The cost of keeping this code (the do nothing option) includes:
- Technical debt, which makes bug fixes and enhancements riskier and more time consuming
- Missed features (deferred due to complexity/time costs) such as proper flag documentation and auto-completion
- Potential lock-in (e.g. the need to support backward compatibility) of functionality that would change if and when this proposal is later implemented (see note below)
flag attribute might be a useful alternative spelling for the corresponding flag def itself. Rather than using a def under
source-config (a linkage that requires an understanding of this scheme and the correct source reference) the user might define the flag def in place.
test: requires: - file: foo.txt flag: name: file description: A required file choices: [foo.txt, bar.txt]
In this case, an explicit
flag value of
off, etc.) would indicate that the plugin should not implicitly create a flag def for the source.
As counterpoint, this leads to multiple ways to configure resource flags. We ought to keep things simple and require users to add new flag defs under the
flags operation attribute and associate any given flag with a resource source using the source