<?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>DEV Community: Gerardo León</title>
    <description>The latest articles on DEV Community by Gerardo León (@gerardo_leon).</description>
    <link>https://dev.to/gerardo_leon</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4057025%2Fc8593235-2590-493c-a5f1-d7e3e789be64.png</url>
      <title>DEV Community: Gerardo León</title>
      <link>https://dev.to/gerardo_leon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gerardo_leon"/>
    <language>en</language>
    <item>
      <title>Do you assume or confirm?</title>
      <dc:creator>Gerardo León</dc:creator>
      <pubDate>Fri, 07 Aug 2026 05:21:41 +0000</pubDate>
      <link>https://dev.to/gerardo_leon/do-you-assume-or-confirm-57m3</link>
      <guid>https://dev.to/gerardo_leon/do-you-assume-or-confirm-57m3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This article describes a debugging case from 2023. Some of the technologies mentioned here have since changed in relevance. In particular, Moment.js is now considered a legacy project in maintenance mode and is generally not recommended for new applications. The debugging lessons and reasoning process described here are still applicable, but the specific technical choices should be understood in their original context.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Chapter 1: How I learnt to distrust JavaScript &lt;code&gt;Date&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Daniel Kahneman’s famous book &lt;em&gt;Thinking, Fast and Slow&lt;/em&gt; describes two modes of thinking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;System 1&lt;/strong&gt; is automatic, fast, emotional, and quick to respond to stimuli. It constantly operates in the background and often bases its output on assumptions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System 2&lt;/strong&gt; is invoked consciously. It is slower, more deliberate, logical, and methodical, basing its output on reasoning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When it comes to problem-solving, we usually expect System 2 to take over. However, familiarity with a domain gradually builds a stack of assumptions that System 1 may stop revalidating.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;That overconfidence can mislead us and make our decisions less reliable than we think.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If we were to visualize both systems while debugging, a good analogy would be Wile E. Coyote realizing that he has been running on thin air while chasing the Road Runner.&lt;/p&gt;

&lt;p&gt;Leaving System 1 in command for too long, without verifying the premises we have already accepted as valid, is basically us playing Coyote.&lt;/p&gt;

&lt;p&gt;Therefore, it is worth regularly asking ourselves:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Am I assuming these premises, or have I already confirmed what I’m basing this on?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This question can help prevent us from getting trapped in debugging dead ends.&lt;/p&gt;

&lt;p&gt;What criteria should trigger this question?&lt;/p&gt;

&lt;p&gt;I tend to use a simple one:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“For the next hypothesis I’m about to accept, have I confirmed its foundations, or am I assuming them?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This simple question can save a significant amount of time and effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application problem
&lt;/h2&gt;

&lt;p&gt;A bit of context:&lt;/p&gt;

&lt;p&gt;Two systems communicate through event-based messaging.&lt;/p&gt;

&lt;p&gt;The originator system sends events in a specific order. The receiving system applies the event data to its state and updates its UI accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Even though the originator system sends events in the expected order, the receiver's UI does not always update in that same order.&lt;/li&gt;
&lt;li&gt;These symptoms were already known, although the root cause remained unclear. A race condition in the receiver's event interpreter had become the main suspect.&lt;/li&gt;
&lt;li&gt;The fact that multiple events were emitted within the same second appeared to support the race-condition theory.&lt;/li&gt;
&lt;li&gt;We were considering changes in the originator system because, after a recent optimization, some events were being produced in roughly half the previous processing time.&lt;/li&gt;
&lt;li&gt;When I was asked why the originator needed to change, I could not provide a solid explanation. I was under pressure to deliver a fix quickly, but my reasoning was mostly based on assumptions.&lt;/li&gt;
&lt;li&gt;I then learned that the receiver had an event timestamp comparator implemented using Moment.js.&lt;/li&gt;
&lt;li&gt;Event A and Event B could arrive within the same second and even share the same first three fractional-second digits.&lt;/li&gt;
&lt;li&gt;Event A could take slightly longer to process than Event B because additional business rules were applied to it.&lt;/li&gt;
&lt;li&gt;Before updating the UI, the receiver's timestamp comparator would skip an event if its timestamp was older than the timestamp of the event previously applied.&lt;/li&gt;
&lt;li&gt;JavaScript's legacy &lt;code&gt;Date&lt;/code&gt; object stores time with millisecond precision. This means that fractional seconds beyond three digits are not preserved by &lt;code&gt;Date&lt;/code&gt;, and Moment.js inherits that limitation when relying on JavaScript dates.&lt;/li&gt;
&lt;li&gt;As a workaround, the comparison was changed to operate on the original timestamp strings so that additional fractional-second digits could be taken into account when two events appeared otherwise equal.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lesson
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Read the documentation.&lt;/li&gt;
&lt;li&gt;Get to the root of the problem before proposing a convenient workaround.&lt;/li&gt;
&lt;li&gt;Do not mistake an assumption for a confirmed constraint.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Keep access to the raw timestamp representation.&lt;/li&gt;
&lt;li&gt;When two events are equal at millisecond precision, use the additional fractional-second digits from the original timestamp as a tiebreaker.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Another problem
&lt;/h3&gt;

