search
HomeBackend DevelopmentGolangPublishing CLI Apps (with Apt & YUM)

Publishing CLI Apps (with Apt & YUM)

Intro

I’ve gotten to become quite the fan of CLI apps as of late. Maybe it’s the allure of the terminal of my childhood (starting with DOS on a 486/33 and my dad’s old Apple IIe). I was born a little too late for the Gen X Commodore64 era, but just in time to know more then just Windows 95. It was interesting era, back when dial up and 56k modems were king. I know most blog posts these days have intro fluff, to pad out word count for SEO, but this really is even why I still love the CLI when so many the younger folks these days only know GUI apps. Nothing makes me happier then to see Gen Z kids fire up the terminal, even for simple tasks. Man, wait till Gen Alpha finds out what a BBS is. “Grandpa computers” they’ll probably say ?. “GET OFF MY LAWN” ✊✊

Projects like CoolRetroTerm definitely have a warm place in my heart, for brining back the CLI love. I still prefer to do some of my blogging in Micro on my old Netbook, really let’s you concentrate on just writing. VSCode ZenMode and MarkText come close I guess?


flowchart LR
Build_App --> GH_Actions --> ??? --> Profit!!!



Packaging

Anyway, I digress…

So after writing my little CLI app Stampy I ran into a small problem, how to distribute it? I was at least smart enough to think a head an write it in GoLang (as much as I wanted to just build it in Python) to avoid the dreaded wrath of Python Packaging. One thing that has always stumped me was, how folks publish their nice CLI apps to fancy package management systems such as APT and YUM.

Normally to get your app built you would just do a simple go build . and boom, instant binary. As great as this is for local dev, it doesn’t do much good for cross platform compiles. There are some nice guides to show you how to do it, but… tl;dr for my ??. So I did some more digging, there had to be a nice tool… and sure as heck, there is GoReleaser!

After reading through some very well written documentation, I was able to do a quick local cross platform build, easy-peasy.


goreleaser --snapshot --clean



Getting builds to happen with GitHub releases was easy as well, as they have nice pre-written GH Actions!

Users can now just install my app with tools like eget (good) and stew (way better)!

While you can also go install github.com/xxx all this would be doing is clone the repo, build it locally, and put the bin in your $GOBIN folder. Not really the same as proper package management tooling, but does work in a pinch for folks who already have Go installed. Not really a option for the average user IMHO. ?

And not only that, GoReleaser offers packaging as well! So now you can easily craft DEBs and RPMs. I was one step closer to the scared apt-get install stampy. The only thing missing was how to create a APT repo. This last key part is not easy for sure. I spent a hour or so looking into how to self host this with GitHub Pages, and while it is doable, it was far easier to just use a free service like Packagecloud to handle the signing and repo hosting for the low low cost of $0 per month?.

You can see a example of the whole workflow HERE

I’ll also include a stripped down version of it here in a code block, for anybody stumbling across the blog post itself.

For a high level overview the GHA does the following:

  • Writes out he GoReleaser config
  • Runs the Releaser itself
  • Uploads the .debs for the next job
  • In chained job, we pull the .deb and upload it to PackageCloud
  • Done!

GitHub Actions Example


name: Release

on:
  pull_request:
  push:
    # run only against tags
    tags:
      - "*"

permissions:
  contents: write
  packages: write
  issues: write

jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: stable

      - name: Release config
        run: |
          cat  /tmp/goreleaser-github.yaml
          project_name: EXAMPLE
          version: 2
          builds:
            - env: [CGO_ENABLED=0]
              goos:
                - linux
              goarch:
                - amd64
          nfpms:
            -
              maintainer: YOU <your>
              bindir: /usr/local/bin
              description: Copy formatted timestamp to system clipboard
              homepage: https://github.com/USERNAME/REPO
              license: MIT
              formats:
                - deb

          release:
            draft: false # If set to true, will not auto-publish the release.
            replace_existing_draft: true
            replace_existing_artifacts: true
            target_commitish: "{{ .Commit }}"
            prerelease: auto
            make_latest: true
            mode: replace
            include_meta: true
          EOF          

      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v6
        with:
          distribution: goreleaser
          # 'latest', 'nightly', or a semver
          version: "~> v2"
          args: release --clean --verbose --config /tmp/goreleaser-github.yaml
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Upload .deb artifact x86
        uses: actions/upload-artifact@v3
        with:
          name: deb-package
          path: dist/*amd64.deb

  pkgcld_amd64-deb:
    runs-on: ubuntu-latest
    needs:
      - goreleaser
    strategy:
      max-parallel: 3
      matrix:
        distro:
        - debian/bookworm
        - ubuntu/noble
        - ubuntu/jammy
    steps:
      - name: Download .deb artifact
        uses: actions/download-artifact@v3
        with:
          name: deb-package

      - name: Push package to packagecloud.io
        uses: computology/packagecloud-github-action@v0.6
        with:
          package-name: ./*.deb
          packagecloud-username: USERNAME
          packagecloud-reponame: APP_NAME
          packagecloud-distro: ${{ matrix.distro }}
          packagecloud-token: ${{ secrets.PACKAGECLOUD_TOKEN }}



</your>

ℹ️ Important

You’ll want to make sure things like program structure and your go.mod file are properly setup, or you’re going to run into issues with publishing your app properly.

Side note: You can also distribute your app with Homebrew, but I didn’t bother due to the extra GH Actions complexity involving PAT secrets and the fact that I’m pretty well covered with Apt, Yum, and Stew… tasty! ?

This leads me to the big second thing when releasing a app. ?DOCUMENTATION? and the much neglected Readme.md?!

Readme Formatting

There are a few elements that I feel like any decent readme should have, as they will help your app stand out from all the apps with little to no documentation, or worse yet, bad documentation.

I highly recommend you follow this format for crafting your own readme! I’m a big fan of badges for flair, but I feel like having a little GIF demo really shows folks what it’s about, just like listing screenshots of your GUI apps. Using ASCIINEMA was easy enough, and they have a nice GIF converter as well to get everything looking just right.

? Tip

As a side note I did have CodeGPT write me some GoLang unit tests, which I know are normally painful to write. It’s a fantastic plugin if you’re on the JetBrains suite.

Readme Example

  • Test badges
  • GIF Demo
  • Coverage badges
  • Go Report card
  • Install
    • Package manager (ex apt)
    • Binary install (ex eget)
    • Go Install snip
  • Usage
    • CLEAR INSTRUCTIONS and example code snips
  • Settings
    • Where settings get saved
    • Do you use INI files, JSON, Env Vars?
  • How to access built-in help
  • How to build the app from source
  • Prior art (aka previous work/inspiration)

Wrap up

Similar to when I set out to learn how to publish Python apps, I’m glad to be able to say I feel like I can properly distribute any app I write in GoLang going forward. It’s a neat skill that I picked up, and with this blog post, I hope it can help others do the same! Cheers!

-Jelloeater


? Mastodon | ? Email | ? Comments | ☕ Buy me a coffee

The above is the detailed content of Publishing CLI Apps (with Apt & YUM). For more information, please follow other related articles on the PHP Chinese website!

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
String Manipulation in Go: Mastering the 'strings' PackageString Manipulation in Go: Mastering the 'strings' PackageMay 14, 2025 am 12:19 AM

Mastering the strings package in Go language can improve text processing capabilities and development efficiency. 1) Use the Contains function to check substrings, 2) Use the Index function to find the substring position, 3) Join function efficiently splice string slices, 4) Replace function to replace substrings. Be careful to avoid common errors, such as not checking for empty strings and large string operation performance issues.

Go 'strings' package tips and tricksGo 'strings' package tips and tricksMay 14, 2025 am 12:18 AM

You should care about the strings package in Go because it simplifies string manipulation and makes the code clearer and more efficient. 1) Use strings.Join to efficiently splice strings; 2) Use strings.Fields to divide strings by blank characters; 3) Find substring positions through strings.Index and strings.LastIndex; 4) Use strings.ReplaceAll to replace strings; 5) Use strings.Builder to efficiently splice strings; 6) Always verify input to avoid unexpected results.

'strings' Package in Go: Your Go-To for String Operations'strings' Package in Go: Your Go-To for String OperationsMay 14, 2025 am 12:17 AM

ThestringspackageinGoisessentialforefficientstringmanipulation.1)Itofferssimpleyetpowerfulfunctionsfortaskslikecheckingsubstringsandjoiningstrings.2)IthandlesUnicodewell,withfunctionslikestrings.Fieldsforwhitespace-separatedvalues.3)Forperformance,st

Go bytes package vs strings package: Which should I use?Go bytes package vs strings package: Which should I use?May 14, 2025 am 12:12 AM

WhendecidingbetweenGo'sbytespackageandstringspackage,usebytes.Bufferforbinarydataandstrings.Builderforstringoperations.1)Usebytes.Bufferforworkingwithbyteslices,binarydata,appendingdifferentdatatypes,andwritingtoio.Writer.2)Usestrings.Builderforstrin

How to use the 'strings' package to manipulate strings in Go step by stepHow to use the 'strings' package to manipulate strings in Go step by stepMay 13, 2025 am 12:12 AM

Go's strings package provides a variety of string manipulation functions. 1) Use strings.Contains to check substrings. 2) Use strings.Split to split the string into substring slices. 3) Merge strings through strings.Join. 4) Use strings.TrimSpace or strings.Trim to remove blanks or specified characters at the beginning and end of a string. 5) Replace all specified substrings with strings.ReplaceAll. 6) Use strings.HasPrefix or strings.HasSuffix to check the prefix or suffix of the string.

Go strings package: how to improve my code?Go strings package: how to improve my code?May 13, 2025 am 12:10 AM

Using the Go language strings package can improve code quality. 1) Use strings.Join() to elegantly connect string arrays to avoid performance overhead. 2) Combine strings.Split() and strings.Contains() to process text and pay attention to case sensitivity issues. 3) Avoid abuse of strings.Replace() and consider using regular expressions for a large number of substitutions. 4) Use strings.Builder to improve the performance of frequently splicing strings.

What are the most useful functions in the GO bytes package?What are the most useful functions in the GO bytes package?May 13, 2025 am 12:09 AM

Go's bytes package provides a variety of practical functions to handle byte slicing. 1.bytes.Contains is used to check whether the byte slice contains a specific sequence. 2.bytes.Split is used to split byte slices into smallerpieces. 3.bytes.Join is used to concatenate multiple byte slices into one. 4.bytes.TrimSpace is used to remove the front and back blanks of byte slices. 5.bytes.Equal is used to compare whether two byte slices are equal. 6.bytes.Index is used to find the starting index of sub-slices in largerslices.

Mastering Binary Data Handling with Go's 'encoding/binary' Package: A Comprehensive GuideMastering Binary Data Handling with Go's 'encoding/binary' Package: A Comprehensive GuideMay 13, 2025 am 12:07 AM

Theencoding/binarypackageinGoisessentialbecauseitprovidesastandardizedwaytoreadandwritebinarydata,ensuringcross-platformcompatibilityandhandlingdifferentendianness.ItoffersfunctionslikeRead,Write,ReadUvarint,andWriteUvarintforprecisecontroloverbinary

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

VSCode Windows 64-bit Download

VSCode Windows 64-bit Download

A free and powerful IDE editor launched by Microsoft

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

Integrate Eclipse with SAP NetWeaver application server.

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

Powerful PHP integrated development environment