<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ghislain B.</title>
    <description>The latest articles on Forem by Ghislain B. (@ghiscoding).</description>
    <link>https://forem.com/ghiscoding</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F234628%2F5e237f4d-ada1-40a7-88fe-6fe84713ec8e.jpeg</url>
      <title>Forem: Ghislain B.</title>
      <link>https://forem.com/ghiscoding</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ghiscoding"/>
    <language>en</language>
    <item>
      <title>OIDC Trusted Publishing with Lerna-Lite</title>
      <dc:creator>Ghislain B.</dc:creator>
      <pubDate>Tue, 30 Sep 2025 05:13:22 +0000</pubDate>
      <link>https://forem.com/ghiscoding/oidc-trusted-publishing-with-lerna-lite-1o98</link>
      <guid>https://forem.com/ghiscoding/oidc-trusted-publishing-with-lerna-lite-1o98</guid>
      <description>&lt;p&gt;&lt;em&gt;This article is a follow-up of a previous article that I wrote couple years ago: &lt;a href="https://dev.to/ghiscoding/how-to-publish-on-npm-with-provenance-using-lerna-lite-3cjf"&gt;How to publish on npm with &lt;code&gt;--provenance&lt;/code&gt; using Lerna-Lite&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can now use OIDC Trusted Publishing to publish your packages to NPM, this process is described on the NPM blog as:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.npmjs.com/trusted-publishers" rel="noopener noreferrer"&gt;https://docs.npmjs.com/trusted-publishers&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Trusted Publishing with OpenID Connect (OIDC) for all users, marking a significant milestone for JavaScript supply chain security. This authentication method eliminates the need for long-lived tokens in CI/CD workflows, replacing them with short-lived, cryptographically-secured credentials that reduce the attack surface for package publishing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also note that this process will also publish with Provenance out of the box.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;provenance data gives consumers a verifiable way to link a package back to its source repository and the specific build instructions used to publish it&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What is Lerna-Lite?
&lt;/h3&gt;