&lt;p&gt;The previous solution improved things, but another issue appeared.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I expected timestamps to compare correctly, yet some reported cases still produced unexpected tiebreaker values.&lt;/li&gt;
&lt;li&gt;Simple string slicing turned out to be unreliable because the fractional-second representation was not always identical.&lt;/li&gt;
&lt;li&gt;I switched to a regular expression that identified the relevant fractional-second portion more explicitly.&lt;/li&gt;
&lt;li&gt;Reliability improved, but some additional reports still came in.&lt;/li&gt;
&lt;li&gt;This time, the problem was not the comparison logic itself. Some timestamps contained fewer fractional-second digits than expected.&lt;/li&gt;
&lt;li&gt;The affected timestamps corresponded to values whose complete representations would have contained trailing zeroes.&lt;/li&gt;
&lt;li&gt;The extraction function behaved correctly when trailing zeroes were present, so the next question became: where were those zeroes disappearing?&lt;/li&gt;
&lt;li&gt;A teammate suggested checking the serialization layer.&lt;/li&gt;
&lt;li&gt;Testing confirmed that serialization was modifying the timestamp representation.&lt;/li&gt;
&lt;li&gt;The application was using Newtonsoft.Json, and its date serialization behavior could omit trailing fractional-second zeroes depending on the configured format.&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;IsoDateTimeConverter&lt;/code&gt; was configured with an explicit datetime format that preserved the required fractional-second representation.&lt;/li&gt;
&lt;li&gt;After that change, the event timestamp comparison became considerably more reliable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lesson
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If the bug is not in the business layer, inspect the supporting layers as well.&lt;/li&gt;
&lt;li&gt;Serialization, parsing, formatting, and transport layers can silently transform data that the business logic assumes to be unchanged.&lt;/li&gt;
&lt;li&gt;When working with structured strings, identify the exact format you expect instead of relying on positional assumptions such as arbitrary slicing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use an explicit and validated format when extracting structured values from strings.&lt;/li&gt;
&lt;li&gt;Configure serialization behavior deliberately when formatting affects application logic.&lt;/li&gt;
&lt;li&gt;Avoid relying on default serializer behavior for data whose exact representation matters.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Looking back
&lt;/h2&gt;

&lt;p&gt;There is another architectural lesson I would add today.&lt;/p&gt;

&lt;p&gt;If event ordering is a fundamental property of the system, relying exclusively on timestamps to establish that order can be fragile.&lt;/p&gt;

&lt;p&gt;Timestamp precision, serialization behavior, clock synchronization, processing delays, and formatting differences can all affect the result.&lt;/p&gt;

&lt;p&gt;When strict ordering is required, an explicit sequence number, event version, or another monotonic ordering mechanism can provide a stronger guarantee than timestamps alone.&lt;/p&gt;

&lt;p&gt;That does not invalidate the debugging solution described above—it simply moves the responsibility for ordering closer to the domain itself instead of deriving it indirectly from time.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Daniel Kahneman, &lt;em&gt;Thinking, Fast and Slow&lt;/em&gt; (2011)&lt;/li&gt;
&lt;li&gt;Moment.js issue regarding sub-millisecond precision:
&lt;code&gt;https://github.com/moment/moment/issues/3256&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Moment.js project status and documentation:
&lt;code&gt;https://momentjs.com/docs/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Newtonsoft.Json issue regarding trailing fractional-second zeroes:
&lt;code&gt;https://github.com/JamesNK/Newtonsoft.Json/issues/1511&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ll come back to this series whenever I stumble upon situations that bring up the same question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Are you assuming or confirming?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I want to keep challenging the reasoning patterns that affect not only how we work, but also how we approach everyday problems.&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Rotate AWS IAM Access Keys by script</title>
      <dc:creator>Gerardo León</dc:creator>
      <pubDate>Thu, 06 Aug 2026 04:50:33 +0000</pubDate>
      <link>https://dev.to/gerardo_leon/rotate-aws-iam-access-keys-by-script-4g8g</link>
      <guid>https://dev.to/gerardo_leon/rotate-aws-iam-access-keys-by-script-4g8g</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update (2026):&lt;/strong&gt; This article documents a legacy approach and is preserved for historical and educational purposes. AWS now recommends temporary credentials through IAM Identity Center for human access and local development, and IAM roles for workloads, instead of long-term IAM user access keys whenever possible.&lt;/p&gt;

