<?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: BinaryPatrick</title>
    <description>The latest articles on DEV Community by BinaryPatrick (@binarypatrick).</description>
    <link>https://dev.to/binarypatrick</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%2F20558%2F7d659ebc-040e-4390-9fc2-ad324e34e3c6.png</url>
      <title>DEV Community: BinaryPatrick</title>
      <link>https://dev.to/binarypatrick</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/binarypatrick"/>
    <language>en</language>
    <item>
      <title>Restart Unhealthy Docker Containers Automatically</title>
      <dc:creator>BinaryPatrick</dc:creator>
      <pubDate>Sat, 19 Jul 2025 02:23:01 +0000</pubDate>
      <link>https://dev.to/binarypatrick/restart-unhealthy-docker-containers-automatically-4c00</link>
      <guid>https://dev.to/binarypatrick/restart-unhealthy-docker-containers-automatically-4c00</guid>
      <description>&lt;p&gt;Posted originally to &lt;a href="//binarypatrick.dev"&gt;binarypatrick.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When using docker compose, I recently got into adding health checks for my containers. This helps a lot with startup, especially with dependednt containers, but I was under the impression if I had something like &lt;code&gt;restart: always&lt;/code&gt; or &lt;code&gt;restart: unless-stopped&lt;/code&gt;, it would automatically try and restart my container. That's just not the case so I looked for something that might.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Watchtower Approach
&lt;/h2&gt;

&lt;p&gt;There is a container you can user to monitor the other running containers and restart them if they are unhealthy. It's called &lt;a href="https://github.com/willfarrell/docker-autoheal" rel="noopener noreferrer"&gt;autoheal by a dev named Will Farrell&lt;/a&gt; (no relation?). The only issue is that running a container to make sure my other containers are running seems a little like asking the kids to watch each other, and also, and maybe more importantly, I don't know Will, and his project isn't marked official. Other than a bunch of stars, like most OSS projects, you just never know what you're running of who it's coming from. It's kind of the catch 22 of open source, but when giving something access to the docker socket, I just want to be careful, so I decided to do something different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack Overflow
&lt;/h2&gt;

&lt;p&gt;The next bit of inspiration came from a &lt;a href="https://stackoverflow.com/a/74014021/4686882" rel="noopener noreferrer"&gt;stack overflow post&lt;/a&gt;. It's a bit down the page and not marked as "the answer" but it's like a gem in the ruff.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;health&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;unhealthy | xargs &lt;span class="nt"&gt;--no-run-if-empty&lt;/span&gt; docker restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Between the answer and the comments, there it is, exactly what I needed. Basically get the unhealthy container IDs and pipe them into a docker restart. The answer recomended cron, but that would have been too easy. Instead I made a systemd unit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Service and Timer
&lt;/h2&gt;

&lt;p&gt;Added to &lt;code&gt;/home/${USER}/.config/systemd/user&lt;/code&gt;, I created my two systemd unit files. One is the server, and the second is a timer to trigger it. I also created a bash script file for the service to run. Three files for what could have been a single line in crontab, but hey, this is the &lt;em&gt;right way&lt;/em&gt;, right?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /home/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/.config/systemd/user
&lt;span class="nb"&gt;cd&lt;/span&gt; /home/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/.config/systemd/user

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; restart-unhealthy.service
[Unit]
Description=Restart unhealthy docker containers
After=docker.service
Wants=docker.service

[Service]
Type=oneshot
ExecStart=/home/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;/.config/systemd/user/restart-unhealthy.sh
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; restart-unhealthy.timer
[Unit]
Description=Run docker unhealthy restart every 5 minutes
Requires=restart-unhealthy.service
After=docker.service
Wants=docker.service

[Timer]
OnCalendar=*:0/5
Persistent=true

