diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..da6b047 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,39 @@ +# .github/workflows/release.yml +name: goreleaser + +on: + push: + # run only against tags + tags: + - "v*" + +permissions: + contents: write + # packages: write + # issues: write + # id-token: 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 + # More assembly might be required: Docker logins, GPG, etc. + # It all depends on your needs. + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + # either 'goreleaser' (default) or 'goreleaser-pro' + distribution: goreleaser + # 'latest', 'nightly', or a semver + version: "~> v2" + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tagged-release.yaml b/.github/workflows/tagged-release.yaml deleted file mode 100644 index 4da4a28..0000000 --- a/.github/workflows/tagged-release.yaml +++ /dev/null @@ -1,34 +0,0 @@ -name: "tagged-release" - -on: - push: - tags: - - "v*" - -permissions: write-all - -jobs: - tagged-release: - name: "Tagged Release" - runs-on: "ubuntu-latest" - - steps: - - name: Install Go - uses: actions/setup-go@v2 - - - name: Checkout code - uses: actions/checkout@v2 - with: - persist-credentials: false - - - name: Build - run: | - sudo apt-get install pandoc --yes - make arch - - - uses: "marvinpinto/action-automatic-releases@latest" - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - prerelease: false - files: | - arch/*.tar.gz diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..3484a36 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,63 @@ +# yaml-language-server: $schema=https://goreleaser.com/static/schema.json +# vim: set ts=2 sw=2 tw=0 fo=cnqoj + +version: 2 + +before: + hooks: + # You may remove this if you don't use go modules. + - go mod tidy + # you may remove this if you don't need go generate + - go generate ./... + +builds: + - env: + - CGO_ENABLED=0 + goos: + - linux + - windows + - darwin + ldflags: + - -s -w -X main.Build={{.Version}} + +archives: + - format: tar.gz + files: + - LICENSE + - README.md + # use zip for windows archives + format_overrides: + - goos: windows + format: zip + +upx: + - enabled: true + compress: best + +nfpms: + - package_name: rpn + homepage: https://github.com/marcopaganini/rpn + maintainer: Marco Paganini + description: |- + A simple but useful CLI RPN calculator. + license: MIT + formats: + - apk + - deb + - rpm + - archlinux + provides: + - rpn + bindir: /usr/bin + contents: + - src: README.md + dst: /usr/share/doc/rpn/README.md + - src: LICENSE + dst: /usr/share/doc/rpn/LICENSE.txt + +changelog: + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" diff --git a/Makefile b/Makefile index ef016b0..cd907f1 100644 --- a/Makefile +++ b/Makefile @@ -10,29 +10,11 @@ git_tag := $(shell git describe --always --tags) # Default target ${bin}: Makefile ${src} - CGO_ENABLED=0 go build -v -ldflags "-X main.BuildVersion=${git_tag}" -o "${bin}" + CGO_ENABLED=0 go build -v -ldflags "-X main.Build=${git_tag}" -o "${bin}" clean: rm -f "${bin}" rm -f "docs/${bin}.1" - rm -rf "${archdir}" install: ${bin} install -m 755 "${bin}" "${bindir}" - -# Creates cross-compiled tarred versions (for releases). -arch: Makefile ${src} - for ga in "linux/amd64" "linux/386" "linux/arm64" "linux/arm" "darwin/amd64" "darwin/arm64"; do \ - export GOOS="$${ga%/*}"; \ - export GOARCH="$${ga#*/}"; \ - dst="./${archdir}/$${GOOS}-$${GOARCH}"; \ - mkdir -p "$${dst}"; \ - echo "=== Building $${GOOS}/$${GOARCH} ==="; \ - CGO_ENABLED=0 go build -v -ldflags "-X main.BuildVersion=${git_tag}" -o "$${dst}/${bin}"; \ - [ -s LICENSE ] && install -m 644 LICENSE "$${dst}"; \ - [ -s README.md ] && install -m 644 README.md "$${dst}"; \ - [ -s dist/install.sh ] && install -m 755 dist/install.sh "$${dst}"; \ - [ -s docs/${bin}.1 ] && install -m 644 docs/${bin}.1 "$${dst}"; \ - tar -C "${archdir}" -zcvf "${archdir}/${bin}-$${GOOS}-$${GOARCH}.tar.gz" "$${dst##*/}"; \ - rm -rf "$${dst}"; \ - done diff --git a/README.md b/README.md index 09f9632..d766a52 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,23 @@ Similar CLI/TUI applications exist, but most (if not all) of them focus on being There are a few ways to install **termotp**: -### Download from releases +### Direct download from releases -To download and install the latest release, cut and paste the following shell command: +To download and install the latest release into `/usr/local/bin` (requires +root), cut and paste the following shell command: ```bash -wget -q -O/tmp/install \ - 'https://raw.githubusercontent.com/marcopaganini/installer/master/install.sh' && \ - sudo sh /tmp/install marcopaganini/termotp +curl -s \ + 'https://raw.githubusercontent.com/marcopaganini/termotp/master/install.sh' | + sudo sh -s -- marcopaganini/termotp +``` + +To download and install the latest release into another directory (say, `~/.local/bin`): + +```bash +curl -s \ + 'https://raw.githubusercontent.com/marcopaganini/termotp/master/install.sh' | + sh -s -- marcopaganini/termotp "${HOME}/.local/bin" ``` ### Compile and install yourself @@ -43,6 +52,11 @@ make sudo make install ``` +### Linux packages + +You'll also find packages for multiple distributions (DEB/RPM/APK files, etc) in the +[Releases](https://github.com/marcopaganini/termotp/releases/) are of the repository. + ## Usage The basic usage is: diff --git a/dist/install.sh b/dist/install.sh deleted file mode 100755 index db2cce2..0000000 --- a/dist/install.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -# Installer for termotp. -# This script is bundled with the releases and should not be executed directly. - -set -eu - -readonly PREFIX="/usr/local" - -die() { - echo >&2 "${*}" - exit 1 -} - -main() { - uid="$(id -u)" - if [ "${uid}" -ne 0 ]; then - die "Please run this program as root (using sudo)." - fi - - bindir="${PREFIX}/bin" - - mkdir -p "${bindir}" - cp termotp "${bindir}" - chmod 755 "${bindir}/termotp" -} - -main "${@}" diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..57b5b92 --- /dev/null +++ b/install.sh @@ -0,0 +1,116 @@ +#!/bin/sh +# See http://github.com/marcopaganini/termotp +# for details on how to use this script. + +set -eu + +readonly DEFAULT_INSTALL_DIR="/usr/local/bin" +readonly PROGRAM="${0##*/}" +readonly OK="✅" +readonly ERR="❌" + +# go_arch retrieves the go equivalent architecture for this machine. +go_arch() { + arch="$(uname -m)" + case "${arch}" in + aarch64_be|aarch64|arm64|armv8b|armv8l) + echo "arm64" ;; + arm) + echo "arm" ;; + i386|i686) + echo "386" ;; + mips) + echo "mips" ;; + mips64) + echo "mips64" ;; + s390|s390x) + echo "s390x" ;; + x86_64) + echo "amd64" ;; + esac +} + +# os_name returns the os_name. +os_name() { + os="$(uname -o)" + # Windows not supported for now. + case "${os}" in + "GNU/Linux") + echo "linux" ;; + "Darwin") + echo "darwin" ;; + esac +} + +# tempname returns a temporary directory name. This is a crude workaround for +# systems that don't have mktemp and rudimentary date commands. +tempname() { + echo "/tmp/${PROGRAM}-$$" +} + +die() { + echo >&2 "${ERR} ${*}" + exit 1 +} + +main() { + # First argument = github username/repo. + if [ $# -lt 1 ]; then + die "Use: install.sh username/repo [destination_dir]" + fi + + # Second argument (optional) installation directory. + install_dir="${DEFAULT_INSTALL_DIR}" + if [ $# -eq 2 ]; then + install_dir="${2}" + fi + + readonly repo="${1}" + readonly release_name="${repo##*/}" + readonly releases_url="https://api.github.com/repos/${repo}/releases" + + os="$(os_name)" + arch="$(go_arch)" + + [ -z "${os}" ] && die "Unknown OS. Please send the result of 'uname -o' and 'uname -m' to the author." + [ -z "${arch}" ] && die "Unknown processor architecture. Please send the result of 'uname -m' to the author." + + echo "${OK} Your OS is: ${os}" + echo "${OK} Your architecture is: ${arch}" + echo "${OK} Install directory: ${install_dir}" + + tgz="${release_name}_[0-9].[0-9].[0-9]_${os}_${arch}.tar.gz" + + # Retrieve the list releases from github (this is hacky but we don't + # want to force people to have a proper JSON parser installed.) + latest="$(wget -q -O- "${releases_url}" | grep browser_download_url | grep -o "https://.*${tgz}" | sort -g | tail -1)" + [ -z "${latest}" ] && die "Unable to find any releases." + echo "${OK} Latest release: ${latest}" + + # Download and install + tmp="$(tempname)" + out="${tmp}/${release_name}.tar.gz" + + rm -rf "${tmp}" + mkdir -p "${tmp}" + echo "${OK} Downloading latest release." + if ! wget -q "${latest}" -O "${out}"; then + die "Error downloading: ${latest}" + fi + + cwd="$(pwd)" + cd "${tmp}" + echo "${OK} Unpacking local installation file." + if ! gzip -d -c "${out}" | tar x; then + die "Error unpacking local release ($out)." + fi + + mv "${tmp}/${release_name}" "${install_dir}" || die "Installation failed." + chmod 755 "${install_dir}/${release_name}" + + cd "${cwd}" + rm -rf "${tmp}" + echo "${OK} Installation finished! Please make sure ${install_dir} is in your PATH." +} + +main "${@}" diff --git a/main.go b/main.go index da86580..b83e387 100644 --- a/main.go +++ b/main.go @@ -22,9 +22,9 @@ import ( "github.com/zalando/go-keyring" ) -// BuildVersion holds the current git head version number. +// Build holds the current git head version number. // this is filled in by the build process (make). -var BuildVersion string +var Build string // otpEntry holds the representation of the internal vault. type otpEntry struct { @@ -181,7 +181,7 @@ func parseFlags() (cmdLineFlags, error) { } if flags.version { - fmt.Printf("Build Version: %s\n", BuildVersion) + fmt.Printf("Build Version: %s\n", Build) os.Exit(0) }