&lt;p&gt;The 12-hour expiration described below applied to temporary credentials or an organization-specific setup; IAM user access keys do not expire automatically. The original script also deletes the oldest key before validating the replacement and overwrites the &lt;code&gt;default&lt;/code&gt; profile in &lt;code&gt;.aws/credentials&lt;/code&gt;. Do not use it unchanged in a production environment. A safer rotation flow is: create the new key, configure and test it, deactivate the old key, verify that it is no longer used, and only then delete it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;Provided IAM access keys were set to expire in 12 hours. Developing locally constantly resulted in &lt;code&gt;Token expired&lt;/code&gt; exceptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Create an IAM user access key on demand through a script, following AWS recommendations to rotate access keys periodically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Creating these access keys and adding them to the &lt;code&gt;.aws/credentials&lt;/code&gt; file is a very manual process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Script the whole process through the AWS CLI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Required
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An IAM user name.&lt;/li&gt;
&lt;li&gt;A valid access key for the one-time setup.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;Given one valid IAM access key for the IAM user, create another access key and configure it under the &lt;code&gt;default&lt;/code&gt; profile in &lt;code&gt;.aws/credentials&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Given two valid IAM access keys for the IAM user, delete the oldest one and then create a new one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Script
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Assuming AWS CLI is installed and you have sufficient permissions&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;param&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="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mandatory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;$true&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="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$IAMUserName&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="c"&gt;# Fetch the access keys for the user&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;list-access-keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--user-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$IAMUserName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertFrom-Json&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# If 2 keys exist, delete the oldest one&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$keys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AccessKeyMetadata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&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="c"&gt;# Sorting by CreateDate to find the oldest key&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$oldestKey&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$keys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AccessKeyMetadata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Sort-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;CreateDate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-First&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="c"&gt;# Delete the oldest key&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;delete-access-key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--user-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$IAMUserName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--access-key-id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$oldestKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AccessKeyId&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="c"&gt;# Create a new access key&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$newKey&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;create-access-key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--user-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$IAMUserName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertFrom-Json&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Write to the .aws/credentials file&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$credentialsPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="s2"&gt;\.aws\credentials"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$credentialsContent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sh"&gt;@"
[default]
aws_access_key_id = &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$newKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AccessKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AccessKeyId&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
aws_secret_access_key = &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$newKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AccessKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SecretAccessKey&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
"@&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Overwrite the credentials file with the new keys&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# You might want to append or edit it if you use multiple profiles&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Set-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$credentialsPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$credentialsContent&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Updated .aws/credentials with new access keys."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\RotateKeys.ps1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-IAMUserName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YourIAMUserName"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Preconditions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;AWS CLI&lt;/a&gt; installed.&lt;/li&gt;
&lt;li&gt;An &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html#id_users_create_console" rel="noopener noreferrer"&gt;AWS IAM user created&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Sufficient IAM permissions to list, create, deactivate, and delete access keys, according to the rotation process you implement.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html" rel="noopener noreferrer"&gt;AWS IAM security best practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id-credentials-access-keys-update.html" rel="noopener noreferrer"&gt;Update access keys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/iam/" rel="noopener noreferrer"&gt;AWS CLI IAM command reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/nosavvy33/9adb00860ef3c9e4e4714a5bf427a141" rel="noopener noreferrer"&gt;Original PowerShell Gist&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cicd</category>
      <category>aws</category>
      <category>cloud</category>
      <category>iam</category>
    </item>
    <item>
      <title>Tinkering with ChatGPT: Gmails to Trello cards</title>
      <dc:creator>Gerardo León</dc:creator>
      <pubDate>Thu, 06 Aug 2026 04:19:35 +0000</pubDate>
      <link>https://dev.to/gerardo_leon/tinkering-with-chatgpt-gmails-to-trello-cards-38df</link>
      <guid>https://dev.to/gerardo_leon/tinkering-with-chatgpt-gmails-to-trello-cards-38df</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update — August 2026&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This article documents an experiment originally published in April 2023. The Gmail-to-Trello concept and both APIs remain available, but the linked implementation should now be treated as an &lt;strong&gt;unmaintained experimental prototype&lt;/strong&gt;, not as a production-ready or plug-and-play tool.&lt;/p&gt;