[Install]
WantedBy=timers.target
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; restart-unhealthy.sh
#!/bin/bash
docker ps -q -f health=unhealthy | xargs --no-run-if-empty docker restart
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x restart-unhealthy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can register everything and start the service timer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; daemon-reload
systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nb"&gt;enable &lt;/span&gt;restart-unhealthy.timer
systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; start restart-unhealthy.timer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;To make this easy, I created a gist that can be run from a script.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Always inspect the code that will be running on your machines. Just like I don't know Will, you don't know me.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://gist.githubusercontent.com/binarypatrick/d4faffc2807c1e68ddf1229acb057582/raw | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>docker</category>
      <category>systemd</category>
    </item>
    <item>
      <title>Using Prune to Manage Backups</title>
      <dc:creator>BinaryPatrick</dc:creator>
      <pubDate>Tue, 04 Jul 2023 03:55:23 +0000</pubDate>
      <link>https://dev.to/binarypatrick/using-prune-to-manage-backups-3nfg</link>
      <guid>https://dev.to/binarypatrick/using-prune-to-manage-backups-3nfg</guid>
      <description>&lt;p&gt;Originally posted to &lt;a href="https://binarypatrick.dev/posts/using-prune-to-manage-archives/" rel="noopener noreferrer"&gt;binarypatrick.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Often times when using a tool like rsync to make backups you end up with lots of archives, and no way to keep them managed over time without a manual process. Prune is a simple tool that lets you remove prune archives in a folder, deleting any archives not matching the specified retention options. Any file type can be an archive and prune allows you to specify which files are in scope to be pruned.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/BinaryPatrick/Prune" rel="noopener noreferrer"&gt;https://github.com/BinaryPatrick/Prune&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;To install Prune you can go to the &lt;a href="https://github.com/BinaryPatrick/Prune/releases" rel="noopener noreferrer"&gt;releases page&lt;/a&gt; and download the latest release for your environment. You can also just clone the repo and compile it yourself with the steps in the readme. If you are running Linux on x64 you can also run the following commands to download and install Prune.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't just take my word for it. Always inspect the code that will be running on your machines, especially from an untrusted and unsigned source.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; prune-install-linux-x64.sh https://raw.githubusercontent.com/BinaryPatrick/Prune/main/scripts/install-linux-x64.sh
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x prune-install-linux-x64.sh
&lt;span class="nb"&gt;sudo&lt;/span&gt; ./prune-install-linux-x64.sh
&lt;span class="nb"&gt;rm &lt;/span&gt;prune-install-linux-x64.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Once it's installed, you should be able to run prune simply by typing in &lt;code&gt;prune&lt;/code&gt;, &lt;strong&gt;but don't&lt;/strong&gt;. Prune is designed to delete things, and you should always run it with &lt;code&gt;--dry-run&lt;/code&gt; and &lt;code&gt;--verbose&lt;/code&gt; until you're certain what will be pruned.&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.amazonaws.com%2Fuploads%2Farticles%2F5lb8j4p9527q10hcdj0s.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.amazonaws.com%2Fuploads%2Farticles%2F5lb8j4p9527q10hcdj0s.png" alt="prune help" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To use prune, you will need to provide a pruning path and some retention value. Keep in mind prune is not recursive, so it will only purge files directly in the path you give. Prune also uses last modified date, not created date to evaluate what files to keep and prune. This allows for incremental backups with updates to be properly evaluated. You must specify some level of retention for prune to run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;prune &lt;span class="nt"&gt;--path&lt;/span&gt; /home/patrick/test/ &lt;span class="nt"&gt;--keep-last&lt;/span&gt; 10 &lt;span class="nt"&gt;--dry-run&lt;/span&gt; &lt;span class="nt"&gt;--verbose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output:&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.amazonaws.com%2Fuploads%2Farticles%2F61bdcxgjz43e6jzuri0s.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.amazonaws.com%2Fuploads%2Farticles%2F61bdcxgjz43e6jzuri0s.png" alt="prune example output" width="614" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also provide more file filtering using &lt;code&gt;--prefix&lt;/code&gt; and &lt;code&gt;--ext&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;prune &lt;span class="nt"&gt;--path&lt;/span&gt; /home/patrick/test/ &lt;span class="nt"&gt;--keep-last&lt;/span&gt; 10 &lt;span class="nt"&gt;--prefix&lt;/span&gt; backup_ &lt;span class="nt"&gt;--ext&lt;/span&gt; tar.gz &lt;span class="nt"&gt;--dry-run&lt;/span&gt; &lt;span class="nt"&gt;--verbose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally you can scope in the exact retention you want using the different retention interval filters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;prune &lt;span class="nt"&gt;--path&lt;/span&gt; /home/patrick/test/ &lt;span class="nt"&gt;--keep-daily&lt;/span&gt; 7 &lt;span class="nt"&gt;--keep-weekly&lt;/span&gt; 2 &lt;span class="nt"&gt;--dry-run&lt;/span&gt; &lt;span class="nt"&gt;--verbose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far I have tested this with Debian 12 and Raspbian and it runs great.&lt;/p&gt;

