Customize SDK installation instructions#
If you use a caching proxy such as Artifactory in front of your Pro, Enterprise, or On-Prem instance of the BSR, you may want to customize the SDK installation instructions displayed by the BSR so that you can direct users to your preferred proxy rather than having them pull SDKs directly from the BSR.
To customize SDK installation instructions, go to Admin panel > Custom SDK instructions:
SDK installation instructions are customized on a per-package manager basis. Clicking Customize instructions for a given package manager allows you to customize the instructions users see for that package manager. (If the instructions have already been customized, you can use the Edit button to update the customization or use the Restore defaults button to delete the customizations.)
When you click Customize instructions you'll see an editor window where you can enter and edit Markdown-formatted instructions for display to users. By default, the BSR starts you off with a Markdown template that closely replicates the default installation instructions: this template is just a starting point and you can modify it however you like.
Notice in the screenshot above that the markdown includes text like {{pkg}}.
SDK installation instructions vary depending on the repo and plugin, so
variables like {{pkg}} are replaced by the name of the actual SDK package when
the instructions are displayed. The rest of this page explains how the
customized Markdown text you enter is processed before being displayed to the
user. Every administrator who customizes SDK installation instructions will
use variable substitutions like {{pkg}} and {{version}}. Some may need to
use the more advanced customization techniques of expression substitution and
conditionals, described below.
Variable substitution#
When you customize the SDK installation instructions for a specific package
manager, you can include variable names inside double curly braces ({{pkg}},
for example) and this text is replaced with the value of the variable when
the instructions for a specific repo and plugin are rendered. For the
googleapis/googleapis SDK generated by the protocolbuffers/go plugin, for
example, {{pkg}} is replaced with googleapis/googleapis/protocolbuffers/go.
The following variables are defined for all package managers, and their value is the same for all package managers. Unless otherwise noted their values are all strings:
| Variable | Value |
|---|---|
| bsrhost | The hostname of your BSR instance |
| bsruser | The username of the current BSR user or the string "YOUR BSR USERNAME" if the user isn't logged in |
| repo.owner | The BSR organization that owns the repo whose SDK is being installed |
| repo.name | The name of the repo (not including the organization) whose SDK is being installed |
| repo.reference | The "reference" for the repo version. This is typically a branch or tag name like "main" |
| plugin.owner | The BSR organization that owns the plugin used to generate the SDK |
| plugin.name | The name of the plugin (not including the organization) used to generate the SDK |
| plugin.version | The semantic version of the plugin used to generate the SDK |
| plugin.revision | The plugin revision (like a minor version number). This is a number, not a string |
The following variables may have different values for different package managers, or may only be defined for some package managers. The values of all of these variables are strings:
| Variable | Package Managers | Value |
|---|---|---|
| pkg | all | Specifies the repo owner, repo name, plugin owner and plugin name for the SDK package to be downloaded, formatted appropriately (with slashes or underscores, for example) for the current package manager. For example: googleapis/googleapis/protocolbuffers/go or googleapis-googleapis-protocolbuffers-python |
| version | all | Specifies both the version of the repo and the version and revision of the plugin used to generate the SDK for that repo. For example: v1.36.2-20260306222252-5ba065e50397.1 (Go) or 30.2.0.1.20260306222252+5ba065e50397 (python). If the latest version of the repo and plugin are used, this variable may be the string latest (for package managers that support that) or it may be the empty string. |
| cmakePkgPath | CMake only | For the CMake package manager, the pkg variable has its parts separated by underscores. This cmakePkgPath replaces the underscores with slashes. |
| curlDownloadUrl | curl only | A direct BSR download URL for the package |
| curlExtension | curl only | The string "zip" or "tar.gz", depending on what archive format the user has selected |
| curlQueryParams | curl only | For Dart packages only, this is a query string required at the end of the download URL |
| goPluginMajorVersion | Go only | If the semantic version of the plugin that generated the SDK has a major version of 2 or greater, then this variable is a string of the form /vx where x is the major version number such as "2" or "12". In all other cases this variable is the empty string |
| mavenGroupId | mvn, Gradle | For Java (or JVM) SDKs, this variable is the group ID for the SDK |
| swiftProduct | Swift, Xcode | This variable contains the same information as pkg, but with different delimiters and capitalization |
If you use an undefined variable, no substitution is performed
and users simply see the original {{variable}} text in the rendered markdown
instructions. For example, if you use the undefined variable username
(instead of bsruser which is defined) then users simply see {{username}}
in the rendered output.
Expression substitution#
In addition to simple variable substitution, double curly braces in SDK customization markdown text can be used to evaluate an expression and substitute the value of the expression into your markdown. The BSR uses the Common Expression Language (CEL). You can learn more about writing CEL expressions at celbyexample.com. For example, if you write {{2+3}} in your customized markdown, the end user sees "5"—the value of that expression, converted to a string.
Expression substitution is generally only useful when the expression involves
a variable. If you wanted to render just the first 10 characters of the
version variable, for example, then instead of writing {{version}} you could
write {{version.substring(0,10)}}.
In practice a common reason to use expression substitution is to specially handle
the case where the version variable is empty. You can do this with the ?:
operator (which may be familiar to you from C, Java and JavaScript). For
example, when installing Python packages, the version string is prefixed with
==, but if there is no version string then the == must be omitted. This can
be achieved with a substitution like this:
If the version variable isn't the empty string, then the value of the expression above is two equals signs concatenated with the version string. Otherwise the value of the expression is the empty string.
If you use an undefined variable in your CEL expression, no substitution is
performed and users simply see the original {{expression}} text in the rendered
markdown instructions.
Note
The BSR validates all CEL expressions at publish time, whether used in
expression substitution, #define directives, or conditional directives. If
a syntax error is found, you won't be able to publish your customized
instructions until it is fixed.
Defining new variables#
In addition to using the pre-defined variables listed in the tables above, your customized SDK markdown can also define its own new variables, using a syntax that will be familiar if you've worked with C or C++ code before:
If your markdown includes a line like the above, then expression is evaluated as a CEL expression and variable is defined as a variable whose value is the value of the expression.
If you are customizing SDK installation instructions to direct the user to install the SDK using your corporate Artifactory instance, for example, then you might find it helpful to define the Artifactory hostname as a variable:
With this variable defined you can simply use {{HOST}} in your markdown any
time you want to reference Artifactory.
There are some additional details to understand about #define directives:
-
The
#definemust appear at the start of a line. No whitespace before or after the#character is allowed. -
The expression in a
#definedirective is a CEL expression. If you want the value of the new variable to be a string, you must enclose it in double quotes. (This is different from C-preprocessor directives you may be used to from C or C++ programming) -
Variables defined in this way can only be used after they're defined, not before.
-
You can use
#defineto change the value of existing variables, though this is generally not a good idea. -
You can delete, or undefine, a variable with
#undef <variable>
Conditionally including or excluding content#
The most advanced level of SDK instruction customization uses conditional directives to include or exclude blocks of text from your customized markdown. These conditional directives allow you to display different instructions for different repositories, organizations, or plugins. Most BSR installations don't need to do this, but here is an example of how the feature might be used:
#if repo.owner == "widgets"
#define HOST "artifactory.widgets.example.com"
#elif repo.owner == "gadgets"
#define HOST "artifactory.gadgets.example.com"
#endif
#ifdef HOST
custom installation instructions for SDKs from the widgets and gadgets
organizations go here, using {{HOST}} to refer to the org-specific
Artifactory instance.
#endif
The #if and #elif ("else if") directives take CEL expressions and include
the lines that follow if the expression is true (or a non-zero number, or a
non-empty string). The example checks for repos in the "widgets" and "gadgets"
organizations and sets the HOST variable to an organization-specific Artifactory
instance just for repos in those two orgs. Next, it checks to see if the HOST
variable has been defined, and if so, it includes custom instructions using that
variable. These are the instructions that are displayed to users when they want
to install an SDK for a repo in either of these two specific organizations.
This example demonstrates one of the important features of conditional
directives: if you write custom markdown with conditional directives that
exclude all the content then the BSR simply displays its default, un-customized,
instructions. In the example above, any repo not owned by the "widgets" or
"gadgets" organizations will never have HOST defined, so the #ifdef HOST block is
skipped and the BSR displays its default instructions instead.
As with #define, conditional directives must start with the # character at the
beginning of a line with no whitespace before or after #. (This avoids conflicts
with Markdown headings, which always have a space after #.) Note that there
are a number of syntax rules associated with conditional directives. Every #if
must have a matching #endif, for example, and you can't have an #else or
#elif without an #if. If you break any of these rules you won't be able to
publish your customized SDK instructions until you correct the error.
The full set of supported directives is documented below:
#define NAME expression#
Defines a variable named NAME and sets its value to the value of the CEL expression. The name must be a simple identifier with no spaces or periods. Everything following the name is the expression. Variables defined this way are available in subsequent CEL expressions.
#undef NAME#
Removes a previously defined variable.
#ifdef NAME / #ifndef NAME#
Conditionally includes the following lines, up to the next #else or #endif
based on whether a variable is defined (#ifdef) or not defined (#ifndef).
Note that the value of a variable isn't relevant; it only matters if the
variable has been defined or not.
#if EXPRESSION / #elif EXPRESSION#
Conditionally includes the following lines, up to the next #else or #endif
based on whether a CEL expression evaluates to true (or to any value other
than false, 0, "", or null).
#else#
Begins the else branch of a conditional block. If all of the previous #if and
#elif were excluded, then lines between this #else and the next
#endif are included. Otherwise, if any of the previous branches were included,
then the lines up to the #endif are excluded.
#endif#
Ends a conditional block. Every #ifdef, #ifndef, or #if must
have a matching #endif.

