<?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: Marc Dougherty</title>
    <description>The latest articles on DEV Community by Marc Dougherty (@muncus).</description>
    <link>https://dev.to/muncus</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%2F267960%2F41a57a74-d023-4412-8541-ec355e6044d3.png</url>
      <title>DEV Community: Marc Dougherty</title>
      <link>https://dev.to/muncus</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/muncus"/>
    <language>en</language>
    <item>
      <title>Handy Yaml Tricks!</title>
      <dc:creator>Marc Dougherty</dc:creator>
      <pubDate>Thu, 09 Feb 2023 02:38:11 +0000</pubDate>
      <link>https://dev.to/muncus/handy-yaml-tricks-415p</link>
      <guid>https://dev.to/muncus/handy-yaml-tricks-415p</guid>
      <description>&lt;p&gt;In the past few years, YAML (&lt;a href="http://yaml.org"&gt;http://yaml.org&lt;/a&gt;) has become an essential part of software, particularly for infrastructure-as-code tools. Yaml at the heart of &lt;a href="http://kubernetes.io"&gt;kubernetes&lt;/a&gt; configuration, kubernetes-inspired APIs like &lt;a href="https://cloud.google.com/config-connector/docs/concepts/resources"&gt;Google's config connector&lt;/a&gt;, and a number of workflow systems like &lt;a href="https://cloud.google.com/workflows"&gt;Google Cloud Workflows&lt;/a&gt; and &lt;a href="http://github.com/features/actions"&gt;Github Actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In its simplest forms, Yaml is quite human-readable, but over time many of these configurations become more complex, and the documentation of these formats is not always as complete or searchable as we might like. Included below are some small tips that may help you make the most of your Yaml configurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  IDE assistance with JSON Schemas!
&lt;/h3&gt;

&lt;p&gt;Do you struggle to remember the names of fields in your Yaml objects? I sure do!&lt;/p&gt;

&lt;p&gt;Most modern editors and IDEs support the &lt;a href="https://microsoft.github.io/language-server-protocol/"&gt;Language Server Protocol&lt;/a&gt;, which powers the code completion, validation, and tooltip features. Combined with a &lt;a href="https://github.com/redhat-developer/yaml-language-server"&gt;Yaml Language Server&lt;/a&gt;, we can get rich completion for Yaml files!&lt;/p&gt;

&lt;p&gt;Installation varies depending on the editor, but I found installation with VSCode to be pretty straightforward.&lt;/p&gt;

&lt;p&gt;For many common yaml files, the correct schema can be inferred from the file name, and looked up automatically with &lt;a href="//schemastore.org"&gt;SchemaStore&lt;/a&gt;.  SchemaStore hosts a wide collection of JSON Schemas, which can be used to validate Yaml. SchemaStore is backed by the &lt;a href="https://github.com/schemastore/schemastore/"&gt;SchemaStore Github Repository&lt;/a&gt;, and contributions of additional JSON Schemas are welcome!&lt;/p&gt;

&lt;p&gt;Schema detection can also be configured manually in your editor if auto-detection is inaccurate.&lt;/p&gt;

&lt;p&gt;Setting up the Yaml Language Server takes a bit of work, but if you regularly work with complex Yaml objects, you'll be glad you took the time to set it up!&lt;/p&gt;

&lt;h3&gt;
  
  
  Better Multiline Strings with &lt;code&gt;&amp;gt;&lt;/code&gt; and &lt;code&gt;|&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Sometimes, with workflow configuration like &lt;code&gt;cloudbuild.yaml&lt;/code&gt; and Github Actions, we write long commandlines that are not very readable in an IDE.&lt;/p&gt;

&lt;p&gt;The Yaml spec has what they call &lt;a href="https://yaml.org/spec/1.2.2/#literal-style"&gt;literal style&lt;/a&gt; (&lt;code&gt;|&lt;/code&gt;) and &lt;a href="https://yaml.org/spec/1.2.2/#813-folded-style"&gt;folded style&lt;/a&gt; (&lt;code&gt;&amp;gt;&lt;/code&gt;) to help with this.&lt;/p&gt;

&lt;p&gt;Literal Style preserves newlines in the string, so it can be used to run multiple commands as a single step. For example, installing python dependencies and running the relevant tests can be done like this:&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;pip install -r requirements.txt&lt;/span&gt;
          &lt;span class="s"&gt;pytest -v .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: In some situations, this may mask failures in the earlier commands if the last command exits successfully.&lt;/p&gt;

&lt;p&gt;Folding Style "condenses" whitespace in the string, replacing spaces and/or newlines with a single space as describe in the &lt;a href="https://yaml.org/spec/1.2.2/#line-folding"&gt;spec section on line folding&lt;/a&gt;. In short, it lets us insert newlines in a string to make it more readable.&lt;/p&gt;

&lt;p&gt;Consider this very long line from a github actions workflow that calls the github API with &lt;code&gt;curl&lt;/code&gt;:&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get current repo settings&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;curl -o repository.json https://api.github.com/repos/${{ github.repository}} -H "Authorization&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bearer ${{ github.token }}"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rewritten with Folding Style, it looks like this instead:&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get current repo settings&lt;/span&gt;
       &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="s"&gt;curl -o repository.json&lt;/span&gt;
          &lt;span class="s"&gt;https://api.github.com/repos/${{ github.repository}}&lt;/span&gt;
          &lt;span class="s"&gt;-H "Authorization: Bearer ${{ github.token }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For long lines, this can make them easier to read, and easier to edit and review.&lt;/p&gt;

&lt;p&gt;These tips have helped me write more maintainable Yaml files, and I hope they help you, too!&lt;/p&gt;

</description>
      <category>yaml</category>
    </item>
    <item>
      <title>Using the Github cli with Multiple Repos</title>
      <dc:creator>Marc Dougherty</dc:creator>
      <pubDate>Thu, 24 Feb 2022 04:25:40 +0000</pubDate>
      <link>https://dev.to/muncus/using-the-github-cli-with-multiple-repos-38k</link>
      <guid>https://dev.to/muncus/using-the-github-cli-with-multiple-repos-38k</guid>
      <description>&lt;p&gt;I have been using the &lt;a href="http://cli.github.com"&gt;Github CLI&lt;/a&gt; for a while now, and&lt;br&gt;
I've found it to be really great when working in the context of a single&lt;br&gt;
repository. But what happens when you're responsible for work in &lt;em&gt;dozens&lt;/em&gt; of&lt;br&gt;
repos? Keeping tabs on many repos can be done, thanks to the &lt;code&gt;gh api&lt;/code&gt;&lt;br&gt;
subcommand, and aliases!&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gh api&lt;/code&gt; allows you to call any github api endpoint, and for my particular&lt;br&gt;
needs, the &lt;a href="https://docs.github.com/en/rest/reference/search#search-issues-and-pull-requests"&gt;&lt;code&gt;search/issues&lt;/code&gt; endpoint&lt;/a&gt; is the most useful, as it searches both&lt;br&gt;
issues and PRs.&lt;/p&gt;

&lt;p&gt;There are two different ways to format responses, either with the &lt;code&gt;--jq&lt;/code&gt; flag,&lt;br&gt;
which uses &lt;a href="https://stedolan.github.io/jq/manual/"&gt;&lt;code&gt;jq&lt;/code&gt; syntax&lt;/a&gt; or &lt;code&gt;--template&lt;/code&gt;&lt;br&gt;
which uses Go's &lt;a href="https://pkg.go.dev/text/template"&gt;&lt;code&gt;text/template&lt;/code&gt; syntax&lt;/a&gt;. I&lt;br&gt;
found prototyping with the &lt;code&gt;jq&lt;/code&gt; syntax to be nice, but the color and formatting&lt;br&gt;
choices in the &lt;code&gt;--template&lt;/code&gt; flag won me over.&lt;/p&gt;

&lt;p&gt;Note that the &lt;code&gt;--template&lt;/code&gt; args in the below examples are all the same, and they&lt;br&gt;
produce output like this (but with some tasteful terminal colors, which do not&lt;br&gt;
appear in markdown):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[  98] chore(deps): update dependency maven to v3.6 (15 days ago)
  https://github.com/GoogleCloudPlatform/cloud-run-samples/pull/98
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More information about formatting output can be found in the &lt;a href="https://cli.github.com/manual/gh_help_formatting"&gt;&lt;code&gt;gh&lt;/code&gt; formatting&lt;br&gt;
help&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  My issues/PRs that need followup
&lt;/h4&gt;

&lt;p&gt;In my job, my team is responsible for the care and feeding of many different&lt;br&gt;
public github repositories, which requires a lot of issue triage, and pr&lt;br&gt;
followups. Switching to each of these contexts to run &lt;code&gt;gh issues&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;gh status&lt;/code&gt; is pretty tiresome, so I made a command that uses &lt;code&gt;gh api&lt;/code&gt; to search&lt;br&gt;
for open issues and PRs in &lt;em&gt;any&lt;/em&gt; repo that are assigned to me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh api &lt;span class="nt"&gt;-XGET&lt;/span&gt; search/issues &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'assignee:@me is:open  '&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--template&lt;/span&gt; &lt;span class="s1"&gt;'{{range .items -}}[{{.number | printf "%5.f" |autocolor "blue" }}] {{.title}} ({{ timeago .updated_at | autocolor "yellow+d" }}) {{printf "\n  " }}{{.html_url}} {{print "\n" -}}{{end}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command is not much fun to type, so I've created an alias within the &lt;code&gt;gh&lt;/code&gt; tool (not to be confused with shell aliases). However, using the &lt;code&gt;gh alias set&lt;/code&gt; command would require escaping the quotation marks, I chose to edit the config file directly - it lives at &lt;code&gt;~/.config/gh/config.yaml&lt;/code&gt; on my linux machine. Adding the following block to the &lt;code&gt;aliases&lt;/code&gt; list will create the alias:&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;mine&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;api -XGET search/issues -f q="assignee:@me is:open"&lt;/span&gt;
        &lt;span class="s"&gt;--template '{{range .items -}}&lt;/span&gt;
        &lt;span class="s"&gt;[{{.number | printf "%4.f" |autocolor "blue" }}] {{.title}} ({{timeago .updated_at | autocolor "yellow+d" }})&lt;/span&gt;
        &lt;span class="s"&gt;{{print "\n  " }}{{.html_url}} {{print "\n" -}}&lt;/span&gt;
        &lt;span class="s"&gt;{{end}}'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Note that the yaml heredoc syntax (&lt;code&gt;|&lt;/code&gt;) is used to allow a multi-line string.&lt;br&gt;
Ensure the indentation is the same for all lines, or there may be problems&lt;br&gt;
parsing your config!)&lt;/p&gt;

