"일꾼이 일을 잘하려면 먼저 도구를 갈고 닦아야 한다." - 공자, 『논어』.
첫 장 > 프로그램 작성 > CLI 앱 게시(Apt 및 YUM 사용)

CLI 앱 게시(Apt 및 YUM 사용)

2024-11-08에 게시됨
검색:161

Publishing CLI Apps (with Apt & YUM)

소개

저는 최근 CLI 앱의 팬이 되었습니다. 어쩌면 그것은 내 어린 시절의 터미널(486/33의 DOS와 아버지의 오래된 Apple IIe로 시작)의 매력일 수도 있습니다. 나는 Gen X Commodore64 시대에 비해 너무 늦게 태어났지만 Windows 95보다 더 많은 것을 알게 된 시기였습니다. 다이얼 업과 56k 모뎀이 왕이었던 시절은 흥미로운 시대였습니다. 요즘 대부분의 블로그 게시물에는 SEO에 대한 단어 수를 추가하기 위한 소개가 포함되어 있다는 것을 알고 있지만, 요즘 많은 젊은 사람들이 GUI 앱만 알고 있는데 이것이 제가 여전히 CLI를 좋아하는 이유이기도 합니다. Z 세대 아이들이 간단한 작업에도 터미널을 작동시키는 것을 보는 것보다 더 행복한 것은 없습니다. 이봐, Gen Alpha가 BBS가 무엇인지 알 때까지 기다리세요. “할아버지 컴퓨터”라고 그들은 아마도 ?라고 말할 것입니다. “내 잔디밭에서 벗어나세요” ✊✊

CoolRetroTerm과 같은 프로젝트는 CLI에 대한 사랑을 다시 불러일으키는 데 있어 제 마음속에 따뜻한 자리를 차지하고 있습니다. 나는 여전히 내 오래된 넷북에서 Micro로 블로그 활동을 하는 것을 선호합니다. 글쓰기에만 집중하겠습니다. VSCode ZenMode와 MarkText가 가까워진 것 같은데요?


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



포장

어쨌든 빗나갔어...

그래서 작은 CLI 앱인 Stampy를 작성한 후 작은 문제에 부딪혔습니다. 어떻게 배포해야 할까요? 나는 최소한 Python Packaging의 무서운 분노를 피하기 위해 머리가 GoLang에서 작성한다고 생각할 만큼 똑똑했습니다(Python으로 빌드하고 싶었던 만큼). 저를 항상 당황하게 했던 것 중 하나는 사람들이 멋진 CLI 앱을 APT 및 YUM과 같은 멋진 패키지 관리 시스템에 게시하는 방법이었습니다.

일반적으로 앱을 빌드하려면 간단한 go build를 수행하면 됩니다. 그리고 붐, 인스턴트 바이너리. 로컬 개발에는 좋지만 크로스 플랫폼 컴파일에는 별로 좋지 않습니다. 이를 수행하는 방법을 보여주는 몇 가지 좋은 가이드가 있지만… 그래서 좀 더 자세히 조사해 보니 좋은 도구가 있어야 하더군요... 물론 GoReleaser가 있습니다!

매우 잘 작성된 문서를 읽은 후 빠르고 쉽게 로컬 크로스 플랫폼 빌드를 수행할 수 있었습니다.


goreleaser --snapshot --clean



GitHub 릴리스를 사용하여 빌드를 수행하는 것도 쉬웠습니다. GitHub 릴리스에 미리 작성된 멋진 GH Action이 있기 때문입니다!

이제 사용자는 eget(좋음) 및 스튜(훨씬 더 좋음)와 같은 도구를 사용하여 내 앱을 설치할 수 있습니다.

github.com/xxx를 설치할 수도 있지만 이 작업은 저장소를 복제하고 로컬로 빌드한 다음 $GOBIN 폴더에 저장소를 넣는 것뿐입니다. 적절한 패키지 관리 도구와 실제로 동일하지는 않지만 이미 Go를 설치한 사람들에게는 긴급하게 작동합니다. 일반 사용자 IMHO에게는 실제로 옵션이 아닙니다. ?

그뿐만 아니라 GoReleaser는 포장도 제공합니다! 이제 DEB와 RPM을 쉽게 만들 수 있습니다. 나는 겁에 질린 apt-get install stampy에 한 걸음 더 가까워졌습니다. 누락된 유일한 것은 APT 저장소를 생성하는 방법이었습니다. 이 마지막 핵심 부분은 확실히 쉽지 않습니다. GitHub 페이지를 사용하여 이를 자체 호스팅하는 방법을 알아보는 데 한 시간 정도 소요되었으며, 가능하기는 하지만 Packagecloud와 같은 무료 서비스를 사용하여 당 $0의 저렴한 비용으로 서명 및 리포지토리 호스팅을 처리하는 것이 훨씬 쉬웠습니다. 월?.

여기에서 전체 워크플로의 예를 볼 수 있습니다.

또한 블로그 게시물 자체를 우연히 발견하는 분들을 위해 여기에 코드 블록에 간단한 버전을 포함하겠습니다.

GHA는 높은 수준의 개요를 위해 다음을 수행합니다.

    GoReleaser 구성을 작성합니다.
  • 릴리서 자체를 실행합니다.
  • 다음 작업을 위해 .debs를 업로드합니다.
  • 체인 작업에서는 .deb를 가져와서 PackageCloud에 업로드합니다.
  • 완료!
GitHub 작업 예

