Skip to content

Commit

Permalink
Add beta feature infrastructure.
Browse files Browse the repository at this point in the history
This allows options to be marked as beta, which will require that the --beta option be supplied to prevent accidental usage of a beta feature.

The online and command-line documentation also show warnings when options are beta.
  • Loading branch information
dwsteele committed Mar 10, 2023
1 parent 6b409d0 commit 24f7252
Show file tree
Hide file tree
Showing 20 changed files with 257 additions and 31 deletions.
45 changes: 30 additions & 15 deletions doc/lib/pgBackRestDoc/Common/DocConfig.pm
Expand Up @@ -19,6 +19,7 @@ use pgBackRestDoc::ProjectInfo;
####################################################################################################################################
# Help types
####################################################################################################################################
use constant CONFIG_HELP_BETA => 'beta';
use constant CONFIG_HELP_COMMAND => 'command';
push @EXPORT, qw(CONFIG_HELP_COMMAND);
use constant CONFIG_HELP_CURRENT => 'current';
Expand Down Expand Up @@ -319,6 +320,7 @@ sub process
$$oCommandOption{&CONFIG_HELP_EXAMPLE} = $oOptionDoc->fieldGet('example');
$oCommandOption->{&CONFIG_HELP_INTERNAL} =
cfgDefineCommand()->{$strCommand}{&CFGDEF_INTERNAL} ? true : $oOptionDefine->{$strOption}{&CFGDEF_INTERNAL};
$oCommandOption->{&CONFIG_HELP_BETA} = $oOptionDefine->{$strOption}{&CFGDEF_BETA};

# If internal is defined for the option/command it overrides everthing else
if (defined($oOptionDefine->{$strOption}{&CFGDEF_COMMAND}{$strCommand}{&CFGDEF_INTERNAL}))
Expand Down Expand Up @@ -370,6 +372,7 @@ sub process
$$oOption{&CONFIG_HELP_DESCRIPTION} = $$oCommandOption{&CONFIG_HELP_DESCRIPTION};
$$oOption{&CONFIG_HELP_EXAMPLE} = $oOptionDoc->fieldGet('example');
$oOption->{&CONFIG_HELP_INTERNAL} = $oOptionDefine->{$strOption}{&CFGDEF_INTERNAL};
$oOption->{&CONFIG_HELP_BETA} = $oOptionDefine->{$strOption}{&CFGDEF_BETA};
}
}
}
Expand Down Expand Up @@ -819,6 +822,12 @@ sub helpOptionGet
$oOptionElement->
nodeAdd('p')->textSet($$oOptionHash{&CONFIG_HELP_SUMMARY});

# Add beta warning
if ($$oOptionHash{&CONFIG_HELP_BETA})
{
$oOptionElement->nodeAdd('p')->textSet({name => 'text', children => ['FOR BETA TESTING ONLY. DO NOT USE IN PRODUCTION.']});
}

$oOptionElement->
nodeAdd('p')->textSet($$oOptionHash{&CONFIG_HELP_DESCRIPTION});

Expand Down Expand Up @@ -862,32 +871,38 @@ sub helpOptionGet
}

# Get the example
my $strExample;
my $strExample = '';

my $strOptionPrefix = $rhConfigDefine->{$strOption}{&CFGDEF_GROUP};
my $strOptionIndex = defined($strOptionPrefix) ?
"${strOptionPrefix}1-" . substr($strOption, length($strOptionPrefix) + 1) : $strOption;

