<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>rubygems on kyrofa&#39;s blog</title>
    <link>https://kyrofa.com/tags/rubygems/</link>
    <description>Recent content in rubygems on kyrofa&#39;s blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <lastBuildDate>Mon, 07 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://kyrofa.com/tags/rubygems/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>How to use RubyGems Trusted Publishing with Semantic Release</title>
      <link>https://kyrofa.com/posts/rubygems-trusted-publishing-with-semantic-release/</link>
      <pubDate>Mon, 07 Sep 2026 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/rubygems-trusted-publishing-with-semantic-release/</guid>
      <description>&amp;ldquo;Can I safely use this new release?&amp;rdquo;
When you&amp;rsquo;re a developer maintaining a piece of software, this is a question you ask every time you update one of its dependencies. How do you answer that question? Read the CHANGELOG? Release notes? Pray? No, you look at the version (first, at least). How to interpret that version generally depends on the project, but I&amp;rsquo;m personally partial to semantic versioning. I think it serves as an elegant contract between dependency author and consumer that can be used to easily answer this question.</description>
      <content:encoded>&lt;p&gt;&amp;ldquo;Can I safely use this new release?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;When you&amp;rsquo;re a developer maintaining a piece of software, this is a question you ask every time you update one of its dependencies. How do you answer that question? Read the CHANGELOG? Release notes? Pray? No, you look at the version (first, at least). How to interpret that version generally depends on the project, but I&amp;rsquo;m personally partial to &lt;a href=&#34;https://semver.org/&#34;&gt;semantic versioning&lt;/a&gt;. I think it serves as an elegant contract between dependency author and consumer that can be used to easily answer this question.&lt;/p&gt;