&lt;p&gt;Now I can see all my assigned issues and PRs by running &lt;code&gt;gh mine&lt;/code&gt;!&lt;/p&gt;
&lt;h4&gt;
  
  
  Unclaimed team reviews
&lt;/h4&gt;

&lt;p&gt;Tracking issues and PRs that are waiting on the team's review (that nobody has&lt;br&gt;
claimed yet) can be done with a similar approach. Note that you'll need to update&lt;br&gt;
the search query to use your own Github Organization and Team in the query below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh api &lt;span class="nt"&gt;-XGET&lt;/span&gt; search/issues &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'team-review-requested:yourorg/yourteam is:open -review:approved no:assignee'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--template&lt;/span&gt; &lt;span class="s1"&gt;'{{range .items -}}[{{.number | printf "%4.f" |autocolor "blue" }}] {{.title}} ({{timeago .updated_at | autocolor "yellow+d" }}) {{printf "\n  " }}{{.html_url}} {{print "\n" -}}{{end}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or ready for your &lt;code&gt;config.yml&lt;/code&gt;:&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;teamreviews&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;api -XGET search/issues -f q="team-review-requested:yourorg/yourteam is:open -review:approved no:assignee"&lt;/span&gt;
        &lt;span class="s"&gt;--template '{{range .items -}}&lt;/span&gt;
        &lt;span class="s"&gt;[{{.number | printf "%4.f" |autocolor "blue" }}] {{.title}} ({{timeago .updated_at | autocolor "yellow+d" }})&lt;/span&gt;
        &lt;span class="s"&gt;{{printf "\n  " }}{{.html_url}} {{print "\n" -}}&lt;/span&gt;
        &lt;span class="s"&gt;{{end}}'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other useful searches