if (defined($strCommand))
if (defined($strCommand) && docConfigOptionTypeTest($strOption, CFGDEF_TYPE_BOOLEAN))
{
if (docConfigOptionTypeTest($strOption, CFGDEF_TYPE_BOOLEAN))
{
if ($$oOptionHash{&CONFIG_HELP_EXAMPLE} ne 'n' && $$oOptionHash{&CONFIG_HELP_EXAMPLE} ne 'y')
{
confess &log(ERROR, "option ${strOption} example should be boolean but value is: " .
$$oOptionHash{&CONFIG_HELP_EXAMPLE});
}

$strExample = '--' . ($$oOptionHash{&CONFIG_HELP_EXAMPLE} eq 'n' ? 'no-' : '') . $strOptionIndex;
}
else
if ($$oOptionHash{&CONFIG_HELP_EXAMPLE} ne 'n' && $$oOptionHash{&CONFIG_HELP_EXAMPLE} ne 'y')
{
$strExample = "--${strOptionIndex}=" . $$oOptionHash{&CONFIG_HELP_EXAMPLE};
confess &log(ERROR, "option ${strOption} example should be boolean but value is: " .
$$oOptionHash{&CONFIG_HELP_EXAMPLE});
}

$strExample = '--' . ($$oOptionHash{&CONFIG_HELP_EXAMPLE} eq 'n' ? 'no-' : '') . $strOptionIndex;
}
else
{
$strExample = "${strOptionIndex}=" . $$oOptionHash{&CONFIG_HELP_EXAMPLE};
foreach my $strLine (split('\\|', $$oOptionHash{&CONFIG_HELP_EXAMPLE}))
{
if ($strExample ne '')
{
$strExample .= ' ';
}

if (defined($strCommand))
{
$strExample .= '--';
}

$strExample .= "${strOptionIndex}=${strLine}";
}
}

$strCodeBlock .= (defined($strCodeBlock) ? "\n" : '') . "example: ${strExample}";
Expand Down
2 changes: 2 additions & 0 deletions doc/lib/pgBackRestDoc/Custom/DocConfigData.pm
Expand Up @@ -147,6 +147,8 @@ use constant CFGDEF_DEFAULT_LITERAL => 'default-
use constant CFGDEF_GROUP => 'group';
push @EXPORT, qw(CFGDEF_GROUP);

use constant CFGDEF_BETA => 'beta';
push @EXPORT, qw(CFGDEF_BETA);
use constant CFGDEF_INDEX => 'index';
push @EXPORT, qw(CFGDEF_INDEX);
use constant CFGDEF_INHERIT => 'inherit';
Expand Down
7 changes: 7 additions & 0 deletions src/build/config/config.yaml
Expand Up @@ -610,6 +610,13 @@ option:
command-role:
main: {}

beta:
section: global
type: boolean
default: false
internal: true
beta: true

buffer-size:
section: global
type: size
Expand Down
7 changes: 7 additions & 0 deletions src/build/config/parse.c
Expand Up @@ -42,6 +42,7 @@ STRING_EXTERN(OPT_TYPE_TIME_STR, OPT_TYPE_TIM
/***********************************************************************************************************************************
Option constants
***********************************************************************************************************************************/
STRING_EXTERN(OPT_BETA_STR, OPT_BETA);
STRING_EXTERN(OPT_STANZA_STR, OPT_STANZA);

/***********************************************************************************************************************************
Expand Down Expand Up @@ -297,6 +298,7 @@ typedef struct BldCfgOptionRaw
const String *type;
const String *section;
bool internal;
bool beta;
const Variant *required;
const Variant *negate;
bool reset;
Expand Down Expand Up @@ -815,6 +817,10 @@ bldCfgParseOptionList(Yaml *const yaml, const List *const cmdList, const List *c
{
optRaw.internal = yamlBoolParse(optDefVal);
}
else if (strEqZ(optDef.value, "beta"))
{
optRaw.beta = yamlBoolParse(optDefVal);
}
else if (strEqZ(optDef.value, "negate"))
{
optRaw.negate = varNewBool(yamlBoolParse(optDefVal));
Expand Down Expand Up @@ -904,6 +910,7 @@ bldCfgParseOptionList(Yaml *const yaml, const List *const cmdList, const List *c
.type = strDup(optRaw->type),
.section = strDup(optRaw->section),
.internal = optRaw->internal,
.beta = optRaw->beta,
.required = varBool(optRaw->required),
.negate = varBool(optRaw->negate),
.reset = optRaw->reset,
Expand Down
3 changes: 3 additions & 0 deletions src/build/config/parse.h
Expand Up @@ -51,6 +51,8 @@ STRING_DECLARE(OPT_TYPE_TIME_STR);
/***********************************************************************************************************************************
Option constants
***********************************************************************************************************************************/
#define OPT_BETA "beta"
STRING_DECLARE(OPT_BETA_STR);
#define OPT_STANZA "stanza"
STRING_DECLARE(OPT_STANZA_STR);

Expand Down Expand Up @@ -118,6 +120,7 @@ struct BldCfgOption
const String *type; // Option type, e.g. integer
const String *section; // Option section, i.e. stanza or global
bool internal; // Is the option internal?
bool beta; // Is the option beta?
bool required; // Is the option required?
bool negate; // Can the option be negated?
bool reset; // Can the option be reset?
Expand Down
3 changes: 3 additions & 0 deletions src/build/config/render.c
Expand Up @@ -711,6 +711,9 @@ bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg, co
" PARSE_RULE_OPTION_TYPE(%s),\n",
strZ(opt->name), strZ(bldEnum("cfgOptType", opt->type)));