&lt;p&gt;That said, I don&amp;rsquo;t actually care about the version itself, I just care about the contract. Bugfixes bump the patch version, features bump the minor version, breaking changes bump the major version. Actually deciding when to do a release, determining the version to use, and then DOING that version bump and release is super boring. The same critique goes for the release notes. Thankfully, with a little commit discipline, both problems can be solved the same way: &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34;&gt;conventional commits&lt;/a&gt;. If each commit can be used to determine if a given change is a bugfix/feature/breaking change, then you can automate the rest with something like &lt;a href=&#34;https://github.com/semantic-release/semantic-release&#34;&gt;Semantic Release&lt;/a&gt;. That&amp;rsquo;s what I&amp;rsquo;ve been doing for the better part of a decade, and I can&amp;rsquo;t recommend it enough.&lt;/p&gt;
&lt;p&gt;Historically, publishing a gem to RubyGems automatically from CI was easy: you create an API token scoped to specifically publish that gem, and use that token in your CI environment to push the gem. However, that token is one of the targets for supply chain attacks. Also, using tokens like that requires that your account doesn&amp;rsquo;t utilize multi-factor authentication (MFA), which you should definitely be using. So what is the solution? &lt;a href=&#34;https://guides.rubygems.org/trusted-publishing/&#34;&gt;Trusted Publishing&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;rubygems-trusted-publishing&#34;&gt;RubyGems Trusted Publishing&lt;/h1&gt;
&lt;p&gt;Trusted Publishing is pretty well documented elsewhere, so I don&amp;rsquo;t want to dwell on the details here for too long. For our purposes, let&amp;rsquo;s gain a high-level understanding of how this works by contrasting it with the API token approach mentioned above.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Without trusted publishing:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create API token, scoped as tightly as possible to just pushing this gem&lt;/li&gt;
&lt;li&gt;Put it in CI variables in as secure a manner as possible (e.g. only defined on your release branch(es), etc.)&lt;/li&gt;
&lt;li&gt;When releasing the gem, use that variable to authenticate to RubyGems, and push the gem&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Repeat (3) every time you need to release a new version. You can see how, if some part of your supply chain is compromised (e.g. your dependencies, or some part of your build system), that key can be stolen every time (3) runs. And since it&amp;rsquo;s a long-lived key, the attackers can use it until you happen to detect that it has been compromised. Yes, thanks to your carefulness in (1) the damage they can do is limited to your gem, but they can still turn your gem into an attack vector for your users by pushing malicious versions of it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;With trusted publishing:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to your gem&amp;rsquo;s settings in RubyGems, and add a new Trusted Publisher. This is identified by your GitHub repository, GitHub Actions workflow file, and environment name. You&amp;rsquo;re basically saying &amp;ldquo;allow this gem to be published from this GitHub Action running in this environment.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;When releasing the gem (from the workflow and environment you specified in (1)), obtain a new token from RubyGems, use it to authenticate, and push the gem&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Repeat (2) every time you need to release a new version. On the surface, perhaps this doesn&amp;rsquo;t sound so different from the &amp;ldquo;without trusted publishing&amp;rdquo; flow, but it is: the token you fetch in (2) is incredibly short-lived. Even if CI is compromised, the window of opportunity for utilizing the stolen token is vanishingly small. This method of authenticating and pushing new releases is far superior.&lt;/p&gt;
&lt;h1 id=&#34;how-do-we-automatically-push-new-gem-versions-with-trusted-publishing&#34;&gt;How do we automatically push new gem versions with Trusted Publishing?&lt;/h1&gt;
&lt;p&gt;You may have noticed that step (2) above, with the &amp;ldquo;with trusted publishing&amp;rdquo; flow, was uselessly high level. I could have written &amp;ldquo;Magic happens here.&amp;rdquo; That&amp;rsquo;s because the process of actually contacting RubyGems to obtain that short-lived token is complicated enough to not really be intended to be done by you, dear developer. RubyGems kindly created a GitHub Action specifically for this, called &lt;a href=&#34;https://github.com/rubygems/release-gem&#34;&gt;release-gem&lt;/a&gt;. That is nifty, but to quote the end of the documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;With the trusted publisher configured and this workflow in your repository, you can release a new version of your gem by simply pushing a git tag. No API tokens, no manual &lt;code&gt;gem push&lt;/code&gt; — just tag and push.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you read the intro to this article, you&amp;rsquo;ll know that&amp;rsquo;s a problem for me. In order for me to push a tag, I have to select a version. I have to write the CHANGELOG. I have to do all the boring stuff that I don&amp;rsquo;t want to do. I want Semantic Release doing this for me! Can I make them work together?&lt;/p&gt;
&lt;h1 id=&#34;integrate-semantic-release-with-trusted-publishing&#34;&gt;Integrate Semantic Release with Trusted Publishing&lt;/h1&gt;
&lt;p&gt;First, we need to gain a better understanding of what exactly release-gem does. To glean those details we need to dig into its &lt;a href=&#34;https://github.com/rubygems/release-gem/blob/7f9650160c1a4e7989fdc9855807bdbd421d8b6b/action.yml&#34;&gt;action.yml&lt;/a&gt;, which at the time of this writing looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Release Gem&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# snip...&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;runs&lt;/span&gt;:
  &lt;span style=&#34;color:#f92672&#34;&gt;using&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;composite&amp;#34;&lt;/span&gt;
  &lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Attribute commits to last committer&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        git config --global user.email &amp;#34;$(git log -1 --pretty=format:&amp;#39;%ae&amp;#39;)&amp;#34;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        git config --global user.name &amp;#34;$(git log -1 --pretty=format:&amp;#39;%an&amp;#39;)&amp;#34;&lt;/span&gt;        
      &lt;span style=&#34;color:#f92672&#34;&gt;shell&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bash&lt;/span&gt;
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Configure Git using cached credentials&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        git credential-cache --timeout=300 store &amp;lt;&amp;lt;EOF
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        protocol=https
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        host=github.com
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        username=x-access-token
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        password=${{ inputs.token }}
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        EOF
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        git config --local credential.helper &amp;#39;cache --timeout=300&amp;#39;&lt;/span&gt;        
      &lt;span style=&#34;color:#f92672&#34;&gt;shell&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bash&lt;/span&gt;
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Fetch tags&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;git fetch --tags --force&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;shell&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bash&lt;/span&gt;
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Configure trusted publishing credentials&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;if&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ inputs.setup-trusted-publisher == &amp;#39;true&amp;#39; }}&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;rubygems/configure-rubygems-credentials@dc5a8d8553e6ee01fc26761a49e99e733d17954a&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# v2.1.0&lt;/span&gt;
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Run release rake task&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bundle exec rake release&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;shell&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bash&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
        &lt;span style=&#34;color:#f92672&#34;&gt;RUBYOPT&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${{ inputs.attestations == &amp;#39;true&amp;#39; &amp;amp;&amp;amp; format(&amp;#39;-r{0}/rubygems-attestation-patch.rb {1}&amp;#39;, github.action_path, env.RUBYOPT) || env.RUBYOPT }}&amp;#34;&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;working-directory&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ inputs.working-directory }}&lt;/span&gt;
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Wait for release to propagate&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;if&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ inputs.await-release == &amp;#39;true&amp;#39; }}&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;gem exec rubygems-await pkg/*.gem&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;shell&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bash&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;working-directory&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ inputs.working-directory }}&lt;/span&gt;
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Clean up credentials&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;if&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;always()&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;git credential-cache exit || true&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;shell&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;From this, you can see that this isn&amp;rsquo;t the GitHub Action that actually does the &amp;ldquo;get my credentials&amp;rdquo; dance: that&amp;rsquo;s &lt;a href=&#34;https://github.com/rubygems/configure-rubygems-credentials&#34;&gt;&lt;code&gt;rubygems/configure-rubygems-credentials&lt;/code&gt;&lt;/a&gt;. If you just want something to fetch credentials for you to use in your own CI workflow, &lt;em&gt;that&lt;/em&gt; is the GitHub Action you need to care about. That&amp;rsquo;s what we need in order to integrate Trusted Publishing with Semantic Release.&lt;/p&gt;
&lt;h2 id=&#34;semantic-release-configuration&#34;&gt;Semantic Release configuration&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s take a momentary detour to discuss the Semantic Release configuration file, &lt;code&gt;.releaserc.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Standard plugins, minus npm, plus rubygems&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;plugins&lt;/span&gt;:
  - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@semantic-release/commit-analyzer&amp;#34;&lt;/span&gt;
  - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@semantic-release/release-notes-generator&amp;#34;&lt;/span&gt;
  - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@semantic-release/github&amp;#34;&lt;/span&gt;
  - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@webhippie/semantic-release-rubygem&amp;#34;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll notice I&amp;rsquo;m using the &lt;a href=&#34;https://github.com/webhippie/semantic-release-rubygem&#34;&gt;semantic-release-rubygem&lt;/a&gt; Semantic Release plugin to actually build/push gems, and the rest are standard.&lt;/p&gt;