&lt;p&gt;The repository targets the unsupported Node.js 16 runtime and uses &lt;code&gt;pkg&lt;/code&gt;, which was archived in 2024. It also requires security and reliability fixes, including removing credentials from logs, correcting email deduplication and startup behavior, improving Gmail time-based queries, and adding safer error handling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not run the current repository with real Gmail or Trello credentials without first reviewing and updating the code and its dependencies.&lt;/strong&gt; If you test it, use non-production accounts, least-privilege credentials, and revoke the tokens afterward.&lt;/p&gt;
&lt;/blockquote&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%2Fmiro.medium.com%2Fv2%2Fda%3Atrue%2Fresize%3Afit%3A1200%2F0%2A5JgWJnbem458UFP8" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fda%3Atrue%2Fresize%3Afit%3A1200%2F0%2A5JgWJnbem458UFP8" alt="Photo by Rolf van Root on Unsplash" width="1200" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@freshvanroot" rel="noopener noreferrer"&gt;Rolf van Root&lt;/a&gt; on &lt;a href="https://unsplash.com/" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why ChatGPT?
&lt;/h2&gt;

&lt;p&gt;As a manager, my coding duties have reduced drastically. So, looked everywhere to build or automate something that could impact any close one’s work. Found ChatGPT could be a helper to get some inspiration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Someone close does some grunt work that constraints it to share a Gmail account with other three people.&lt;/li&gt;
&lt;li&gt;Work is distributed by emails, each member has to pick their corresponding emails and address them.&lt;/li&gt;
&lt;li&gt;Problem: most of the times, someone opens an email that does not matter to its responsibility, and leaves it marked as read; so confusion as to if that email was addressed takes over the place.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What’s in the market?
&lt;/h2&gt;

&lt;p&gt;My diagnostic is the group is prone to carelessly leave emails read by any reason; so my solution, after speaking with the affected, is to move the emails to a place where their state is visible and it’s easier to distribute tasks to the responsible ones. The most common tool for transparentizing such type of workspace, to me, is Trello or any board-like tool, besides that has a free tier, accessible to many users, and allows to order work in lists.&lt;/p&gt;

&lt;p&gt;Did some research prior to get dirty with code, found there’s a &lt;a href="https://workspace.google.com/marketplace/app/trello_for_gmail/710372554046" rel="noopener noreferrer"&gt;Trello add-on for Gmail&lt;/a&gt;. However, it ain’t cool enough: asks to open a mail to see the add-on in action, this did not avoid the scenario were oblivious users could skip creating Trello cards for each email read. Besides that did not provide any way to automate card creation. So that was a no-way.&lt;/p&gt;

&lt;p&gt;Without further ado, hands on work…&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;There, I came up with some requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tool should create cards automatically based off a emails from an account.&lt;/li&gt;
&lt;li&gt;Tool should not prompt user for any input once it’s setup.&lt;/li&gt;
&lt;li&gt;Tool should create cards on a time basis.&lt;/li&gt;
&lt;li&gt;Tool should integrate with Gmail and Trello.&lt;/li&gt;
&lt;li&gt;Tool should be a plug-and-play thingy.&lt;/li&gt;
&lt;li&gt;Cards should be concesive enough and avoid to copy-paste all of the email contents.&lt;/li&gt;
&lt;li&gt;Cards should not duplicate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From there, the inputs and outputs define as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inputs:&lt;/strong&gt; Gmail credentials, Trello integration requirements, user preferences on time basis for these cards to create, target Trello list, target Trello board.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outputs:&lt;/strong&gt; Trello cards in given Trello list of a Trello board.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I already mentioned I don’t code much often, and have got a bit dusty so decided to use ChatGPT for this journey.&lt;/li&gt;
&lt;li&gt;Trello has its capricious way to integrate with custom tools, so that adds a bit of overhead to the final solution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Talk is cheap, show me the code:&lt;/p&gt;