if (opt->beta)
strCatZ(configOpt, " PARSE_RULE_OPTION_BETA(true),\n");

if (opt->negate)
strCatZ(configOpt, " PARSE_RULE_OPTION_NEGATE(true),\n");

Expand Down
10 changes: 10 additions & 0 deletions src/build/help/help.xml
Expand Up @@ -151,6 +151,16 @@
</text>

<config-key-list>
<config-key id="beta" name="Allow Beta Features">
<summary>Allow beta features.</summary>

<text>
<p>Allow beta features to be enabled but do not actually enable them. Beta features need to be separately enabled using related options.</p>
</text>

<example>2MiB</example>
</config-key>

<config-key id="buffer-size" name="Buffer Size">
<summary>Buffer size for I/O operations.</summary>

Expand Down
28 changes: 19 additions & 9 deletions src/command/help/help.c
Expand Up @@ -97,11 +97,14 @@ helpRenderSplitSize(const String *string, const char *delimiter, size_t size)
Helper function for helpRender() to make output look good on a console
***********************************************************************************************************************************/
static String *
helpRenderText(const String *const text, const bool internal, const size_t indent, const bool indentFirst, const size_t length)
helpRenderText(
const String *const text, const bool internal, const bool beta, const size_t indent, const bool indentFirst,
const size_t length)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(STRING, text);
FUNCTION_LOG_PARAM(BOOL, internal);
FUNCTION_LOG_PARAM(BOOL, beta);
FUNCTION_LOG_PARAM(SIZE, indent);
FUNCTION_LOG_PARAM(BOOL, indentFirst);
FUNCTION_LOG_PARAM(SIZE, length);
Expand All @@ -116,7 +119,12 @@ helpRenderText(const String *const text, const bool internal, const size_t inden
{
// Split the text into paragraphs
const StringList *const lineList = strLstNewSplitZ(
strNewFmt("%s%s", strZ(text), internal ? "\n\nFOR INTERNAL USE ONLY. DO NOT USE IN PRODUCTION." : ""), "\n");
strNewFmt(
"%s%s", strZ(text),
beta ?
"\n\nFOR BETA TESTING ONLY. DO NOT USE IN PRODUCTION." :
(internal ? "\n\nFOR INTERNAL USE ONLY. DO NOT USE IN PRODUCTION." : "")),
"\n");

// Iterate through each paragraph and split the lines according to the line length
for (unsigned int lineIdx = 0; lineIdx < strLstSize(lineList); lineIdx++)
Expand Down Expand Up @@ -313,7 +321,7 @@ helpRender(const Buffer *const helpData)
strCatFmt(
result, " %s%*s%s\n", cfgParseCommandName(commandId),
(int)(commandSizeMax - strlen(cfgParseCommandName(commandId)) + 2), "",
strZ(helpRenderText(commandData[commandId].summary, false, commandSizeMax + 6, false, CONSOLE_WIDTH)));
strZ(helpRenderText(commandData[commandId].summary, false, false, commandSizeMax + 6, false, CONSOLE_WIDTH)));
}

