Day 013 - 100DaysAWSIaCDevopsChallenge
Recently, I developed an npm package for JavaScript and TypeScript projects. Initially, I manually handled the publishing process, which involved testing, compiling, and publishing the library using various npm commands. This approach was time-consuming, required significant effort, and left little room for error—I had to remember the exact sequence of commands and their specific options. Recognizing the inefficiency, I decided to automate the publishing process.
In this article, I’ll walk you through the steps I took to automate the process and streamline my workflow.
By the end, you’ll have a clear understanding of how to efficiently automate your npm package publishing with GitHub Actions. The process will involve the following steps:
Create a GitHub Workflow to automate
Linting
,building
, andTesting
process for every push events - We'll start by setting up a GitHub Actions workflow that automatically runs linting, builds your project, and executes tests each time code is pushed to the repository. This ensures that any issues are caught early in the development process, maintaining the integrity of your code source.Create the second Workflow to automate the
Release creation
andPublishing proccess
for every tag push events - Next, we’ll create a dedicated workflow to handle the release process. This workflow will trigger when a new tag is pushed, automatically compiling the code, updating version numbers, and publishing the package to npm. This step removes the manual intervention from the release cycle, making it more efficient and less prone to errors.Configure Github Actions (Setting up NPM Access Tokens) - To enable GitHub Actions to publish your package to npm, we’ll configure the necessary secrets within your repository. This involves generating an npm access token and securely storing it in your GitHub repository settings, allowing for seamless and secure automated publishing.
Update
README.md
by adding the build badges status - Finally, we’ll update your project’s README.md file to include build status badges. These badges provide instant visual feedback on the status of your project’s workflows, showing whether your builds are passing, if tests are successful, and the overall health of your code source.
Flow diagram
Create a GitHub Workflow to automate Linting
, building
, and Testing
This step consists of creating a workflow that allows me to automate the linting, testing, and building processes for every push event. By doing this, we ensure that any potential errors or overlooked issues are caught early in development, ensuring that the code is clean and ready before considering deployment.
Create a new file named ci.yml
in the following directory: .github/workflows
, and add this content:
Github Actions trigger
name: CI-Build
on:
push:
branches:
- "**"
tags-ignore:
- "v*"
pull_request:
types:
- closed
branches:
- master
-
name
- The name of the pipeline -
on
- The configuration of event trigger. In my case,- The workflow is triggered by any
push
event to any branch (branch: "*"), *except for tags starting with "v" (tags-ignore: "v*"). - It is also triggered when a
pull request
is closed on the master branch.
- The workflow is triggered by any
Jobs: linting code
# ....
jobs:
eslint:
name: Check Syntax with ESLint
runs-on: ubuntu-22.04
if: github.event_name == 'push' && github.actor.name != 'github-actions[bot]'
strategy:
matrix:
node-version: [ 18.x, 20.x, 22.x ]
steps:
- name: Checkout code style
uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }} to check Lint
uses: actions/setup-node@v4
with:
always-auth: 'false'
cache: 'npm'
node-version: ${{ matrix.node-version }}
- name: Install Dependencies
run: npm ci
- name: Run ESLint
run: |
npm run lint
- This job defines an automated process to check the code's syntax using ESLint↗ whenever certain conditions are met (like a push event). It runs on multiple Node.js versions (
strategy.matrix.node-version: [ 18.x, 20.x, 22.x ]
) to ensure compatibility and helps maintain code quality by catching issues early. -
if: github.event_name == 'push' && github.actor.name != 'github-actions[bot]'
- This condition checks if the event that triggered the workflow is a push, and if the person who triggered the event is not the github-actionsbot.
Jobs: Testing
tests:
if: github.event_name == 'push' && github.actor.name != 'github-actions[bot]'
name: Run Tests
needs: eslint
runs-on: ubuntu-22.04
strategy:
matrix:
node-version: [ 18.x, 20.x, 22.x ]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }} to run Tests Covering
uses: actions/setup-node@v4
with:
always-auth: 'false'
node-version: ${{ matrix.node-version }}
cache: "npm"
- name: Install Dependencies
run: npm ci
- name: Run Tests Command (Jest-CI)
run: npm test
- This job,
tests
, is designed to run the project's tests across multiple Node.js versions (18.x, 20.x, and 22.x). The job is executed after theeslint
job completes successfully, ensuring that only clean, lint-free code is tested. The job checks out the code, sets up the appropriate Node.js environment, installs dependencies, and then runs the test suite.
Jobs: Building
jobs:
# {...} <-- Linting job
buildDist:
if: github.event_name == 'push' && github.actor.name != 'github-actions[bot]'
name: Build-to-JS
needs: [ eslint ]
runs-on: ubuntu-22.04
strategy:
matrix:
node-version: [ 18.x, 20.x, 22.x ]
arch:
- x64
# - x86
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Use Node.js ${{matrix.node-version}} to run build dist on ${{ matrix.arch }}
uses: actions/setup-node@v4
with:
cache: "npm"
node-version: ${{ matrix.node-version}}
- name: Install dependencies
run: npm ci
- name: Run Build Dist
run: npm run build
This job,
buildDist
, is designed to compile the project's distribution files (dist) afterlinting
andtesting
have successfully completed. It does so across different Node.js versions (18.x, 20.x, and 22.x). The job uses caching for npm dependencies to improve speed and reliability.The job runs conditionally, only on push events and only if the workflow wasn't triggered by GitHub's automated bot account, ensuring that it only executes in relevant contexts.
Full code
name: CI-Build
on:
push:
branches:
- "**"
tags-ignore:
- "v*"
pull_request:
types:
- closed
branches:
- master
jobs:
eslint:
name: Check Syntax with ESLint
runs-on: ubuntu-22.04
if: github.event_name == 'push' && github.actor.name != 'github-actions[bot]'
strategy:
matrix:
node-version: [ 18.x, 20.x, 22.x ]
steps:
- name: Checkout code style
uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }} to check Lint
uses: actions/setup-node@v4
with:
always-auth: 'false'
cache: 'npm'
node-version: ${{ matrix.node-version }}
- name: Install Dependencies
run: npm ci
- name: Run ESLint
run: |
npm run lint
buildDist:
if: github.event_name == 'push' && github.actor.name != 'github-actions[bot]'
name: Build-to-JS
needs: [ eslint, tests ]
runs-on: ubuntu-22.04
strategy:
matrix:
node-version: [ 18.x, 20.x, 22.x ]
arch:
- x64
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Use Node.js ${{matrix.node-version}} to run build dist on ${{ matrix.arch }}
uses: actions/setup-node@v4
with:
cache: "npm"
node-version: ${{ matrix.node-version}}
- name: Install dependencies
run: npm ci
- name: Run Build Dist
run: npm run build
tests:
if: github.event_name == 'push' && github.actor.name != 'github-actions[bot]'
name: Run Tests
needs: eslint
runs-on: ubuntu-22.04
strategy:
matrix:
node-version: [ 18.x, 20.x, 22.x ]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }} to run Tests Covering
uses: actions/setup-node@v4
with:
always-auth: 'false'
node-version: ${{ matrix.node-version }}
cache: "npm"
- name: Install Dependencies
run: npm ci
- name: Run Tests Command (Jest-CI)
run: npm test
Create the second Workflow to automate the Release creation
and Publishing proccess
This step consists of creating a workflow that automates the release and publishing processes for every tag push event. Let's dive into the code and explore how I automated these processes.
First, create a new file named release.yaml
in the .github/workflows
directory.
Github Action trigger
name: Release
on:
create:
permissions:
contents: write
-
on.create:
- The workflow is triggered by the creation of a tag, branch, or release. -
permissions.contents: write
- The workflow is allowed to write to the repository's contents, which is essential for automating tasks like tagging, updating files, or committing changes as part of the release process. In this case we need the content write permission to create a newGithub Release
.
Jobs: Github releasing
jobs:
release:
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
name: Release co2mjs
runs-on: ubuntu-latest
steps:
- name: Create release
env:
TAG_NAME: ${{ github.ref_name }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "$TAG_NAME" \
--repo="$GITHUB_REPOSITORY" \
--title="${TAG_NAME#v}" \
--generate-notes
This job automates the creation of a release in a GitHub repository whenever a new tag starting with "v"
is pushed.
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
- This line ensures that the job only runs if the event is triggered by pushing a tag that starts with"v"
(e.g., v1.0.0). This line very important, because remember that the workflow is triggered by the creation of a tag, branch, or release.GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- This sets the GitHub token required to authenticate the gh command. GitHub automatically provides this token, and it is securely stored insecrets.GITHUB_TOKEN
The job uses the
gh
(GitHub CLI) command to create the release, setting the title and generating release notes based on the tag.
Jobs: Github publishing
jobs:
publish-npm:
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
name: "Publish the new version to the npmjs"
runs-on: ubuntu-22.04
needs: release
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
cache: "npm"
node-version: 18.x
architecture: x64
registry-url: https://registry.npmjs.org/
- name: Run NPM Install
run: npm ci && npm run build
- name: Publish
run: |
cd dist
npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- The
publish-npm
job automates the process of publishing a new version of the package to npm whenever a new tag starting with"v"
is pushed. It relies on the successful completion of therelease
job and performs the following actions: checks out the code, installs dependencies and builds the project, and finally publishes the built package to npm.
⚠️⚠️
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- Provides the authentication token for npmjs.com. This token is stored in GitHub secrets (⚠️repository⚠️ section) to securely authenticate thenpm publish
command. -
registry-url: https://registry.npmjs.org/
- Sets the npm registry URL to the default public npm registry.
Full code
name: Release
on:
create:
permissions:
contents: write
jobs:
release:
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
name: Release co2mjs
runs-on: ubuntu-latest
steps:
- name: Create release
env:
TAG_NAME: ${{ github.ref_name }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "$TAG_NAME" \
--repo="$GITHUB_REPOSITORY" \
--title="${TAG_NAME#v}" \
--generate-notes
publish-npm:
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
name: "Publish the new version to the npmjs"
runs-on: ubuntu-22.04
needs: release
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
cache: "npm"
node-version: 18.x
architecture: x64
registry-url: https://registry.npmjs.org/
- name: Run NPM Install
run: npm ci && npm run build
- name: Publish
run: |
cd dist
npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Configure Github Actions
To enable GitHub Actions to publish your package to npm, you'll need to configure the necessary access tokens.
1. Create an NPM Access Token
- Log in to your NPM account
- Navigate to your account settings
- Access Tokens
- click Create New Token
- choose Automation as the token type
- copy the generated token
2. Add the Token to Github Secrets
- Click on the Settings tab in your repository.
- In the left sidebar, select Secrets and variables and then Actions.
- Click New repository secret
-
Name the secret
NPM_TOKEN
and paste the npm access token you copied earlier into the Value field.
Update README.md
by adding the build badges status
To provide visibility into the status of your GitHub Actions jobs, you can add build status badges to your README.md file. Insert the following block into your README to display the status of the build and release workflows:
##### 🚦 Build Status
![CI workflow](https://github.com/<OWNER>/<REPO>/actions/workflows/ci-pipeline.yml/badge.svg?branch=master)
![Release workflow](https://github.com/<OWNER>/<REPO>/actions/workflows/release.yml/badge.svg)
___
Now that everything is set up, you can test the workflow by following these steps:
- Create a New Branch and Push Changes
git checkout -b feature/your-branch-name # Create and switch to a new branch
touch ./nothing.txt && echo "some text here" >> ./nothing.txt # Create a new file and add some content
git add ./nothing.txt # Stage the new file
git commit -m "my changes" # Commit the changes
git push origin feature/your-branch-name # Push the branch to the remote repository
- Monitor the Actions Tab:
- Go to the Actions tab of your repository to observe the CI and release workflows in action.
- Result
Create and Merge a Pull Request
Create and Push a New Tag
git checkout master # Switch to your default branch
npm version patch # this command update package.json and package-lock.json, commit the changes and then, create a new tag locally.
# output : v1.0.1 - or a different version number based on your package.json
git push origin v1.0.1 # Push the new tag to the remote repository
This will create new release and publish a new version to npmjs.com.
Github release ==> https://github.com/nivekalara237/co2mjs/releases
NPM Package ==> https://www.npmjs.com/package/co2m.js
__
🥳✨
We have reached the end of the article.
Thank you so much 🙂
Your can find the full source code on GitHub Repo↗
Top comments (0)