&lt;p&gt;Happy pruning!&lt;/p&gt;

</description>
      <category>linux</category>
      <category>backup</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Bitwarden Automated Backup</title>
      <dc:creator>BinaryPatrick</dc:creator>
      <pubDate>Tue, 27 Jun 2023 02:06:00 +0000</pubDate>
      <link>https://dev.to/binarypatrick/bitwarden-automated-backup-374a</link>
      <guid>https://dev.to/binarypatrick/bitwarden-automated-backup-374a</guid>
      <description>&lt;p&gt;Crossposted from &lt;a href="https://binarypatrick.dev/posts/bitwarden-automated-backup/" rel="noopener noreferrer"&gt;binarypatrick.dev&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fkvwb4mxpvfo21ze7ogfl.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.amazonaws.com%2Fuploads%2Farticles%2Fkvwb4mxpvfo21ze7ogfl.png" alt="Bitwarden Logo" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The purpose of this backup process arose from a moment when I broke my phone. My phone was my lone source of MFA/2FA codes via the authenticator app, and I genuinely felt terror that I would be locked out of my Bitwarden account. After getting the phone working enough to log in and transfer my 2FA seed to another device, I decided to create this script to have a backup of my passwords locally.&lt;/p&gt;

&lt;p&gt;Key requirements for this script are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Reasoning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Secure&lt;/td&gt;
&lt;td&gt;The script must run securely and its resulting output must also be secure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testable&lt;/td&gt;
&lt;td&gt;An untested backup is not a backup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;td&gt;It should ideally run without human intervention, rain or shine&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You will need &lt;code&gt;unzip&lt;/code&gt; and &lt;code&gt;cron&lt;/code&gt; simply to install the Bitwarden CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;unzip cron &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installing the Bitwarden CLI
&lt;/h2&gt;

&lt;p&gt;Unfortunately it does not exist in the distro repo or flatpak. Though it is in snap, if you are interested in such things. I installed it manually through a handy script I wrote. It will make updating it manually later a bit easier it at least. Either way the CLI binary will need to be somewhere we can run it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; bw.zip &lt;span class="s2"&gt;"https://vault.bitwarden.com/download/?platform=linux&amp;amp;app=cli"&lt;/span&gt;
unzip bw.zip
&lt;span class="nb"&gt;sudo mv&lt;/span&gt; ./bw /usr/local/bin
&lt;span class="nb"&gt;rm &lt;/span&gt;bw.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bitwarden User
&lt;/h2&gt;

