diff --git a/.editorconfig b/.editorconfig index a4b1aa8..de212fc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,3 +15,6 @@ trim_trailing_whitespace = false [*.yml] indent_size = 2 + +[GNUmakefile] +indent_style = tab diff --git a/.fpm b/.fpm index b25e880..befdd08 100644 --- a/.fpm +++ b/.fpm @@ -1,4 +1,5 @@ -s dir +-f --name mommy --license unlicense --architecture all diff --git a/.github/img/demo.gif b/.github/img/demo.gif index 052fb64..477ea08 100644 Binary files a/.github/img/demo.gif and b/.github/img/demo.gif differ diff --git a/.github/img/demo.sh b/.github/img/demo.sh index c5c95d1..9cb83c4 100755 --- a/.github/img/demo.sh +++ b/.github/img/demo.sh @@ -8,8 +8,9 @@ # * Resize window to 80x20 # * Hide console border # 3. In SimpleScreenRecorder, select console window, and change width to 1900 and height to 800 -# 4. Start recording, activate this script with global shortcut, then stop recording -# 5. Create a GIF with `ffmpeg -i simplescreenrecorder*.mkv -vf "fps=24,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 demo.gif` +# 4. Open `sh` shell +# 5. Start recording, activate this script with global shortcut, then stop recording +# 6. Create a GIF with `ffmpeg -y -i ~/Videos/simplescreenrecorder*.mkv -vf "fps=24,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 ./.github/img/demo.gif` set -e @@ -17,12 +18,12 @@ echo "Waiting 1 second before starting" sleep "1" echo "Run faulty command" -xdotool type --delay 100 "mommy ./trest.sh" +xdotool type --delay 100 "mommy make tesr" sleep "1.5" xdotool key Return sleep "2" echo "Run working command" -xdotool type --delay 100 "mommy ./test.sh" +xdotool type --delay 100 "mommy make test" sleep "1.5" xdotool key Return diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index b062756..7e7c4fe 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -4,43 +4,13 @@ on: push: branches: - main - pull_request: - branches: - - main + workflow_dispatch: permissions: contents: write packages: read jobs: - create-release: - runs-on: ubuntu-latest - needs: [build-linux, build-macos, build-freebsd, build-netbsd, build-openbsd] - if: github.ref == 'refs/heads/main' # For other branches, artifacts are attached to the workflow run as `dist.zip` - - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Download built packages - uses: actions/download-artifact@v3 - with: - name: dist - - name: Extract version number - run: echo "VERSION=v$(head -n 1 ./version)" >> $GITHUB_ENV - - name: Extract release notes - id: extract-release-notes - uses: ffurrer2/extract-release-notes@v1 - with: - release_notes_file: RELEASE_NOTES.md - - name: Create release - uses: softprops/action-gh-release@v1 - with: - draft: true - prerelease: false - tag_name: ${{ env.VERSION }} - body_path: RELEASE_NOTES.md - files: mommy* - build-linux: runs-on: ubuntu-latest steps: @@ -50,8 +20,8 @@ jobs: run: | sudo apt install -y rubygems libarchive-tools rpm zstd sudo gem install --no-document fpm - - name: Build package - run: ./build.sh apk deb rpm pacman + - name: Build packages + run: make dist/apk dist/deb dist/rpm dist/pacman - name: Upload built package uses: actions/upload-artifact@v3 with: @@ -66,7 +36,7 @@ jobs: - name: Install fpm run: sudo gem install --no-document fpm - name: Build package - run: ./build.sh osxpkg + run: make dist/osxpkg - name: Upload built package uses: actions/upload-artifact@v3 with: @@ -83,6 +53,10 @@ jobs: with: usesh: true prepare: | + echo "::group::Install basic packages" + pkg install -y git gmake || exit 1 + echo "::endgroup::" + # fpm echo "::group::Install fpm: Actually install fpm" pkg install -y devel/ruby-gems || exit 1 @@ -97,13 +71,12 @@ jobs: # /fpm echo "::group::Ignore ownership issues" - pkg install -y git || exit 1 git config --global --add safe.directory "$GITHUB_WORKSPACE" || exit 1 echo "::endgroup::" run: | set -e - ./build.sh freebsd + gmake dist/freebsd - name: Upload built package uses: actions/upload-artifact@v3 with: @@ -122,6 +95,10 @@ jobs: prepare: | set -e + echo "::group::Install basic packages" + pkg_add git gmake || exit 1 + echo "::endgroup::" + echo "::group::Install fpm" pkg_add ruby /usr/pkg/bin/gem* install --no-document fpm @@ -129,14 +106,13 @@ jobs: echo "::endgroup::" echo "::group::Ignore ownership issues" - pkg_add git git config --global --add safe.directory "$GITHUB_WORKSPACE" echo "::endgroup::" run: | set -e export PATH="/usr/sbin:$PATH" # Add `pkg_*` commands to path - ./build.sh netbsd + gmake dist/netbsd - name: Upload built package uses: actions/upload-artifact@v3 with: @@ -155,6 +131,10 @@ jobs: prepare: | set -e + echo "::group::Install basic packages" + pkg_add git gmake + echo "::endgroup::" + echo "::group::Install fpm" pkg_add "$(pkg_info -Q ruby | grep "^ruby-[0-9]" | tail -n 1)" /usr/local/bin/gem* install --no-document fpm @@ -162,15 +142,161 @@ jobs: echo "::endgroup::" echo "::group::Ignore ownership issues" - pkg_add git git config --global --add safe.directory "$GITHUB_WORKSPACE" echo "::endgroup::" run: | set -e - ./build.sh openbsd + gmake dist/openbsd - name: Upload built package uses: actions/upload-artifact@v3 with: name: dist path: dist/mommy* + + + release-mommy: + runs-on: ubuntu-latest + needs: [ build-linux, build-macos, build-freebsd, build-netbsd, build-openbsd ] + if: github.ref == 'refs/heads/main' && github.event_name != 'workflow_dispatch' + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Download built packages + uses: actions/download-artifact@v3 + with: + name: dist + - name: Extract version number + run: echo "MOMMY_VERSION=v$(head -n 1 ./version)" >> $GITHUB_ENV + - name: Extract release notes + id: extract-release-notes + uses: ffurrer2/extract-release-notes@v1 + with: + release_notes_file: RELEASE_NOTES.md + - name: Checkout release + uses: softprops/action-gh-release@v1 + with: + draft: false + prerelease: false + tag_name: ${{ env.MOMMY_VERSION }} + body_path: RELEASE_NOTES.md + files: mommy* + + + release-aur: + runs-on: ubuntu-latest + container: archlinux:latest + needs: [ release-mommy ] + + steps: + - name: Set up basic system + run: | + echo "::group::Update system" + pacman -Syu --noconfirm + echo "::endgroup::" + + echo "::group::Install basic packages" + pacman -S --noconfirm --needed git base-devel + echo "::endgroup::" + + echo "::group::Add non-privileged user to run makepkg" + useradd -m build + echo "build ALL=(ALL:ALL) NOPASSWD: ALL" >> /etc/sudoers + echo "::endgroup::" + + - name: Checkout mommy + uses: actions/checkout@v3 + with: + path: mommy + - name: Extract version number + run: echo "MOMMY_VERSION=v$(head -n 1 ./mommy/version)" >> $GITHUB_ENV + + - name: Checkout aur-mommy + uses: actions/checkout@v3 + with: + repository: FWDekker/aur-mommy + path: aur-mommy + ref: master + fetch-depth: 0 + # Required to trigger CI action when pushed + token: ${{ secrets.personal_access_token }} + - name: Fix aur-mommy directory ownership + run: chown -R build:build ./aur-mommy/ + - name: Update build files + working-directory: ./aur-mommy/ + run: | + echo "::group::Fast-forward main" + git checkout dev + git checkout master + git merge --commit dev + echo "::endgroup::" + + echo "::group::Update build files" + ./update.sh "$MOMMY_VERSION" + echo "::endgroup::" + + echo "::group::Commit update" + git config --global user.name "FWDekkerBot" + git config --global user.email "bot@fwdekker.com" + git commit -am "mommy updated the build files to mommy $MOMMY_VERSION~" + echo "::endgroup::" + + echo "::group::Fast-forward dev" + git checkout dev + git merge --commit master + echo "::endgroup::" + + echo "::group::Push changes" + git push origin master dev + echo "::endgroup::" + + + release-homebrew: + runs-on: ubuntu-latest + needs: [ release-mommy ] + + steps: + - name: Checkout mommy + uses: actions/checkout@v3 + with: + path: mommy + - name: Extract version number + run: echo "MOMMY_VERSION=v$(head -n 1 ./mommy/version)" >> $GITHUB_ENV + + - name: Checkout homebrew-mommy + uses: actions/checkout@v3 + with: + repository: FWDekker/homebrew-mommy + path: homebrew-mommy + ref: main + fetch-depth: 0 + # Required to trigger CI action when pushed + token: ${{ secrets.personal_access_token }} + - name: Update formula + working-directory: ./homebrew-mommy/ + run: | + echo "::group::Fast-forward main" + git checkout dev + git checkout main + git merge --commit dev + echo "::endgroup::" + + echo "::group::Update formula" + ./update.sh "$MOMMY_VERSION" + echo "::endgroup::" + + echo "::group::Commit update" + git config --global user.name "FWDekkerBot" + git config --global user.email "bot@fwdekker.com" + git commit -am "mommy updated the formula to mommy $MOMMY_VERSION~" + echo "::endgroup::" + + echo "::group::Fast-forward dev" + git checkout dev + git merge --commit main + echo "::endgroup::" + + echo "::group::Push changes" + git push origin main dev + echo "::endgroup::" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0541b1e..bc57c0d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,58 +8,267 @@ jobs: test-linux: runs-on: ubuntu-latest steps: + - name: Install dependencies for mommy + run: | + echo "::group::Install ShellSpec" + git clone --depth=1 https://github.com/shellspec/shellspec.git + sudo make -C shellspec install + echo "::endgroup::" + + echo "::group::Install additional shells" + sudo apt install fish zsh + touch "$HOME/.zshrc" + echo "::endgroup::" + - name: Checkout uses: actions/checkout@v3 - - name: Install ShellSpec - run: git clone --depth=1 https://github.com/shellspec/shellspec.git "$HOME/shellspec/" - name: Test script - run: PATH="$HOME/shellspec:$PATH" ./test.sh + run: make test + - name: Install fpm and build dependencies run: | sudo apt install -y rubygems libarchive-tools rpm zstd sudo gem install --no-document fpm - - name: Build package - run: ./build.sh deb - - name: Test package + - name: Test fpm package run: | + echo "::group::Build" + make dist/deb + echo "::endgroup::" + + echo "::group::Install" sudo apt install -y ./dist/mommy-*.deb - MOMMY_EXEC=mommy PATH="$HOME/shellspec:$PATH" ./test.sh + echo "::endgroup::" + + echo "::group::Test" + make system=1 test + echo "::endgroup::" + + echo "::group::Uninstall" sudo apt purge -y mommy + echo "::endgroup::" + + - name: Install Linuxbrew + run: NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + - name: Checkout homebrew-mommy + uses: actions/checkout@v3 + with: + repository: FWDekker/homebrew-mommy + path: homebrew-mommy + ref: dev + - name: Test Linuxbrew package + run: | + echo "::group::Enable Homebrew" + eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" + mkdir -p "$HOME/.config/fish/"; echo "set -gx fish_complete_path \$fish_complete_path $(brew --prefix)/share/fish/vendor_completions.d/" >> "$HOME/.config/fish/config.fish" + echo "FPATH=\"$(brew --prefix)/share/zsh/site-functions/:\$FPATH\"" >> "$HOME/.zshrc" + echo "::endgroup::" + + echo "::group::Install" + brew tap local/mommy "$(pwd)/homebrew-mommy" + brew install mommy --HEAD + echo "::endgroup::" + + echo "::group::Test" + make system=1 test + echo "::endgroup::" + + echo "::group::Uninstall" + brew uninstall mommy + brew untap local/mommy + echo "::endgroup::" + + + test-archlinux: + runs-on: ubuntu-latest + container: archlinux:latest + steps: + - name: Set up basic system + run: | + echo "::group::Update system" + pacman -Syu --noconfirm + echo "::endgroup::" + + echo "::group::Install basic packages" + pacman -S --noconfirm --needed git base-devel + echo "::endgroup::" + + echo "::group::Add non-privileged user to run makepkg" + useradd -m build + echo "build ALL=(ALL:ALL) NOPASSWD: ALL" >> /etc/sudoers + echo "::endgroup::" + - name: Install dependencies for mommy + run: | + echo "::group::Install man-db" + pacman -S --noconfirm man-db + sed -i "/NoExtract.*man/d" /etc/pacman.conf # Extract man pages + echo "::endgroup::" + + echo "::group::Install ShellSpec" + git clone https://aur.archlinux.org/shellspec-bin.git + chown -R build:build ./shellspec-bin + cd ./shellspec-bin + sudo -u build makepkg -si --noconfirm + cd - + echo "::endgroup::" + + echo "::group::Install additional shells" + pacman -S --noconfirm fish zsh + echo "::endgroup::" + + - name: Checkout mommy + uses: actions/checkout@v3 + with: + path: mommy + - name: Fix mommy directory ownership + run: chown -R build:build ./mommy/ + - name: Test script + working-directory: ./mommy/ + run: make test + + - name: Install fpm + run: | + pacman -S --noconfirm ruby + gem install --no-document fpm + ln -s "$HOME/.local/share/gem/ruby/"*"/bin/fpm" /usr/local/bin/fpm # Symlink `fpm` to latest version + - name: Test fpm package + working-directory: ./mommy/ + run: | + echo "::group::Build" + make dist/pacman + echo "::endgroup::" + + echo "::group::Install" + pacman -U --noconfirm ./dist/mommy*.pacman + echo "::endgroup::" + + echo "::group::Test" + make system=1 test + echo "::endgroup::" + + echo "::group::Uninstall" + pacman -R --noconfirm mommy + echo "::endgroup::" + + - name: Checkout aur-mommy + uses: actions/checkout@v3 + with: + repository: FWDekker/aur-mommy + path: aur-mommy + ref: dev + - name: Fix aur-mommy directory ownership + run: chown -R build:build ./aur-mommy/ + - name: Test AUR package + working-directory: ./aur-mommy/ + run: | + echo "::group::Patch" + sudo -u build ./update.sh dev + echo "::endgroup::" + + echo "::group::Build and install" + sudo -u build makepkg -si --noconfirm + echo "::endgroup::" + + cd ../mommy/ + + echo "::group::Test" + make system=1 test + echo "::endgroup::" + + echo "::group::Uninstall" + pacman -R --noconfirm mommy + echo "::endgroup::" + test-macos: runs-on: macos-latest steps: + - name: Install dependencies for mommy + run: | + echo "::group::Install ShellSpec" + brew tap shellspec/shellspec + brew install shellspec + echo "::endgroup::" + + echo "::group::Install additional shells" + brew install fish + echo "::endgroup::" + - name: Checkout uses: actions/checkout@v3 - - name: Install ShellSpec - run: git clone --depth=1 https://github.com/shellspec/shellspec.git "$HOME/shellspec/" - name: Test script - run: PATH="$HOME/shellspec:$PATH" ./test.sh + run: make test + - name: Install fpm run: sudo gem install --no-document fpm - - name: Build package - run: ./build.sh osxpkg - - name: Test package + - name: Test fpm package run: | + echo "::group::Build" + make dist/osxpkg + echo "::endgroup::" + + echo "::group::Install" sudo installer -pkg ./dist/mommy*+osx.pkg -target / - MOMMY_EXEC=mommy PATH="$HOME/shellspec:$PATH" ./test.sh - sudo rm -f /usr/local/bin/mommy /usr/local/share/man/man1/mommy.1.gz + echo "::endgroup::" + + echo "::group::Test" + make system=1 test + echo "::endgroup::" + + echo "::group::Uninstall" + sudo rm -f /usr/local/bin/mommy /usr/local/share/man/man1/mommy.1.gz /usr/local/share/fish/vendor_completions.d/mommy.fish /usr/local/share/zsh/site-functions/_mommy + echo "::endgroup::" + + - name: Checkout homebrew-mommy + uses: actions/checkout@v3 + with: + repository: FWDekker/homebrew-mommy + path: homebrew-mommy + ref: dev + - name: Test Homebrew package + run: | + echo "::group::Enable Homebrew" + mkdir -p "$HOME/.config/fish/"; echo "set -gx fish_complete_path \$fish_complete_path $(brew --prefix)/share/fish/vendor_completions.d/" >> "$HOME/.config/fish/config.fish" + echo "FPATH=\"$(brew --prefix)/share/zsh/site-functions/:\$FPATH\"" >> "$HOME/.zshrc" + echo "::endgroup::" + + echo "::group::Install" + brew tap local/mommy "$(pwd)/homebrew-mommy" + brew install mommy --HEAD + echo "::endgroup::" + + echo "::group::Test" + make system=1 test + echo "::endgroup::" + + echo "::group::Uninstall" + brew uninstall mommy + brew untap local/mommy + echo "::endgroup::" + test-freebsd: runs-on: macos-12 steps: - name: Checkout uses: actions/checkout@v3 - - name: Install ShellSpec && Install fpm && Test script && Build package && Test package + - name: Test script and package uses: vmactions/freebsd-vm@v0 with: usesh: true # TODO: Use `set -e` in `prepare` once https://github.com/vmactions/freebsd-vm/issues/66 is deployed # Probably once the latest release at https://github.com/vmactions/freebsd-vm/releases is newer than v0.3.0 prepare: | + echo "::group::Install basic packages" + pkg install -y git gmake || exit 1 + echo "::endgroup::" + echo "::group::Install ShellSpec" - pkg install -y git || exit 1 - git clone --depth=1 https://github.com/shellspec/shellspec.git "$HOME/shellspec/" || exit 1 + git clone --depth=1 https://github.com/shellspec/shellspec.git || exit 1 + gmake -C shellspec install || exit 1 + echo "::endgroup::" + + echo "::group::Install additional shells" + pkg install -y fish zsh || exit 1 echo "::endgroup::" # fpm @@ -82,34 +291,50 @@ jobs: set -e echo "::group::Test script" - PATH="$HOME/shellspec:$PATH" ./test.sh + gmake test echo "::endgroup::" echo "::group::Build package" - ./build.sh freebsd + gmake dist/freebsd echo "::endgroup::" - echo "::group::Test package" + echo "::group::Install package" pkg add ./dist/mommy-*.freebsd - MOMMY_EXEC=mommy PATH="$HOME/shellspec:$PATH" ./test.sh + echo "::endgroup::" + + echo "::group::Test package" + gmake system=1 test + echo "::endgroup::" + + echo "::group::Uninstall package" pkg delete -y mommy echo "::endgroup::" + test-netbsd: runs-on: macos-12 steps: - name: Checkout uses: actions/checkout@v3 - - name: Install ShellSpec && Install fpm && Test script && Build package && Test package + - name: Test script and package uses: vmactions/netbsd-vm@v0 with: usesh: true prepare: | set -e + echo "::group::Install basic packages" + pkg_add git gmake + echo "::endgroup::" + echo "::group::Install ShellSpec" - pkg_add git - git clone --depth=1 https://github.com/shellspec/shellspec.git "$HOME/shellspec/" + git clone --depth=1 https://github.com/shellspec/shellspec.git + gmake -C shellspec install + echo "::endgroup::" + + echo "::group::Install additional shells" + pkg_add fish zsh + touch "$HOME/.zshrc" echo "::endgroup::" echo "::group::Install fpm" @@ -124,36 +349,53 @@ jobs: run: | set -e export PATH="/usr/sbin:$PATH" # Add `pkg_*` commands to path + export MOMMY_ZSH_SKIP=1 # Zsh completion capturing totally does not work echo "::group::Test script" - PATH="$HOME/shellspec:$PATH" ./test.sh + gmake test echo "::endgroup::" echo "::group::Build package" - ./build.sh netbsd + gmake dist/netbsd echo "::endgroup::" - echo "::group::Test package" + echo "::group::Install package" pkg_add ./dist/mommy-*+netbsd.tgz - MOMMY_EXEC=mommy PATH="$HOME/shellspec:$PATH" ./test.sh + echo "::endgroup::" + + echo "::group::Test package" + gmake system=1 test + echo "::endgroup::" + + echo "::group::Uninstall package" pkg_delete mommy echo "::endgroup::" + test-openbsd: runs-on: macos-12 steps: - name: Checkout uses: actions/checkout@v3 - - name: Install ShellSpec && Test script && Build package && Test package + - name: Install dependencies for mommy && Test script && Build package && Test package uses: vmactions/openbsd-vm@v0 with: usesh: true prepare: | set -e + echo "::group::Install basic packages" + pkg_add git gmake + echo "::endgroup::" + echo "::group::Install ShellSpec" - pkg_add git - git clone --depth=1 https://github.com/shellspec/shellspec.git "$HOME/shellspec/" + git clone --depth=1 https://github.com/shellspec/shellspec.git + gmake -C shellspec install + echo "::endgroup::" + + echo "::group::Install additional shells" + pkg_add fish zsh + touch "$HOME/.zshrc" echo "::endgroup::" echo "::group::Install fpm" @@ -167,17 +409,24 @@ jobs: echo "::endgroup::" run: | set -e + export MOMMY_ZSH_SKIP=1 # `script` does not have the `-q` option in OpenBSD echo "::group::Test script" - PATH="$HOME/shellspec:$PATH" ./test.sh + gmake test echo "::endgroup::" echo "::group::Build package" - ./build.sh openbsd + gmake dist/openbsd echo "::endgroup::" - echo "::group::Test package" + echo "::group::Install package" pkg_add -D unsigned ./dist/mommy-*+openbsd.tgz - MOMMY_EXEC=mommy PATH="$HOME/shellspec:$PATH" ./test.sh + echo "::endgroup::" + + echo "::group::Test package" + gmake system=1 test # Zsh completion tests do not work in OpenBSD + echo "::endgroup::" + + echo "::group::Uninstall package" pkg_delete mommy echo "::endgroup::" diff --git a/.shellspec b/.shellspec index 3a7bd45..3b50faf 100644 --- a/.shellspec +++ b/.shellspec @@ -1,5 +1,8 @@ --execdir @specfile --no-warning-as-failure +--skip-message quiet +--require spec_helper +--helperdir src/test/helper/ # OpenBSD: Workaround for https://github.com/shellspec/shellspec/issues/291 --shell sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c1f283..f9fcc3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,46 @@ # Changelog ## [Unreleased] +### added + +### changed + +### deprecated + +### removed + +### fixed + +### security + + +## [1.2.2] -- 2023-03-09 +### added +* mommy has shell completions for fish and zsh~ ๐ŸŸ + they are enabled by default on most machines. + if you installed mommy with brew, check the + [brew documentation on how to enable shell completions](https://docs.brew.sh/Shell-Completion)~ + ([#43](https://github.com/FWDekker/mommy/issues/43)) + ([#48](https://github.com/FWDekker/mommy/pull/48)) + +### changed +* mommy no longer talks like a robot when unknown options are used~ ๐Ÿค– + ([#47](https://github.com/FWDekker/mommy/pull/47)) +* mommy's build system has been revamped~ โš—๏ธ + ([#38](https://github.com/FWDekker/mommy/issues/38)) + ([#42](https://github.com/FWDekker/mommy/issues/42)) +* mommy has a bunch more emoji in her readme~ โญ + ([#40](https://github.com/FWDekker/mommy/issues/40)) + +### deprecated + +### removed + +### fixed +* mommy installs herself into `/usr/bin` instead of `/usr/local/bin` on linux, to comply with the standards of various + operating systems~ ๐Ÿ“ +* mommy better tolerates missing optional dependencies when installing from aur~ ๐Ÿ’ช + +### security ## [1.2.1] -- 2023-02-26 diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..da7db0a --- /dev/null +++ b/GNUmakefile @@ -0,0 +1,173 @@ +# Extracted values +version := $(shell head -n 1 version) +date := $(shell tail -n 1 version) + +comment := $(shell grep -- "--description" .fpm | tr -d "\"" | cut -d " " -f 2-) +maintainer := $(shell grep -- "--maintainer" .fpm | tr -d "\"" | cut -d " " -f 2-) + +# Define default output directories +# Separating these variables into a `*_prefix` and `*_prefix_default` allows, for example, the `fpm` target to specify +# certain defaults, while also allowing the `deb` target to override that default when invoking `fpm`, and then also +# allows the user to override that default when running `make osxpkg`. +prefix_default = /usr/ +bin_prefix_default = $(prefix)/bin/ +man_prefix_default = $(prefix)/share/man/ +fish_prefix_default = $(prefix)/share/fish/vendor_completions.d/ +zsh_prefix_default = $(prefix)/share/zsh/site-functions/ + +install fpm: prefix ?= $(prefix_default) +install fpm: bin_prefix ?= $(bin_prefix_default) +install fpm: man_prefix ?= $(man_prefix_default) +install fpm: fish_prefix ?= $(fish_prefix_default) +install fpm: zsh_prefix ?= $(zsh_prefix_default) + + +# Output list of targets +.PHONY: list +list: + @# Taken from https://stackoverflow.com/a/26339924/ + @LC_ALL=C $(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/(^|\n)# Files(\n|$$)/,/(^|\n)# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' + +# Clean up previous builds +.PHONY: clean +clean: + @rm -rf build/ dist/ + + +# Run tests +.PHONY: test +test: test/unit test/integration + +.PHONY: test/% +test/%: system ?= 0 +test/%: + @MOMMY_SYSTEM=$(system) shellspec "src/test/sh/$(@:test/%=%)_spec.sh" + + +## Compilation +# "Compile" files into `build/` +.PHONY: build +build: + @# Copy relevant files + @mkdir -p build/bin/; cp src/main/sh/mommy build/bin/ + @mkdir -p build/man/man1/; cp src/main/man/man1/mommy.1 build/man/man1/ + @mkdir -p build/completions/fish/; cp src/main/completions/fish/mommy.fish build/completions/fish/ + @mkdir -p build/completions/zsh/; cp src/main/completions/zsh/_mommy build/completions/zsh/ + + @# Insert version information + @sed -i".bak" "s/%%VERSION_NUMBER%%/$(version)/g;s/%%VERSION_DATE%%/$(date)/g" build/bin/mommy build/man/man1/mommy.1 + @rm -f build/bin/mommy.bak build/man/man1/mommy.1.bak + + @# Compress + @gzip -f build/man/man1/mommy.1 + + +# Copy built files into appropriate directories +.PHONY: install +install: build + @# Create directories + @install -m 755 -d "$(bin_prefix)" "$(man_prefix)/man1/" "$(fish_prefix)" "$(zsh_prefix)" + + @# Copy files + @install -m 755 build/bin/* "$(bin_prefix)" + @install -m 644 build/man/man1/* "$(man_prefix)/man1/" + @install -m 644 build/completions/fish/* "$(fish_prefix)" + @install -m 644 build/completions/zsh/* "$(zsh_prefix)" + +# Invoke fpm on built files to create `fpm_target` type output +# For valid `fpm_target`s, see https://fpm.readthedocs.io/en/latest/packaging-types.html +.PHONY: fpm +fpm: build +ifndef fpm_target + $(error fpm_target is undefined) +endif + @mkdir -p dist + @fpm -t "$(fpm_target)" \ + -p "dist/mommy-$(version).$(fpm_target)" \ + --version "$(version)" \ + \ + "build/bin/mommy=$(bin_prefix)/mommy" \ + "build/man/man1/mommy.1.gz=$(man_prefix)/man1/mommy.1.gz" \ + "build/completions/fish/mommy.fish=$(fish_prefix)/mommy.fish" \ + "build/completions/zsh/_mommy=$(zsh_prefix)/_mommy" + +# Build Debian package with fpm +.PHONY: dist/deb +dist/deb: + @$(MAKE) fpm_target="deb" zsh_prefix='$$(prefix)/share/zsh/vendor-completions/' fpm + +# Build AlpineLinux / Debian / ArchLinux / RedHat package with fpm +.PHONY: dist/apk dist/pacman dist/rpm +dist/apk dist/pacman dist/rpm: + @$(MAKE) fpm_target="$(@:dist/%=%)" fpm + +# Build macOS package with fpm +.PHONE: dist/osxpkg +dist/osxpkg: + @$(MAKE) fpm_target="osxpkg" prefix="/usr/local/" fpm + + @# `installer` program requires `pkg` extension + @mv dist/*.osxpkg "dist/mommy-$(version)+osx.pkg" + +# Build FreeBSD package with fpm +.PHONY: dist/freebsd +dist/freebsd: + @$(MAKE) fpm_target="freebsd" prefix="/usr/local/" fpm + +# Build NetBSD package manually +.PHONY: dist/netbsd +dist/netbsd: + @$(MAKE) prefix='build/netbsd/usr/pkg/' man_prefix='$$(prefix)/man/' install + + @cd build/netbsd; find . -type f | sed -e "s/^/.\//" > +CONTENTS + + @echo "$(comment)" > build/netbsd/+COMMENT + + @echo "$(comment)" > build/netbsd/+DESC + @echo "" >> build/netbsd/+DESC + @echo "Maintainer: $(maintainer)" >> build/netbsd/+DESC + + @echo "MACHINE_ARCH=$$(uname -p)" > build/netbsd/+BUILD_INFO + @echo "OPSYS=$$(uname)" >> build/netbsd/+BUILD_INFO + @echo "OS_VERSION=$$(uname -r)" >> build/netbsd/+BUILD_INFO + @echo "PKGTOOLS_VERSION=$$(pkg_create -V)" >> build/netbsd/+BUILD_INFO + + + @cd build/netbsd; \ + pkg_create \ + -B +BUILD_INFO \ + -c +COMMENT \ + -d +DESC \ + -f +CONTENTS \ + -I / \ + -p . \ + "mommy-$(version)+netbsd.tgz" + + @mkdir -p dist/ + @mv build/netbsd/mommy*.tgz dist/ + +# Build OpenBSD package manually +.PHONY: dist/openbsd +dist/openbsd: + @$(MAKE) prefix='build/openbsd/usr/local/' man_prefix='$$(prefix)/man/' install + + @cd build/openbsd; find . -type f | sed -e "s/^/.\//" > +CONTENTS + + @echo "$(comment)" > build/openbsd/+COMMENT + + @echo "$(comment)" > build/openbsd/+DESC + + + @cd build/openbsd; \ + pkg_create \ + -d +DESC \ + -D COMMENT="$(comment)" \ + -D FULLPKGPATH="mommy-$(version)+netbsd" \ + -D MAINTAINER="$(maintainer)" \ + -f +CONTENTS \ + -B "$$(pwd)/" \ + -p / \ + "mommy-$(version)+openbsd.tgz" + + @mkdir -p dist/ + @mv build/openbsd/mommy*.tgz dist/ diff --git a/README.md b/README.md index aa1f291..cdb6dc2 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# mommy +# mommy ๐Ÿ’ [![github latest release](https://img.shields.io/github/v/release/FWDekker/mommy?style=for-the-badge)](https://github.com/FWDekker/mommy/releases/latest) +[![mommy is on aur](https://img.shields.io/aur/version/mommy?style=for-the-badge)](https://aur.archlinux.org/packages/mommy/) [![github ci status](https://img.shields.io/github/actions/workflow/status/FWDekker/mommy/ci.yml?style=for-the-badge)](https://github.com/FWDekker/mommy/actions/workflows/ci.yml?query=branch%3Amain) [![mommy is licensed under unlicense](https://img.shields.io/github/license/FWDekker/mommy?style=for-the-badge)](https://github.com/FWDekker/mommy/blob/main/LICENSE) -[![mommy is on aur](https://img.shields.io/aur/version/mommy?style=for-the-badge)](https://aur.archlinux.org/packages/mommy/) mommy's here to support you! mommy will compliment you if things go well, and will encourage you if things are not going so well~ @@ -13,11 +13,11 @@ much~ โค๏ธ ![mommy demo](.github/img/demo.gif) -## installation +## installation ๐Ÿšš mommy works on any unix system. mommy is tested on ubuntu, debian, macos, freebsd, netbsd, and openbsd~ -### package manager +### from a repository ๐Ÿค _don't see your favorite package manager? check the distribution archives below. not satisfied? @@ -30,13 +30,14 @@ install on arch linux from the [aur mommy package](https://aur.archlinux.org/pac [an aur helper](https://wiki.archlinux.org/title/AUR_helpers): ```shell +# if you use yay +yay -S mommy # if you use paru paru -S mommy # if you use aura aura -A mommy # and so on ``` -
@@ -44,13 +45,16 @@ aura -A mommy install with homebrew/linuxbrew from the [mommy tap](https://github.com/FWDekker/homebrew-mommy): +after installing, check the +[brew documentation on how to enable shell completions](https://docs.brew.sh/Shell-Completion)~ + ```shell brew tap fwdekker/mommy brew install mommy ```
-### distribution archives +### distribution archives ๐Ÿ“ฆ [download the latest release](https://github.com/FWDekker/mommy/releases/latest) for your platform and install as usual: * on debian/ubuntu/etc, run `sudo apt install ./mommy-*.deb`, * on red hat/fedora/etc, run `sudo dnf install ./mommy-*.rpm`, @@ -61,19 +65,20 @@ brew install mommy * on netbsd, run `pkg_add ./mommy-*+netbsd.tgz`, * on openbsd, run `pkg_add -D unsigned ./mommy-*+openbsd.tgz`, * alternatively, on any unix system you can also download and extract the source code `.zip`, and copy - `./src/main/sh/mommy` into the appropriate directory + `src/main/sh/mommy` into the appropriate directory (usually `/usr/local/bin/`) - (and optionally also copy `./src/main/resources/mommy.1` into `/usr/local/man/man1/`) - -### what's next? -after installation, you can [configure mommy](#configuration) and [integrate mommy with your shell](#shell-integration)~ + (and optionally also copy `src/main/resources/mommy.1` into `/usr/local/man/man1/`) to update mommy, just repeat the installation process~ +### what's next? ๐Ÿ”ฎ +check out [how to use mommy](#usage-), read all about [ways you can configure mommy](#configuration-), and +[integrate mommy with your shell](#shell-integration-)~ + mommy integrated with the fish shell -## usage +## usage ๐Ÿ“– mommy integrates with your normal command-line usage and compliments you if the command succeeds and encourages you if it fails~ @@ -93,11 +98,11 @@ by default, mommy outputs to stderr, but if you use `mommy -1 [other options]` s use `mommy -v` to see which version of mommy you're using~ -## configuration +## configuration ๐Ÿ™‹ mommy's behavior can be configured by defining variables in `~/.config/mommy/config.sh`. or specify a different config file with `mommy -c ./my_config.sh [other options]`~ -### config file format +### config file format ๐Ÿ—ƒ๏ธ mommy executes the config file as a shell script and keeps the environment variables. so, to change the value of `MOMMY_SWEETIE`, add the following line to your config file: ```shell @@ -105,7 +110,7 @@ MOMMY_SWEETIE="catgirl" ``` make sure you do not put spaces around the `=`~ -### available settings +### available settings ๐Ÿ‘› | variable | description | list? | default | |--------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------|---------------| | `MOMMY_CAREGIVER` | what mommy calls herself | yes | `mommy` | @@ -124,7 +129,7 @@ make sure you do not put spaces around the `=`~ | `MOMMY_FORBIDDEN_WORDS` | mommy will not use templates that contain forbidden / trigger words | yes | <empty> | | `MOMMY_IGNORED_STATUSES` | exit codes that mommy should never reply to. set to empty string to ignore nothing | yes | `130` | -### lists +### lists ๐Ÿชฃ some of these settings support lists. mommy chooses a random element from each list each time she is called by you. (except for `MOMMY_FORBIDDEN_WORDS` and `MOMMY_SUPPRESS_EXIT`, where all elements of the list are always considered.) @@ -153,7 +158,7 @@ elements that contain whitespace only, and elements that start with a `#` are ig ``` then mommy will never use templates that contain `cat`, and will never use templates that contain `dog`~ -### custom templates +### custom templates ๐Ÿงฌ you can add your own compliments to either `MOMMY_COMPLIMENTS` or `MOMMY_COMPLIMENTS_EXTRA`, but there is a slight difference: * if you want both the default _and_ your own compliments, add your own compliments to `MOMMY_COMPLIMENTS_EXTRA`, but @@ -161,7 +166,7 @@ difference: and similarly so for encouragements~ -### template variables +### template variables ๐Ÿ“› inside compliments and encouragements, you can put placeholders that contain the random values that mommy chose. for example, if you add the compliment `%%CAREGIVER%% loves you`, and have `MOMMY_CAREGIVER=your mommy`, then mommy outputs `your mommy loves you`~ @@ -174,7 +179,7 @@ outputs `your mommy loves you`~ | `%%THEIR%%` | mommy's possessive pronoun (e.g. his, her, their) | | `%%SWEETIE%%` | what mommy calls you | -### renaming the mommy executable +### renaming the mommy executable โœ๏ธ if you want to write `daddy npm test` instead of `mommy npm test`, you can just create a symlink. mommy is installed in slightly different locations on different systems, but you can easily find where mommy is installed with `whereis mommy`: @@ -193,14 +198,14 @@ sudo ln -fs /usr/local/bin/mommy /usr/local/bin/daddy sudo ln -fs /usr/local/man/man1/mommy.1.gz /usr/local/man/man1/daddy.1.gz ``` -## shell integration +## shell integration ๐Ÿš instead of calling mommy for each command, you can also fully integrate mommy with your shell to get mommy's output each time you run any command. here are some examples on how you can do that in various shells. recall that you can add `MOMMY_COMPLIMENTS_ENABLED=0` to your mommy config file to disable compliments while keeping encouragements~ -### bash +### bash ๐Ÿช… in bash you can set [`PROMPT_COMMAND`](https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#index-PROMPT_005fCOMMAND) to run mommy after each command. @@ -209,7 +214,7 @@ just add the following line to `~/.bashrc`: PROMPT_COMMAND="mommy -1 -s \$?; $PROMPT_COMMAND" ``` -### fish +### fish ๐ŸŸ in fish you can have mommy output a message on the right side of your prompt by creating `~/.config/fish/functions/fish_right_prompt.fish` with the following contents: ```shell @@ -222,7 +227,7 @@ see if there's an easy way to extend the theme's right prompt. if not, you can either overwrite it with the above code, or copy-paste the theme's code into your own config file and then add mommy yourself~ -### zsh +### zsh ๐Ÿ’ค in zsh you can put mommy's output after each command by adding the following line to `~/.zshrc`: ```shell precmd() { mommy -1 -s $? } @@ -246,7 +251,7 @@ this code randomly changes the output between magenta and cyan~ mommy integrated with the zsh shell -### other shells +### other shells ๐ŸŒ as a generic method, in any posix shell (including `sh`, `ash`, `dash`, `bash`) you can change the prompt itself to contain a message from mommy by setting the `$PS1` variable: ```shell @@ -263,28 +268,83 @@ after that, add the line that defines `PS1` to `~/.shrc`. log out and back in, and mommy will appear in your shell~ -## development -to build your own mommy, first install the requirements. -on debian-like systems, run -```shell -sudo apt install rubygems libarchive-tools rpm zstd -sudo gem install fpm -``` +## development โš—๏ธ +this section explains how to build mommy from source, in case you want to help with development or for any other reason~ -after that, just run `./build.sh deb` (or better: `mommy ./build.sh deb`), and outputs appear in `dist/`. -replace `deb` with [one or more supported output types](https://fpm.readthedocs.io/en/v1.15.1/packaging-types.html). -except don't use `pkgin`, but use `openbsd` for openbsd, and use `netbsd` for netbsd~ +### run ๐ŸŽฌ +you can actually just directly run the script in `src/main/sh/mommy`. +note that the version number will be a bit off unless you [package mommy](#packaging-)~ -you can also run `PREFIX=/usr ./build.sh install` to install mommy into `/usr`, but this is mostly intended for -integration with package managers such as brew and arch's makepkg~ +### tests ๐Ÿงช +to run tests, install [shellspec](https://github.com/shellspec/shellspec) and run `make test`. +specifically, run `make test/unit` to test the mommy executable in isolation, and run `make test/integration` to test +the integration with external programs (if they are installed). +by default, tests are run against the files in `src/`. +if you want to run tests against installed files, run `make system=1 test` instead~ -before a new release, make sure to update `./version` and `./CHANGELOG.md`~ - -to run tests, install [shellspec](https://github.com/shellspec/shellspec) and run `./test.sh`. -by default, tests are run against `./src/main/sh/mommy`. -to change that, set the `MOMMY_EXEC` environment variable before running tests, as in -`MOMMY_EXEC=/usr/local/bin/mommy ./test.sh`~ +### packaging ๐Ÿ“ฆ +mommy can be packaged in different ways. +mommy uses [fpm](https://github.com/jordansissel/fpm) to create the [distribution archives](#distribution-archives-) +that are attached to each release. +but mommy is also [available on some repositories](#from-a-repository-). +the build files for those repositories are in [homebrew-mommy](https://github.com/FWDekker/homebrew-mommy) and +[aur-mommy](https://github.com/FWDekker/aur-mommy)~ +to build distribution archives, first install the requirements. +for all systems, you need at least gnu make, ruby, and fpm. +on debian-like systems, you already have gnu make, so you only need +```shell +sudo apt install ruby +sudo gem install fpm +``` -## acknowledgements -mommy was very much inspired by [cargo-mommy](https://github.com/Gankra/cargo-mommy)~ +after that, just run `make dist/deb` (or better: `mommy make dist/deb`), and a `.deb` package will be built in `dist/`. +run `make` or `make list` for a list of valid building targets. +a special target is `install`, which directly copies the files into the specified directories on your system. +these directories can be changed by setting `prefix` variables, as in `make prefix=/usr/ install`. +i recommend running `make --dry-run prefix=/usr/ install` first so you can verify that all directories are calculated +correctly. +check the `GNUmakefile` for more details~ + +all systems can build packages for themselves without additional dependencies. +if you want to compile for a different system, you may need additional dependencies. +for example, if you want to build packages for alpinelinux, archlinux, and rpm from a debian-like system, you will need +```shell +sudo apt install libarchive-tools rpm zstd +``` +and then you can run +```shell +make apk pacman rpm +``` +unfortunately, packages for macos, netbsd, and openbsd cannot be built on systems other than themselves~ + +### contribution guidelines ๐Ÿค  +* add relevant documentation and tests~ +* ensure that the tests pass~ +* describe your changes in `CHANGELOG.md`~ +* your pull request should go into `dev`, not into `main`~ + +### release ๐Ÿ“ฏ +before a new release, make sure to update `version`, `CHANGELOG.md`, and the acknowledgements in `README.md`. +when a branch is merged into `main`, a new release is created automatically, and repository distributions are updated +automatically~ + + +## acknowledgements ๐Ÿ’– +mommy recognises _all_ contributors, no matter the size of the contribution. +if mommy should add, remove, or change anything here, [open an issue](https://github.com/FWDekker/mommy/issues/new) or +[contact the author](https://fwdekker.com/about/)~ + +* mommy thanks [aria beingessner](https://github.com/Gankra) for creating + [cargo-mommy](https://github.com/Gankra/cargo-mommy), which inspired mommy to spawn herself into existence~ +* mommy thanks [austin burk](https://github.com/sudofox) for creating + [shell-mommy](https://github.com/sudofox/shell-mommy) and contributing to the mommy-sphere; + mommy did not know about shell-mommy before embarking on her journey, but loves her very much~ +* mommy thanks [natawie](https://github.com/FWDekker/mommy/issues/39) for + [suggesting publishing mommy on copr](https://github.com/FWDekker/mommy/issues/39)~ +* mommy thanks [amber sprenkels](https://github.com/dsprenkels) for + [reporting a bug](https://github.com/FWDekker/mommy/issues/45), + [sharing great ideas](https://github.com/FWDekker/mommy/issues/46), and + [making mommy talk less like a robot](https://github.com/FWDekker/mommy/pull/47)~ +* mommy thanks [natawie](https://github.com/natawie) for + [writing the zsh completions](https://github.com/FWDekker/mommy/pull/48)~ diff --git a/build.sh b/build.sh deleted file mode 100755 index 2d9dc95..0000000 --- a/build.sh +++ /dev/null @@ -1,153 +0,0 @@ -#!/bin/sh -set -e -cd -P -- "$(dirname -- "$0")" - -# Load configuration -version="$(head -n 1 ./version)" -date="$(tail -n 1 ./version)" - -# Clean -rm -rf build/ dist/ - -# Prepare -mkdir build/ -cp src/main/sh/mommy src/main/resources/mommy.1 build/ -sed -i".bak" "s/%%VERSION_NUMBER%%/$version/g;s/%%VERSION_DATE%%/$date/g" build/* -gzip build/mommy.1 - -# Build -mkdir dist/ -for target in "$@"; do - echo "# Build $target" - - # Select targets - case "$target" in - install) - target_exe="build/mommy=${PREFIX:?Prefix not specified}/bin/mommy" - target_man="build/mommy.1.gz=$PREFIX/share/man/man1/mommy.1.gz" - ;; - netbsd) - target_exe="build/mommy=/usr/pkg/bin/mommy" - target_man="build/mommy.1.gz=/usr/pkg/man/man1/mommy.1.gz" - ;; - osxpkg) - target_exe="build/mommy=/usr/local/bin/mommy" - target_man="build/mommy.1.gz=/usr/local/share/man/man1/mommy.1.gz" # `/usr/local/man` is not on macOS manpath - ;; - *) - target_exe="build/mommy=/usr/local/bin/mommy" - target_man="build/mommy.1.gz=/usr/local/man/man1/mommy.1.gz" - ;; - esac - - # Pre-process - case "$target" in - install) - # Do nothing - ;; - netbsd|openbsd) - # Extract properties - comment="$(<"./.fpm" grep -- "--description")" - comment="$(echo "${comment#* }" | tr -d "\"")" - - maintainer="$(<"./.fpm" grep -- "--maintainer")" - maintainer="$(echo "${maintainer#* }" | tr -d "\"")" - - # Prepare tmp directory - rm -rf /tmp/mommy - mkdir -p /tmp/mommy - - # Copy input files - mkdir -p "/tmp/mommy/$(dirname "${target_exe#*=}")" "/tmp/mommy/$(dirname "${target_man#*=}")" - cp "./${target_exe%%=*}" "/tmp/mommy/${target_exe#*=}" - cp "./${target_man%%=*}" "/tmp/mommy/${target_man#*=}" - - # Create control files - cd /tmp/mommy - - ## Comment - echo "$comment" >> ./+COMMENT - - ## Description - { - echo "$comment" - echo "" - echo "Maintainer: $maintainer" - } >> ./+DESC - - ## Pack file - { - echo "./${target_exe#*=}" - echo "./${target_man#*=}" - } >> ./+CONTENTS - - # Build info - if [ "$target" = "netbsd" ]; then - { - echo "MACHINE_ARCH=$(uname -p)" - echo "OPSYS=$(uname)" - echo "OS_VERSION=$(uname -r)" - echo "PKGTOOLS_VERSION=$(pkg_create -V)" - } >> ./+BUILD_INFO - fi - - cd - - ;; - *) - # Do nothing - ;; - esac - - # Process - case "$target" in - install) - mkdir -p "$(dirname "${target_exe#*=}")" "$(dirname "${target_man#*=}")" - cp "./${target_exe%%=*}" "${target_exe#*=}" - cp "./${target_man%%=*}" "${target_man#*=}" - ;; - netbsd) - cd /tmp/mommy - pkg_create \ - -B ./+BUILD_INFO \ - -c ./+COMMENT \ - -d ./+DESC \ - -f ./+CONTENTS \ - -I / \ - -p . \ - "./mommy-$version+netbsd.tgz" - cd - - mv /tmp/mommy/mommy*.tgz ./dist/ - ;; - openbsd) - cd /tmp/mommy - pkg_create \ - -d ./+DESC \ - -D COMMENT="$comment" \ - -D FULLPKGPATH="mommy-$version+netbsd" \ - -f ./+CONTENTS \ - -B /tmp/mommy \ - -p / \ - "./mommy-$version+openbsd.tgz" - cd - - mv /tmp/mommy/mommy*.tgz ./dist/ - ;; - *) - fpm -t "$target" -p "./dist/mommy-$version.$target" --version "$version" "$target_exe" "$target_man" - ;; - esac - - # Post-process - case "$target" in - install) - # Do nothing - ;; - netbsd|openbsd) - # Clean up - rm -rf /tmp/mommy - ;; - osxpkg) - # `installer` program requires `pkg` extension - mv ./dist/*.osxpkg "./dist/mommy-$version+osx.pkg" - ;; - esac -done diff --git a/src/main/completions/fish/mommy.fish b/src/main/completions/fish/mommy.fish new file mode 100644 index 0000000..60b4a2a --- /dev/null +++ b/src/main/completions/fish/mommy.fish @@ -0,0 +1,85 @@ +# Extracts the non-option commands from `$argv` and writes to stdout. +# For example, given `mommy -c ./config.sh apt update -f`, writes `apt update -f`. +function extract_command + set -e argv[1] + + set -l is_option_argument 0 + for arg in $argv + if test $is_option_argument -eq 1 + set -e argv[1] + set is_option_argument 0 + continue + end + + switch $arg + case '-c' '-e' '-s' + set -e argv[1] + set is_option_argument 1 + case '-*' + set -e argv[1] + case '*' + echo $argv + return 0 + end + end + + return 0 +end + +# Extract the args, excluding the arg that the user is currently writing +function get_args + set -l tokens (commandline -opc) + + extract_command $tokens +end + +# Extract the args, including the arg that the user is currently writing +function get_args_with_token + set -l tokens (commandline -opc) (commandline -ct) + + extract_command $tokens +end + + +# Set common elements +set -l opt_help "-o h -l help" +set -l opt_version "-o v -l version" + + +# Add completions +complete -c mommy -f + +complete -c mommy -o h -l help \ + -d "Show manual" \ + -n "__fish_is_first_arg" +complete -c mommy -o v -l version \ + -d "Show version" \ + -n "__fish_is_first_arg" + +complete -c mommy -o 1 \ + -d "Write to stdout" \ + -n "not __fish_seen_argument $opt_help $opt_version" \ + -n "test -z (get_args)" +complete -c mommy -o c \ + -rF \ + -d "Configuration file" \ + -n "not __fish_seen_argument $opt_help $opt_version"\ + -n "test -z (get_args)" + +complete -c mommy -o e \ + -r \ + -d "Evaluate string" \ + -n "not __fish_seen_argument $opt_help $opt_version -o s" \ + -n "test -z (get_args)" +complete -c mommy -o s \ + -rf \ + -d "Exit code" \ + -a "(echo 0\tSuccess\n1\tError)" \ + -n "not __fish_seen_argument $opt_help $opt_version -o e" \ + -n "test -z (get_args)" + +complete -c mommy \ + # `complete -C` requires one argument, so must be wrapped in quotes. Fish <3.4.0 cannot do `$(...)`, so workaround + # is to assign to temporary variable. + -k -a "(set -l command (get_args_with_token); complete -C \"\$command\")" \ + -n "test -n (get_args_with_token); or not __fish_seen_argument $opt_help $opt_version -o e -o s" diff --git a/src/main/completions/zsh/_mommy b/src/main/completions/zsh/_mommy new file mode 100644 index 0000000..f530527 --- /dev/null +++ b/src/main/completions/zsh/_mommy @@ -0,0 +1,23 @@ +#compdef mommy + +local exit_codes state + +exit_codes=( + 0:"Success" + 1:"Error" +) + +_arguments \ + "(- *)"{-h,--help}'[Show manual]' \ + "(- *)"{-v,--version}'[Show version]' \ + -1'[Write to stdout]' \ + -c'[Configuration file]:config:_files' \ + -e'[Evaluate string]:string' \ + -s'[Exit code]:code:->status' \ + '*::command:' + +# suggest exit codes for --status +# $state is required, otherwise it'll always suggest exit codes +[[ $state == status ]] && _describe -t code "Exit code" exit_codes + +# vim: ft=zsh diff --git a/src/main/resources/mommy.1 b/src/main/man/man1/mommy.1 similarity index 100% rename from src/main/resources/mommy.1 rename to src/main/man/man1/mommy.1 diff --git a/src/main/sh/mommy b/src/main/sh/mommy index f0db972..0644e9d 100755 --- a/src/main/sh/mommy +++ b/src/main/sh/mommy @@ -197,11 +197,9 @@ MOMMY_OPT_EVAL="" MOMMY_OPT_STATUS="" while getopts ":hv1c:e:s:-:" OPTION; do - # Workaround for long options: https://stackoverflow.com/a/28466267/ + # Cheap workaround for long options without arguments if [ "$OPTION" = "-" ]; then - OPTION="${OPTARG%%=*}" - OPTARG="${OPTARG#$OPTION}" - OPTARG="${OPTARG#=}" + OPTION="$OPTARG" fi # shellcheck disable=SC2214 # Handled by workaround @@ -212,8 +210,8 @@ while getopts ":hv1c:e:s:-:" OPTION; do c) MOMMY_OPT_CONFIG_FILE="$OPTARG" ;; e) MOMMY_OPT_EVAL="$OPTARG" ;; s) MOMMY_OPT_STATUS="$OPTARG" ;; - ?) echo "Illegal option -$OPTARG" >&2; exit 1 ;; - *) echo "Illegal option --$OPTION" >&2; exit 1 ;; + ?) echo "mommy does not know option -$OPTARG~" >&2; exit 1 ;; + *) echo "mommy does not know option --$OPTION~" >&2; exit 1 ;; esac done @@ -227,15 +225,7 @@ shift "$((OPTIND - 1))" ## Output if [ -n "$MOMMY_OPT_HELP" ]; then - local_man_path="$(dirname -- "$0")/../resources/mommy.1" - - if [ -r "$local_man_path" ]; then - man "$local_man_path" - exit "$?" - else - man mommy - exit "$?" - fi + man mommy elif [ -n "$MOMMY_OPT_VERSION" ]; then echo "mommy, v%%VERSION_NUMBER%%, %%VERSION_DATE%%" exit 0 diff --git a/src/test/helper/spec_helper.sh b/src/test/helper/spec_helper.sh new file mode 100644 index 0000000..1920042 --- /dev/null +++ b/src/test/helper/spec_helper.sh @@ -0,0 +1,39 @@ +#!/bin/sh +## Configuration +# "1" to run against installed files, "0" to run against files in `src/` +: "${MOMMY_SYSTEM:=0}" +export MOMMY_SYSTEM + +# Path to mommy executable to test +if [ "$MOMMY_SYSTEM" = "1" ]; then + : "${MOMMY_EXEC:=mommy}" +else + : "${MOMMY_EXEC:=../../main/sh/mommy}" +fi +export MOMMY_EXEC + +# Path to directory for temporary files +: "${MOMMY_TMP_DIR:=/tmp/mommy-test/}" +export MOMMY_TMP_DIR + + +## Constants +export n=" +" + + +## Hooks +spec_helper_configure() { + rm -rf "$MOMMY_TMP_DIR" + + before_each "mommy_before_each" + after_each "mommy_after_each" +} + +mommy_before_each() { + mkdir -p "$MOMMY_TMP_DIR" +} + +mommy_after_each() { + rm -rf "$MOMMY_TMP_DIR" +} diff --git a/src/test/resources/zsh_loader.zsh b/src/test/resources/zsh_loader.zsh new file mode 100755 index 0000000..3b8b4ad --- /dev/null +++ b/src/test/resources/zsh_loader.zsh @@ -0,0 +1,62 @@ +#!/usr/bin/env zsh +# Extracts zsh's auto-completions using a pseudo-terminal with emulated inputs. In zsh, source this file, then invoke +# `compget `. +# Code taken from https://stackoverflow.com/a/69164362/ + + +# Define our test function. +comptest() { + # Gather all matching completions in this array. + # -U discards duplicates. + typeset -aU completions=() + + # Override the builtin compadd command. + compadd() { + # Gather all matching completions for this call in $reply. + # Note that this call overwrites the specified array. + # Therefore we cannot use $completions directly. + builtin compadd -O reply "$@" + + completions+=("$reply[@]") # Collect them. + builtin compadd "$@" # Run the actual command. + } + + # Bind a custom widget to TAB. + bindkey "^I" complete-word + zle -C {,,}complete-word + complete-word() { + # Make the completion system believe we're on a normal + # command line, not in vared. + unset 'compstate[vared]' + + _main_complete "$@" # Generate completions. + + # Print out our completions. + # Use of ^B and ^C as delimiters here is arbitrary. + # Just use something that won't normally be printed. + print -n $'\C-B' + print -nlr -- "$completions[@]" # Print one per line. + print -n $'\C-C' + exit + } + + vared -c tmp +} + +compget() { + zmodload zsh/zpty # Load the pseudo terminal module. + zpty {,}comptest # Create a new pty and run our function in it. + + # Simulate a command being typed, ending with TAB to get completions. + zpty -w comptest "$1"$'\t' + + # Read up to the first delimiter. Discard all of this. + zpty -r comptest REPLY $'*\C-B' + + zpty -r comptest REPLY $'*\C-C' # Read up to the second delimiter. + + # Print out the results. + print -r -- "$REPLY" | col # Trim off the escape characters, just in case. + + zpty -d comptest # Delete the pty +} diff --git a/src/test/sh/integration_spec.sh b/src/test/sh/integration_spec.sh new file mode 100644 index 0000000..7d509a8 --- /dev/null +++ b/src/test/sh/integration_spec.sh @@ -0,0 +1,120 @@ +#!/bin/sh +## Configuration +# Man +: "${MOMMY_MAN_SKIP:=0}" # "1" to run man-related tests, "0" to skip them + +# Fish +: "${MOMMY_FISH_SKIP:=0}" # "1" to run fish-related tests, "0" to skip them +: "${MOMMY_FISH_EXEC:=fish}" # Path to fish to invoke + +# Zsh +: "${MOMMY_ZSH_SKIP:=0}" # "1" to run zsh-related tests, "0" to skip them +: "${MOMMY_ZSH_EXEC:=zsh}" # Path to zsh to invoke +: "${MOMMY_ZSH_PREAMBLE_FILE:=$MOMMY_TMP_DIR/zsh_preamble.sh}" # Path to temporary zsh preamble file + + +## Run tests +Describe "integration of mommy with other programs" + Describe "-h/--help: help information" + man_is_skipped_or_not_installed() { test "$MOMMY_MAN_SKIP" = "1" || ! test -x "$(command -v man)"; } + Skip if "man is skipped or not installed" man_is_skipped_or_not_installed + + man_before_each() { + if [ "$MOMMY_SYSTEM" != "1" ]; then + export MANPATH="$(readlink -f "$(pwd)/../../main/man/")" + fi + } + BeforeEach "man_before_each" + + + It "outputs help information using -h" + When run "$MOMMY_EXEC" -h + The word 1 of output should equal "mommy(1)" + The status should be success + End + + It "outputs help information using --help" + When run "$MOMMY_EXEC" --help + The word 1 of output should equal "mommy(1)" + The status should be success + End + + It "outputs help information even when -h is not the first option" + When run "$MOMMY_EXEC" -s 432 -h + The word 1 of output should equal "mommy(1)" + The status should be success + End + + It "outputs help information even when --help is not the first option" + When run "$MOMMY_EXEC" -s 221 --help + The word 1 of output should equal "mommy(1)" + The status should be success + End + End + + Describe "fish shell autocompletion" + fish_is_skipped_or_not_installed() { test "$MOMMY_FISH_SKIP" = "1" || ! test -x "$(command -v "$MOMMY_FISH_EXEC")"; } + Skip if "fish is skipped or not installed" fish_is_skipped_or_not_installed + + fish_before_each() { + if [ "$MOMMY_SYSTEM" != "1" ]; then + fish_preamble=" + fish_add_path --path --prepend '$(pwd)/../../main/sh/' # Fish requires executable to be on path + set fish_complete_path '$(pwd)/../../main/completions/fish/' \$fish_complete_path + " + fi + } + BeforeEach "fish_before_each" + + fish_complete() { + "$MOMMY_FISH_EXEC" -c "$fish_preamble; complete -C '$1'" + } + + + It "outputs an option if the argument starts with -" + When run fish_complete "mommy -" + The output should include "-1" + End + + It "outputs files if the previous option was -c" + When run fish_complete "mommy -c " + The output should include "integration_spec.sh" + End + End + + Describe "zsh shell autocompletion" + zsh_is_skipped_or_not_installed() { test "$MOMMY_ZSH_SKIP" = "1" || ! test -x "$(command -v "$MOMMY_ZSH_EXEC")"; } + Skip if "zsh is skipped or not installed" zsh_is_skipped_or_not_installed + + zsh_before_each() { + echo "source '$(pwd)/../resources/zsh_loader.zsh'" > "$MOMMY_ZSH_PREAMBLE_FILE" + if [ "$MOMMY_SYSTEM" != "1" ]; then + echo "FPATH='$(pwd)/../../main/completions/zsh/:'\"\$FPATH\"" >> "$MOMMY_ZSH_PREAMBLE_FILE" + fi + echo "autoload -U compinit; compinit -u" >> "$MOMMY_ZSH_PREAMBLE_FILE" + } + BeforeEach "zsh_before_each" + + zsh_complete() { + # `script` emulates an interactive terminal during GitHub actions + if script -q -c true /dev/null 1>/dev/null 2>/dev/null; then + # Linux + script -q -c "$MOMMY_ZSH_EXEC -i -u -c \"source '$MOMMY_ZSH_PREAMBLE_FILE'; compget '$1'\"" /dev/null + else + # *BSD + script -q /dev/null "$MOMMY_ZSH_EXEC" -i -u -c "source '$MOMMY_ZSH_PREAMBLE_FILE'; compget '$1'" + fi + } + + + It "outputs an option if the argument starts with -" + When run zsh_complete "mommy -" + The output should include "-1" + End + + It "outputs files if the previous option was -c" + When run zsh_complete "mommy -c " + The output should include "integration_spec.sh" + End + End +End diff --git a/src/test/sh/mommy_spec.sh b/src/test/sh/unit_spec.sh similarity index 93% rename from src/test/sh/mommy_spec.sh rename to src/test/sh/unit_spec.sh index 9497d6b..912a888 100755 --- a/src/test/sh/mommy_spec.sh +++ b/src/test/sh/unit_spec.sh @@ -1,62 +1,34 @@ -n=" -" +#!/bin/sh +## Configuration +# Temporary file to store mommy's configuration in +: "${MOMMY_CONFIG_FILE:=$MOMMY_TMP_DIR/config.sh}" -# Settings -[ -z "$MOMMY_EXEC" ] && export MOMMY_EXEC="../../main/sh/mommy" -[ -z "$MOMMY_CONFIG_FILE" ] && export MOMMY_CONFIG_FILE="./config" +## Functions # Writes `$1` to the config file, setting `MOMMY_COLOR` and `MOMMY_SUFFIX` to the empty string if not set in `$1`. set_config() { echo "MOMMY_COLOR='';MOMMY_SUFFIX='';$1" > "$MOMMY_CONFIG_FILE" } -# Tests +## Run tests Describe "mommy" - clean_config() { rm -f "$MOMMY_CONFIG_FILE"; } - BeforeEach "clean_config" - AfterEach "clean_config" - Describe "command-line options" It "gives an error for unknown short options" When run "$MOMMY_EXEC" -d - The error should equal "Illegal option -d" + The error should equal "mommy does not know option -d~" The status should be failure End It "gives an error for unknown long options" When run "$MOMMY_EXEC" --doesnotexist - The error should equal "Illegal option --doesnotexist" + The error should equal "mommy does not know option --doesnotexist~" The status should be failure End - Describe "-h/--help: help information" - It "outputs help information using -h" - When run "$MOMMY_EXEC" -h - The word 1 of output should equal "mommy(1)" - The status should be success - End - - It "outputs help information using --help" - When run "$MOMMY_EXEC" --help - The word 1 of output should equal "mommy(1)" - The status should be success - End - - It "outputs help information even when -h is not the first option" - When run "$MOMMY_EXEC" -s 432 -h - The word 1 of output should equal "mommy(1)" - The status should be success - End - - It "outputs help information even when --help is not the first option" - When run "$MOMMY_EXEC" -s 221 --help - The word 1 of output should equal "mommy(1)" - The status should be success - End - End + # -h/--help is tested in `integration_spec.sh` - Describe "-v/--help: version information" + Describe "-v/--version: version information" It "outputs version information using -v" When run "$MOMMY_EXEC" -v The word 1 of output should equal "mommy," diff --git a/test.sh b/test.sh deleted file mode 100755 index 214e89c..0000000 --- a/test.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh -set -e -cd -P -- "$(dirname -- "$0")" - -shellspec src/test/sh/mommy_spec.sh diff --git a/version b/version index e692a26..156588c 100644 --- a/version +++ b/version @@ -1,2 +1,2 @@ -1.2.1 -2023-02-26 +1.2.2 +2023-03-09