&lt;/h3&gt;

&lt;p&gt;By replacing the search query (the &lt;code&gt;q&lt;/code&gt; parameter) with another &lt;a href="https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests"&gt;github search&lt;br&gt;
query&lt;/a&gt;, you can use this method to display other handy results.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reviews I approved, but are not merged: &lt;code&gt;reviewed-by:@me review:approved
is:open&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Incomplete reviews I am assigned to directly: &lt;code&gt;user-review-requested:@me
-review:approved&lt;/code&gt; (note the &lt;code&gt;-&lt;/code&gt; prefix, which will match un-approved PRs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I found myself using the same display with different search queries, so I ended&lt;br&gt;
up making an alias for doing arbitrary searches:&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="c1"&gt;# global search, given a *quoted* github search string.&lt;/span&gt;
    &lt;span class="na"&gt;gsearch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;api -XGET search/issues -f q="$1"&lt;/span&gt;
        &lt;span class="s"&gt;--template '{{range .items -}}&lt;/span&gt;
        &lt;span class="s"&gt;[{{.number | printf "%4.f" |autocolor "green" }}] {{.title}} ({{timeago .updated_at | autocolor "yellow+d" }})&lt;/span&gt;
        &lt;span class="s"&gt;{{- printf "\n  " }}{{.html_url}} {{println -}}&lt;/span&gt;
        &lt;span class="s"&gt;{{end}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Future directions
&lt;/h3&gt;

&lt;p&gt;I would like to have a bit of extra information about these PRs, like whether or&lt;br&gt;
not their required checks are passing, all in the same output. To achieve this,&lt;br&gt;
I'd probably have to write a &lt;br&gt;
&lt;a href="https://docs.github.com/en/github-cli/github-cli/creating-github-cli-extensions"&gt;gh-extension&lt;/a&gt;.&lt;br&gt;
To avoid replicating existing &lt;code&gt;gh&lt;/code&gt; functionality, there's a &lt;a href="https://pkg.go.dev/github.com/cli/go-gh"&gt;go-gh&lt;br&gt;
library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Do you use other github searches or &lt;code&gt;gh&lt;/code&gt; tricks to keep tabs on your work? Tell&lt;br&gt;
me about it in the comments!&lt;/p&gt;

</description>
      <category>github</category>
    </item>
    <item>
      <title>Elgato Stream Deck with Linux</title>
      <dc:creator>Marc Dougherty</dc:creator>
      <pubDate>Fri, 21 Jan 2022 22:42:31 +0000</pubDate>
      <link>https://dev.to/muncus/elgato-stream-deck-with-linux-2664</link>
      <guid>https://dev.to/muncus/elgato-stream-deck-with-linux-2664</guid>
      <description>&lt;p&gt;With all the time I'm spending on video chat these days, I was looking for an&lt;br&gt;
easier way to manage my video setup. I was intrigued by the Elgato Stream Deck,&lt;br&gt;
with its small footprint, and fancy LCD screen buttons -- but I run linux, and&lt;br&gt;
(as usual) that requires a bit more effort.&lt;/p&gt;

&lt;p&gt;Fortunately, there is plenty of effort that's already been put in by others in&lt;br&gt;
the community. For those not looking to build something custom, there's the&lt;br&gt;
&lt;a href="https://timothycrosley.github.io/streamdeck-ui/"&gt;streamdeck ui&lt;/a&gt; which allows&lt;br&gt;
the configuration of different types of built-in actions, all configurable&lt;br&gt;
graphically.&lt;/p&gt;

&lt;p&gt;While this is a great way to get started with the Stream Deck, I found myself&lt;br&gt;
wanting more than the actions available with this tool. So I kept looking.&lt;/p&gt;

&lt;p&gt;I soon found a &lt;a href="https://opensource.com/article/20/12/stream-deck"&gt;blog post from Lorna&lt;br&gt;
Mitchell&lt;/a&gt;, and the associated&lt;br&gt;
&lt;a href="https://github.com/lornajane/streamdeck-tricks"&gt;streamdeck-tricks repo&lt;/a&gt;. Thanks&lt;br&gt;
to their thorough explanation and ample references, I started tinkering with the&lt;br&gt;
same underlying library they suggest&lt;br&gt;
(&lt;a href="http://github.com/magicmonkey/go-streamdeck"&gt;http://github.com/magicmonkey/go-streamdeck&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;I've since learned of similar library that interfaces with the Stream&lt;br&gt;
Deck (&lt;a href="https://github.com/dh1tw/streamdeck"&gt;https://github.com/dh1tw/streamdeck&lt;/a&gt;) - while it looks equally compelling,&lt;br&gt;
I had already started my work when I found this library. If you're starting&lt;br&gt;
fresh, this library appears to be more actively developed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Mental Model
&lt;/h2&gt;

&lt;p&gt;Before I get into the app, let me introduce the way I think about the stream&lt;br&gt;
deck, as this impacts what I built, as well as how the components relate to each&lt;br&gt;
other.&lt;/p&gt;

&lt;p&gt;The largest level of organization is the Stream Deck itself. This layer handles&lt;br&gt;
the physical communication with the device.&lt;/p&gt;

&lt;p&gt;When it comes to creating Buttons on the device, I expect them to share some&lt;br&gt;
resources - for example, Lorna mentions several buttons that all interact with&lt;br&gt;
their audio mixer. I chose to group this type of complexity into what I called a&lt;br&gt;
&lt;strong&gt;plugin&lt;/strong&gt;. Specific plugins are discussed below.&lt;/p&gt;

&lt;p&gt;The lowest level of organization is a &lt;strong&gt;button&lt;/strong&gt;. Buttons have an appearance,&lt;br&gt;
and generally take some action when pressed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Plugins
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Google Meet
&lt;/h3&gt;

&lt;p&gt;I spend a lot of time in Google Meet, and I sometimes forget the keystrokes for&lt;br&gt;
muting audio and video, so the first plugin I built has dedicated buttons for&lt;br&gt;
these.&lt;/p&gt;

&lt;p&gt;Prior to the streamdeck, I had used some keyboard shortcuts that called&lt;br&gt;
&lt;code&gt;xdotool&lt;/code&gt;, and the plugin basically just runs those. &lt;code&gt;xdotool&lt;/code&gt; is a bit arcane,&lt;br&gt;
but it can be very powerful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It can find windows based on their title or window class: &lt;code&gt;xdotool search
--name "Meet - "&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;It can also send key presses to those windows without stealing your mouse and
keyboard focus. Even if the window is on a different virtual desktop! This is
done by combining the &lt;code&gt;windowfocus&lt;/code&gt; and &lt;code&gt;key&lt;/code&gt; commands of &lt;code&gt;xdotool&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It &lt;em&gt;can&lt;/em&gt; move your mouse and keyboard focus to the chosen window, if desired,
using the &lt;code&gt;windowactivate&lt;/code&gt; command instead of &lt;code&gt;windowfocus&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, to mute my audio in Google Meet, i can run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;xdotool search --name "Meet -" windowfocus key ctrl+d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The streamdeck plugin just runs these commands for me, at the push of a&lt;br&gt;
dedicated button.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lights
&lt;/h3&gt;

&lt;p&gt;The next plugin I created was to control my video chat lighting, so I could turn&lt;br&gt;
it off when not in use (to decrease eye strain), and adjust brightness as&lt;br&gt;
needed. The lighting needs in my office change considerably depending on the&lt;br&gt;
weather and time of day.&lt;/p&gt;

&lt;p&gt;To achieve this, I invested in an Elgato Key Light Air. They are not the most&lt;br&gt;
affordable light, but the build quality is excellent, and after using my&lt;br&gt;
previous (cheap) option for a year, I deserved the upgrade :)&lt;/p&gt;

&lt;p&gt;The Elgato Key lights are interesting, because they are operated by making calls&lt;br&gt;
to an onboard api server in the light itself, over HTTP. This means that not&lt;br&gt;
only was a golang client readily available, but i started thinking differently&lt;br&gt;
about the streamdeck - as a way to not just run commands on my local machine,&lt;br&gt;
but to interact with APIs and other connected devices directly!&lt;/p&gt;

&lt;p&gt;The initial setup of the Elgato light was frustrating, as my dual-band wifi&lt;br&gt;
network was not visible in the setup app, and required a &lt;a href="https://dev.to/muncus/elgato-keylight-wifi-setup-workaround-3e1a"&gt;fussy&lt;br&gt;
workaround&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  New challenge: managing button state
&lt;/h4&gt;

&lt;p&gt;Now that I could control the light, I realized that i wanted the buttons to&lt;br&gt;
reflect whether the light was connected, so I would not press them if the light&lt;br&gt;
was, for example, unplugged.&lt;/p&gt;

&lt;p&gt;I first tried this out with the go-streamdeck library's &lt;code&gt;Decorator&lt;/code&gt; objects,&lt;br&gt;
which allow you to apply visual effects over the normal button appearance. I&lt;br&gt;
found that that this caused some corruption in the button appearance. In&lt;br&gt;
hindsight, this was probably just me calling button update handlers too often,&lt;br&gt;
from multiple goroutines.&lt;/p&gt;

&lt;p&gt;But before I figured that out, I removed the use of Decorators, and created my&lt;br&gt;
own ImageButton class, which implements the Button interface. It also has a&lt;br&gt;
pre-set "disabled" appearance, which greys out the button, so i can tell at a&lt;br&gt;
glance that my light is not available.&lt;/p&gt;

&lt;h3&gt;
  
  
  OBS: Open Broadcaster Software
&lt;/h3&gt;

&lt;p&gt;With all my current video chat needs addressed, it was time to tackle something&lt;br&gt;
new: &lt;a href="http://obsproject.com"&gt;OBS Studio&lt;/a&gt;. OBS Studio is popular with streamers,&lt;br&gt;
who use it to customize and combine video and audio sources. In addition to&lt;br&gt;
Recording and Streaming, OBS supports a Virtual Camera, which can be used&lt;br&gt;
transparently by most video chat software.&lt;/p&gt;

&lt;p&gt;I know OBS has a lot of capabilities, but my current desire is to have a&lt;br&gt;
"intermission"-style image replace my camera feed, while I step away briefly to&lt;br&gt;
grab a snack, or a drink, before a meeting gets started.&lt;/p&gt;

&lt;p&gt;To allow streamdeck control of OBS, i'm using the&lt;br&gt;
&lt;a href="https://obsproject.com/forum/resources/obs-websocket-remote-control-obs-studio-from-websockets.466/"&gt;obs-websockets&lt;/a&gt;&lt;br&gt;
plugin, and a &lt;a href="http://github.com/christopher-dG/go-obs-websocket"&gt;go library for&lt;br&gt;
obs&lt;/a&gt;. The only action I've&lt;br&gt;
implemented so far is to switch Scenes. If you're a more advanced user of OBS,&lt;br&gt;
you may want to use a library that is more actively maintained (like &lt;a href="https://github.com/andreykaipov/goobs"&gt;this&lt;br&gt;
one&lt;/a&gt;), to get more recent features, like&lt;br&gt;
the ability to start/stop the Virtual Camera.&lt;/p&gt;

&lt;p&gt;The scene switching buttons use the same state-tracking built for the Keylight,&lt;br&gt;
and grey out when obs is not connected. A goroutine watches the OBS connection&lt;br&gt;
in the background, and will reconnect automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;So far, the streamdeck has improved a few routine tasks for me, and provided a&lt;br&gt;
fun project. As I get more familiar with OBS, I expect I'll create more&lt;br&gt;
elaborate configurations to switch between.&lt;/p&gt;

&lt;p&gt;While I currently use the streamdeck plugged into my laptop, I also see&lt;br&gt;
opportunity for a "headless" streamdeck, attached to something like a Raspberry&lt;br&gt;
Pi Zero, which can serve as a physical interface to any connected API (e.g.&lt;br&gt;
smart home lights).&lt;/p&gt;

&lt;p&gt;Do you have a streamdeck? How do you use it? Leave a comment to let me know!&lt;/p&gt;

</description>
      <category>elgato</category>
      <category>streamdeck</category>
      <category>linux</category>
    </item>
    <item>
      <title>Elgato keylight wifi setup workaround</title>
      <dc:creator>Marc Dougherty</dc:creator>
      <pubDate>Fri, 21 Jan 2022 22:28:42 +0000</pubDate>
      <link>https://dev.to/muncus/elgato-keylight-wifi-setup-workaround-3e1a</link>
      <guid>https://dev.to/muncus/elgato-keylight-wifi-setup-workaround-3e1a</guid>
      <description>&lt;p&gt;I recently purchased an Elgato Keylight Air, and had some trouble with the&lt;br&gt;
setup. This article documents the process I went through to get it set up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;The problem I had was during setup, when my wifi network did not appear in&lt;br&gt;
Elgato's Android app (though i believe this problem also applies to other setup methods).&lt;/p&gt;

&lt;p&gt;My wifi network is dual-band, meaning it is broadcasing on both the 2.4Ghz&lt;br&gt;
spectrum (which the keylight supports), and the 5Ghz spectrum (which the&lt;br&gt;
keylight does not support). It does this using the same network information on&lt;br&gt;
both networks, and steers clients toward the best frequency for their needs.&lt;/p&gt;

&lt;p&gt;It seems that Elgato's app is &lt;em&gt;removing&lt;/em&gt; from the setup list any network that&lt;br&gt;
has a 5Ghz component, so I could not select my wifi network from the dropdown in&lt;br&gt;
the app, even though it also uses the 2.4Ghz spectrum.&lt;/p&gt;

&lt;h2&gt;
  
  
  The workaround
&lt;/h2&gt;

&lt;p&gt;The workaround was a simple bait-and-switch.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;First, I powered off my main wifi network.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using my phone's hotspot function, I created a new wifi network using the&lt;br&gt;
same network name and password as my main wifi network.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using the Elgato app at this point, I was able to configure the keylight with&lt;br&gt;
the network name and password, and it successfully connected to the phone&lt;br&gt;
wifi hotspot.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Power off the keylight, disable the phone hotspot, and restart the main wifi&lt;br&gt;
access point.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At this point, the keylight is able to join the main wifi network, because it is&lt;br&gt;
configured with the correct network name and password.&lt;/p&gt;

&lt;p&gt;This little workaround is rather annoying, and took me quite a while to figure&lt;br&gt;
out, but now that it is configured on the network, the keylight is working well&lt;br&gt;
for me!&lt;/p&gt;

</description>
      <category>howto</category>
      <category>techsupport</category>
    </item>
    <item>
      <title>Continuous Delivery of dev.to articles with Github Actions</title>
      <dc:creator>Marc Dougherty</dc:creator>
      <pubDate>Tue, 29 Dec 2020 01:05:56 +0000</pubDate>
      <link>https://dev.to/muncus/continuous-delivery-of-dev-to-articles-with-github-actions-5a63</link>
      <guid>https://dev.to/muncus/continuous-delivery-of-dev-to-articles-with-github-actions-5a63</guid>
      <description>&lt;p&gt;I'm a big proponent of Continuous Delivery: delivering value should be routine, fast, and easy. I've recently applied that mindset to my writing as well, thanks to &lt;a href="https://github.com/features/actions"&gt;Github Actions&lt;/a&gt;, and the &lt;a href="https://docs.dev.to/api"&gt;dev.to API&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;My typical workflow for writing is to start brainstorming in a text file (usually with vim or vscode), and gradually shape that into a coherent piece of writing. I prefer to write in markdown, since the format is simple enough to do by hand, and is easy to render as html, or just paste into Google Docs.&lt;/p&gt;

&lt;p&gt;The publishing part is usually where I'd get bogged down. I'd debate with myself what the right platform was, and then have to reformat to make sure links worked. What if I needed to make a correction? Repeat the process.&lt;br&gt;
Often I'd just stop working on a piece, because the process was annoying. This felt exactly like a problem Continuous Delivery could solve.&lt;/p&gt;
&lt;h2&gt;
  
  
  A writer's CI/CD with Github Actions
&lt;/h2&gt;

&lt;p&gt;My normal writing workflow starts with markdown and text files, so there was no change needed for me to keep my writing in a git repo. In fact, I had a mostly-empty repo already laying around, with some old writing already in it!&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating the Github Action
&lt;/h3&gt;

&lt;p&gt;I will admit up front that creating my own Github Action for publishing was unnecessary, and I only did it as a learning exercise. There was already &lt;a href="https://github.com/marketplace/actions/publish-to-dev-to"&gt;an action to publish to dev.to&lt;/a&gt;, but I am not much good at javascript, and wanted to understand Actions better, so I built my own: &lt;a href="http://github.com/muncus/devto-publish-action"&gt;muncus/devto-publish-action&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Golang code to call the Dev.to API is fairly straightforward, and I spent more time with the Dockerfile and the Github Actions YAML files.&lt;/p&gt;

&lt;p&gt;The binary basically takes 3 inputs: a directory full of markdown files, a dev.to api key, and a json file in which to store article ids (which allows us to re-publish the same file when there are changes).&lt;/p&gt;

&lt;p&gt;These arguments need to be declared in the &lt;code&gt;inputs&lt;/code&gt; section of &lt;a href="https://github.com/muncus/devto-publish-action/blob/master/action.yaml"&gt;action.yaml&lt;/a&gt;, and referenced in the &lt;code&gt;args&lt;/code&gt; section.&lt;/p&gt;

&lt;p&gt;As an extra bonus, I added a workflow to build and test the go code for the action as &lt;a href="https://github.com/muncus/devto-publish-action/blob/master/.github/workflows/build-go.yml"&gt;.github/workflows/build-go.yml&lt;/a&gt;. This workflow is taken from an Actions tutorial (but I forgot the source, sorry!).&lt;/p&gt;

&lt;p&gt;Now that our action repo is created and published, it is time to use it in our writing repo, by creating another workflow.&lt;/p&gt;
&lt;h3&gt;
  
  
  Calling the Action from a Workflow
&lt;/h3&gt;

&lt;p&gt;The full workflow for publishing an article has 3 steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check out my private repo containing my posts&lt;/li&gt;
&lt;li&gt;Publish the articles for dev.to&lt;/li&gt;
&lt;li&gt;Update &lt;code&gt;ids.json&lt;/code&gt; with newly published article IDs, so I can edit posts after initial publishing.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  Setting up some Secrets
&lt;/h4&gt;

&lt;p&gt;Before we can set up this workflow, we need a safe place to store two key pieces of secret information.&lt;br&gt;
&lt;a href=""&gt;Github Secrets&lt;/a&gt; is perfect for this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Github credentials to read my private repository

&lt;ul&gt;
&lt;li&gt;I created a Personal Access Token, and named it &lt;code&gt;MY_GH_PAT&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Dev.to credentials to post on my behalf.

&lt;ul&gt;
&lt;li&gt;These are created in the &lt;a href="https://dev.to/settings/account"&gt;dev.to account settings&lt;/a&gt; under "DEV API Keys".&lt;/li&gt;
&lt;li&gt;This secret is named &lt;code&gt;DEVTO_API_TOKEN&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these secrets created, we can create a workflow that references them, without exposing our precious credentials! Let's look at each step individually, and the full example is below.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 1: Check out private repo
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out repo&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@v2&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secrets.MY_GH_PAT&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This uses the built-in action to check out a repo, which accepts a &lt;code&gt;token&lt;/code&gt; input. Because I keep my writing in a private repo, we must specify an access token.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 2: Publish with our custom action
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish to Dev.to&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;muncus/devto-publish-action@release/v1&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;directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$GITHUB_WORKSPACE/dev.to/"&lt;/span&gt;
        &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secrets.DEVTO_API_TOKEN&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;state-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$GITHUB_WORKSPACE/ids.json"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This references my custom action, and provides the inputs it needs. Note that &lt;code&gt;$GITHUB_WORKSPACE&lt;/code&gt; is a built-in environment variable that is the base directory of the repo we checked out in the previous step.&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 3: Make a PR with local changes
&lt;/h4&gt;

&lt;p&gt;When my publishing action runs, it updates a "state file" which maps filenames to dev.to article IDs. This means after a new article is added, there will be local changes. To preserve those changes, we'll need to create a Pull Request from those local changes.&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create PR to update state file&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;peter-evans/create-pull-request@v3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[CI]&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Update&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;published&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;article&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ids"&lt;/span&gt;
        &lt;span class="na"&gt;reviewers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.actor }}&lt;/span&gt;
        &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secrets.MY_GH_PAT&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
        &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ci/state-file-update"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are many options for the &lt;a href="//github.com/peter-evans/create-pull-request"&gt;peter-evans/create-pull-request&lt;/a&gt; action, but lets run through the ones I'm using here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;title&lt;/code&gt;: entirely cosmetic, but this helps me approve PRs quickly when I know they're from my CI system.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reviewers&lt;/code&gt;: to clarify that whoever pushed the new article is responsible for approving the PR, they are listed as the reviewer.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;token&lt;/code&gt;: this is the github access token created above. it uses my credentials in this private repo, so all these PRs look like they were made by me. Its not ideal for a shared repo, but fine for my needs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;base&lt;/code&gt;: the branch against which to make the PR. We check out master to publish, so that's what we're comparing to.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;branch&lt;/code&gt;: using a stable PR branch name keeps merges simpler, and helps "stack up" multiple changes to the state file, in case I forget to approve a previous PR before publishing a new article.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusions
&lt;/h3&gt;

&lt;p&gt;In my experience, the hardest part of the Action development process was the trial-and-error needed to get the YAML "just right". There's still room for improvement in the Dockerfile, and I could use a pre-built image to save another 30 seconds on each article publication. But it works well enough to get us back to writing prose instead of code.&lt;/p&gt;

&lt;p&gt;I hope this inspires you to use Github Actions in a non-traditional way!&lt;/p&gt;

&lt;h3&gt;
  
  
  Appendix: full workflow yaml
&lt;/h3&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&lt;/span&gt;
&lt;span class="c1"&gt;# Only trigger this workflow on master, and a feature branch specifically for testing the workflow.&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;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;master&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;ci-action&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish&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="c1"&gt;# First, check out the private repo.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out repo&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@v2&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secrets.MY_GH_PAT&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="c1"&gt;# Then publish articles.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish to Dev.to&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;muncus/devto-publish-action@release/v1&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;directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$GITHUB_WORKSPACE/dev.to/"&lt;/span&gt;
        &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secrets.DEVTO_API_TOKEN&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;state-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$GITHUB_WORKSPACE/ids.json"&lt;/span&gt;
    &lt;span class="c1"&gt;# Last, update the state file by making a PR.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create PR to update state file&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;peter-evans/create-pull-request@v3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[CI]&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Update&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;published&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;article&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ids"&lt;/span&gt;
        &lt;span class="na"&gt;reviewers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.actor }}&lt;/span&gt;
        &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secrets.MY_GH_PAT&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
        &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ci/state-file-update"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>writing</category>
      <category>github</category>
      <category>cicd</category>
    </item>
    <item>
      <title>My Top 5 Books for DevOps/SRE</title>
      <dc:creator>Marc Dougherty</dc:creator>
      <pubDate>Sun, 29 Nov 2020 21:13:55 +0000</pubDate>
      <link>https://dev.to/muncus/my-top-5-books-for-devops-sre-564b</link>
      <guid>https://dev.to/muncus/my-top-5-books-for-devops-sre-564b</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Disclaimer: The opinions expressed here are my own, and do not represent those of my employer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I am the type of person that learns well from self-study like reading books, or watching instructional videos. I recognize that this method does not work for everyone, but if it works for you, I'd like to recommend a few books that have helped me grow in my career as a Site Reliability Engineer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: these are not affiliate links, and I have not been compensated in any way to recommend these items.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  1. &lt;a href="https://bookshop.org/books/crucial-conversations-tools-for-talking-when-stakes-are-high-second-edition-9780071771320/9780071771320"&gt;Crucial Conversations&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;You may have heard the old Operations adage "when things are going well, everyone forgets you exist". The corrolary is that we're usually involved when other teams are having a Bad Day, and tempers can flare on both sides.&lt;/p&gt;