&lt;p&gt;Before we begin we will need to create a new user for running the script. This user will securely store the environment variables as well. Unfortunately even though we have the API key, Bitwarden still requires your vault password. I assume this is for actually decrypting the vault. To store them securely, I put them in the service user's &lt;code&gt;~/.bash_profile&lt;/code&gt;. This way only that user, and sudoers/root will have access and the job won't need to run as root. Obviously plaintext isn't ideal, but for the script to use it, it must be read somewhere. No amount of encryption would change that. Also note this machine should be isolated on your network from other services. &lt;/p&gt;

&lt;p&gt;To create your user 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;&lt;span class="nb"&gt;sudo &lt;/span&gt;adduser &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--shell&lt;/span&gt; /bin/bash &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--group&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--disabled-password&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--home&lt;/span&gt; /home/bitwarden &lt;span class="se"&gt;\&lt;/span&gt;
   bitwarden
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bitwarden API Credentials
&lt;/h2&gt;

&lt;p&gt;To get your API key log into your Bitwarden web vault. From the user menu in the upper right, go to &lt;em&gt;Account Settings&lt;/em&gt;. Then on the left hand menu go to &lt;em&gt;Security&lt;/em&gt;, then &lt;em&gt;Keys&lt;/em&gt; in the top menu. This should bring you to &lt;em&gt;Encryption Key Settings&lt;/em&gt; and at the bottom of the page &lt;em&gt;API Key&lt;/em&gt;. Your account will only have one set of client ID/secret. Click &lt;em&gt;View API Key&lt;/em&gt; to retrieve it. You'll need these values to add to the &lt;code&gt;.bash_profile&lt;/code&gt; along with your vault password.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo touch&lt;/span&gt; /home/bitwarden/.bash_profile
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /home/bitwarden/.bash_profile
&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /home/bitwarden/.bash_profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;BW_CLIENTID&lt;/span&gt;=&lt;span class="s2"&gt;"&amp;lt;your_client_id&amp;gt;"&lt;/span&gt;
&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;BW_CLIENTSECRET&lt;/span&gt;=&lt;span class="s2"&gt;"&amp;lt;your_client_secret&amp;gt;"&lt;/span&gt;
&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;BW_PASSWORD&lt;/span&gt;=&lt;span class="s2"&gt;"&amp;lt;your_vault_password"&lt;/span&gt;
&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;BW_NOTIFICATION_EMAIL&lt;/span&gt;=&lt;span class="s2"&gt;"&amp;lt;your_notification_email_address&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting up the Script
&lt;/h2&gt;

&lt;p&gt;The script needs to be put somewhere the Bitwarden user can read it, and it needs to be set as executable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;REPO_BACKUP_SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://raw.githubusercontent.com/BinaryPatrick/BitwardenBackup/main/backup.sh
&lt;span class="nb"&gt;sudo &lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /home/bitwarden/backup.sh &lt;span class="nv"&gt;$REPO_BACKUP_SCRIPT&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;bitwarden /home/bitwarden/backup.sh
&lt;span class="nb"&gt;sudo chgrp &lt;/span&gt;bitwarden /home/bitwarden/backup.sh
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;744 /home/bitwarden/backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script also includes an email notification if the vault fails to unlock or authentication fails. You'll need to &lt;a href="https://dev.to/posts/configuring-postfix-with-gmail/"&gt;set up postfix to send email&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding to &lt;code&gt;crontab&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Now that the script is in place, we can add it to &lt;code&gt;crontab&lt;/code&gt;. We need to do a little hand holding to make sure the &lt;code&gt;crontab&lt;/code&gt; environment can see the bw binary, and will use the environment variables we configured. Also notice, our script requires an output directory to run. We'll set this to &lt;code&gt;/home/bitwarden&lt;/code&gt;. Feel free to configure this to use a mounted share or something though. Remember the output is encrypted using the vault password so it is relatively safe to have on a restricted file share.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su bitwarden
crontab &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;PATH&lt;/span&gt;=/&lt;span class="n"&gt;bin&lt;/span&gt;:/&lt;span class="n"&gt;sbin&lt;/span&gt;:/&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;bin&lt;/span&gt;:/&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;sbin&lt;/span&gt;:/&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;local&lt;/span&gt;/&lt;span class="n"&gt;bin&lt;/span&gt;:/&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;local&lt;/span&gt;/&lt;span class="n"&gt;sbin&lt;/span&gt;
&lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; * * * &lt;span class="n"&gt;BASH_ENV&lt;/span&gt;=/&lt;span class="n"&gt;home&lt;/span&gt;/&lt;span class="n"&gt;bitwarden&lt;/span&gt;/.&lt;span class="n"&gt;bash_profile&lt;/span&gt; /&lt;span class="n"&gt;bin&lt;/span&gt;/&lt;span class="n"&gt;bash&lt;/span&gt; /&lt;span class="n"&gt;home&lt;/span&gt;/&lt;span class="n"&gt;bitwarden&lt;/span&gt;/&lt;span class="n"&gt;backup&lt;/span&gt;.&lt;span class="n"&gt;sh&lt;/span&gt; /&lt;span class="n"&gt;home&lt;/span&gt;/&lt;span class="n"&gt;bitwarden&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Validate Decryption
&lt;/h2&gt;