이름: 릴리스 에: 풀 요청: 푸시: # 태그에 대해서만 실행 태그: - "*" 권한: 내용: 쓰다 패키지: 쓰기 문제: 쓰기 직업: 고릴리서: 실행: 우분투 최신 단계: - 이름 : 체크아웃 용도: actions/checkout@v4 와 함께: 가져오기 깊이: 0 - 이름 : Go 설정 용도: actions/setup-go@v5 와 함께: go 버전: 안정 - 이름: 릴리스 구성 실행: | 고양이 /tmp/goreleaser-github.yaml project_name: 예 버전: 2 빌드: - 환경: [CGO_ENABLED=0] 구스: - 리눅스 고아치: - amd64 nfpms: - 관리자: 귀하 바인딩: /usr/local/bin 설명: 형식화된 타임스탬프를 시스템 클립보드에 복사합니다. 홈페이지: https://github.com/USERNAME/REPO 라이센스: MIT 형식: - 뎁 풀어 주다: 초안: false # true로 설정하면 릴리스가 자동으로 게시되지 않습니다. 교체_기존_초안: true 교체_기존_아티팩트: true target_commitish: "{{ .커밋 }}" 시험판: 자동 make_latest: 사실 모드: 교체 include_meta: 사실 EOF - 이름: GoReleaser 실행 용도: goreleaser/goreleaser-action@v6 와 함께: 배포: 고릴리서 # '최신', '야간' 또는 semver 버전: "~> v2" 인수: release --clean --verbose --config /tmp/goreleaser-github.yaml 환경: GITHUB_TOKEN: ${{ 비밀.GITHUB_TOKEN }} - 이름: .deb 아티팩트 x86 업로드 용도: actions/upload-artifact@v3 와 함께: 이름: deb-패키지 경로: dist/*amd64.deb pkgcld_amd64-deb: 실행: 우분투 최신 필요: - 고릴리서 전략: 최대 병렬: 3 행렬: 배포판: - 데비안/책벌레 - 우분투/노블 - 우분투/잼미 단계: - 이름: .deb 아티팩트 다운로드 용도: actions/download-artifact@v3 와 함께: 이름: deb-패키지 - 이름: packagecloud.io에 패키지 푸시 용도: 계산학/[email protected] 와 함께: 패키지 이름: ./*.deb packagecloud-사용자 이름: USERNAME packagecloud-reponame: APP_NAME packagecloud-distro: ${{ 매트릭스.distro }} packagecloud-token: ${{ secrets.PACKAGECLOUD_TOKEN }}

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 Actions 복잡성과

Apt로 꽤 잘 다루어지고 있기 때문에 신경쓰지 않았습니다. 냠냠 그리고 스튜… 맛있어요! ?

이것은 앱을 출시할 때 중요한 두 번째 문제로 이어집니다.

?문서? 그리고 많이 무시되는 Readme.md?!

읽어보기 형식 지정

괜찮은 추가 정보에는 문서가 거의 또는 전혀 없거나 더 나쁘게도 잘못된 문서가 없어도 모든 앱 중에서 앱이 돋보일 수 있도록 도와주는 몇 가지 요소가 있습니다.

자신만의 Readme를 작성하려면 이 형식을 따르는 것이 좋습니다! 저는 멋진 배지를 좋아하지만 GUI 앱의

스크린샷을 나열하는 것과 마찬가지로 작은 GIF 데모를 통해 사람들에게 그 내용을 실제로 보여줄 수 있다고 생각합니다. ASCIINEMA를 사용하는 것은 충분히 쉬웠으며 모든 것을 올바르게 표시할 수 있는 멋진 GIF 변환기도 있습니다.

? 팁

부담으로 저는 CodeGPT에서 GoLang 단위 테스트를 작성해 달라고 요청했는데, 일반적으로 작성하기 힘든 작업이었습니다. JetBrains 제품군을 사용하고 있다면 환상적인 플러그인입니다.

읽어보기 예

    테스트 배지
  • GIF 데모
  • 보장 배지
  • 성적표 바로가기
  • 설치하다
    • 패키지 관리자(예: 적절)
    • 바이너리 설치(예: eget)
    • 스닙 설치로 이동
  • 용법
    • 지침 및 예제 코드 캡처
  • 설정
    • 설정이 저장되는 위치
    • INI 파일, JSON, Env Vars를 사용하시나요?
  • 내장 도움말에 액세스하는 방법
  • 소스에서 앱을 빌드하는 방법
  • 선행예술(일명 이전 작품/영감)
마무리

Python 앱을 게시하는 방법을 배우기 시작했을 때와 마찬가지로 앞으로 GoLang에서 작성하는 모든 앱을 제대로 배포할 수 있을 것 같다고 말할 수 있어서 기쁩니다. 이는 제가 습득한 훌륭한 기술이며, 이 블로그 게시물을 통해 다른 사람들도 같은 작업을 수행하는 데 도움이 되기를 바랍니다! 건배!

-젤로이터


? 마스토돈 | ? 이메일 | ? 댓글 | 😄 커피 한잔 사주세요

릴리스 선언문 이 글은 https://dev.to/jelloeater/publishing-cli-apps-with-apt-yum-568c?1에서 복제됩니다.1 침해 내용이 있는 경우, [email protected]으로 연락하여 삭제하시기 바랍니다.
최신 튜토리얼 더>

부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.

Copyright© 2022 湘ICP备2022001581号-3