From f36998e797d3cc7313056085eec55c5ee1f31985 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Fri, 21 Aug 2026 11:58:17 -0400 Subject: [PATCH 1/6] Add Homebrew formula and release checklist (#49) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `Formula/git-trees.rb` as the source of truth for the Homebrew formula, pinned to the v1.0.2 release tarball with a verified sha256. The formula installs the script as `git-trees` on PATH and stages `AGENTS.md.template` in `pkgshare`, since `TREES_AGENTS_TEMPLATE` defaults to `~/.config/git-trees/AGENTS.md` and a formula must not write into the user's home directory — a `caveats` block explains how to put it in place. Add `docs/RELEASING.md` covering the manual release process: cut and push the tag, hash the new tarball, bump `url` + `sha256`, copy the formula into `brightdigit/homebrew-tap`, and verify with `brew install`. It also records the shell-completions follow-up for v1.0.3 (#51). Add a Homebrew section to the README's `## Install`, noting the formula is not yet published to the tap. This does not push to the tap repo; the formula must be copied there to take effect. Co-Authored-By: Claude Opus 5 (1M context) --- Formula/git-trees.rb | 36 +++++++++++++++ README.md | 14 ++++++ docs/RELEASING.md | 106 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 Formula/git-trees.rb create mode 100644 docs/RELEASING.md diff --git a/Formula/git-trees.rb b/Formula/git-trees.rb new file mode 100644 index 0000000..85821e7 --- /dev/null +++ b/Formula/git-trees.rb @@ -0,0 +1,36 @@ +class GitTrees < Formula + desc "Git subcommand for managing a bare-repo plus worktrees layout" + homepage "https://github.com/brightdigit/git-trees" + url "https://github.com/brightdigit/git-trees/archive/refs/tags/v1.0.2.tar.gz" + sha256 "301a8ab3f3a860c8e3125a61d45527f7a2f70a881c41d3d2ea899d47d4b547ef" + license "MIT" + + def install + bin.install "git-trees" + # TREES_AGENTS_TEMPLATE defaults to ~/.config/git-trees/AGENTS.md, which a + # formula must not write. Stage the template in the prefix and let caveats + # tell the user how to put it in place. + pkgshare.install "AGENTS.md.template" + end + + def caveats + <<~EOS + `git trees init` and `git trees root --agents` seed an AGENTS.md at the + container root from TREES_AGENTS_TEMPLATE, which defaults to + ~/.config/git-trees/AGENTS.md. Formulae cannot write there, so copy the + bundled template yourself: + + mkdir -p ~/.config/git-trees + cp #{pkgshare}/AGENTS.md.template ~/.config/git-trees/AGENTS.md + + Or point TREES_AGENTS_TEMPLATE at the bundled copy instead: + + export TREES_AGENTS_TEMPLATE=#{pkgshare}/AGENTS.md.template + EOS + end + + test do + # `help` writes its usage to stderr and exits 0. + assert_match "usage: git trees", shell_output("#{bin}/git-trees help 2>&1") + end +end diff --git a/README.md b/README.md index e5588cf..5fb5d45 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,20 @@ fi `main` is the stable release. A re-install from these URLs picks up the current stable script and template. +**Homebrew.** Not yet published — the formula lives at +[`Formula/git-trees.rb`](Formula/git-trees.rb) in this repo and takes effect +once it is pushed to the tap (see [`docs/RELEASING.md`](docs/RELEASING.md)). +After that: + +```bash +brew tap brightdigit/tap +brew install git-trees +``` + +Homebrew cannot write to your home directory, so this path installs the script +but not the agents template. `brew install` prints the one command that puts the +bundled template at `~/.config/git-trees/AGENTS.md`. + Either way, make sure the destination is on your `PATH`: ```bash diff --git a/docs/RELEASING.md b/docs/RELEASING.md new file mode 100644 index 0000000..576d27e --- /dev/null +++ b/docs/RELEASING.md @@ -0,0 +1,106 @@ +# Releasing + +There is no release automation. Tags are cut by hand, and the Homebrew formula +is updated by hand to match. This file is the process. + +`Formula/git-trees.rb` in this repo is the **source of truth** for the formula, +but Homebrew never reads it from here. It takes effect only once it is copied +into the tap repo, [`brightdigit/homebrew-tap`](https://github.com/brightdigit/homebrew-tap), +which is what `brew tap brightdigit/tap` clones. + +## 1. Cut and push the tag + +Update `CHANGELOG.md` with the new version's `## What's Changed` section first, +then tag the release commit: + +```bash +git tag -a v1.0.3 -m "v1.0.3" +git push origin v1.0.3 +``` + +GitHub generates the source tarball for the tag automatically at +`https://github.com/brightdigit/git-trees/archive/refs/tags/v1.0.3.tar.gz`. + +## 2. Compute the sha256 of the new tarball + +Wait for the tag to appear on GitHub, then hash the tarball: + +```bash +curl -fsSL https://github.com/brightdigit/git-trees/archive/refs/tags/v1.0.3.tar.gz \ + | shasum -a 256 +``` + +`curl -f` makes a missing tag fail rather than hashing a 404 body — without it +you get a plausible-looking hash for the wrong bytes. If you want to be sure, +download to a file and check the contents: + +```bash +curl -fsSL -o /tmp/git-trees.tar.gz \ + https://github.com/brightdigit/git-trees/archive/refs/tags/v1.0.3.tar.gz +tar tzf /tmp/git-trees.tar.gz # should list git-trees, AGENTS.md.template, LICENSE +shasum -a 256 /tmp/git-trees.tar.gz +``` + +## 3. Bump the formula in this repo + +Edit `Formula/git-trees.rb` and update both fields together — a stale `sha256` +against a new `url` fails every install with a checksum mismatch: + +- `url` → the new tag's tarball +- `sha256` → the hash from step 2 + +Check it before committing: + +```bash +ruby -c Formula/git-trees.rb +brew style Formula/git-trees.rb +``` + +Commit the bump to `main`. + +## 4. Copy the formula into the tap + +`brew audit` requires a formula that lives in a tap, so run it after copying: + +```bash +git clone git@github.com:brightdigit/homebrew-tap.git +cp Formula/git-trees.rb homebrew-tap/Formula/git-trees.rb +cd homebrew-tap +brew audit --strict --formula brightdigit/tap/git-trees +git add Formula/git-trees.rb +git commit -m "git-trees 1.0.3" +git push +``` + +## 5. Verify with brew install + +From a clean state, install through the tap the way a user would: + +```bash +brew untap brightdigit/tap 2>/dev/null # ensure a fresh clone +brew tap brightdigit/tap +brew install git-trees +git trees help +``` + +`git trees help` writes its usage to stderr and exits 0. Then leave the machine +as you found it if this was only a verification: + +```bash +brew uninstall git-trees +``` + +## Follow-up: shell completions + +Once shell completions ship (PR #51, targeted at v1.0.3), the completion files +are part of the release tarball and the formula's `install` block should install +them: + +```ruby +bash_completion.install "completions/git-trees.bash" +zsh_completion.install "completions/_git-trees" => "_git-trees" +``` + +Add those lines only in the formula revision whose `url` points at a tag that +actually contains `completions/` — referencing files missing from the tarball +breaks `brew install` outright. From 7043a614c1f940f66425baea51324b2a045464ee Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Tue, 25 Aug 2026 14:33:23 -0400 Subject: [PATCH 2/6] Embed homebrew-tap as a subrepo and automate formula bumps. Move the formula under homebrew-tap/ and add a release workflow that updates url/sha256 and git-subrepo pushes to brightdigit/homebrew-tap. Co-authored-by: Cursor --- .github/workflows/homebrew-tap.yml | 95 ++++++++++++++++++ README.md | 8 +- docs/RELEASING.md | 99 +++++++++---------- .../Formula}/git-trees.rb | 0 homebrew-tap/README.md | 14 +++ 5 files changed, 163 insertions(+), 53 deletions(-) create mode 100644 .github/workflows/homebrew-tap.yml rename {Formula => homebrew-tap/Formula}/git-trees.rb (100%) create mode 100644 homebrew-tap/README.md diff --git a/.github/workflows/homebrew-tap.yml b/.github/workflows/homebrew-tap.yml new file mode 100644 index 0000000..66942fc --- /dev/null +++ b/.github/workflows/homebrew-tap.yml @@ -0,0 +1,95 @@ +name: Homebrew tap + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: Release tag to publish (e.g. v1.0.3) + required: true + type: string + +permissions: + contents: write + +jobs: + update: + runs-on: ubuntu-latest + steps: + - name: Check tap token + env: + HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + run: | + if [ -z "$HOMEBREW_TAP_TOKEN" ]; then + echo "HOMEBREW_TAP_TOKEN secret is not set" >&2 + echo "Create a PAT (contents:write on brightdigit/homebrew-tap and brightdigit/git-trees)" >&2 + echo "and add it as repository secret HOMEBREW_TAP_TOKEN." >&2 + exit 1 + fi + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.HOMEBREW_TAP_TOKEN }} + + - name: Install git-subrepo + run: | + git clone --depth 1 https://github.com/ingydotnet/git-subrepo.git /tmp/git-subrepo + echo "/tmp/git-subrepo/lib" >> "$GITHUB_PATH" + + - name: Resolve tag + id: meta + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + tag='${{ inputs.tag }}' + else + tag='${{ github.event.release.tag_name }}' + fi + case "$tag" in + v*) ;; + *) + echo "Expected a v-prefixed tag, got: $tag" >&2 + exit 1 + ;; + esac + url="https://github.com/${{ github.repository }}/archive/refs/tags/${tag}.tar.gz" + echo "tag=$tag" >> "$GITHUB_OUTPUT" + echo "url=$url" >> "$GITHUB_OUTPUT" + + - name: Bump formula url and sha256 + run: | + tag='${{ steps.meta.outputs.tag }}' + url='${{ steps.meta.outputs.url }}' + formula=homebrew-tap/Formula/git-trees.rb + + # curl -f fails on a missing tag instead of hashing a 404 body. + sha=$(curl -fsSL "$url" | sha256sum | awk '{ print $1 }') + echo "sha256=$sha" + + # Replace only the formula url/sha256 string values. + perl -i -pe "s/^(\\s*url\\s+)\"[^\"]*\"/\${1}\"$url\"/" "$formula" + perl -i -pe "s/^(\\s*sha256\\s+)\"[^\"]*\"/\${1}\"$sha\"/" "$formula" + + grep -F "url \"$url\"" "$formula" + grep -F "sha256 \"$sha\"" "$formula" + ruby -c "$formula" + + - name: Commit formula bump + run: | + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add homebrew-tap/Formula/git-trees.rb + if git diff --staged --quiet; then + echo "Formula already at ${{ steps.meta.outputs.tag }}; nothing to commit" + else + git commit -m "homebrew: git-trees ${{ steps.meta.outputs.tag }}" + git push + fi + + - name: Push subrepo to brightdigit/homebrew-tap + env: + HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + run: | + git config --global url."https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/".insteadOf "https://github.com/" + git subrepo push homebrew-tap diff --git a/README.md b/README.md index 5fb5d45..4d6795a 100644 --- a/README.md +++ b/README.md @@ -139,9 +139,11 @@ fi stable script and template. **Homebrew.** Not yet published — the formula lives at -[`Formula/git-trees.rb`](Formula/git-trees.rb) in this repo and takes effect -once it is pushed to the tap (see [`docs/RELEASING.md`](docs/RELEASING.md)). -After that: +[`homebrew-tap/Formula/git-trees.rb`](homebrew-tap/Formula/git-trees.rb) (a +[git subrepo](https://github.com/ingydotnet/git-subrepo) of +[`brightdigit/homebrew-tap`](https://github.com/brightdigit/homebrew-tap)) and +takes effect once it is pushed to the tap (see +[`docs/RELEASING.md`](docs/RELEASING.md)). After that: ```bash brew tap brightdigit/tap diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 576d27e..78d41e0 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -1,80 +1,79 @@ # Releasing -There is no release automation. Tags are cut by hand, and the Homebrew formula -is updated by hand to match. This file is the process. +There is no release automation for cutting tags — those are still done by hand. +Once a GitHub release is published, `.github/workflows/homebrew-tap.yml` bumps +the formula and pushes it to the tap via the +[`homebrew-tap/`](../homebrew-tap) [git subrepo](https://github.com/ingydotnet/git-subrepo). -`Formula/git-trees.rb` in this repo is the **source of truth** for the formula, -but Homebrew never reads it from here. It takes effect only once it is copied -into the tap repo, [`brightdigit/homebrew-tap`](https://github.com/brightdigit/homebrew-tap), -which is what `brew tap brightdigit/tap` clones. +`homebrew-tap/Formula/git-trees.rb` in this repo is the **source of truth** for +the formula. Homebrew installs it from +[`brightdigit/homebrew-tap`](https://github.com/brightdigit/homebrew-tap), which +is what `brew tap brightdigit/tap` clones. -## 1. Cut and push the tag +## One-time setup -Update `CHANGELOG.md` with the new version's `## What's Changed` section first, -then tag the release commit: +### 1. Embed the tap as a subrepo + +From a commit that already contains `homebrew-tap/Formula/git-trees.rb`: ```bash -git tag -a v1.0.3 -m "v1.0.3" -git push origin v1.0.3 +git subrepo init homebrew-tap \ + -r https://github.com/brightdigit/homebrew-tap.git \ + -b main +git subrepo push homebrew-tap ``` -GitHub generates the source tarball for the tag automatically at -`https://github.com/brightdigit/git-trees/archive/refs/tags/v1.0.3.tar.gz`. +`init` records `homebrew-tap/.gitrepo`; `push` populates the (possibly empty) +tap remote. Requires push access to `brightdigit/homebrew-tap`. -## 2. Compute the sha256 of the new tarball +### 2. Repository secret -Wait for the tag to appear on GitHub, then hash the tarball: +Add a `HOMEBREW_TAP_TOKEN` secret on `brightdigit/git-trees`: a classic PAT or +fine-grained token with `contents: write` on both `brightdigit/git-trees` and +`brightdigit/homebrew-tap`. The workflow uses it to commit the formula bump here +and to `git subrepo push` the tap. -```bash -curl -fsSL https://github.com/brightdigit/git-trees/archive/refs/tags/v1.0.3.tar.gz \ - | shasum -a 256 -``` +## Cut a release -`curl -f` makes a missing tag fail rather than hashing a 404 body — without it -you get a plausible-looking hash for the wrong bytes. If you want to be sure, -download to a file and check the contents: +Update `CHANGELOG.md` with the new version's `## What's Changed` section, then: ```bash -curl -fsSL -o /tmp/git-trees.tar.gz \ - https://github.com/brightdigit/git-trees/archive/refs/tags/v1.0.3.tar.gz -tar tzf /tmp/git-trees.tar.gz # should list git-trees, AGENTS.md.template, LICENSE -shasum -a 256 /tmp/git-trees.tar.gz +git tag -a v1.0.3 -m "v1.0.3" +git push origin v1.0.3 ``` -## 3. Bump the formula in this repo +Create the GitHub release for that tag (or publish from the tag). The +`Homebrew tap` workflow then: -Edit `Formula/git-trees.rb` and update both fields together — a stale `sha256` -against a new `url` fails every install with a checksum mismatch: +1. Downloads `https://github.com/brightdigit/git-trees/archive/refs/tags/v1.0.3.tar.gz` +2. Rewrites `url` / `sha256` in `homebrew-tap/Formula/git-trees.rb` +3. Commits and pushes that bump to this repo +4. Runs `git subrepo push homebrew-tap` -- `url` → the new tag's tarball -- `sha256` → the hash from step 2 +To re-run for an existing tag: **Actions → Homebrew tap → Run workflow** and +pass the tag (e.g. `v1.0.3`). -Check it before committing: +## Manual fallback -```bash -ruby -c Formula/git-trees.rb -brew style Formula/git-trees.rb -``` +If the workflow cannot run, bump and push by hand: -Commit the bump to `main`. - -## 4. Copy the formula into the tap +```bash +tag=v1.0.3 +url="https://github.com/brightdigit/git-trees/archive/refs/tags/${tag}.tar.gz" +sha=$(curl -fsSL "$url" | shasum -a 256 | awk '{ print $1 }') -`brew audit` requires a formula that lives in a tap, so run it after copying: +# edit homebrew-tap/Formula/git-trees.rb — set url and sha256 together +ruby -c homebrew-tap/Formula/git-trees.rb +brew style homebrew-tap/Formula/git-trees.rb -```bash -git clone git@github.com:brightdigit/homebrew-tap.git -cp Formula/git-trees.rb homebrew-tap/Formula/git-trees.rb -cd homebrew-tap +# optional: audit requires the formula to live in a tap checkout +git subrepo push homebrew-tap +brew untap brightdigit/tap 2>/dev/null +brew tap brightdigit/tap brew audit --strict --formula brightdigit/tap/git-trees -git add Formula/git-trees.rb -git commit -m "git-trees 1.0.3" -git push ``` -## 5. Verify with brew install - -From a clean state, install through the tap the way a user would: +## Verify with brew install ```bash brew untap brightdigit/tap 2>/dev/null # ensure a fresh clone diff --git a/Formula/git-trees.rb b/homebrew-tap/Formula/git-trees.rb similarity index 100% rename from Formula/git-trees.rb rename to homebrew-tap/Formula/git-trees.rb diff --git a/homebrew-tap/README.md b/homebrew-tap/README.md new file mode 100644 index 0000000..107d428 --- /dev/null +++ b/homebrew-tap/README.md @@ -0,0 +1,14 @@ +# brightdigit/homebrew-tap + +Homebrew tap for [BrightDigit](https://github.com/brightdigit) formulae. + +## Usage + +```bash +brew tap brightdigit/tap +brew install git-trees +``` + +This repository is maintained as a [git subrepo](https://github.com/ingydotnet/git-subrepo) +inside [brightdigit/git-trees](https://github.com/brightdigit/git-trees). Formula bumps +are pushed here automatically on each git-trees GitHub release. From 08b6fff8c93b96cf8b676efbd33f00b5ec187277 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Tue, 25 Aug 2026 14:33:26 -0400 Subject: [PATCH 3/6] git subrepo init --remote=https://github.com/brightdigit/homebrew-tap.git --branch=main homebrew-tap subrepo: subdir: "homebrew-tap" merged: "7043a61" upstream: origin: "https://github.com/brightdigit/homebrew-tap.git" branch: "main" commit: "none" git-subrepo: version: "0.4.9" origin: "https://github.com/Homebrew/brew" commit: "b48c7994b5" --- homebrew-tap/.gitrepo | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 homebrew-tap/.gitrepo diff --git a/homebrew-tap/.gitrepo b/homebrew-tap/.gitrepo new file mode 100644 index 0000000..2499766 --- /dev/null +++ b/homebrew-tap/.gitrepo @@ -0,0 +1,11 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme +; +[subrepo] + remote = https://github.com/brightdigit/homebrew-tap.git + branch = main + commit = + method = merge + cmdver = 0.4.9 From b31130808bac3fff1918dbf23090640bb7387ce8 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Tue, 25 Aug 2026 14:33:38 -0400 Subject: [PATCH 4/6] git subrepo push homebrew-tap subrepo: subdir: "homebrew-tap" merged: "9c94a73" upstream: origin: "https://github.com/brightdigit/homebrew-tap.git" branch: "main" commit: "9c94a73" git-subrepo: version: "0.4.9" origin: "https://github.com/Homebrew/brew" commit: "b48c7994b5" --- homebrew-tap/.gitrepo | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homebrew-tap/.gitrepo b/homebrew-tap/.gitrepo index 2499766..85c8894 100644 --- a/homebrew-tap/.gitrepo +++ b/homebrew-tap/.gitrepo @@ -6,6 +6,7 @@ [subrepo] remote = https://github.com/brightdigit/homebrew-tap.git branch = main - commit = + commit = 9c94a73ab51d66c9141a2b3d5bd99c69ac077f58 method = merge cmdver = 0.4.9 + parent = 08b6fff8c93b96cf8b676efbd33f00b5ec187277 From 38bf437e84eaa789b738088e96d8503a90d284d0 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Tue, 25 Aug 2026 14:41:39 -0400 Subject: [PATCH 5/6] Trigger Homebrew tap workflow on 49-homebrew pushes. workflow_dispatch only registers once the file is on the default branch; a branch push trigger lets us verify the tap write with HOMEBREW_TAP_TOKEN first. Co-authored-by: Cursor --- .github/workflows/homebrew-tap.yml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/homebrew-tap.yml b/.github/workflows/homebrew-tap.yml index 66942fc..7e67284 100644 --- a/.github/workflows/homebrew-tap.yml +++ b/.github/workflows/homebrew-tap.yml @@ -9,6 +9,10 @@ on: description: Release tag to publish (e.g. v1.0.3) required: true type: string + # Temporary: exercise the tap push from this PR branch (workflow_dispatch + # only works once the workflow exists on the default branch). + push: + branches: [49-homebrew] permissions: contents: write @@ -41,10 +45,18 @@ jobs: - name: Resolve tag id: meta run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - tag='${{ inputs.tag }}' - else - tag='${{ github.event.release.tag_name }}' + case "${{ github.event_name }}" in + workflow_dispatch) tag='${{ inputs.tag }}' ;; + release) tag='${{ github.event.release.tag_name }}' ;; + push) tag=$(git tag -l 'v*' --sort=-v:refname | head -n1) ;; + *) + echo "Unsupported event: ${{ github.event_name }}" >&2 + exit 1 + ;; + esac + if [ -z "$tag" ]; then + echo "No tag to publish" >&2 + exit 1 fi case "$tag" in v*) ;; From 6489165e8ed518f4396ba0239e603aa77ad668cf Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Tue, 25 Aug 2026 14:42:17 -0400 Subject: [PATCH 6/6] Fix formula bump when the release URL contains slashes. Pass url/sha256 through ENV into perl so s/// is not split on https://. Co-authored-by: Cursor --- .github/workflows/homebrew-tap.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/homebrew-tap.yml b/.github/workflows/homebrew-tap.yml index 7e67284..5b9d21b 100644 --- a/.github/workflows/homebrew-tap.yml +++ b/.github/workflows/homebrew-tap.yml @@ -79,9 +79,9 @@ jobs: sha=$(curl -fsSL "$url" | sha256sum | awk '{ print $1 }') echo "sha256=$sha" - # Replace only the formula url/sha256 string values. - perl -i -pe "s/^(\\s*url\\s+)\"[^\"]*\"/\${1}\"$url\"/" "$formula" - perl -i -pe "s/^(\\s*sha256\\s+)\"[^\"]*\"/\${1}\"$sha\"/" "$formula" + # Pass values via ENV so / in the URL cannot break s/// delimiters. + URL="$url" perl -i -pe 's/^(\s*url\s+)"[^"]*"/$1"$ENV{URL}"/' "$formula" + SHA="$sha" perl -i -pe 's/^(\s*sha256\s+)"[^"]*"/$1"$ENV{SHA}"/' "$formula" grep -F "url \"$url\"" "$formula" grep -F "sha256 \"$sha\"" "$formula"