&lt;p&gt;From Lerna's website, it is described as&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Lerna is a fast, modern build system for managing and publishing multiple JavaScript/TypeScript packages from the same repository.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/lerna-lite/lerna-lite" rel="noopener noreferrer"&gt;Lerna-Lite&lt;/a&gt; is a lighter version of Lerna (every commands are optional in Lerna-Lite) as opposed to Lerna which is a "all-in-one" tool. Another difference is that the newest Lerna version has Nx as a dependency, but on the other hand Lerna-Lite does not require neither use Nx.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you will need
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to each of your NPM monorepo packages and add a Trusted Publisher via the "Settings" (currently supported publishers are "GitHub Actions" and "GitLab CI/CD"&lt;/li&gt;
&lt;li&gt;Take a look at the "Publishing Access", it's recommended to set it to "Require two-factor authentication and disallow tokens"&lt;/li&gt;
&lt;li&gt;Repeat step 1 and 2 for all your monorepo packages (hopefully in the future, NPM will bring a global setup, but for now we have to change them one-by-one)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can see a super basic demo at this link:&lt;br&gt;
&lt;a href="https://github.com/lerna-lite-test/oidc" rel="noopener noreferrer"&gt;https://github.com/lerna-lite-test/oidc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This basic demo has the following configuration set as Trusted Publisher:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F7595a662-8ec1-41bc-aa97-03af3a1b036b" class="article-body-image-wrapper"&gt;&lt;img alt="Image" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F7595a662-8ec1-41bc-aa97-03af3a1b036b" width="911" height="915"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Basic Usage
&lt;/h3&gt;

&lt;p&gt;If you already have a Release CI Workflow, then the change is super easy, you just need to get rid of any NPM Token and/or Provenance config and that's about it, you're now good to go with OIDC&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# release.yml
... 
   - name: Lerna Version 🏷️
     env:
       GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
&lt;span class="gd"&gt;-      NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
-      NPM_CONFIG_PROVENANCE: true
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or if you want a full sample of a Release CI Workflow, then see below for a basic setup using pnpm, you can tweak it for other package managers.&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;code&gt;.github/workflows/release.yml&lt;/code&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;🏷️ Release to NPM&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
  &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt; &lt;span class="c1"&gt;# required for OIDC&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy-npm-latest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Clone repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
          &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install pnpm&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm/action-setup@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
          &lt;span class="na"&gt;run_install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set NodeJS&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;registry-url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://registry.npmjs.org/'&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pnpm'&lt;/span&gt;

      &lt;span class="c1"&gt;# OIDC requires npm v11.5.1 or later &lt;/span&gt;
      &lt;span class="c1"&gt;# or simply use Node 24 to avoid running the next line&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install -g npm@latest&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node --version&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm --version&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run pnpm install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm install&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Everything&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm build&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lerna Version 🏷️&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GH_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;git config --global user.name "${{ github.actor }}"&lt;/span&gt;
          &lt;span class="s"&gt;git config --global user.email "${{ github.actor }}@users.noreply.github.com"&lt;/span&gt;
          &lt;span class="s"&gt;pnpm exec lerna version --yes&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lerna Publish 📦&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GH_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lerna-publish&lt;/span&gt;
        &lt;span class="na"&gt;continue-on-error&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;pnpm exec lerna publish from-package --force-publish --yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Provenance will also be used by default (you don't even need to enable it, it comes as default).&lt;/p&gt;

&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;With that in place, we are now successfully publishing with Provenance with Short Lived Token using Lerna-Lite which makes our toolchain much more secure. Following these steps, you should be able to do the same with your project as well. &lt;/p&gt;

&lt;p&gt;Also note that you can also do the exact same steps with Lerna (the actual implementation actually came from Lerna, so credit goes to them).&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://github.com/lerna-lite/lerna-lite/releases/tag/v4.9.0" rel="noopener noreferrer"&gt;Lerna-Lite@4.9.0&lt;/a&gt; release for more details.&lt;/p&gt;

</description>
      <category>lerna</category>
      <category>lernalite</category>
      <category>npm</category>
    </item>
    <item>
      <title>How to publish on npm with `--provenance` using Lerna-Lite</title>
      <dc:creator>Ghislain B.</dc:creator>
      <pubDate>Fri, 17 Nov 2023 05:37:22 +0000</pubDate>
      <link>https://forem.com/ghiscoding/how-to-publish-on-npm-with-provenance-using-lerna-lite-3cjf</link>
      <guid>https://forem.com/ghiscoding/how-to-publish-on-npm-with-provenance-using-lerna-lite-3cjf</guid>
      <description>&lt;p&gt;I recently started publishing Lerna-Lite project on NPM with Provenance and decided to write a blog post about the steps since it took me quite a few commits to find the best configuration...&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Provenance?
&lt;/h3&gt;

&lt;p&gt;For a more detailed explanation, I would suggest you read this GitHub blog post &lt;a href="https://github.blog/2023-04-19-introducing-npm-package-provenance/" rel="noopener noreferrer"&gt;Introducing npm package provenance&lt;/a&gt;, below is a quote pulled from the blog:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;provenance data gives consumers a verifiable way to link a package back to its source repository and the specific build instructions used to publish it&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What is Lerna-Lite?
&lt;/h3&gt;

&lt;p&gt;From Lerna's website, it is described as&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Lerna is a fast, modern build system for managing and publishing multiple JavaScript/TypeScript packages from the same repository.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/lerna-lite/lerna-lite" rel="noopener noreferrer"&gt;Lerna-Lite&lt;/a&gt; is a lighter version of Lerna (every commands are optional in Lerna-Lite) as opposed to Lerna which is a "all-in-one" tool which includes 15 commands. Another difference is that the newest Lerna version has Nx as a dependency, but on the other hand Lerna-Lite does not require neither use Nx. Lastly, Lerna-Lite was created couple months before Nx company took over stewardship of Lerna.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you will need
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create an NPM Token for publishing (login on NPM then click on "Access Tokens -&amp;gt; Generate Token -&amp;gt; Classic Token" &lt;/li&gt;
&lt;li&gt;Go to your GitHub project and add a secret, click on "Settings -&amp;gt; General -&amp;gt; (security) Secret and Variables -&amp;gt; Actions"

&lt;ul&gt;
&lt;li&gt;add new secret "NPM_TOKEN" and paste your NPM token from previous step.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Create a GitHub Token, go to your GitHub profile click on "Settings -&amp;gt; Developer Settings -&amp;gt; Personal Access Token -&amp;gt; Classic Token and give &lt;code&gt;repo:public_repo&lt;/code&gt; scope.&lt;/li&gt;
&lt;li&gt;Create NPM Scripts for Lerna version/publish&lt;/li&gt;
&lt;li&gt;Create a GitHub Action Workflow (2 use cases are shown below)&lt;/li&gt;
&lt;li&gt;Enable provenance via &lt;code&gt;.npmrc&lt;/code&gt; or within the workflow
a) in &lt;code&gt;.npmrc&lt;/code&gt; via &lt;code&gt;provenance=true&lt;/code&gt; 
b) or in the workflow via &lt;code&gt;env: NPM_CONFIG_PROVENANCE: true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Execute the workflow&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;h4&gt;
  
  
  a) Basic Usage
