End-to-end SDK generation and publishing with GitHub Actions
This tutorial includes the following SDK languages and versions:
TypeScript Java Python C# Go PHP ✅1 ✅ ✅1 ✅ ✅ ✅ 1 By default the control repo template mentioned below creates a config file for TypeScript v1 and Python v2 SDK generation. To use TypeScript v2 or Python v1, ensure the
liblabVersion
in thelanguageOptions
section for the relevant language is set as required.
One of the most useful offerings of liblab is the ability to automate SDK creation and publishing. At liblab, we believe in total code ownership and the ability to control the entire SDK lifecycle. This means that you can use liblab to generate SDKs and then use your own CI/CD pipeline to publish them to your own private or public repositories. We emphasize that you should be able to control the entire SDK lifecycle, and we provide the tools to do so.
This tutorial shows you how to use GitHub Actions with liblab to automate the SDK generation and publishing process. This will take you from an API spec file all the way to an SDK published to the relevant public package manager, such as npm, PyPI, NuGet or Go Packages.
End goal
This is the end-to-end process that you will set up in this tutorial:
Video walkthrough
You can find a video walkthrough of this tutorial on the liblab YouTube channel:
Prerequisites
To compete this tutorial you will need:
- The liblab CLI installed and logged in
- An API spec
- A GitHub account, with the permissions to create new repositories, Actions secrets, and fine-grained access tokens in your GitHub organization, or in your personal account
- An account with the relevant package manager, such as npm, PyPI or NuGet, with publishing permissions
Steps
In this tutorial you will:
- Create a control repo for your API spec and liblab config file
- Create SDK repos for each SDK language you want to publish
- Create liblab and GitHub tokens
- Create package manager tokens
- Update your spec and config file
- Merge SDK PRs
- Create a release in GitHub
- Verify the publish
1. Create a control repo
The central driver of the liblab CI/CD workflow is a control repo, a repository that contains your liblab config file, any hooks code, and optionally your API spec file. This repo is the source of truth for your SDKs and documentation.
liblab provides a template repo that you can use to get started.
-
Select the button below to create a new control repo using this template.
-
Give your new control repository a name, a description, and make it public or private.
-
Select Create repository to create the repository from the template.
The repository that is created has:
- An example liblab config file
- An API spec for the Swagger Petstore
- A GitHub Action to create PRs for your SDKs when your API spec or config file changes
- A GitHub Action that polls for liblab updates and creates PRs for your SDKs when there is a liblab update
You can read more about the control repo in the control repo documentation.
This is what you should have so far:
2. Create SDK repos
Your generated SDKs live in separate repos - one per SDK language. These repos need to be created before you can publish your SDKs.
liblab provides template repos for these SDK repos that you can use to get started.
-
Select the relevant buttons below to create repos from the different templates:
-
For each repo you create, give your new SDK repository a name, a description, and make it public or private.
-
Select Create repository to create the repository from the template.
The repository that is created has:
- A GitHub Action to publish the SDK to the relevant package manager
-
Repeat this process for each SDK language you want to publish.
You can read more about the SDK repos in the SDK repos documentation.
This is what you should have so far:
3. Create liblab and GitHub tokens
The GitHub Actions that create SDKs need 2 secrets set - a liblab token that authenticates the liblab CLI, and allows it to run as you, and a GitHub token that allows the liblab CLI to push to your SDK repos.
Create a liblab token
-
From the liblab CLI, run the following command to generate a liblab token:
Terminalliblab token create GITHUB_PR_TOKEN
You can replace
GITHUB_PR_TOKEN
with any name you choose for the token. If you have multiple APIs that you are generating SDKs for, you can either share the token between them, or create different tokens for each API.TerminalToken successfully generated, it will be valid for 90 days
-TOKEN-----------------------------------------
liblab_WoVBcuKKvwIGw2-EBjGHRjJdAJ46qggy8caFiqSP
-----------------------------------------------noteBy default this token will expire after 90 days. You can increase this to up to 364 days by passing the
--durationInDays
parameter.You can read more on the
liblab token
command in the CLI documentation. -
Copy the token that is generated. You need the string between the
-TOKEN-
and-------
lines, not those lines themselves. -
Add this new token to your Github Control Repo's Actions secrets as
LIBLAB_TOKEN
.This token will be used to interact with liblab during each Action run. You can find the documentation on how to do this in the Github secrets documentation.
Create a GitHub token
For the Action in your control repo to push to your SDK repos, you will need to generate a GitHub Fine Grained Access Token that has access to write to your SDK repos.
-
From GitHub, select your profile picture, then select Settings, Developer settings, Personal access tokens, then Fine-grained tokens.
-
Give the token a name and a description
-
Set the expiration for the token based on your needs or your organizations security policies.
tipIt's helpful to create a reminder for yourself such as a calendar item to refresh the token just before it expires!
-
Select the repositories that this token will have access too. It is good practice to limit the scope of these tokens, so select just the SDK repositories that you want to publish to.
-
Set the token permissions. This token will need the following permissions:
Permission Access Commit Statuses Read/Write Contents Read/Write Metadata Read Pull Requests Read/Write Workflows Read/Write When you set these permissions, you will see the following:
-
Add this token to your control repo GitHub Action secrets as
LIBLAB_GITHUB_TOKEN
.
This is what you should have so far:
4. Create package manager tokens
Depending on what SDK language you are using, you will need to create additional secrets.
- TypeScript
- Python
- C#
- Java
- Go
- PHP
To publish to npm, you will need an npm access token. You can generate a new access token from your user settings on npm.
-
Select the Access Tokens tab, drop down the Generate New Token button, then select Granular Access Token.
-
Fill in all the required details for the token such as the name and expiry.
-
Make sure that this token has read and write permission to publish packages. If the package already exists, you can scope this token to just that package, otherwise this token needs read and write for all packages.
-
Once the token has been created, make a copy of it.
-
In your TypeScript SDK repo, add this token as an Actions secret named
NPM_TOKEN
.
You can learn more about creating npm tokens in the npm access tokens documentation.
To publish to PyPI you will need an API token. You can generate a new API token from your account settings page on PyPI.
-
Select Account settings, scroll to the API tokens section, then select the Add API token button.
-
Give the token a name.
-
Select the scope for the token. If the package already exists, select that package for the scope, otherwise select Entire account (all packages).
-
Select the Create token button.
-
Once the token has been created, make a copy of it.
-
In your Python SDK repo, add this token as an Actions secret named
PYPI_TOKEN
.
You can learn more about creating PyPI tokens in the PyPI API tokens documentation.
To publish to NuGet, you will need a NuGet API key. You can generate an API key from your account on NuGet.
-
Select your name on the top right, then select the API Keys.
-
From the API Keys page, expand the Create section.
-
Give your API Key a name, and set the required expiry and package owner.
-
Set the scope for this API Key.
- If the package already exists, select Push and Push only new package versions. Then in the Select Packages section, select the package you want to publish updates for.
- If the package does not exist, select Push and Push new packages and package versions, then in the Glob pattern, put
*
. This will allow you to push a new package.
-
Select the Create button.
-
Once the API Key has been created, expand the Manage section, and make a copy of it using the Copy button.
-
In your C# SDK repo, add this token as an Actions secret named
NUGET_TOKEN
.
You can learn more about creating NuGet API Keys in the NuGet API Keys documentation.
To publish to the Maven Central Repository, you will need Maven credentials and a GPG key with a passphrase to sign your artifacts.
As of March 12th, 2024, the Central Portal became the default publishing server for Maven packages. At the moment, we do not support automatic publishing through the Legacy OSSRH.
Central Portal Setup
The Central Portal Account Documentation explains how to create an account. An account is required for claiming namespaces, generating credentials and managing package deployments.
Namespace Registration
Your namespace is the crucial prerequisite for publishing a package to the Central Repository as it is the groupId
of the package. The Central Repository Namespace Documentation provides detailed instructions for claiming a namespace.
The namespace verification process is not instant.
- For own domain namespaces (e.g.
com.liblab
), an additional step is required, which involves setting up the verification key as a DNS record in order for the domain name to be verified by the Central Repository. - For GitHub namespaces (namespace in the form of
io.github.githuborgname
), though, verification process is automatic if the user is registered using the GitHub SSO.
Once your namespace is verified, the next step is to generate your Maven credentials.
Generate Maven Credentials
-
Head to the Central Portal at central.sonatype.com, and either create an account, or sign in.
-
Select your email on the top right, then select View Account.
-
From the Account page, select the Generate User Token button.
-
When the dialog appears, confirm that you want to generate a new token, and credentials will be generated.
-
In your Java SDK repo, add value of the
Username
field as a new GitHub Actions secret calledMAVEN_USERNAME
and the value of thePassword
field as a secret calledMAVEN_PASSWORD
.
You can learn more about creating Central Portal credentials in the Central Portal Documentation.
Generate GPG Key
The Central Repository requires all artifacts to be signed with PGP, such as using a GPG key. You will need to generate your own key pair, distribute it to the key server and obtain the private key, then set this as an other GitHub Actions secret.
The following steps will guide you through the process of creating a GPG key using GnuPG. If you already have a GPG key, you can skip this step. If you want to use a different tool to generate your GPG key, you will need to follow the relevant documentation for that tool.
-
Install GnuPG if you don't have it installed.
-
Initialize the key generation prompt by running:
Terminalgpg --gen-key
-
Follow the instructions from
gpg
, and choose a passphrase -
Once the key is generated, you will need to get the key id. This is the 40-character hex string on the second line of the
pub
section in the output from generating the key:Terminalpublic and secret key created and signed.
pub ed25519 2024-05-07 [SC] [expires: 2027-05-07]
AB1C2D3456EF78A90BC12D34567890123456789E
uid Exciting Soda <[email protected]>
sub cv25519 2024-05-07 [E] [expires: 2027-05-07] -
Send the key to your key server, such as
openpgp
by running the following command:Terminalgpg --keyserver keys.openpgp.org --send-keys <key-id>
Replace
<key-id>
with the key id you obtained in the previous step. -
Get the private key using the following command:
Terminalgpg --export-secret-keys --armour <key-id>
Replace
<key-id>
with your key id.Terminal➜ ~ gpg --export-secret-keys --armour AB1C2D3456EF78A90BC12D34567890123456789E
-----BEGIN PGP PRIVATE KEY BLOCK-----
mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U
b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE
ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy
MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO
dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4
OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s
E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb
DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn
0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE=
=iIGO
-----END PGP PRIVATE KEY BLOCK----- -
Copy the output from this command, and add it to a new GitHub Actions secret called
GPG_PRIVATE_KEY
. -
Create another GitHub Actions secret called
GPG_PASSPHRASE
, and set this to the passphrase you chose when generating the key.
Go package management differs from many other languages in that it does not require API tokens for publishing to the Go Packages repository. Unlike some ecosystems where API tokens are necessary to interact with package registries, Go relies on its module proxy system, which integrates seamlessly with GitHub.
As explained in the official publishing tutorial on Go's website, packages are published to the Go Packages repository by tagging a release on GitHub. The Create a release in GitHub section later in this tutorial provides a step-by-step guide on how to create a release in GitHub which will automatically trigger the publishing of the package to the Go Packages repository. Upon creating a release, it should not take longer than one hour for the package to become available on the Go Packages repository.
To publish your PHP SDK to Packagist, you will first need an account on Packagist and your Packagist API Token.
-
Create an Account on Packagist or login to Packagist using your GitHub account.
-
Obtain your Packagist API Token from your profile settings
-
In your PHP SDK repo, add this token as an Actions secret named
PACKAGIST_TOKEN
and set the secretPACKAGIST_USERNAME
to your packagist username as it appears on your profile. -
Submit your package
- Log in to your Packagist account.
- Go to the Submit tab.
- Enter the URL of your SDK's GitHub repository.
- Select Check to validate the repository and Submit to add your package to Packagist.
You should now have the following complete setup:
5. Update your spec and config file
Now that all your repos are created and configured, you need to update the contents of the control repo with your API spec, and configure the liblab config file.
-
Check out your control repo, and open it in your code editor. You can also open the repo in a GitHub codespace, or edit individual files through the GitHub web UI.
-
The control repo has a sample API spec file using the Swagger petstore. You need to replace this with your API spec.
- If you are using an API spec from a local file, add this to the repo, replacing the
spec.json
file. - If you are using a remote spec:
- Update the
specFilePath
option in theliblab.config.json
file to point to the URL of your API spec. - Delete the
spec.json
file from the repo.
- Update the
noteYou can leave this as the pet store spec for now, and update it later. This will allow you to test the workflow without needing to update the spec file.
- If you are using an API spec from a local file, add this to the repo, replacing the
-
Update your liblab config file:
- Update the
sdkName
,apiName
, andapiVersion
options to match your API and the name you want for your SDK. - Update the
languages
for the SDK languages you want to generate. - In the
languageOptions
section:-
remove any options for languages that you don't want to support.
-
Update the
githubRepoName
option to match the name of the SDK repo you created for that language. This just needs to be the repo name, not the full URL (for example,typescript-sdk
nothttps://github.com/myorg/typescript-sdk
). -
Update the
sdkVersion
option to the version number you want to use for each SDK. -
Update the relevant options to set details for each package manager:
- TypeScript
- Python
- C#
- Java
- Go
- PHP
- Set the
pypiPackageName
option to the name of your PyPI package. This needs to be unique.
- Set the
packageId
option to the name of your NuGet package.
- Set the
groupId
option to your namespace verified by the Maven Central Portal. - Set the
homepage
option to the valid public URL of your SDK homepage. - Add at least one developer to the
developers
option. - (Recommended) Set the
artifactId
option. If not set, it will default to the kebab-cased version ofsdkName
.
-
Set the
goModuleName
option to the name of your Go module. This should match the SDK repo name, for examplegithub.com/myorg/go-sdk
. -
Ensure your license is set.
noteThe Go Packages License Policy requires that all packages published to the Go Packages repository must include a recognized license file. It is important to note that if the package does not include a recognized license file, only limited package information will be displayed on the Go Packages repository.
For more information on how to specify a license for your SDK, see the license config file option.
- Set the
packageName
option to the name of your PHP package. It must be in the formatvendor/packageName
.
-
- In the
publishing
section, set thegithubOrg
option to the name of your GitHub organization. - There are other options you can configure, but these are not required for now. To learn more, see the config file documentation.
When complete, your config file should look something like this:
liblab.config.json{
"sdkName": "test-sdk",
"apiVersion": "1.0.0",
"apiName": "test-api",
"specFilePath": "spec.json",
"languages": [
"csharp",
"python",
"typescript",
"go",
"php",
"java"
],
"languageOptions": {
"csharp": {
"liblabVersion": "2",
"packageId": "Test.SDK",
"githubRepoName": "csharp-sdk",
"sdkVersion": "1.0.0"
},
"python": {
"liblabVersion": "2",
"pypiPackageName": "test-sdk",
"githubRepoName": "python-sdk",
"sdkVersion": "1.0.0"
},
"typescript": {
"npmName": "test-sdk",
"npmOrg": "myorg",
"githubRepoName": "typescript-sdk",
"sdkVersion": "1.0.0"
},
"java": {
"groupId": "com.myorg",
"artifactId": "test-sdk",
"homepage": "https://myorg.com",
"githubRepoName": "java-sdk",
"sdkVersion": "1.0.0",
"developers": [
{
"name": "John Doe",
"email": "[email protected]",
"organization": "My Organization",
"organizationUrl": "https://myorg.com"
}
]
},
"go": {
"goModuleName": "github.com/myorg/go-sdk",
"githubRepoName": "go-sdk",
"sdkVersion": "1.0.0",
},
"php": {
"packageName": "myorg/test-sdk",
"githubRepoName": "php-sdk",
"sdkVersion": "1.0.0"
},
},
"publishing": {
"githubOrg": "myorg"
}
} - Update the
-
Commit the changes, and push them to your control repo. This will trigger the GitHub Action to generate your SDKs, and raise PRs against your SDK repos. You can check the progress of this in the Actions tab of your control repo.
6. Merge SDK PRs
Once the control repo Action is complete, you will see a PR raised against each SDK repo.
If you don't see the PRs, check the Actions tab of your control repo to see if there were any errors during the SDK generation process. You can find more information on common errors in the common errors section.
- Review the PRs to ensure that the changes are correct.
- Once you are happy, approve and merge the PRs.
7. Create a release in GitHub
To trigger the Action to publish your SDK to the package manager, you need to create a release in the SDK repo. Releases need a tag to tag the commit that you want to release.
-
From each SDK repo, select Create a new release from the Releases section on the side.
-
Drop down the Choose a tag button, enter a tag, and select Create new tag. The tag should be a version number using semantic versioning, and it is common to prefix the version with
v
, such asv1.0.0
.Note on Versioning Go PackagesIt is important to note that the release tag version will be used as the version of the package in the Go Packages repository. Therefore, it is important to follow semantic versioning and ensure that the version in the
liblab.config.json
file corresponds to the version of the release tag. -
Give the release a title and description. The title can be the same as the tag.
-
Select Publish release to create the release.
This will trigger the GitHub Action to publish the SDK to the relevant package manager.
8. Verify the publish
Once the publishing Action is complete, you can check the relevant package manager to see if the SDK has been published.
You are now all configured to automatically generate and publish SDKs using liblab and GitHub Actions so that your latest API updates are available to your users as soon as possible.
If you do not see the packages, check the Actions tab in each SDK repo to see if there were any errors during the publish process.
Common errors
If your repos are not configured correctly, you may see one of the following errors:
-
If the generate SDKs Action fails with this error:
GitHub Actions outputError: Secrets LIBLAB_TOKEN and LIBLAB_GITHUB_TOKEN are required
Then you haven't set up the
LIBLAB_TOKEN
orLIBLAB_GITHUB_TOKEN
secrets correctly. Check that the tokens are set correctly, and that the tokens are valid. -
If the generate SDKs Action fails with this error:
Error: Unable to create branch for <language>
You will need to check the permissions of your GitHub token, or your config file.
- If this is for all SDK languages:
- Verify that the GitHub organization is correctly set in the liblab config file
- Check that your access token has the required permissions for all the SDK repositories listed in your config file
- If you are part of a GitHub organization and not an admin, the access token you create might be pending approval. If you do not have admin permissions, you may have had to fill in a field describing why your personal access token needs access to your organization. In this case, check with your organization admins to approve the token.
- If the error is only for one SDK language, check that the GitHub repo name is correct in the liblab config file options for that language.
- If this is for all SDK languages: