From 1b09a05abd0cf1e9dc8d19a8546c57d698c6180c Mon Sep 17 00:00:00 2001 From: Anmol Nagpal Date: Tue, 21 Jul 2026 05:39:25 -0400 Subject: [PATCH 1/2] feat: add noddy formula noddy is a macOS command line tool collection for DevOps, published from clouddrove/noddy. The formula tracks the v1.0.0 tag. It is a shell tool rather than a compiled binary, so there is a single source tarball rather than per-architecture archives. The script and its toyland/ payload install together into libexec, with only the entry point linked onto PATH, because noddy sources its plugins from a path relative to itself. Completions for zsh, bash and fish are installed from the tarball. --- Formula/noddy.rb | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 Formula/noddy.rb diff --git a/Formula/noddy.rb b/Formula/noddy.rb new file mode 100644 index 0000000..6d75492 --- /dev/null +++ b/Formula/noddy.rb @@ -0,0 +1,39 @@ +class Noddy < Formula + desc "macOS command line tools for DevOps" + homepage "https://github.com/clouddrove/noddy" + url "https://github.com/clouddrove/noddy/archive/refs/tags/v1.0.0.tar.gz" + sha256 "7824218f528fa0de6d25d064a8a9def1ead8bdd4dfcd88edf43173bea2754463" + license "Apache-2.0" + version "1.0.0" + + depends_on :macos + + def install + # noddy sources its plugins from toyland/ beside the script, so the two + # travel together into libexec and only the entry point is linked onto + # PATH. The script resolves symlinks before locating toyland/. + libexec.install "noddy", "toyland" + bin.install_symlink libexec/"noddy" + + bash_completion.install "toyland/completion/noddy.bash" => "noddy" + zsh_completion.install "toyland/completion/_noddy" + fish_completion.install "toyland/completion/noddy.fish" + end + + def caveats + <<~EOS + Optional extras some commands use: + brew install pv # progress bar for tar:compress + brew install fdupes # find:duplicated + brew install glances # system + Commands that drive the UI, such as lock and eject-all, need your + terminal granted under System Settings > Privacy & Security. + EOS + end + + test do + assert_match "noddy", shell_output("#{bin}/noddy help") + assert_match "git", shell_output("#{bin}/noddy categories") + system "#{bin}/noddy", "info" + end +end From ade99542e5bb3801ab060e37f09bd46f66bc3b9b Mon Sep 17 00:00:00 2001 From: Anmol Nagpal Date: Tue, 21 Jul 2026 06:03:59 -0400 Subject: [PATCH 2/2] ci: test changed formulae on macOS The tap had no CI, so a formula could be merged without anyone confirming it installs. Add a workflow that audits, installs from source and runs the test block for every formula a change touches. Scoped to changed files deliberately: installing every formula on every push would fail on unrelated pre-existing issues and would say nothing about the change under review. brew audit, install and test take a formula name rather than a path, and passing a path to audit is disabled outright, so the checkout is registered as the tap first and the formulae referred to by name. Also fixes the noddy formula, which this workflow would have caught: libexec.install moves toyland/ rather than copying it, so the completion files no longer existed by the time they were installed and the build failed. Completions are now installed first. Four brew audit --strict findings are fixed alongside: a redundant version, 'command line' in the description, and a string interpolation in the test block. --- .github/workflows/test-formulae.yml | 115 ++++++++++++++++++++++++++++ Formula/noddy.rb | 16 ++-- 2 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/test-formulae.yml diff --git a/.github/workflows/test-formulae.yml b/.github/workflows/test-formulae.yml new file mode 100644 index 0000000..d6f13b2 --- /dev/null +++ b/.github/workflows/test-formulae.yml @@ -0,0 +1,115 @@ +name: Test formulae + +on: + pull_request: + push: + branches: [master] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: Install and test changed formulae + runs-on: macos-latest + env: + HOMEBREW_NO_AUTO_UPDATE: "1" + HOMEBREW_NO_INSTALL_CLEANUP: "1" + HOMEBREW_NO_ANALYTICS: "1" + HOMEBREW_NO_ENV_HINTS: "1" + steps: + - uses: actions/checkout@v7 + with: + # Need the base commit to diff against. + fetch-depth: 0 + + # Only the formulae touched by this change are installed. Testing every + # formula on every push would fail on unrelated pre-existing issues and + # would say nothing about the change under review. + - name: Determine changed formulae + id: changed + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + base="origin/${{ github.base_ref }}" + git fetch -q origin "${{ github.base_ref }}" + else + base="${{ github.event.before }}" + # First push to a branch has no usable before-commit. + if ! git cat-file -e "$base^{commit}" 2>/dev/null; then + base="HEAD~1" + fi + fi + + formulae=$(git diff --name-only --diff-filter=d "$base" HEAD -- '*.rb' | tr '\n' ' ') + echo "formulae=${formulae}" >> "$GITHUB_OUTPUT" + + if [ -z "$formulae" ]; then + echo "No formula changes in this diff." + else + echo "Changed formulae: ${formulae}" + fi + + # brew audit, install and test all take a formula name rather than a + # path: "brew audit [path ...]" is disabled outright. Register this + # checkout as the tap so the formulae can be referred to by name. + - name: Register the checkout as a tap + if: steps.changed.outputs.formulae != '' + run: | + tap_dir="$(brew --repository)/Library/Taps/clouddrove/homebrew-tap" + mkdir -p "$(dirname "$tap_dir")" + rm -rf "$tap_dir" + ln -s "$GITHUB_WORKSPACE" "$tap_dir" + brew tap | grep clouddrove + + - name: Audit + if: steps.changed.outputs.formulae != '' + run: | + # --online also checks that the homepage and download URL resolve + # and that the checksum matches what is published. + for f in ${{ steps.changed.outputs.formulae }}; do + name="clouddrove/tap/$(basename "$f" .rb)" + echo "::group::brew audit $name" + brew audit --strict --online "$name" || { + echo "::error file=$f::brew audit failed" + exit 1 + } + echo "::endgroup::" + done + + - name: Install from source + if: steps.changed.outputs.formulae != '' + run: | + for f in ${{ steps.changed.outputs.formulae }}; do + name="clouddrove/tap/$(basename "$f" .rb)" + echo "::group::brew install $name" + brew install --build-from-source --verbose "$name" || { + echo "::error file=$f::brew install failed" + exit 1 + } + echo "::endgroup::" + done + + - name: Run the formula test block + if: steps.changed.outputs.formulae != '' + run: | + for f in ${{ steps.changed.outputs.formulae }}; do + name="clouddrove/tap/$(basename "$f" .rb)" + echo "::group::brew test $name" + brew test --verbose "$name" || { + echo "::error file=$f::brew test failed" + exit 1 + } + echo "::endgroup::" + done + + - name: List what was installed + if: steps.changed.outputs.formulae != '' + run: | + for f in ${{ steps.changed.outputs.formulae }}; do + name=$(basename "$f" .rb) + echo "::group::brew list $name" + brew list --verbose "$name" + echo "::endgroup::" + done diff --git a/Formula/noddy.rb b/Formula/noddy.rb index 6d75492..b5a85b7 100644 --- a/Formula/noddy.rb +++ b/Formula/noddy.rb @@ -1,23 +1,24 @@ class Noddy < Formula - desc "macOS command line tools for DevOps" + desc "macOS command-line tools for DevOps" homepage "https://github.com/clouddrove/noddy" url "https://github.com/clouddrove/noddy/archive/refs/tags/v1.0.0.tar.gz" sha256 "7824218f528fa0de6d25d064a8a9def1ead8bdd4dfcd88edf43173bea2754463" license "Apache-2.0" - version "1.0.0" depends_on :macos def install + # Completions first: libexec.install moves toyland/ rather than copying + # it, so these paths stop existing once it has run. + bash_completion.install "toyland/completion/noddy.bash" => "noddy" + zsh_completion.install "toyland/completion/_noddy" + fish_completion.install "toyland/completion/noddy.fish" + # noddy sources its plugins from toyland/ beside the script, so the two # travel together into libexec and only the entry point is linked onto # PATH. The script resolves symlinks before locating toyland/. libexec.install "noddy", "toyland" bin.install_symlink libexec/"noddy" - - bash_completion.install "toyland/completion/noddy.bash" => "noddy" - zsh_completion.install "toyland/completion/_noddy" - fish_completion.install "toyland/completion/noddy.fish" end def caveats @@ -26,6 +27,7 @@ def caveats brew install pv # progress bar for tar:compress brew install fdupes # find:duplicated brew install glances # system + Commands that drive the UI, such as lock and eject-all, need your terminal granted under System Settings > Privacy & Security. EOS @@ -34,6 +36,6 @@ def caveats test do assert_match "noddy", shell_output("#{bin}/noddy help") assert_match "git", shell_output("#{bin}/noddy categories") - system "#{bin}/noddy", "info" + system bin/"noddy", "info" end end