&lt;p&gt;An untested backup is no backup at all. Make sure to try and decrypt the file that is created using the decryption script once you have a backup. It should create a json file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;REPO_DECRYPTION_SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://raw.githubusercontent.com/BinaryPatrick/BitwardenBackup/main/decrypt.sh
&lt;span class="nb"&gt;sudo &lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; decrypt.sh &lt;span class="nv"&gt;$REPO_DECRYPTION_SCRIPT&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; +x decrypt.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run the script, pass the filename of the file you want to decrypt and you will be prompted for your vault password.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./decrypt.sh bw_export_xxxxxxxxxxxxxxx.enc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/send-email-linux-command-line" rel="noopener noreferrer"&gt;https://www.digitalocean.com/community/tutorials/send-email-linux-command-line&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://easyengine.io/tutorials/linux/ubuntu-postfix-gmail-smtp/" rel="noopener noreferrer"&gt;https://easyengine.io/tutorials/linux/ubuntu-postfix-gmail-smtp/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bitwarden.com/blog/how-to-back-up-and-encrypt-your-bitwarden-vault-from-the-command-line/" rel="noopener noreferrer"&gt;https://bitwarden.com/blog/how-to-back-up-and-encrypt-your-bitwarden-vault-from-the-command-line/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bitwarden.com/help/cli-auth-challenges/" rel="noopener noreferrer"&gt;https://bitwarden.com/help/cli-auth-challenges/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>bitwarden</category>
      <category>backup</category>
      <category>homelab</category>
      <category>linux</category>
    </item>
    <item>
      <title>Start a SPA and it's API in one click</title>
      <dc:creator>BinaryPatrick</dc:creator>
      <pubDate>Wed, 16 Jun 2021 19:25:43 +0000</pubDate>
      <link>https://dev.to/binarypatrick/start-a-spa-and-it-s-api-in-one-click-5cg3</link>
      <guid>https://dev.to/binarypatrick/start-a-spa-and-it-s-api-in-one-click-5cg3</guid>
      <description>&lt;p&gt;I've been using start or jump scripts for a long time to start up my API and SPA at the same time. With &lt;a href="https://devblogs.microsoft.com/aspnet/introducing-project-tye/" rel="noopener noreferrer"&gt;project TYE&lt;/a&gt; on the horizon, I thought I would share more about my more simplistic, "poor man", implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes
&lt;/h2&gt;

