Scan & Upload Code References Overview
ConfigCat's CLI has the ability to scan your source code for feature flag and setting usages and upload the found code references to ConfigCat.
This feature makes the elimination of the technical debt easier, as it can show which repositories reference your feature flags and settings in one centralized place on your Dashboard.
You can integrate the CLI into your CI/CD pipeline or use it with other execution mechanisms like scheduled jobs or VCS push triggered workflows.
Scan
The following example shows a simple scan execution that prints the scan result to the console. The scan command searches for every feature flag and setting key defined within a selected Config.
If you want to see this in action on your project, run the following command in its root folder:
configcat scan . --print
See the scan command's reference documentation for all available command parameters.
Deleted Flags
As part of the scanning operation, the CLI gathers each deleted feature flag and setting from the last 180 days and looks for their usages in your source code. When it finds a reference for a deleted feature flag or setting, it prints out a warning that lists their keys.
[warning]: 5 deleted feature flag/setting reference(s) found in 4 file(s). Keys: [featureThatWasDeleted1, featureThatWasDeleted2]
Exclude Flags from Scanning
There's an option to exclude feature flags or settings from the scanning operation by their keys.
configcat scan <dir> --exclude-flag-keys featureFlagToExclude1 featureFlagToExclude2
Config ID
In non-interactive environments, like in a CI/CD pipeline, you have to pass the ID of your ConfigCat Config that you want to scan against. The scanner will use this ID to determine which feature flags & settings to search for in your source code.
To get the ID of a Config, follow the steps below:
- Go to your ConfigCat Dashboard, select the desired Config, and click the code references icon on one of your feature flags.
- Copy the Config ID from the highlighted box.
How Scanning Works
The scanner looks for feature flag and setting keys between quotation marks (' " `) in the first place.
There's an option to extend the feature flag and setting key usage regex patterns with the --usage-patterns argument of the scan command.
configcat scan <dir> --usage-patterns "<pattern1>" "<pattern2>"
Usage patterns can also be set via the CONFIGCAT_USAGE_PATTERNS environment variable as a comma delimited list.
export CONFIGCAT_USAGE_PATTERNS="<pattern1>,<pattern2>";
The regex pattern must include the CC_KEY placeholder that represents the actual feature flag or setting key in your code.
For example, the following pattern allows the recognition of Ruby symbols as flag key usage:
configcat scan <dir> --usage-patterns ":CC_KEY"
It will match for the following usage:
if FeatureFlags.enabled(:my_feature_key)
#...
end
Aliases
The found keys' context is examined for aliases, like variables, constants, or enumerations used to store these keys. Aliases are treated as indirect references and are included in the searching process.
For example, the following C# constant's name (MyAwesomeFeature) will be recognized as an alias:
public static class FeatureFlagKeys
{
public const string MyAwesomeFeature = "my_awesome_feature";
}
The scanner will treat this constant's usage as an indirect reference to the flag.
if (configCatClient.GetValue(FeatureFlagKeys.MyAwesomeFeature, false))
{
// the feature is on.
}
The alias recognition adapts to the characteristics of different languages.
For example, it can find aliases in Go constants/variable assignments:
const (
myAwesomeFeature string = "my_awesome_feature"
)
myAwesomeFeature := "my_awesome_feature"
And in Swift enums/variable assignments as well:
enum FlagKeys : String {
case MyAwesomeFeature = "my_awesome_feature"
}
let myAwesomeFeature: String = "my_awesome_feature"
You can check here a bunch of other samples that we tested.
An alias must be at least 30% identical to the feature flag/setting key.
The similarity check is case insensitive and ignores _ characters.
This behavior prevents false recognitions in expressions like <input type="text" value="my_awesome_feature"> where value shouldn't be treated as alias.
Custom alias match patterns
There's an option to set custom regex patterns for identifying additional aliases. The scan command accepts a list of patterns via the --alias-patterns argument.
configcat scan <dir> --alias-patterns "<pattern1>" "<pattern2>"
The CLI expects the patterns from CONFIGCAT_ALIAS_PATTERNS as a comma delimited list.
export CONFIGCAT_ALIAS_PATTERNS="<pattern1>,<pattern2>";
Match pattern format
The regex pattern must include at least one capture group that represents the actual alias. When more then one group is defined, the first one is selected.
To bind the actual flag keys to aliases, the CLI uses a CC_KEY placeholder to inject the known keys into the pattern.
For example, the following pattern allows to find aliases that point to Ruby symbols:
configcat scan <dir> --alias-patterns "(\w+) = client\.get_value\(:CC_KEY"
It will match to the following usage:
# 'my_feature_enabled' now is treated as an alias to the 'my_feature_key' flag.
my_feature_enabled = client.get_value(:my_feature_key, false)
The CLI implicitly adds the optional `, ', " wrapping around flag keys, so you don't have to wrap the CC_KEY placeholder manually.
For example, the pattern (\w+) = CC_KEY will either match to alias = flag_key, alias = "flag_key", alias = 'flag_key', or alias = `flag_key` .
Wrappers
In addition to aliases, the scanner also looks for different feature flag/setting key usage patterns. This helps to recognize functions and properties used to wrap direct ConfigCat SDK calls as indirect references. Aliases are also included in this search.
For example, the scanner will treat the IsMyAwesomeFeatureEnabled function of the following C# wrapper class as an indirect reference:
public class FeatureFlagProvider
{
public bool IsMyAwesomeFeatureEnabled(bool defaultValue = false)
{
return configCatClient.GetValue("my_awesome_feature", defaultValue);
}
}
And will include it's usage in the scan report:
if (featureFlagProvider.IsMyAwesomeFeatureEnabled())
{
// the feature is on.
}
The scanner uses the following patterns to look for wrapper usages (case insensitive):
[.|->|::]{settingKeyOrAlias}[.|->|::]get{settingKeyOrAlias}[.|->|::]is{settingKeyOrAlias}[.|->|::]is{settingKeyOrAlias}enabled
Given the key/alias my_awesome_feature, the scanner will find any of the following usage examples:
.my_awesome_feature(also:->my_awesome_feature/::my_awesome_feature).MY_AWESOME_FEATURE(also:->MY_AWESOME_FEATURE/::MY_AWESOME_FEATURE).get_my_awesome_feature(also:->get_my_awesome_feature/::get_my_awesome_feature).GET_MY_AWESOME_FEATURE(also:->GET_MY_AWESOME_FEATURE/::GET_MY_AWESOME_FEATURE).is_my_awesome_feature(also:->is_my_awesome_feature/::is_my_awesome_feature).is_my_awesome_feature_enabled(also:->is_my_awesome_feature_enabled/::is_my_awesome_feature_enabled).myAwesomeFeature(also:->myAwesomeFeature/::myAwesomeFeature).getMyAwesomeFeature(also:->getMyAwesomeFeature/::getMyAwesomeFeature).isMyAwesomeFeature(also:->isMyAwesomeFeature/::isMyAwesomeFeature).isMyAwesomeFeatureEnabled(also:->isMyAwesomeFeatureEnabled/::isMyAwesomeFeatureEnabled).IsMyAwesomeFeatureEnabled(also:->IsMyAwesomeFeatureEnabled/::IsMyAwesomeFeatureEnabled)