”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 发布 CLI 应用程序(使用 Apt 和 YUM)

发布 CLI 应用程序(使用 Apt 和 YUM)

发布于2024-11-08
浏览:197

Publishing CLI Apps (with Apt & YUM)

简介

我最近非常喜欢 CLI 应用程序。也许这是我童年时期终端的诱惑(从 486/33 上的 DOS 和我父亲的旧 Apple IIe 开始)。对于 X 代 Commodore64 时代,我出生得有点晚,但正好赶上了了解 Windows 95 之外的更多内容。那是一个有趣的时代,当时拨号和 56k 调制解调器还是王者。我知道现在大多数博客文章都有一些介绍性的内容,以增加 SEO 的字数,但这确实是为什么我仍然喜欢 CLI,因为现在很多年轻人只知道 GUI 应用程序。看到 Z 世代的孩子们打开终端,即使是为了简单的任务,没有什么比这更让我高兴的了。伙计,等阿尔法一代知道什么是 BBS 吧。他们可能会说“电脑爷爷”? “离开我的草坪”✊✊

像 CoolRetroTerm 这样的项目在我心里肯定有一个温暖的地方,因为它带回了对 CLI 的热爱。我仍然更喜欢在我的旧上网本上用 Micro 写一些博客,真的让你专注于写作。我猜 VSCode ZenMode 和 MarkText 很接近?


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



包装

不管怎样,我离题了……

所以在编写我的小 CLI 应用程序 Stampy 后,我遇到了一个小问题,如何分发它?我至少足够聪明,能够思考并用 GoLang 编写它(就像我想用 Python 构建它一样)以避免 Python 包装的可怕愤怒。一直困扰我的一件事是,人们如何将他们漂亮的 CLI 应用程序发布到 APT 和 YUM 等精美的包管理系统。

通常要构建您的应用程序,您只需执行简单的 go build 即可。和繁荣,即时二进制。尽管这对于本地开发来说很棒,但对于跨平台编译却没有多大好处。有一些很好的指南可以向您展示如何做到这一点,但是... tl;dr 我的??。所以我做了更多的挖掘,必须有一个不错的工具......当然,有 GoReleaser!

阅读了一些写得很好的文档后,我能够快速进行本地跨平台构建,非常简单。


goreleaser --snapshot --clean



通过 GitHub 版本进行构建也很容易,因为它们有很好的预先编写的 GH 操作!

用户现在可以使用 eget(好)和 Steute(更好)等工具安装我的应用程序!

虽然您也可以安装 github.com/xxx,但要做的就是克隆存储库,在本地构建它,然后将 bin 放入您的 $GOBIN 文件夹中。与正确的包管理工具并不完全相同,但对于已经安装了 Go 的人来说确实有用。恕我直言,对于普通用户来说这并不是一个真正的选择。 ?

不仅如此,GoReleaser 还提供包装!现在您可以轻松制作 DEB 和 RPM。我离害怕的 apt-get install stampy 又近了一步。唯一缺少的是如何创建 APT 存储库。这最后一个关键部分肯定并不容易。我花了一个小时左右的时间研究如何使用 GitHub Pages 自行托管它,虽然这是可行的,但使用像 Packagecloud 这样的免费服务来处理签名和存储库托管要容易得多,而且成本低廉,每次 0 美元月?。

您可以在此处查看整个工作流程的示例

我还将在代码块中包含它的精简版本,供任何偶然发现博客文章本身的人使用。

为了获得高层次的概述,GHA 执行以下操作:

  • 写出 GoReleaser 配置
  • 运行发布程序本身
  • 上传下一个作业的 .debs
  • 在链式作业中,我们提取 .deb 并将其上传到 PackageCloud
  • 完毕!

