Detailed instructions on publishing a non-scoped public package using semantic release leveraging the power of GitHub Actions
In the evolving software development landscape, maintaining version consistency and automating the release process is more important than ever. Enter Semantic Release: a tool designed to ensure clear and structured versioning. For developers leveraging public non-scoped packages, the process may seem daunting. However, with the power of GitHub Actions at our fingertips, automation becomes a breeze.
This article offers a detailed and comprehensive guide, providing step-by-step instructions to seamlessly integrate Semantic Release into your workflow, specifically tailored for those using public non-scoped packages. Dive in and discover the streamlined approach to software releases.
When I created my public package, I encountered hurdles in publishing it seamlessly to NPM. It was a challenge to ensure the right configurations.
To nail it, let’s go through a step-by-step experience for proper publishing public packages to NPM.
Name of package
Before jumping into implementing our package, it is better to find the proper name for it. To be sure the name is not taken already — check my_package_name and take it for your package. I chose “tokky.” From that point, reserving the package’s name is impossible. For the name in npm, you have to publish the package.
Create a package
The objective is to develop a straightforward package that outputs content to the console. We need to make sure that we can install it and run it. For the build process, let’s use simple esbuild. During this article, I will use the name of the package tokky. Let’s create package.json with the initial data.
mkdir tokky && cd tokky && npm init -y
After executing the command, the system generated a default package.json file for the project, which looks like this:
{
"name": "tokky",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
In this guide, the package.json file plays a crucial role in ensuring the right configuration. At this juncture, let’s specify the node version for our package:
echo "v18" > .nvmrc
and activate the specified version with the following:
nvm use
For the README.md file:
echo "# TokkynnA simple zero dependency logger for node js." > README.md
Finally, install the development dependencies:
npm i -D esbuild eslint prettier
In our initial configuration, we need to address several key points in the package.json:
- main: This designates the primary entry point for the module.
- bin: Here, you’ll specify any executables your module provides.
- files: This should contain an array of file patterns that will be included when the package is packed and subsequently published to the npm registry.
- private: Ensure this is set to false as our package is intended to be public.
- publishConfig: The access for this should be set to public.
After these configurations, your package.json should resemble the following:
Additionally, let’s add two ignore files:
and for npm:
Lastly, I’ll outline my setup for ESLint. However, remember that the configuration may vary based on the specific requirements of your package.
Next, head to GitHub and establish a new repository. Name it after your package.
Proceed by executing the subsequent commands:
git init
git add .
git commit -m "first commit"
git branch -M main
git remote add origin git@github.com:<your_github_username>/tokky.git
git push -u origin main
Package source
Next, let’s craft a basic application and set it up for building. Inside the src folder, generate an index.js file and populate it with the following content:
The concept is straightforward: executing my_package_name hi should display “Hello [username].” To validate this functionality, execute the command directly from your repository using:
node src/index.js hi
If the output aligns with expectations, it’s time to build the source:
npm run build
Successfully running this command will produce a dist folder containing a minified index.js file.
Tokens
Execute Semantic Release, which will determine version bumps and handle the release process based on commit messages, requires environment variables (GITHUB_TOKEN, NPM_TOKEN) to operate correctly. The tokens are fetched from GitHub secrets, ensuring they remain confidential.
To set GITHUB_TOKEN, navigate here: https://github.com/settings/tokens
Generate the token using a dropdown. Click on the new personal access token (classic) and set permission as in the picture. Use your package name as shown below:
Once generated, copy the token value and keep it confidential — it’s crucial not to share this with others. Temporarily store this token securely, as we’ll need it shortly for the Semantic Release CLI.
To generate the NPM_TOKEN, you first need an account on npm’s official website. If you haven’t registered yet, go through the registration process. After that, navigate to:
https://www.npmjs.com/settings/<your_user_name>/tokens/new
and generate a “classic” token with the “publish” option.
Copy the generated value of the token and navigate to GitHub secrets:
https://github.com/<your_user_name>/<your_repo_name>/settings/secrets/actions/new
and put new secret as NPM_TOKEN to repository secrets:
With our secrets now set up, we can configure GitHub Actions.
GitHub Actions setup
To automate our processes, we are going to use GitHub Actions. This is a CI/CD tool integrated within GitHub. It allows developers to automate workflows directly from their GitHub repositories, such as building, testing, and deploying applications. By defining workflows in YAML files, users can trigger actions based on specific events like push and pull requests or scheduled times, making the software development process more efficient and automated.
To begin, create a .github directory at the root of your project. Within this directory, establish a workflows subfolder. Here, craft our configuration file named release.yml and populate it with the following content:
This workflow triggers a push event to the main branch. It’s configured to run the job on the latest Ubuntu virtual machine GitHub offers. While it’s not imperative to delve into every job, let’s spotlight some specific ones. Toward the end, note how we invoke npm run semantic-release using the designated tokens.
Semantic release
For the automated release process, we are going to use Semantic Release. This tool handles versioning and package publishing based on commit message semantics. It follows the conventions of Semantic Versioning (SemVer) to determine version bumps (major, minor, or patch). By analyzing commit messages, it eliminates the manual steps of versioning, ensures consistent version numbers, and streamlines the release process. Let’s set it up.
For that setup, we will use this GitHub code and run it on your repository:
npx semantic-release-cli setup
And follow the questions:
% npx semantic-release-cli setup
? What is your npm registry? https://registry.npmjs.org/
? What is your npm username? your_user_name
? What is your npm password? [hidden]
? What is your NPM two-factor authentication code? 00000000
? Provide a GitHub Personal Access Token (create a token at https://github.com/s
ettings/tokens/new?scopes=repo) ghp_your_token_here
? What CI are you using? Github Actions
You should already have your Personal Token. Simply input it when prompted. Similarly, the GitHub Actions we’ve set up will utilize the NPM_TOKEN that we’ve previously established in the repository secrets. If you now check your package.json, the version will display as:
"version": "0.0.0-development",
and new script:
"semantic-release": "semantic-release"
which was auto-generated by the Semantic Release CLI. We’ll need to enhance this script as follows:
"semantic-release": "semantic-release --branches main"
This indicates that releases will only be made from the main branch.
Additionally, Semantic Release generates a description based on the repository field in your package.json. This field offers details about the location of the package’s source code.
"repository": {
"type": "git",
"url": "https://github.com/<your_github_username>/your_github_repo.git"
}
Now, let’s push all of our changes with:
git add . && git commit -m "semantic release" && git push
Commit format
Semantic Release relies on the convention of structured commit messages to determine the type of version bump (major, minor, or patch) and generate changelogs. This commit convention is often called the “Conventional Commits” format.
For this configuration, we’ll need several plugins. Ensure your package.json contains the following content:
For the setup commit format tool, we are going to use commitizen. To install it, follow this command:
npx commitizen init cz-conventional-changelog --save-dev --save-exact
This command will take a few minutes. Then update your package.json with a new script:
"scripts": {
// ...
"commit": "cz"
},
and it’s time to utilize that script. Begin by executing git add ., then run npm run commit and provide the necessary details for your commit. Here’s what that looks like:
? Select the type of change that you're committing: feat: A new feature
? What is the scope of this change (e.g. component or file name): (press enter
to skip) commit
? Write a short, imperative tense description of the change (max 86 chars):
(14) add commitizen
? Provide a longer description of the change: (press enter to skip)
? Are there any breaking changes? No
? Does this change affect any open issues? No
After that, do a git push.
In GitHub actions, you will see that our commit failed because we still have not installed the rest of the packages for the automated commit message process.
npm i -D @semantic-release/commit-analyzer @semantic-release/release-notes-generator @semantic-release/npm @semantic-release/changelog
A crucial step, often overlooked in most references, is setting the workflow permissions. Navigate to https://github.com/<your_user_name>/tokky/settings/actions and configure the permissions to allow GitHub actions to both read and write.
Next, let’s change things up a bit. Commit with a specific keyword, feat:, followed by your message.
git add . && git commit -m "feat: my feature commit" && git push
Do you recall the releaseRules within the package.json? These rules dictate how we increment the version of our package release. With this in place, you can create a pull request using specific keywords like feat, fix, refactor, and so on. Once this pull request is approved and subsequently merged into the main branch, it will initiate a trigger. This trigger then activates the GitHub action, automates the release process, and ensures your package is updated seamlessly.
Published package
The package has been successfully published, and the entire process has been automated for efficiency. To confirm the publication, head to your npm settings https://www.npmjs.com/settings/<your_user_name>/packages and look under the packages section; there, you will find your newly published package.
Now, with a simple command like npx your_package_name hi, you can immediately see the results of our development tests. Additionally, the package can be globally installed using the command npm i -g your_package_name.
Conclusion
As we’ve seen throughout this article, while initial setups can be riddled with challenges, the reward lies in establishing a streamlined and consistent release process. Leveraging GitHub Actions simplifies these complexities, ensuring developers can focus on code quality rather than logistical intricacies.
Whether you’re just beginning your journey with public packages or have encountered setbacks in your publishing endeavors, there’s undeniable value in adopting a structured, automated workflow. By integrating Semantic Release, you’re ensuring consistent versioning and championing a future-forward approach to software development.
Here’s to seamless publishing, fewer headaches, and more time spent perfecting the code that drives our digital world forward.
Remember, it’s essential that both NPM_TOKEN and GITHUB_TOKEN are granted the appropriate permissions within GitHub Actions. Additionally, your package.json should be correctly configured with settings for publishConfig access, and ensure that the private config is set to false. If you encounter any issues or have insights, please don’t hesitate to comment.
References
Repository: https://github.com/antonkalik/tokky
Semantic Release CLI: https://github.com/semantic-release/cli
Commitizen: https://github.com/commitizen/cz-cli
Want to Connect?
If you've encountered any challenges, please share them in the comments of
my article, or don't hesitate to reach out to me directly at the following
places:
GitHub: https://github.com/antonkalik
LinkedIn: https://www.linkedin.com/in/antonkalik/
Twitter: https://twitter.com/idedycom
Website: https://idedy.com
Implementing Semantic Release for Public Non-Scoped Packages was originally published in Better Programming on Medium, where people are continuing the conversation by highlighting and responding to this story.