&lt;p&gt;It is important to note, a lot of configuration information is specified ahead of time in different files to make this work. For ASP.Net, the ports are configured in the &lt;em&gt;launch.json&lt;/em&gt; and for node, proxy information is set up in a &lt;em&gt;proxy.config.json&lt;/em&gt; file. Below is a simple example of the proxy config I use most often to route api requests to the ASP.Net port. It is also good to remember both apps are running locally under localhost.&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;proxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;config&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;"/api"&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;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:5000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"secure"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"logLevel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"debug"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"changeOrigin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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;p&gt;it's also important to declare the script you want node to run in scripts section of your package.json. Typically I use:&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;"start"&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 serve --proxy-config proxy.config.json"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Humble beginnings
&lt;/h2&gt;

&lt;p&gt;For my first attempt at makin my start script, I created a batch file that started two scripts. One new Powershell window for &lt;code&gt;dotnet watch run&lt;/code&gt; and another for &lt;code&gt;npm run start&lt;/code&gt;. This had some hiccups initially. The windows would close if the Powershell stopped running, which was problematic for collecting errors. Also the command prompt window would linger. After a few iterations, I was able to solve those problems and landed on this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ./src/api
&lt;span class="nb"&gt;start&lt;/span&gt; &lt;span class="kd"&gt;powershell&lt;/span&gt;&lt;span class="err"&gt;.exe&lt;/span&gt; &lt;span class="na"&gt;-NoExit &lt;/span&gt;&lt;span class="kd"&gt;dotnet&lt;/span&gt; &lt;span class="kd"&gt;watch&lt;/span&gt; &lt;span class="nb"&gt;run&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ../spa
&lt;span class="nb"&gt;start&lt;/span&gt; &lt;span class="kd"&gt;powershell&lt;/span&gt;&lt;span class="err"&gt;.exe&lt;/span&gt; &lt;span class="na"&gt;-NoExit &lt;/span&gt;&lt;span class="kd"&gt;npm&lt;/span&gt; &lt;span class="nb"&gt;run&lt;/span&gt; &lt;span class="nb"&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fpatrickmoore.dev%2Fimages%2Fautostart-api-spa-app-1.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%2Fpatrickmoore.dev%2Fimages%2Fautostart-api-spa-app-1.png" alt="Original script result" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next evolution
&lt;/h2&gt;

&lt;p&gt;This script worked well from ASP.Net Core 1.3 and Angular 2, all the way through today with .Net 5 and Angular 12. Recently though, I started to dabble in Powershell Core and Windows Terminal which led me to a more efficient approach to starting and managing my scripts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;wt&lt;/span&gt; &lt;span class="na"&gt;--title &lt;/span&gt;&lt;span class="s2"&gt;"dotnet watch run"&lt;/span&gt; &lt;span class="na"&gt;-d &lt;/span&gt;&lt;span class="s2"&gt;"./src/api"&lt;/span&gt; &lt;span class="kd"&gt;powershell&lt;/span&gt; &lt;span class="na"&gt;-noExit &lt;/span&gt;&lt;span class="s2"&gt;"dotnet watch run"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="na"&gt;--title &lt;/span&gt;&lt;span class="s2"&gt;"npm run start"&lt;/span&gt; &lt;span class="na"&gt;-d &lt;/span&gt;&lt;span class="s2"&gt;"./src/spa/"&lt;/span&gt; &lt;span class="kd"&gt;powershell&lt;/span&gt; &lt;span class="na"&gt;-noExit &lt;/span&gt;&lt;span class="s2"&gt;"npm run start"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this one line, I can start a Windows Terminal with both scripts running in tabs. This allows me to keep both together and but also manage the window as one unit. The commands are similar, but the tabs are labeled, so I can quickly jump where I need to look. Also, this is far more expandable if I needed to run other startup scripts together in the future.&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%2Fpatrickmoore.dev%2Fimages%2Fautostart-api-spa-app-2.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%2Fpatrickmoore.dev%2Fimages%2Fautostart-api-spa-app-2.png" alt="New script result" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Originally posted to &lt;a href="https://patrickmoore.dev/autostart-api-spa-app/" rel="noopener noreferrer"&gt;https://patrickmoore.dev/autostart-api-spa-app/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Adding Build Versions in Angular</title>
      <dc:creator>BinaryPatrick</dc:creator>
      <pubDate>Fri, 05 Mar 2021 04:22:25 +0000</pubDate>
      <link>https://dev.to/binarypatrick/build-versions-for-angular-build-pipeline-5ef7</link>
      <guid>https://dev.to/binarypatrick/build-versions-for-angular-build-pipeline-5ef7</guid>
      <description>&lt;p&gt;Have you ever found yourself wondering which version of an SPA is running when you pull up your site? This was a problem I wanted to answer in my Angular SPA. Utilizing the &lt;code&gt;npm version&lt;/code&gt; command in my build pipeline, I was able to include my pipeline build number in my app, significantly decreased troubleshooting time and version confusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some considerations