&lt;p&gt;Below is the link to the GitHub repo, where I include prompts used with ChatGPT in &lt;code&gt;prompts.txt&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/nosavvy33/gmails-to-trello" rel="noopener noreferrer"&gt;&lt;strong&gt;nosavvy33/gmails-to-trello on GitHub&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;See if this can become a Google add-on so that setup overhead reduces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;Surprised about how good of a work starter ChatGPT can be, no doubt it still needs polishing and human intervention for its proposed solutions to work. Personally, I come from another profession menaced by AI: translator; where there’s a saying “machine translation will replace translators that translate like machines”. I think of ChatGPT the same way, machine-generated software will replace software developers that write software as machines.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Connect your Github repo to NPM for package deployments</title>
      <dc:creator>Gerardo León</dc:creator>
      <pubDate>Sat, 01 Aug 2026 21:41:18 +0000</pubDate>
      <link>https://dev.to/gerardo_leon/connect-your-github-repo-to-npm-for-package-deployments-33ec</link>
      <guid>https://dev.to/gerardo_leon/connect-your-github-repo-to-npm-for-package-deployments-33ec</guid>
      <description>&lt;h3&gt;
  
  
  Most important... 🧐
&lt;/h3&gt;

&lt;p&gt;You should have already a library to publish to npm, if not, you can &lt;a href="https://dev.to/gerardo_leon/create-your-first-angular-components-library-2co"&gt;follow my previous tutorial&lt;/a&gt;, it will have you ready for this post 😀 &lt;/p&gt;

&lt;h2&gt;
  
  
  The NPM fun: first publish 👨‍💻
&lt;/h2&gt;

&lt;p&gt;Now we are ready for the NPM fun, add these two scripts to your root package.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"build-lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ng build custom-ui"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"publish-lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run build-lib &amp;amp;&amp;amp; npm login &amp;amp;&amp;amp; npm publish ./dist/custom-ui --access public"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, make sure to add these lines to the library's package.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@gblp/custom-ui"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;---&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;make&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;sure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;npm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;username&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"repository"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git+https://github.com/elparaquecosadeque/npm-test.git"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"publishConfig"&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;"access"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"public"&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;p&gt;After doing so, run this in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run publish-lib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And your terminal should prompt you for login to npm and then publish&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9q05gdwwlubmbfcypxmb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9q05gdwwlubmbfcypxmb.png" alt=" " width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is our package is already published to NPM:&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff7p59rxbk5noyp6d12sq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff7p59rxbk5noyp6d12sq.png" alt=" " width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's automate through Github Actions 🐙
&lt;/h2&gt;

&lt;p&gt;Create a .github/workflows directory in your root, and then a publish.yml resembling below:&lt;br&gt;
&lt;/p&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 package&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;read&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;publish&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;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@v5&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@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;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="s"&gt;npm&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="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 ci --ignore-scripts&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 run build:lib&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 publish ./dist/custom-ui --access public --provenance&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Push it and let's go to npm interface. There click on Settings &amp;gt; in Trusted Publisher, enable GitHub Actions:&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frp575fmkbdchmi361ayv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frp575fmkbdchmi361ayv.png" alt=" " width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will ask you in the form for some details you are good to fill out:&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fakvuqj5ecyaqbhmzc7dl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fakvuqj5ecyaqbhmzc7dl.png" alt=" " width="791" height="704"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finish with Setup connection button and you're done here:&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffic66xqpsgqf8bt6slfz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffic66xqpsgqf8bt6slfz.png" alt=" " width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Github side: trigger NPM publish 🚀
&lt;/h3&gt;

&lt;p&gt;In your repo, go to Actions &amp;gt; Under Actions, "Publish npm package" &amp;gt; Run workflow&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkna335y0scwsrcizqkiq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkna335y0scwsrcizqkiq.png" alt=" " width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we have defined the trigger workflow_dispatch this means the trigger is manual, let's give it the first go by click on "Run workflow"&lt;/p&gt;

&lt;h3&gt;
  
  
  Beware: Version conflict 🚀
&lt;/h3&gt;