&lt;/h4&gt;

&lt;p&gt;Let's start with a basic usage, we'll create a GitHub Action workflow and publish on the registry using NPM to publish with Provenance. &lt;/p&gt;

&lt;p&gt;Note, I have to admit that I did not personally test this use case, I copied some part from this &lt;a href="https://docs.npmjs.com/generating-provenance-statements#example-github-actions-workflow" rel="noopener noreferrer"&gt;NPM&lt;/a&gt; and modified it a bit to demo this use case with Lerna-Lite, however in my case I opted for the second approach with OTP shown further down.&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;code&gt;package.json&lt;/code&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ci:publish:latest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lerna publish --conventional-commits --dist-tag latest --no-verify-access --yes"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  &lt;code&gt;.github/workflows/release.yml&lt;/code&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish Package to npmjs&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;created&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
      &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
          &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;18&lt;/span&gt;
          &lt;span class="c1"&gt;# a registry is required to publish&lt;/span&gt;
          &lt;span class="na"&gt;registry-url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://registry.npmjs.org/&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm&lt;/span&gt;
          &lt;span class="na"&gt;cache-dependency-path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/package.json'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NPM install&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install -g npm&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Version &amp;amp; Publish&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
          &lt;span class="na"&gt;GH_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;NODE_AUTH_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.NPM_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;NPM_CONFIG_PROVENANCE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;git config --global user.name "${{ github.actor }}"&lt;/span&gt;
          &lt;span class="s"&gt;git config --global user.email "users.noreply.github.com"&lt;/span&gt;
          &lt;span class="s"&gt;npm whoami&lt;/span&gt;
          &lt;span class="s"&gt;npm run ci:publish:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  b) Lerna-Lite's project with pnpm workspace and 2FA (OTP)
&lt;/h4&gt;

&lt;p&gt;For a more extensive and preferred use case which is to use OTP (One Time Password), it is the configuration that we use in Lerna-Lite itself to publish the project with provenance for better security.&lt;/p&gt;

&lt;p&gt;To deal with the OTP (or any other 2FA), we can use &lt;a href="https://github.com/step-security/wait-for-secrets" rel="noopener noreferrer"&gt;wait-for-secrets&lt;/a&gt;. When comparing this approach with the previous basic usage, we can see that the Lerna-Lite Version &amp;amp; Publish were split into 2 separate tasks. The reason is simple, calling the OTP too early would timeout even before reaching the publish phase, so calling the OTP just before the publish is ideal to make sure that the token is still valid at the time of the publish. &lt;/p&gt;