&lt;/h2&gt;

&lt;p&gt;Keep in mind, your build version numbers will need to be compliant with &lt;a href="https://github.com/NuGet/Home/wiki/SemVer-2.0.0-support#spec" rel="noopener noreferrer"&gt;normalized SEMVER2&lt;/a&gt; to be valid for NPM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instructions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 - Adding node types to Angular
&lt;/h3&gt;

&lt;p&gt;In your &lt;code&gt;tsconfig.app.json&lt;/code&gt; file, add &lt;code&gt;node&lt;/code&gt; to the &lt;code&gt;types&lt;/code&gt; array. If there is not a already &lt;code&gt;types&lt;/code&gt; array, add it under &lt;code&gt;compilerOptions&lt;/code&gt; in the json root.&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&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;"types"&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="s2"&gt;"node"&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;h3&gt;
  
  
  Step 2 - Adding environment variables
&lt;/h3&gt;

&lt;p&gt;Find your &lt;code&gt;environment&lt;/code&gt; constant in &lt;code&gt;src/environments&lt;/code&gt;. You should see two files, one for prod, &lt;code&gt;environment.prod.ts&lt;/code&gt;, and one for not-prod, &lt;code&gt;environment.ts&lt;/code&gt;. In production, I keep it simple and add the &lt;code&gt;appVersion&lt;/code&gt; variable without any changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;appVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;production&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;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;area&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;In the non-production environment though, I like to add a dev tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;appVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--dev&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;This helps me remember if I'm in a non production environment, because there is nothing worse than troubleshooting a problem in the wrong environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Adding the version to an Angular component
&lt;/h3&gt;

&lt;p&gt;Likewise this is easier than it sounds. Now that the node types and version variable are available, they just need to be accessed and displayed somewhere in the app. I prefer to add this to the bootstrapped component, which is usually &lt;code&gt;AppComponent&lt;/code&gt;. This best way I've found to do this is with a host binding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;em&gt;You do not need to specify &lt;code&gt;prod&lt;/code&gt; vs &lt;code&gt;non-prod&lt;/code&gt; in your environment import. When Angular builds, it will use the production environment if the &lt;code&gt;--prod&lt;/code&gt; build flag is used.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;HostBinding&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;environment&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;src/environments/environment&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="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;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostBinding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attr.app-version&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;appVersionAttr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appVersion&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;h3&gt;
  
  
  Step 5 - Adding &lt;code&gt;npm version&lt;/code&gt; to your pipeline
&lt;/h3&gt;

