Skip to main content

On a Prettier PATH

·877 words·5 mins·
bash CI GitHub is
Table of Contents
❤️ It's great to see you here! I'm currently available on evenings and weekends for consulting and freelance work. Let's chat about how I can help you achieve your goals.

Knowing about your ENV variables can be really helpful. Knowing how to make them more readable can be even more helpful. Today we are going to look at some strategies for making env variables (and $PATH in particular) easier to read and easier to reason about. We’ll also touch on how to install tools like ubi and is in GitHub CI and take a quick peek at how using $GITHUB_STEP_SUMMARY can provide a quality of life improvement for those of us looking at CI logs.

As I run through various scenarios, I will use a Docker container so that we have a minimal environment. My real life environment is way messier. I won’t burden you with that, so let’s see what the Docker env looks like by default:

The Default env
#

$ env
HOSTNAME=ada083235895
SHLVL=1
HOME=/root
GOTOOLCHAIN=local
TERM=xterm
PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
GOPATH=/go
PWD=/workspace
GOLANG_VERSION=1.24.4

I see a couple of problems. First off, it’s hard for me to see what I want when things aren’t sorted. Let’s sort our env.

The Sorted env
#

$ env | sort
GOLANG_VERSION=1.24.4
GOPATH=/go
GOTOOLCHAIN=local
HOME=/root
HOSTNAME=ada083235895
PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/workspace
SHLVL=1
TERM=xterm

This is already better, but I have to say that the PATH is not easy to to read. I don’t want to spend a lot of time staring at it. Can we do better? Well, we can isolate the variable to start with.

The Split PATH
#

$ echo $PATH
/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

What if we could split on the colon? That would help.

$ echo $PATH | tr : '\n'
/go/bin
/usr/local/go/bin
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin

A pretty path

A pretty path

"Forrest H. Dutlinger Natural Area (Revisit) (2)" by Nicholas_T is licensed under CC BY 2.0 .

A Tabular env
#

Fortunately we’re getting somewhere. Unfortunately I’ve now run out of patience with shell scripting. It turns out that is has us covered, though. Let’s see the ENV in a tabular format.

$ is known summary var
┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
┃ Name           ┃ Value             ┃
┣━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━┫
┃ GOLANG_VERSION ┃ 1.24.4            ┃
┣━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━┫
┃ GOPATH         ┃ /go               ┃
┣━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━┫
┃ GOTOOLCHAIN    ┃ local┣━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━┫
┃ HOME           ┃ /root             ┃
┣━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━┫
┃ HOSTNAME       ┃ ada083235895      ┃
┣━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━┫
┃ PATH           ┃ /go/bin           ┃
┃                ┃ /usr/local/go/bin ┃
┃                ┃ /usr/local/sbin   ┃
┃                ┃ /usr/local/bin    ┃
┃                ┃ /usr/sbin         ┃
┃                ┃ /usr/bin          ┃
┃                ┃ /sbin             ┃
┃                ┃ /bin              ┃
┣━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━┫
┃ PWD            ┃ /workspace        ┃
┣━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━┫
┃ SHLVL          ┃ 1┣━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━┫
┃ TERM           ┃ xterm             ┃
┗━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━┛

This feels like an improvement to me. We have a sortend ENV with aligned columns and our $PATH is split on newlines, making it easier to distinguish between similar paths like /usr/local/bin and /usr/local/sbin.

A JSON env
#

Maybe we want do do something programmatic with ENV or maybe the tabular layout does not look good in our terminal logging. We do have another option: JSON.

$ is known summary var --json
{
    "GOLANG_VERSION": "1.24.4",
    "GOPATH": "/go",
    "GOTOOLCHAIN": "local",
    "HOME": "/root",
    "HOSTNAME": "ada083235895",
    "PATH": [
        "/go/bin",
        "/usr/local/go/bin",
        "/usr/local/sbin",
        "/usr/local/bin",
        "/usr/sbin",
        "/usr/bin",
        "/sbin",
        "/bin"
    ],
    "PWD": "/workspace",
    "SHLVL": "1",
    "TERM": "xterm"
}

Cool! Now I can get the first item in my PATH via jq.

$ is known summary var --json | jq .PATH[0]
"/go/bin"

Logging env in CI via Markdown
#

I’d love to emit this in my CI logs so that I can get debugging info on my environment. Let’s install is into our container in GitHub Actions:

---
jobs:
  linux:
    runs-on: ubuntu-latest
    steps:
      - name: Install is
        uses: oalders/install-ubi-action@v0.0.6
        with:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          projects: oalders/is

      - name: Display summaries
        run: |
          is known summary os  --md >> $GITHUB_STEP_SUMMARY
          is known summary var --md >> $GITHUB_STEP_SUMMARY          

What happened?

  • We used oalders/install-ubi-action to install ubi, which is the “Universal Binary Installer”. ubi allows us to install is without needing to care about which release we need to download and install.
  • We installed is
  • We emitted a summary for the os and for the environment variables
  • The summary was added to the $GITHUB_STEP_SUMMARY environment variable
  • We used a flag which has just recently been added to is: --md

The --md flag allows us to emit data in a Markdown table format. Why would we want to do this? Well, it turns out that GitHub logs are not always easy to read, and they can be constrained by width. So, if you have some really long paths in your environment, tabular data becomes close to unreadable.

GitHub offers the $GITHUB_STEP_SUMMARY variable as a nice solution to this. If markdown ends up in this variable, it gets rendered in an HTML summary of that build step. This means you get way more flexibility in how the data is rendered. There’s also the bonus that you don’t have to scroll through giant log files to find it.

CI

And our PATH looks great:

CI

Also, I can now embed the table directly into this blog post without needing to do any manual conversion to Markdown.

is known summary var --md
NameValue
GOLANG_VERSION1.24.4
GOPATH/go
GOTOOLCHAINlocal
HOME/root
HOSTNAME44db2d2f4c32
PATH/go/bin
/usr/local/go/bin
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
PWD/workspace
SHLVL1
TERMxterm

So Long!
#

I think I’m not quite done with adding new commands to is, but for today I’m done with writing about it. 😅


Related

How Many Versions of "X" Are in my $PATH?
·506 words·3 mins
is bash cli
Battery Power-Ups: Enhancing SketchyBar with "is"
·1042 words·5 mins
is battery bash dotfiles cli Go macos sketchybar
is: an inspector for your environment
·1273 words·6 mins
Go bash dotfiles is