&lt;h2 id=&#34;ci-workflow&#34;&gt;CI Workflow&lt;/h2&gt;
&lt;p&gt;Alright, let&amp;rsquo;s put all the pieces together. Writing the CI workflow is almost exactly as if you had a long-lived token. Here&amp;rsquo;s an example deploy job:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;deploy&lt;/span&gt;:
  &lt;span style=&#34;color:#f92672&#34;&gt;runs-on&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ubuntu-latest&lt;/span&gt;
  &lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;rubygems&lt;/span&gt;

  &lt;span style=&#34;color:#75715e&#34;&gt;# Only deploy if running on master, our release branch in this case&lt;/span&gt;
  &lt;span style=&#34;color:#f92672&#34;&gt;if&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;github.ref == &amp;#39;refs/heads/master&amp;#39;&lt;/span&gt;

  &lt;span style=&#34;color:#f92672&#34;&gt;permissions&lt;/span&gt;:
    &lt;span style=&#34;color:#f92672&#34;&gt;id-token&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;write&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# Required for trusted publishing&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;contents&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;write&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# Required to push tags&lt;/span&gt;

  &lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Checkout code&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# v6.1.0&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
        &lt;span style=&#34;color:#f92672&#34;&gt;persist-credentials&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;

    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Setup node&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# v6.5.0&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
        &lt;span style=&#34;color:#f92672&#34;&gt;node-version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;26&amp;#39;&lt;/span&gt;

    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Install semantic release&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;npm install semantic-release@25 @webhippie/semantic-release-rubygem@7&lt;/span&gt;

    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Obtain RubyGems credentials&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;rubygems/configure-rubygems-credentials@dc5a8d8553e6ee01fc26761a49e99e733d17954a&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# v2.1.0&lt;/span&gt;

    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Run semantic release&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
        &lt;span style=&#34;color:#f92672&#34;&gt;GITHUB_TOKEN&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;npx semantic-release&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. That CI workflow obtains a short-lived token from RubyGems, and then runs Semantic Release. Semantic Release will process all the commit messages that have taken place since the last release, determine first if a release should even be made, and if so, the version that should be used. Once that is determined, it will tag that commit and push it up. It will then update the gem version, build the gem, and then push it using the credentials obtained by &lt;code&gt;configure-rubygems-credentials&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Semantic versioning is a great way to help your users understand if they can update to your new releases without issue. Combining conventional commits and Semantic Release lets you honor that contract without the tedious work of manually picking versions and writing changelogs. Trusted Publishing eliminates the long-lived API token from that workflow entirely, but the documentation and GitHub Actions make some assumptions that aren&amp;rsquo;t entirely compatible with Semantic Release. I hope this article was helpful in smoothing over that sharp edge and showing that, with a little care, you can take advantage of the increased security offered by Trusted Publishing without sacrificing any of that release automation. The result is magical.&lt;/p&gt;
</content:encoded>
    </item>
    
  </channel>
</rss>