&lt;p&gt;Crucial Conversations has helped me build the skills to better understand the needs of partner teams, and leadership, when they may not be communicating very clearly (because things are a 🗑🔥).&lt;/p&gt;

&lt;p&gt;This book has helped me identify my own defensive reactions, and build habits to help avoid them. This leads to the perception that I am keeping a cooler head during outages, even if i'm still freaking out!&lt;/p&gt;

&lt;p&gt;This is a book that I try to at least skim every year or two, because the lessons and practices that speak to me are different each time I read it.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. &lt;a href="https://bookshop.org/books/thanks-for-the-feedback-the-science-and-art-of-receiving-feedback-well/9780143127130"&gt;Thanks for the Feedback&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;This book is all about asking for feedback, and really understanding it, (regardless of how poorly it might be delivered).&lt;/p&gt;

&lt;p&gt;I'm not going to lie, this might be the hardest book i've ever read. The book illustrates difficult situations, and I often found myself identifying with the more antagonistic party.&lt;/p&gt;

&lt;p&gt;Because of that, this book had a real effect on how I treat feedback, and I certainly feel better about receiving difficult feedback than before reading this book.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. &lt;a href="https://bookshop.org/books/accelerate-the-science-of-lean-software-and-devops-building-and-scaling-high-performing-technology-organizations-9781942788331/9781942788331"&gt;Accelerate: The science of lean software and devops&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Finally, we get to the books with technical content!&lt;/p&gt;