GitHub 操作示例


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 
              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/[email protected]
        with:
          package-name: ./*.deb
          packagecloud-username: USERNAME
          packagecloud-reponame: APP_NAME
          packagecloud-distro: ${{ matrix.distro }}
          packagecloud-token: ${{ secrets.PACKAGECLOUD_TOKEN }}




ℹ️重要

您需要确保程序结构和 go.mod 文件等内容正确设置,否则您将在正确发布应用程序时遇到问题。

旁注:您还可以使用 Homebrew 分发您的应用程序,但我没有打扰,因为涉及 PAT 秘密的额外 GH 操作复杂性以及我已经很好地覆盖了 Apt好吃,还有炖菜……好吃! ?

这让我想到了发布应用程序时的第二件事。 ?文档? 以及备受忽视的 Readme.md?!

自述文件格式

我觉得任何像样的自述文件都应该具备一些元素,因为它们将帮助您的应用程序从所有几乎没有文档或更糟糕的文档的应用程序中脱颖而出。

我强烈建议您遵循这种格式来制作您自己的自述文件!我是天赋徽章的忠实粉丝,但我觉得有一个小 GIF 演示确实可以向人们展示它的含义,就像列出 GUI 应用程序的 屏幕截图 一样。使用 ASCIINEMA 非常简单,而且他们还有一个很好的 GIF 转换器,让一切看起来都恰到好处。

?提示

顺便说一句,我确实让 CodeGPT 给我写了一些 GoLang 单元测试,我知道编写这些测试通常很痛苦。如果您使用 JetBrains 套件,这是一个很棒的插件。

自述文件示例

  • 测试徽章
  • GIF 演示
  • 覆盖徽章
  • 去报告卡
  • 安装
    • 包管理器(ex apt)
    • 二进制安装(例如 eget)
    • 去安装片段
  • 用法
    • 清晰的说明和示例代码片段
  • 设置
    • 设置保存位置
    • 你使用 INI 文件、JSON、环境变量吗?
  • 如何访问内置帮助
  • 如何从源代码构建应用程序
  • 现有技术(又名以前的工作/灵感)

包起来

与我开始学习如何发布 Python 应用程序时类似,我很高兴能够说我觉得我可以正确分发我在 GoLang 中编写的任何应用程序。这是我学到的一项巧妙的技能,通过这篇博文,我希望它可以帮助其他人做同样的事情!干杯!

-Jelloeater


?乳齿象 | ?电子邮件 | ?评论 | ☕ 请我喝杯咖啡

版本声明 本文转载于:https://dev.to/jelloeater/publishing-cli-apps-with-apt-yum-568c?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何使用“ JSON”软件包解析JSON阵列?
    如何使用“ JSON”软件包解析JSON阵列?
    parsing JSON与JSON软件包 QUALDALS:考虑以下go代码:字符串 } func main(){ datajson:=`[“ 1”,“ 2”,“ 3”]`` arr:= jsontype {} 摘要:= = json.unmarshal([] byte(...
    编程 发布于2025-05-22
  • Go语言如何动态发现导出包类型?
    Go语言如何动态发现导出包类型?
    与反射软件包中的有限类型的发现能力相反,本文探索了替代方法,探索了在Runruntime。go import( “ FMT” “去/进口商” ) func main(){ pkg,err:= incorter.default()。导入(“ time”) 如果err...
    编程 发布于2025-05-22
  • 在C#中如何高效重复字符串字符用于缩进?
    在C#中如何高效重复字符串字符用于缩进?
    在基于项目的深度下固定字符串时,重复一个字符串以进行凹痕,很方便有效地有一种有效的方法来返回字符串重复指定的次数的字符串。使用指定的次数。 constructor 这将返回字符串“ -----”。 字符串凹痕= new String(' - ',depth); console.Wr...
    编程 发布于2025-05-22
  • eval()vs. ast.literal_eval():对于用户输入,哪个Python函数更安全?
    eval()vs. ast.literal_eval():对于用户输入,哪个Python函数更安全?
    称量()和ast.literal_eval()中的Python Security 在使用用户输入时,必须优先确保安全性。强大的Python功能Eval()通常是作为潜在解决方案而出现的,但担心其潜在风险。 This article delves into the differences betwee...
    编程 发布于2025-05-22
  • 用户本地时间格式及时区偏移显示指南
    用户本地时间格式及时区偏移显示指南
    在用户的语言环境格式中显示日期/时间,并使用时间偏移在向最终用户展示日期和时间时,以其localzone and格式显示它们至关重要。这确保了不同地理位置的清晰度和无缝用户体验。以下是使用JavaScript实现此目的的方法。方法:推荐方法是处理客户端的Javascript中的日期/时间格式化和时...
    编程 发布于2025-05-22
  • 如何在Chrome中居中选择框文本?
    如何在Chrome中居中选择框文本?
    选择框的文本对齐:局部chrome-inly-ly-ly-lyly solument 您可能希望将文本中心集中在选择框中,以获取优化的原因或提高可访问性。但是,在CSS中的选择元素中手动添加一个文本 - 对属性可能无法正常工作。初始尝试 state)</option> < op...
    编程 发布于2025-05-22
  • 如何使用PHP将斑点(图像)正确插入MySQL?
    如何使用PHP将斑点(图像)正确插入MySQL?
    essue VALUES('$this->image_id','file_get_contents($tmp_image)')";This code builds a string in PHP, but the function call ...
    编程 发布于2025-05-22
  • 如何使用Regex在PHP中有效地提取括号内的文本
    如何使用Regex在PHP中有效地提取括号内的文本
    php:在括号内提取文本在处理括号内的文本时,找到最有效的解决方案是必不可少的。一种方法是利用PHP的字符串操作函数,如下所示: 作为替代 $ text ='忽略除此之外的一切(text)'; preg_match('#((。 &&& [Regex使用模式来搜索特...
    编程 发布于2025-05-22
  • 如何使用不同数量列的联合数据库表?
    如何使用不同数量列的联合数据库表?
    合并列数不同的表 当尝试合并列数不同的数据库表时,可能会遇到挑战。一种直接的方法是在列数较少的表中,为缺失的列追加空值。 例如,考虑两个表,表 A 和表 B,其中表 A 的列数多于表 B。为了合并这些表,同时处理表 B 中缺失的列,请按照以下步骤操作: 确定表 B 中缺失的列,并将它们添加到表的末...
    编程 发布于2025-05-22
  • 为什么PHP的DateTime :: Modify('+1个月')会产生意外的结果?
    为什么PHP的DateTime :: Modify('+1个月')会产生意外的结果?
    使用php dateTime修改月份:发现预期的行为在使用PHP的DateTime类时,添加或减去几个月可能并不总是会产生预期的结果。正如文档所警告的那样,“当心”这些操作的“不像看起来那样直观。 考虑文档中给出的示例:这是内部发生的事情: 现在在3月3日添加另一个月,因为2月在2001年只有2...
    编程 发布于2025-05-22
  • 为什么尽管有效代码,为什么在PHP中捕获输入?
    为什么尽管有效代码,为什么在PHP中捕获输入?
    在php ;?>" method="post">The intention is to capture the input from the text box and display it when the submit button is clicked.但是,输出...
    编程 发布于2025-05-22
  • 使用jQuery如何有效修改":after"伪元素的CSS属性?
    使用jQuery如何有效修改":after"伪元素的CSS属性?
    在jquery中了解伪元素的限制:访问“ selector 尝试修改“:”选择器的CSS属性时,您可能会遇到困难。 This is because pseudo-elements are not part of the DOM (Document Object Model) and are th...
    编程 发布于2025-05-22
  • 在Python中如何创建动态变量?
    在Python中如何创建动态变量?
    在Python 中,动态创建变量的功能可以是一种强大的工具,尤其是在使用复杂的数据结构或算法时,Dynamic Variable Creation的动态变量创建。 Python提供了几种创造性的方法来实现这一目标。利用dictionaries 一种有效的方法是利用字典。字典允许您动态创建密钥并分...
    编程 发布于2025-05-22
  • 如何使用Python有效地以相反顺序读取大型文件?
    如何使用Python有效地以相反顺序读取大型文件?
    在python 中,如果您使用一个大文件,并且需要从最后一行读取其内容,则在第一行到第一行,Python的内置功能可能不合适。这是解决此任务的有效解决方案:反向行读取器生成器 == ord('\ n'): 缓冲区=缓冲区[:-1] ...
    编程 发布于2025-05-22
  • Python环境变量的访问与管理方法
    Python环境变量的访问与管理方法
    Accessing Environment Variables in PythonTo access environment variables in Python, utilize the os.environ object, which represents a mapping of envir...
    编程 发布于2025-05-22

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3