Skip to content

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:

Screenshot of SDK installation instructions customization screen

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.

Screenshot of customizing SDK instructions for pip

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:

{{version != "" ? "==" + version : ""}}

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:

#define <variable> <expression>

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:

#define HOST "https://artifactory.example.com"

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 #define must appear at the start of a line. No whitespace before or after the # character is allowed.

  • The expression in a #define directive 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 #define to 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.