&lt;p&gt;As an SRE, I spend a &lt;em&gt;lot&lt;/em&gt; of my time making the case for change, whether in business requirements, or in engineering time. This book provides the data to make these cases more easily, and point you at the right measurements to show the impact of the change.&lt;/p&gt;

&lt;p&gt;If you're not familiar with this book, it is based on the last 4(?) years of &lt;a href="https://cloud.google.com/devops/"&gt;DORA State of DevOps Report&lt;/a&gt; survey data, making it the largest dataset you could hope to find on the topic.&lt;/p&gt;

&lt;p&gt;The book (and to some extent the site above) lays out a set of 24 Key Capabilities, and the relationships between them. The relationships between these capabilities are sometimes obvious (e.g. Continuous Delivery drives Software Delivery Performance), and sometimes more surprising (e.g. Continuous Delivery also drives Job Satisfaction).&lt;/p&gt;

&lt;p&gt;My favorite parts are "intuitive" correlations that do not hold up in this large of a data set, e.g. having a separate QA team that owns tests &lt;strong&gt;does not&lt;/strong&gt; correlate to fewer failed changes.&lt;/p&gt;

&lt;p&gt;If you're looking to make change in your organization, this book is a must-have.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. &lt;a href="https://landing.google.com/sre/workbook/toc/"&gt;Site Reliability Workbook&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The sequel to the first SRE Book, The &lt;a href="https://landing.google.com/sre/workbook/toc/"&gt;SRE Workbook&lt;/a&gt; improves on the original by using more thorough non-Google examples, and tools and techniques available to those outside of Google. Many of the success stories come from Google customers, but since they are using industry-standard technologies, the examples are more relateable.&lt;/p&gt;

