<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>emre şahin's digital garden 🍃 - gh</title>
    <link>https://emresahin.net/tags/gh/</link>
    <description>Posts in the gh tag</description>
    <language>en</language>
    <managingEditor>contact@emresahin.net (Emre Şahin)</managingEditor>
    <lastBuildDate>Tue, 15 Sep 2026 19:46:32 +0000</lastBuildDate>
    <atom:link href="https://emresahin.net/tags/gh/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>My Nushell aliases for `gh` command</title>
      <published>2025-01-29T08:04:19+00:00</published>
      <updated>2025-01-29T08:04:19+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Wed, 29 Jan 2025 08:04:19 +0000</pubDate>
      <link>https://emresahin.net/my-nushell-aliases-for--gh--command/</link>
      <guid isPermaLink="true">https://emresahin.net/my-nushell-aliases-for--gh--command/</guid>
      <description>Almost every day, I start my work by playing with my configuration. Today, I added aliases for gh (GitHub CLI) to Nushell. # List issues related to me export def ghil [] { (gh issue list --search "involves:iesahin" --json title,url) | from json } # Add a comment to an issue export alias ghic = gh...</description>
      <category>Shell</category>
      <category>Nushell</category>
      <category>nushell</category>
      <category>gh</category>
      <category>GitHub</category>
      <category>CLI</category>
      <content:encoded><![CDATA[<p>Almost every day, I start my work by playing with my configuration. Today, I added aliases for <code>gh</code> (GitHub CLI) to Nushell.</p>
<pre><code class="language-nu"># List issues related to me
export def ghil [] { 
  (gh issue list --search "involves:iesahin" --json title,url) | from json
}

# Add a comment to an issue
export alias ghic = gh issue comment

# Create an issue
export alias ghiC = gh issue create $in

# View an issue
export alias ghiv = gh issue view

# View an issue in the browser
export alias ghiw = gh issue view --web

# List PRs in a table
export def ghpl [] {
  (gh pr list --json "title,url,headRefName" ) | from json
}

# View a PR
export alias ghpv = gh pr view

# View a PR in GitHub
export alias ghpw = gh pr view --web

# Checkout a PR
export alias ghco = gh pr checkout

# Add a comment to a PR
export alias ghpc = gh pr comment

# Show changes in a PR
export alias ghpd = gh pr diff

# Add a review to a PR
export alias ghpR = gh pr review

# Create a PR
export alias ghpC = gh pr create

# Merge a PR to main
export alias ghpM = gh pr merge

# List GH CI runs in a table
export def ghrl [] {
  (gh run list --json conclusion,displayTitle,headBranch,url ) | from json 
}

# View a GH CI run
export alias ghrv = gh run view

# View a GH CI run in the browser
export alias ghrw = gh run view --web 

# View output from a failed run
export alias ghrf = gh run view --log-failed

# Search code and get results in a table
export def ghsc [] { 
  (gh search code $in --json repository,path,textMatches,url) | from json
}
</code></pre>
<p>Aliases don’t allow pipes in Nushell, so I used <code>def</code> for those that require them.</p>
<p>When there is an <code>$in</code> parameter in an alias, it expects the input from a pipe.
An issue can be created like this, for example:</p>
<pre><code class="language-nu">"Your software sucks" | ghiC
</code></pre>
<p>To search for the code copied to the clipboard and get only the URLs, you can use:</p>
<pre><code class="language-nu">pbpaste | ghsc | get url
</code></pre>
<p>To open them in different browser windows:</p>
<pre><code class="language-nu">pbpaste | ghsc | get url | each { |u| start $u }
</code></pre>
<p>I’ll add a shortcut key for this last one. It’s so quick! :D</p>]]></content:encoded>
    </item>
    <item>
      <title>Devlog 11: Homebrew Taps and Automation</title>
      <published>2025-01-19T09:37:37+00:00</published>
      <updated>2025-01-19T09:37:37+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 19 Jan 2025 09:37:37 +0000</pubDate>
      <link>https://emresahin.net/devlog-11/</link>
      <guid isPermaLink="true">https://emresahin.net/devlog-11/</guid>
      <description>🐢 Let’s move slowly. Let’s create a brew project first. 🦊 There is a brew create command, but could we really copy it from someone else? 🐢 I read the espanso example , and I’ll start by adding a repository. Welcome https://github.com/iesahin/homebrew-xvc 🦊 We can search to automate this for Rust ...</description>
      <category>Devlog</category>
      <category>XVC</category>
      <category>Homebrew</category>
      <category>homebrew</category>
      <category>espanso</category>
      <category>ripgrep</category>
      <category>gh</category>
      <category>wget</category>
      <category>github-actions</category>
      <category>rust</category>
      <content:encoded><![CDATA[<p>🐢 Let’s move slowly. Let’s create a brew project first.</p>
<p>🦊 There is a <code>brew create</code> command, but could we really copy it from someone else?</p>
<p>🐢 I read the <a href="https://federicoterzi.com/blog/how-to-publish-your-rust-project-on-homebrew/">espanso example</a>, and I’ll start by adding a repository. Welcome https://github.com/iesahin/homebrew-xvc</p>
<p>🦊 We can search to automate this for Rust stuff first. Maybe it’s easier to automate than to do it manually with examples.</p>
<p>🐢 Let’s search “how to automate homebrew tap formula updates with github actions”</p>
<p>🐲 There is a <a href="https://josh.fail/2023/automate-updating-custom-homebrew-formulae-with-github-actions/">shell script</a> that updates the brew repo with a shell script. It fires the action from the main repository with a command like:</p>
<pre><code>gh workflow run release.yml -f version=${{ env.VERSION }} -R itspriddle/homebrew-slack-notify
</code></pre>
<p>🐢 It looks a little brittle, though.</p>
<p>🐲 There is also a GitHub action: https://github.com/marketplace/actions/homebrew-tap but it doesn’t look very popular. There is another one https://github.com/marketplace/actions/bump-homebrew-formula but this is for formulas, or the default settings are those.</p>
<p>🐢 There is an example https://github.com/marketplace/actions/bump-homebrew-formula#examples for taps as well.</p>
<p>🐲 This one is simpler, and probably we can just add this first: https://github.com/marketplace/actions/homebrew-bump-formula Most of the fields are optional.</p>
<p>🐇 I think this is much easier than the other. Let’s start with this.</p>
<p>🐢 Now let’s add a token to the Xvc repo. ✅</p>
<p>🦊 Add a branch to Xvc and add the release action file.</p>
<p>🐢 Added the release action file <code>~/github.com/iesahin/xvc/.github/workflows/homebrew.yml</code>, but we still don’t have a tap; maybe we can just add one.</p>
<p>🦊 Can you check espanso’s for example?</p>
<p>🐢 It has a cask and is in the core. We need a tap example.</p>
<p>🐲 I believe we need to clone <code>homebrew-xvc</code> and run <code>gh workflow run</code>.</p>
<p>🐢 Yes, let’s clone the repo and try to run it manually.</p>
<p>🐇 We don’t have a formula yet.</p>
<p>🦊 What’s the directory structure of a Homebrew tap project?</p>
<p>🐢 It’s something like this, but we don’t need tests for this, I believe.</p>
<pre><code>.
├── Formula
│   └── &lt;formula_name&gt;.rb
├── LICENSE
├── README.md
└── .github
    └── workflows
        └── tests.yml
</code></pre>
<p>🐇 What goes into the Formula?</p>
<p>🦊 Let’s take a look at ripgrep’s example:</p>
<pre><code class="language-ruby">class Ripgrep &lt; Formula
  desc "Search tool like grep and The Silver Searcher"
  homepage "https://github.com/BurntSushi/ripgrep"
  url "https://github.com/BurntSushi/ripgrep/archive/refs/tags/14.1.1.tar.gz"
  sha256 "4dad02a2f9c8c3c8d89434e47337aa654cb0e2aa50e806589132f186bf5c2b66"
  license "Unlicense"
  head "https://github.com/BurntSushi/ripgrep.git", branch: "master"

  livecheck do
    url :stable
    strategy :github_latest
  end

  bottle do
    sha256 cellar: :any,                 arm64_sequoia:  "b8bf5e73c9c9b441de067ec86ac167b071ecc2078dcb1d89d2cebbb151feab35"
    sha256 cellar: :any,                 arm64_sonoma:   "47b9c3515c866b147f0e98735cab165d6471b9f28fab1ba2c57e59c43da5c10b"
    sha256 cellar: :any,                 arm64_ventura:  "e14a94e84c028ff53c1be3b106fdeb5aca4d7c893a819e7fb967e0719b946a28"
    sha256 cellar: :any,                 arm64_monterey: "ad8dc4ab475c84e2a1e60f5b3107f52dd59e33f84a08284b19681d8b98508fd7"
    sha256 cellar: :any,                 sonoma:         "71d434eeabc2af220285b037f7264563ce9bc77a41af35eabe2213276a37ec2b"
    sha256 cellar: :any,                 ventura:        "0cdb547c696992d08c6613c40934218964f4a061b5413c4b2f013c3f0c3ed253"
    sha256 cellar: :any,                 monterey:       "2ce54302e4524ad28389aca5a16333d4193128e911de2881e6b0e953559d89cd"
    sha256 cellar: :any_skip_relocation, x86_64_linux:   "97d7cbd33b4d0ed09551e3dbc07f830d3df018c2aefbb2222a12ccfb829aae30"
  end

  depends_on "asciidoctor" =&gt; :build
  depends_on "pkgconf" =&gt; :build
  depends_on "rust" =&gt; :build
  depends_on "pcre2"

  def install
    system "cargo", "install", "--features", "pcre2", *std_cargo_args

    generate_completions_from_executable(bin/"rg", "--generate", shell_parameter_format: "complete-")
    (man1/"rg.1").write Utils.safe_popen_read(bin/"rg", "--generate", "man")
  end

  test do
    (testpath/"Hello.txt").write("Hello World!")
    system bin/"rg", "Hello World!", testpath
  end
end
</code></pre>
<p>🐇 What’s in that tar file?</p>
<pre><code>wget https://github.com/BurntSushi/ripgrep/archive/refs/tags/14.1.1.tar.gz 
--2025-01-03 06:28:07--  https://github.com/BurntSushi/ripgrep/archive/refs/tags/14.1.1.tar.gz
Resolving github.com (github.com)... 140.82.121.3
Connecting to github.com (github.com)|140.82.121.3|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://codeload.github.com/BurntSushi/ripgrep/tar.gz/refs/tags/14.1.1 [following]
--2025-01-03 06:28:08--  https://codeload.github.com/BurntSushi/ripgrep/tar.gz/refs/tags/14.1.1
Resolving codeload.github.com (codeload.github.com)... 140.82.121.10
Connecting to codeload.github.com (codeload.github.com)|140.82.121.10|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/x-gzip]
Saving to: ‘14.1.1.tar.gz’

     0K .......... .......... .......... .......... ..........  355K
    50K .......... .......... .......... .......... .......... 1.05M
   100K .......... .......... .......... .......... .......... 1.35M
   150K .......... .......... .......... .......... .......... 3.61M
   200K .......... .......... .......... .......... ..........  876K
   250K .......... .......... .......... .......... .......... 6.50M
   300K .......... .......... .......... .......... .......... 1.15M
   350K .......... .......... .......... .......... .......... 1.65M
   400K .......... .......... .......... .......... .......... 4.88M
   450K .......... .......... .......... .......... .......... 1.22M
   500K .......... .......... .......... .......... .......... 1.56M
   550K .......... .......                                     3.62M=0.5s

2025-01-03 06:28:09 (1.21 MB/s) - ‘14.1.1.tar.gz’ saved [581402]

mv 14.1.1.tar.gz $HOME/Downloads/ripgrep-14.1.1.tar.gz

tar xvzf $HOME/Downloads/ripgrep-14.1.1.tar.gz 
</code></pre>
<p>🐢 It’s a source distribution. It doesn’t contain any binaries.</p>
<p>🦊 We can use the same URL format, it looks. Let’s try this:</p>
<pre><code>wget https://github.com/iesahin/xvc/archive/refs/tags/0.6.13.tar.gz 
--2025-01-03 06:34:00--  https://github.com/iesahin/xvc/archive/refs/tags/0.6.13.tar.gz
Resolving github.com (github.com)... 140.82.121.4
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://codeload.github.com/iesahin/xvc/tar.gz/refs/tags/0.6.13 [following]
--2025-01-03 06:34:01--  https://codeload.github.com/iesahin/xvc/tar.gz/refs/tags/0.6.13
Resolving codeload.github.com (codeload.github.com)... 140.82.121.9
Connecting to codeload.github.com (codeload.github.com)|140.82.121.9|:443... connected.
HTTP request sent, awaiting response... 404 Not Found
2025-01-03 06:34:01 ERROR 404: Not Found.
</code></pre>
<p>🐢 Our source should be in another location.</p>
<p>🐇 We had an unpublished release. Didn’t we release the binaries a few days ago?</p>
<p>🐢 Umm, there must be something about the <code>gh</code> command where we set <code>draft=false</code>, but seemingly it didn’t work out.</p>
<p>🐇 Ok, the URL is something like:</p>
<pre><code>wget https://github.com/iesahin/xvc/archive/refs/tags/v0.6.13.tar.gz 
</code></pre>
<p>🐢 Ok, this works. We’ll use this one for the URL.</p>
<p>🐇 What should we put for SHA256?</p>
<p>🦊 Let’s check ripgrep’s again.</p>
<p>🐢 Downloading and using <code>xvc file hash</code> (alias <code>xvcfh</code>):</p>
<pre><code>wget https://github.com/BurntSushi/ripgrep/archive/refs/tags/14.1.1.tar.gz 
...

xvc file hash -a sha2 14.1.1.tar.gz
4dad02a2f9c8c3c8d89434e47337aa654cb0e2aa50e806589132f186bf5c2b66	14.1.1.tar.gz
</code></pre>
<p>🐢 Yep, this matches the source.</p>
<p>🦊 Then we can just use the same.</p>
<p>🐇 I want to learn how to download to <code>$TMPDIR</code> with <code>wget</code>.</p>
<p>🐢 The <code>-P</code> option is used for this.</p>
<pre><code class="language-sh">wget -P $TMPDIR https://github.com/iesahin/xvc/archive/refs/tags/v0.6.13.tar.gz 
--2025-01-03 06:55:02--  https://github.com/iesahin/xvc/archive/refs/tags/v0.6.13.tar.gz
Resolving github.com (github.com)... 140.82.121.4
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://codeload.github.com/iesahin/xvc/tar.gz/refs/tags/v0.6.13 [following]
--2025-01-03 06:55:03--  https://codeload.github.com/iesahin/xvc/tar.gz/refs/tags/v0.6.13
Resolving codeload.github.com (codeload.github.com)... 140.82.121.9
Connecting to codeload.github.com (codeload.github.com)|140.82.121.9|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/x-gzip]
Saving to: ‘/var/folders/gf/18gghtz15_zf0cr8v6dymglw0000gn/T/v0.6.13.tar.gz’
...
2025-01-03 06:55:07 (2.59 MB/s) - ‘/var/folders/gf/18gghtz15_zf0cr8v6dymglw0000gn/T/v0.6.13.tar.gz’ saved [8785449]
</code></pre>
<pre><code class="language-sh">xvcfh -a sha2 $TMPDIR/v0.6.13.tar.gz
01bee5d840eefec7be1f52cc75546e1ffd7e332dfac83d875655f84e61e3a9f6	/var/folders/gf/18gghtz15_zf0cr8v6dymglw0000gn/T//v0.6.13.tar.gz
</code></pre>
<p>🐇 According to documentation, bottles are produced by Brew itself, and the documentation around bottling taps is limited.</p>
<p>🦊 We can just start with the source distribution. We can test it now.</p>
<p>🐢 The source distribution seems to work, but it requires Rust, and Brew downloads everything related to it.</p>
<p>🐇 I think that may be enough for the time being. With 0.6.14, we can work on providing bottles.</p>
<p>🐢 Umm, yes. I’m already bored with this stuff.</p>]]></content:encoded>
    </item>
  </channel>
</rss>