&lt;p&gt;If you followed to the letter, you must have come across the error below:&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcocy5u63eauqeqtlxzur.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcocy5u63eauqeqtlxzur.png" alt=" " width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is quite expressive: NPM cannot publish over previous versions. Let's quickly fix by adding to our projects/custom-ui/package.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"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;"0.0.2"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, let's try again:&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqmk2qqbwhvdfuvnpdhu3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqmk2qqbwhvdfuvnpdhu3.png" alt=" " width="799" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now lovingly crafted 💓, we see it mirrored in your NPM profile:&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu9swvb99z22vps9xnnkj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu9swvb99z22vps9xnnkj.png" alt=" " width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Achievements 🎉🙌
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Created your first NPM package for reusability in your multiple projects&lt;/li&gt;
&lt;li&gt;Integrated CI/CD for your repo&lt;/li&gt;
&lt;li&gt;Revisit the repo to verify if you missed something: &lt;a href="https://github.com/elparaquecosadeque/npm-test" rel="noopener noreferrer"&gt;https://github.com/elparaquecosadeque/npm-test&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>npm</category>
      <category>angular</category>
      <category>cicd</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Create your first Angular components library</title>
      <dc:creator>Gerardo León</dc:creator>
      <pubDate>Sat, 01 Aug 2026 05:30:58 +0000</pubDate>
      <link>https://dev.to/gerardo_leon/create-your-first-angular-components-library-2co</link>
      <guid>https://dev.to/gerardo_leon/create-your-first-angular-components-library-2co</guid>
      <description>&lt;h2&gt;
  
  
  Build the local lib 🔨🤓
&lt;/h2&gt;

&lt;p&gt;To create an angular components lib, we add an parameter to the usual "ng new":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng new npm-test &lt;span class="nt"&gt;--create-application&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false

cd &lt;/span&gt;npm-test

ng generate library custom-ui &lt;span class="nt"&gt;--prefix&lt;/span&gt; ui

ng generate component components/button &lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;custom-ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will get you a simple component called button in projects/custom-ui/src/lib&lt;/p&gt;




&lt;h3&gt;
  
  
  Optional: Build a tester app 📖*️
&lt;/h3&gt;

&lt;p&gt;We create a test app to see what we built in our library. For so, run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng generate application library-tester
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There, to invoke the library in the library-tester app, do:&lt;/p&gt;

&lt;p&gt;1) Export the component in *&lt;em&gt;public-api.ts *&lt;/em&gt; by copying the line below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lib/components/button.component/button.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Import the component in the library-tester app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RouterOutlet&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ButtonComponent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-ui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;RouterOutlet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ButtonComponent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="na"&gt;styleUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;library-tester&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// app.html&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ui-button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Run these to verify in two different terminals:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng serve &lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;library-tester

ng build custom-ui &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once done, in your localhost you should get something close to this:&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdhkt606m4ise0jcl6h5a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdhkt606m4ise0jcl6h5a.png" alt=" " width="750" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's make the component configurable 🌶️🔥
&lt;/h2&gt;

&lt;p&gt;Do the following changes:&lt;/p&gt;

&lt;p&gt;1) In button.component.html:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"custom-btn"&lt;/span&gt; &lt;span class="na"&gt;[style.background-color]=&lt;/span&gt;&lt;span class="s"&gt;"color"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      {{ text }}
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Your button.component.ts should resemble this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CommonModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ui-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CommonModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./button.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./button.component.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ButtonComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Click me&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#f68f3b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.a) &lt;em&gt;Optional, add some custom css:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.custom-btn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3b82f6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.custom-btn&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#2563eb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) In the library tester's app.html you can now pass those variables in the DOM definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ui-button&lt;/span&gt; &lt;span class="na"&gt;text=&lt;/span&gt;&lt;span class="s"&gt;"Hey there!"&lt;/span&gt; &lt;span class="na"&gt;color=&lt;/span&gt;&lt;span class="s"&gt;"blue"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ui-button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;By doing all these steps, now your component should look like below:&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Floma1ir4oe97qsxywhlb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Floma1ir4oe97qsxywhlb.png" alt=" " width="345" height="197"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Achievements 🎉🙌
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Created your first Angular library for reusability in your multiple projects&lt;/li&gt;
&lt;li&gt;Keep style, look-and-feel consistency across your apps&lt;/li&gt;
&lt;li&gt;Revisit the repo to verify if you missed something: &lt;a href="https://github.com/elparaquecosadeque/npm-test" rel="noopener noreferrer"&gt;https://github.com/elparaquecosadeque/npm-test&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What's next 🏁🤓
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In this next article, we will learn to publish our Angular components library to npmjs (NPM) to make it available thru &lt;code&gt;npm install&lt;/code&gt; command&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding! 👨‍💻&lt;/p&gt;

</description>
      <category>angular</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