// Construct message for more help
Expand Down Expand Up @@ -405,10 +413,10 @@ helpRender(const Buffer *const helpData)
"%s\n"
"\n"
"%s\n",
strZ(helpRenderText(commandData[commandId].summary, false, 0, true, CONSOLE_WIDTH)),
strZ(helpRenderText(commandData[commandId].summary, false, false, 0, true, CONSOLE_WIDTH)),
strZ(
helpRenderText(
commandData[commandId].description, commandData[commandId].internal, 0, true, CONSOLE_WIDTH)));
commandData[commandId].description, commandData[commandId].internal, false, 0, true, CONSOLE_WIDTH)));

// Construct key/value of sections and options
KeyValue *optionKv = kvNew();
Expand Down Expand Up @@ -484,7 +492,7 @@ helpRender(const Buffer *const helpData)
strCatFmt(
result, " --%s%*s%s\n",
cfgParseOptionName(optionId), (int)(optionSizeMax - strlen(cfgParseOptionName(optionId)) + 2), "",
strZ(helpRenderText(summary, false, optionSizeMax + 6, false, CONSOLE_WIDTH)));
strZ(helpRenderText(summary, false, false, optionSizeMax + 6, false, CONSOLE_WIDTH)));
}
}

Expand All @@ -507,7 +515,7 @@ helpRender(const Buffer *const helpData)
if (!option.found || !cfgParseOptionValid(cfgCommand(), cfgCmdRoleMain, option.id))
THROW_FMT(OptionInvalidError, "option '%s' is not valid for command '%s'", strZ(optionName), commandName);

// Output option summary and description. Add a warning for internal options.
// Output option summary and description. Add a warning for internal and beta options.
CHECK(
AssertError, optionData[option.id].summary != NULL && optionData[option.id].description != NULL,
"option help missing");
Expand All @@ -520,9 +528,11 @@ helpRender(const Buffer *const helpData)
"\n"
"%s\n",
cfgParseOptionName(option.id),
strZ(helpRenderText(optionData[option.id].summary, false, 0, true, CONSOLE_WIDTH)),
strZ(helpRenderText(optionData[option.id].summary, false, false, 0, true, CONSOLE_WIDTH)),
strZ(
helpRenderText(optionData[option.id].description, optionData[option.id].internal, 0, true, CONSOLE_WIDTH)));
helpRenderText(
optionData[option.id].description, optionData[option.id].internal, option.beta, 0, true,
CONSOLE_WIDTH)));

// Output current and default values if they exist
const String *defaultValue = cfgOptionDefault(option.id);
Expand Down
4 changes: 3 additions & 1 deletion src/config/config.auto.h
Expand Up @@ -55,6 +55,7 @@ Option constants
#define CFGOPT_ARCHIVE_PUSH_QUEUE_MAX "archive-push-queue-max"
#define CFGOPT_ARCHIVE_TIMEOUT "archive-timeout"
#define CFGOPT_BACKUP_STANDBY "backup-standby"
#define CFGOPT_BETA "beta"
#define CFGOPT_BUFFER_SIZE "buffer-size"
#define CFGOPT_CHECKSUM_PAGE "checksum-page"
#define CFGOPT_CIPHER_PASS "cipher-pass"
Expand Down Expand Up @@ -131,7 +132,7 @@ Option constants
#define CFGOPT_TYPE "type"
#define CFGOPT_VERBOSE "verbose"

#define CFG_OPTION_TOTAL 164
#define CFG_OPTION_TOTAL 165

/***********************************************************************************************************************************
Option value constants
Expand Down Expand Up @@ -372,6 +373,7 @@ typedef enum
cfgOptArchivePushQueueMax,
cfgOptArchiveTimeout,
cfgOptBackupStandby,
cfgOptBeta,
cfgOptBufferSize,
cfgOptChecksumPage,
cfgOptCipherPass,
Expand Down

0 comments on commit 24f7252

Please sign in to comment.