&lt;p&gt;If your pipeline stack offers automatically created version numbers, just make sure they are compliant with &lt;a href="https://github.com/NuGet/Home/wiki/SemVer-2.0.0-support#spec" rel="noopener noreferrer"&gt;normalized SEMVER2&lt;/a&gt;. Once you have add a task in your pipeline to run the npm command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm version &lt;span class="si"&gt;$(&lt;/span&gt;build.buildNumber&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The build variable above is for &lt;a href="https://docs.microsoft.com/en-us/azure/devops/?view=azure-devops" rel="noopener noreferrer"&gt;Azure Devops&lt;/a&gt; specifically. Your pipeline stack may differ.&lt;/p&gt;

&lt;h2&gt;
  
  
  Last Thoughts
&lt;/h2&gt;

&lt;p&gt;Now that you've added a version number, build, build, build and check the check your work. You should see an html attribute on whatever html element you added the host binding to. For me it looks like this:&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;app-root&lt;/span&gt; &lt;span class="na"&gt;app-version=&lt;/span&gt;&lt;span class="s"&gt;"3.1.28-master"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/app-root&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Originally posted to &lt;a href="https://patrickmoore.dev/angular-build-versioning" rel="noopener noreferrer"&gt;https://patrickmoore.dev/angular-build-versioning&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>devops</category>
      <category>buildpipeline</category>
    </item>
    <item>
      <title>Machine Learning Tutorial</title>
      <dc:creator>BinaryPatrick</dc:creator>
      <pubDate>Tue, 18 Sep 2018 02:55:36 +0000</pubDate>
      <link>https://dev.to/binarypatrick/machine-learning-tutorial-1l3l</link>
      <guid>https://dev.to/binarypatrick/machine-learning-tutorial-1l3l</guid>
      <description>&lt;p&gt;I've been scouring the internet looking for a good, recent tutorial on machine learning. The type of machine learning I want to do is neural network classification with a spreadsheet's worth of values, similar to predicting bank loan defaults. &lt;/p&gt;

&lt;p&gt;Anyone know of any good tutorials, specifically using a recent version of Python 3?&lt;/p&gt;

</description>
      <category>help</category>
      <category>machinelearning</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What does good onboarding look like?</title>
      <dc:creator>BinaryPatrick</dc:creator>
      <pubDate>Sat, 14 Jul 2018 14:44:46 +0000</pubDate>
      <link>https://dev.to/binarypatrick/what-does-good-onboarding-look-like--106o</link>
      <guid>https://dev.to/binarypatrick/what-does-good-onboarding-look-like--106o</guid>
      <description>&lt;p&gt;On Monday, &lt;em&gt;two&lt;/em&gt; new developers start at our company. We only have &lt;strong&gt;&lt;em&gt;two&lt;/em&gt;&lt;/strong&gt; developers now, so this is a big increase for us. I've been thinking over the past two weeks what would be the best way to bring these new people into our organization, with our limited culture, and how to grow the culture with them moving forward. &lt;/p&gt;

&lt;p&gt;What does a good on onboarding process look like? Are there any specifics it should or should not have?&lt;/p&gt;

</description>
      <category>devdiscuss</category>
      <category>newhire</category>
      <category>devculture</category>
    </item>
    <item>
      <title>The best Easter Egg you ever did leave</title>
      <dc:creator>BinaryPatrick</dc:creator>
      <pubDate>Sun, 13 May 2018 07:15:28 +0000</pubDate>
      <link>https://dev.to/binarypatrick/the-best-easter-egg-you-ever-did-leave-5a2h</link>
      <guid>https://dev.to/binarypatrick/the-best-easter-egg-you-ever-did-leave-5a2h</guid>
      <description>&lt;p&gt;Where I work, a developer left an Easter Egg long ago where if you came to our ERP self service dashboard by way of 302 and had an expired SSO cookie, you'd be greeted by a little page saying "Dude, you got no cookie." Luckily it was a rare occurrence, and flew under the radar for a long long time. &lt;/p&gt;

&lt;p&gt;Recently I reimplemented that middleware component. Part of the project requirement was removing that message. To keep the spirit of the Easter egg alive though, I have Dude you have no cookie written to the console instead. I feel good about what I've done. I think Easter Eggs in code are an important part of the job.&lt;/p&gt;

&lt;p&gt;Does anyone have a story about an Easter Egg they've written or found that was particularly delightful?&lt;/p&gt;

</description>
      <category>eastereggs</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