&lt;p&gt;Another thing that is worth considering, though optional, is to add a condition to make sure that the current actor (user currently logged in GitHub) is in the allowed list of users to execute the publish; if not it will simply exit the workflow. You'll need to modify the &lt;code&gt;["user1", "user2"]&lt;/code&gt; shown below with your username and anyone else that is allowed to publish. We also use &lt;code&gt;workflow_dispatch&lt;/code&gt; to have a prompt to start the workflow, you could also add extra inputs to release a fixed version if you wish.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; Lerna-Lite/Lerna uses NPM to publish on the registry, even if you set &lt;code&gt;"npmClient": "pnpm"&lt;/code&gt; (or Yarn), which is actually a good thing since Yarn does not yet support Provenance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  &lt;code&gt;.npmrc&lt;/code&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;provenance=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  &lt;code&gt;package.json&lt;/code&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ci:version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lerna version --yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ci:publish"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lerna publish from-package --force-publish --yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  &lt;code&gt;.github/workflows/release.yml&lt;/code&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;🏷️ Publish NPM Latest&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;workflow_dispatch&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
  &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy-npm-latest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Clone repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
          &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.pull_request.merged != &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="s"&gt; &amp;amp;&amp;amp; contains('["user1", "user2"]', github.actor) != &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="s"&gt; }}&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ensure current actor is allowed to run the workflow&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "Error: Your GitHub username (${{ github.actor }}) is not on the allowed list of admins for this workflow"&lt;/span&gt;
          &lt;span class="s"&gt;exit 1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set NodeJS&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;registry-url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://registry.npmjs.org/'&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install pnpm&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm/action-setup@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;
          &lt;span class="na"&gt;run_install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get pnpm store directory&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "STORE_PATH=$(pnpm store path --silent)" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup pnpm cache&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.STORE_PATH }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}&lt;/span&gt;
          &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;${{ runner.os }}-pnpm-store-&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run pnpm install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm install&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run all workspace TSC builds&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pnpm build:full&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lerna Version 🏷️&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GH_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;NODE_AUTH_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.NPM_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;git config --global user.name "${{ github.actor }}"&lt;/span&gt;
          &lt;span class="s"&gt;git config --global user.email "users.noreply.github.com"&lt;/span&gt;
          &lt;span class="s"&gt;pnpm whoami&lt;/span&gt;
          &lt;span class="s"&gt;pnpm run ci:version&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OTP&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;step-security/wait-for-secrets@v1&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wait-for-secrets&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;OTP:&lt;/span&gt;
              &lt;span class="s"&gt;name: 'OTP to publish package'&lt;/span&gt;
              &lt;span class="s"&gt;description: 'OTP from authenticator app'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lerna Publish 📦&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GH_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;NODE_AUTH_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.NPM_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;pnpm run ci:publish --otp ${{ steps.wait-for-secrets.outputs.OTP }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;With that in place, we are now successfully publishing with Provenance using Lerna-Lite which makes our toolchain much more secure. Following these steps, you should be able to do the same with your project as well. &lt;/p&gt;

&lt;p&gt;Also note that you can also do the exact same steps with Lerna (the actual implementation actually came from Lerna, so credit goes to them).&lt;/p&gt;

&lt;p&gt;For an example, you can visit one of Lerna-Lite's package, like the &lt;a href="https://www.npmjs.com/package/@lerna-lite/cli/v/3.11.0#provenance" rel="noopener noreferrer"&gt;@lerna-lite/cli&lt;/a&gt; package, to find the provenance badge applied on each new versions and that's it!&lt;/p&gt;

&lt;h4&gt;
  
  
  Follow-Up
&lt;/h4&gt;

&lt;p&gt;The open source and tooling world is changing rapidly and I wrote a follow-up for this article, check it out:&lt;br&gt;
&lt;a href="https://dev.to/ghiscoding/oidc-trusted-publishing-with-lerna-lite-1o98"&gt;OIDC Trusted Publishing with Lerna-Lite&lt;/a&gt;&lt;/p&gt;

</description>
      <category>provenance</category>
      <category>npm</category>
      <category>publish</category>
      <category>lernalite</category>
    </item>
  </channel>
</rss>