&lt;p&gt;I especially appreciate that SLOs are called out as the first step toward reliability. Reliable systems are inherently more expensive, and high-availability systems often &lt;em&gt;extremely&lt;/em&gt; so. Getting a shared understanding of reliability requirements between Engineering, Operations (or SRE), and Product (representing the Customer) records the expectations of all parties, and can make it clear that for some systems, 90% uptime (or lower) can be the Right Thing.&lt;/p&gt;

&lt;p&gt;There are several chapters about how Google has structured SRE, and how it structures SRE Engagements. I'd caution readers to be skeptical here, as these decisions have consequences on how your SRE org is able to engage with partner teams and other parts of your organization, and what worked for Google may not work for you.&lt;/p&gt;

&lt;h4&gt;
  
  
  5. &lt;a href="https://bookshop.org/books/everybody-writes-your-go-to-guide-to-creating-ridiculously-good-content/9781118905555"&gt;Everybody Writes&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;For most of us, our written communication has the largest audience. Unless you're doing a lot of public speaking, the emails and documents that we write reach many more people than our verbal communications.&lt;/p&gt;

&lt;p&gt;The author of this book works in Marketing, but the first 3/4 of this book is full of lessons that anyone can benefit from. Perhaps most importantly, it teaches us that writing well is a &lt;em&gt;learned&lt;/em&gt; skill, not an innate ability. The best way for us to improve our written communication is to do more of it!&lt;/p&gt;

&lt;p&gt;I was surprised to see a discussion of "content limits", basically maximum sizes for specific types of content (e.g. a blog post should be at most 1500 words).  This was a new concept for me, and these guidelines help me make content (from blog posts to emails) that are more accessible to the reader. (even if it does take me longer to write them!).&lt;/p&gt;

&lt;p&gt;Do you have a favorite book that's helped you grow personally or professionally?&lt;br&gt;
Tell me about it in the comments!&lt;/p&gt;

</description>
      <category>readinglist</category>
      <category>top5</category>
      <category>devops</category>
      <category>sre</category>
    </item>
  </channel>
</rss>
