Skip to content

Keep the Homebrew formula on the current release tarball - #2558

Merged
timsehn merged 2 commits into
masterfrom
fix/2542-homebrew-formula-bump
Sep 2, 2026
Merged

Keep the Homebrew formula on the current release tarball#2558
timsehn merged 2 commits into
masterfrom
fix/2542-homebrew-formula-bump

Conversation

@timsehn

@timsehn timsehn commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Fixes #2542.

packaging/homebrew/doltlite.rb pinned v0.10.6, which predates the beta format freeze. Homebrew-core will not accept the formula until notability, but this in-repo copy is the source of truth and was shipping a stale engine to anyone who used it.

The formula now points at the v0.50.2 autoconf tarball. packaging/homebrew/bump-formula.sh rewrites url/sha256. A release-homebrew job runs after the GitHub release has the tarball, hashes it, and pushes the bump to master.

test/homebrew_formula_test.sh rejects a v0.10.6 pin, checks url/tarball versions agree, and exercises the bump script.

Co-Authored-By: Grok 4.6 noreply@x.ai

@itoqa

itoqa Bot commented Sep 2, 2026

Copy link
Copy Markdown

Ito QA test results
Commit: 2877cca: 14 test cases ran, 4 failed ❌, 10 passed ✅.

Summary

Coverage spans normal package-version updates and release publication, validation of malformed inputs and authorization boundaries, and operational edge cases such as duplicate entries and overlapping releases. It also exercises whether the published package can actually build and install, revealing problems in both release integrity and downstream usability.

Not safe to merge yet — a high-severity packaging failure makes the released package unusable, and additional medium-severity defects can publish mixed, malformed, or stale formula updates. These are all attributable to this PR and affect the release path rather than being isolated background observations.

Tests run by Ito

View full run

Result Severity Type Description
High severity Install The package build stops before installing any of the expected tools or development files because the downloaded source archive does not contain one of its required build definition files.
Medium severity General The updater exited with status 0 even though the fixture had two matching URLs. It rewrote the first URL and the checksum, left the second URL at version 0.50.2, and therefore did not meet the expected fail-closed behavior for duplicate matches.
Medium severity Rev When two releases update the formula at the same time, one update can be rejected or missing from the final branch.
Medium severity Rev The helper returned success for a path-like version and changed the formula. It wrote a URL containing path traversal segments, although malformed versions should fail and leave the file unchanged.
General When the push was denied, the release check failed and the remote formula stayed unchanged. A later authorized retry published the complete intended update.
General A release token without permission to push master was denied, the workflow reported failure, and the remote formula stayed unchanged.
Bump The updater changed the temporary formula to the 9.9.9 release URL and supplied checksum while keeping the selected formula intact.
Bump The updater accepted version v9.9.9 and created one release path using 9.9.9, without adding a second v or changing the tarball name incorrectly.
Bump The updater rejected the malformed checksum with status 2 and left the formula unchanged.
Formula The local formula checks passed all 6 checks. The package source uses version 0.50.2, the URL and tarball versions match, and the checksum has the required format. A real Homebrew or GitHub download was not attempted.
Release An automatic release used the matching archive checksum, updated the formula, and published the change to master.
Release An empty release token and an unavailable release file both stopped the job before checkout or push, and the formula stayed unchanged.
Rev The package source points to version 0.50.2, and the downloaded archive has the exact SHA-256 listed for it. The archive also contains the expected versioned DoltLite source directory.
Suite The Homebrew checks ran successfully as part of the native suite. The full aggregate had unrelated failures in other suites caused by missing build files and unavailable native test setup.

Tip

Reply with @itoqa to send us feedback on this test run.

Comment thread packaging/homebrew/bump-formula.sh Outdated
url = sys.argv[2]
sha256 = sys.argv[3]
text = path.read_text()
text, n_url = re.subn(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

View replay

Medium severity Duplicate formula leaves mixed versions

What failed: The updater exited with status 0 even though the fixture had two matching URLs. It rewrote the first URL and the checksum, left the second URL at version 0.50.2, and therefore did not meet the expected fail-closed behavior for duplicate matches.

Impact · Steps · Stub / mock · Analysis · Why this is likely a bug
  • Severity: Medium Medium severity
  • Impact: Users installing the affected package may encounter a formula that points to two different releases, causing installation to fail or use the wrong release until the formula is corrected.
  • Steps to Reproduce:
    1. Copy packaging/homebrew/doltlite.rb to a temporary formula file.
    2. Add a second copy of the canonical release URL to the temporary file.
    3. Run bump-formula.sh with version 9.9.9 and a valid 64-character lowercase checksum.
    4. Check the exit status and inspect every URL and checksum in the temporary file.
  • Stub / mock content: The test used a temporary copy of the Homebrew formula with an extra URL entry and a synthetic lowercase checksum; no application mocks, route interception, or bypasses were used.
  • Code Analysis: The PR-added implementation in packaging/homebrew/bump-formula.sh reads the entire formula at line 59, then calls re.subn for the URL at lines 60-65 with count=1 and for the checksum at lines 66-71 with count=1. With two matching URLs, n_url is still 1 because Python intentionally replaces only the first match; the same behavior applies independently to duplicate checksum entries. The guard at lines 72-75 checks only whether each replacement count is nonzero and equal to 1, so it cannot detect that additional matches remain. Because the first URL and checksum are held in the in-memory text and line 76 writes that text after the guard, a duplicate URL can be published alongside an unchanged old URL while the command returns success. The release workflow invokes this updater at .github/workflows/release.yml:1323 and then stages and pushes the formula at lines 1328-1330, so the partial rewrite is on the publication path. The smallest practical fix is to count all matches before writing and require exactly one URL and exactly one checksum, or to reject when either pattern has more than one match; retain the original file until both counts pass.
  • Why this is likely a bug: This is not a runner-only symptom: the local reproduction produced exit_status=0 and showed a new 9.9.9 URL next to an unchanged 0.50.2 URL. The source explains the result exactly, because count=1 makes the match count mean replacements performed rather than total matches found, and write_text persists the incomplete result. Duplicate formula entries are an organic integrity failure that can arise from an accidental merge or manual edit, and the release job treats a zero exit status as safe to commit and push. Rejecting every count other than exactly one before writing is a targeted fix that preserves the normal single-entry update path.
Relevant code

packaging/homebrew/bump-formula.sh:60-75

text, n_url = re.subn(
    r'url "https://github.com/dolthub/doltlite/releases/download/v[^"]+"',
    f'url "{url}"',
    text,
    count=1,
)
text, n_sha = re.subn(
    r'sha256 "[0-9a-fA-F]+"',
    f'sha256 "{sha256}"',
    text,
    count=1,
)
if n_url != 1 or n_sha != 1:

packaging/homebrew/bump-formula.sh:76-77

path.write_text(text)

.github/workflows/release.yml:1320-1330

gh release download "${VERSION}" --repo dolthub/doltlite \
+  --pattern "${asset}" --dir /tmp
sha256=$(sha256sum "/tmp/${asset}" | awk '{print $1}')
bash packaging/homebrew/bump-formula.sh "${version}" "${sha256}"
...
git add packaging/homebrew/doltlite.rb
git commit -m "Bump Homebrew formula to ${version}"
git push origin master
Evidence Package
Copy prompt for an agent
Ito QA identified the following failure during automated PR testing. Please investigate and propose a fix.

**Medium severity — Duplicate formula leaves mixed versions**

**What failed:** The updater exited with status 0 even though the fixture had two matching URLs. It rewrote the first URL and the checksum, left the second URL at version 0.50.2, and therefore did not meet the expected fail-closed behavior for duplicate matches.

- **Impact:** Users installing the affected package may encounter a formula that points to two different releases, causing installation to fail or use the wrong release until the formula is corrected.
- **Steps to reproduce:**
  1. Copy packaging/homebrew/doltlite.rb to a temporary formula file.
  2. Add a second copy of the canonical release URL to the temporary file.
  3. Run bump-formula.sh with version 9.9.9 and a valid 64-character lowercase checksum.
  4. Check the exit status and inspect every URL and checksum in the temporary file.
- **Stub / mock content:** The test used a temporary copy of the Homebrew formula with an extra URL entry and a synthetic lowercase checksum; no application mocks, route interception, or bypasses were used.
- **Code analysis:** The PR-added implementation in packaging/homebrew/bump-formula.sh reads the entire formula at line 59, then calls re.subn for the URL at lines 60-65 with count=1 and for the checksum at lines 66-71 with count=1. With two matching URLs, n_url is still 1 because Python intentionally replaces only the first match; the same behavior applies independently to duplicate checksum entries. The guard at lines 72-75 checks only whether each replacement count is nonzero and equal to 1, so it cannot detect that additional matches remain. Because the first URL and checksum are held in the in-memory text and line 76 writes that text after the guard, a duplicate URL can be published alongside an unchanged old URL while the command returns success. The release workflow invokes this updater at .github/workflows/release.yml:1323 and then stages and pushes the formula at lines 1328-1330, so the partial rewrite is on the publication path. The smallest practical fix is to count all matches before writing and require exactly one URL and exactly one checksum, or to reject when either pattern has more than one match; retain the original file until both counts pass.
- **Why this is likely a bug:** This is not a runner-only symptom: the local reproduction produced exit_status=0 and showed a new 9.9.9 URL next to an unchanged 0.50.2 URL. The source explains the result exactly, because count=1 makes the match count mean replacements performed rather than total matches found, and write_text persists the incomplete result. Duplicate formula entries are an organic integrity failure that can arise from an accidental merge or manual edit, and the release job treats a zero exit status as safe to commit and push. Rejecting every count other than exactly one before writing is a targeted fix that preserves the normal single-entry update path.

**Relevant code:**

`packaging/homebrew/bump-formula.sh:60-75`

~~~python
text, n_url = re.subn(
    r'url "https://github.com/dolthub/doltlite/releases/download/v[^"]+"',
    f'url "{url}"',
    text,
    count=1,
)
text, n_sha = re.subn(
    r'sha256 "[0-9a-fA-F]+"',
    f'sha256 "{sha256}"',
    text,
    count=1,
)
if n_url != 1 or n_sha != 1:
~~~

`packaging/homebrew/bump-formula.sh:76-77`

~~~python
path.write_text(text)
~~~

`.github/workflows/release.yml:1320-1330`

~~~yaml
gh release download "${VERSION}" --repo dolthub/doltlite \
+  --pattern "${asset}" --dir /tmp
sha256=$(sha256sum "/tmp/${asset}" | awk '{print $1}')
bash packaging/homebrew/bump-formula.sh "${version}" "${sha256}"
...
git add packaging/homebrew/doltlite.rb
git commit -m "Bump Homebrew formula to ${version}"
git push origin master
~~~


uses_from_macos "zlib"

def install

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

View All Evidence

High severity Homebrew package cannot finish building

What failed: The package build stops before installing any of the expected tools or development files because the downloaded source archive does not contain one of its required build definition files.

Impact · Steps · Stub / mock · Analysis · Why this is likely a bug
  • Severity: High High severity
  • Impact: Users who install the affected Homebrew package cannot install or use the DoltLite command-line tool, remote server, or development files. The package remains unusable until a release with the missing build file is published.
  • Steps to Reproduce:
    1. Install the formula from packaging/homebrew/doltlite.rb using Homebrew.
    2. Let Homebrew download and extract the v0.50.2 doltlite-autoconf-0.50.2.tar.gz source archive.
    3. Run the formula build, which configures the source and requests doltlite, doltlite-remotesrv, and doltlite-lib.
    4. Observe that the build cannot complete and that the expected binaries, library, and header are not installed.
  • Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
  • Code Analysis: packaging/homebrew/doltlite.rb lines 21-35 runs ../configure and then invokes make for doltlite, doltlite-remotesrv, and doltlite-lib before installing the binaries, header, and libraries. main.mk lines 559-561 unconditionally executes include $(TOP)/doltlite.mk, so a clean extracted source tree must contain doltlite.mk for configure-generated makefiles to work. The PR-added Package autoconf source step in .github/workflows/release.yml lines 96-104 uses an explicit tar command and includes main.mk, source directories, and other build inputs, but not doltlite.mk. The repository checkout has doltlite.mk, confirming the build dependency exists in source control, while the explicit release file list prevents it from entering the archive. The recorded formula information points at v0.50.2 and the recorded expected installation paths are absent, matching this source-level failure. The targeted remediation is to add doltlite.mk to the tar command's file list, then regenerate and validate the release archive with the formula build commands.
  • Why this is likely a bug: This is a deterministic artifact contract failure rather than a Homebrew or browser limitation. The formula explicitly requires a clean source build and the makefile explicitly requires doltlite.mk, but the PR's release packaging command excludes that file from the archive it publishes. Any user installing the formula from that archive receives the same incomplete source and cannot reach the documented binaries or C API smoke check. The failure is directly attributable to the changed file list, and adding the missing makefile is a narrow fix.
Relevant code

packaging/homebrew/doltlite.rb:21-35

def install
  mkdir "build" do
    system "../configure"
    system "make", "doltlite", "doltlite-remotesrv", "doltlite-lib"
    bin.install "doltlite", "doltlite-remotesrv"
    include.install "sqlite3.h" => "doltlite.h"
    lib.install "libdoltlite.a"
    lib.install OS.mac? ? "libdoltlite.dylib" : "libdoltlite.so"
  end
end

main.mk:559-561

# Prolly engine and version-control layer (see doltlite.mk).
#
include $(TOP)/doltlite.mk

.github/workflows/release.yml:96-104

tar czf doltlite-autoconf-${VERSION}.tar.gz \
  --transform "s,^,doltlite-autoconf-${VERSION}/," \
  configure Makefile.in Makefile.linux-generic Makefile.msc main.mk \
  auto.def VERSION manifest manifest.tags manifest.uuid \
  pragma.h magic.txt lempar.c \
  sqlite3.pc.in sqlite.pc.in \
  src/ ext/ tool/ autosetup/ autoconf/ \
  sqlite3.c sqlite3.h sqlite3ext.h sqlite3session.h \
  .dolt_release_version

doltlite.mk:1-7

#
# doltlite.mk -- prolly engine and version-control layer build definitions.
#
# Kept out of main.mk so upstream SQLite changes to main.mk merge without
# stepping on doltlite's additions. Included from main.mk at the point
# where these lines append to LIBOBJS0 and OPT_FEATURE_FLAGS, so ordering
# is identical to having them inline.
Evidence Package
Copy prompt for an agent
Ito QA identified the following failure during automated PR testing. Please investigate and propose a fix.

**High severity — Homebrew package cannot finish building**

**What failed:** The package build stops before installing any of the expected tools or development files because the downloaded source archive does not contain one of its required build definition files.

- **Impact:** Users who install the affected Homebrew package cannot install or use the DoltLite command-line tool, remote server, or development files. The package remains unusable until a release with the missing build file is published.
- **Steps to reproduce:**
  1. Install the formula from packaging/homebrew/doltlite.rb using Homebrew.
  2. Let Homebrew download and extract the v0.50.2 doltlite-autoconf-0.50.2.tar.gz source archive.
  3. Run the formula build, which configures the source and requests doltlite, doltlite-remotesrv, and doltlite-lib.
  4. Observe that the build cannot complete and that the expected binaries, library, and header are not installed.
- **Stub / mock content:** No stubs, mocks, or bypasses were applied for this test in the recorded run.
- **Code analysis:** packaging/homebrew/doltlite.rb lines 21-35 runs ../configure and then invokes make for doltlite, doltlite-remotesrv, and doltlite-lib before installing the binaries, header, and libraries. main.mk lines 559-561 unconditionally executes `include $(TOP)/doltlite.mk`, so a clean extracted source tree must contain doltlite.mk for configure-generated makefiles to work. The PR-added Package autoconf source step in .github/workflows/release.yml lines 96-104 uses an explicit tar command and includes main.mk, source directories, and other build inputs, but not doltlite.mk. The repository checkout has doltlite.mk, confirming the build dependency exists in source control, while the explicit release file list prevents it from entering the archive. The recorded formula information points at v0.50.2 and the recorded expected installation paths are absent, matching this source-level failure. The targeted remediation is to add doltlite.mk to the tar command's file list, then regenerate and validate the release archive with the formula build commands.
- **Why this is likely a bug:** This is a deterministic artifact contract failure rather than a Homebrew or browser limitation. The formula explicitly requires a clean source build and the makefile explicitly requires doltlite.mk, but the PR's release packaging command excludes that file from the archive it publishes. Any user installing the formula from that archive receives the same incomplete source and cannot reach the documented binaries or C API smoke check. The failure is directly attributable to the changed file list, and adding the missing makefile is a narrow fix.

**Relevant code:**

`packaging/homebrew/doltlite.rb:21-35`

~~~ruby
def install
  mkdir "build" do
    system "../configure"
    system "make", "doltlite", "doltlite-remotesrv", "doltlite-lib"
    bin.install "doltlite", "doltlite-remotesrv"
    include.install "sqlite3.h" => "doltlite.h"
    lib.install "libdoltlite.a"
    lib.install OS.mac? ? "libdoltlite.dylib" : "libdoltlite.so"
  end
end
~~~

`main.mk:559-561`

~~~make
# Prolly engine and version-control layer (see doltlite.mk).
#
include $(TOP)/doltlite.mk
~~~

`.github/workflows/release.yml:96-104`

~~~yaml
tar czf doltlite-autoconf-${VERSION}.tar.gz \
  --transform "s,^,doltlite-autoconf-${VERSION}/," \
  configure Makefile.in Makefile.linux-generic Makefile.msc main.mk \
  auto.def VERSION manifest manifest.tags manifest.uuid \
  pragma.h magic.txt lempar.c \
  sqlite3.pc.in sqlite.pc.in \
  src/ ext/ tool/ autosetup/ autoconf/ \
  sqlite3.c sqlite3.h sqlite3ext.h sqlite3session.h \
  .dolt_release_version
~~~

`doltlite.mk:1-7`

~~~make
#
# doltlite.mk -- prolly engine and version-control layer build definitions.
#
# Kept out of main.mk so upstream SQLite changes to main.mk merge without
# stepping on doltlite's additions. Included from main.mk at the point
# where these lines append to LIBOBJS0 and OPT_FEATURE_FLAGS, so ordering
# is identical to having them inline.
~~~

# homebrew-core accepts a submission. Pinning a stale tarball ships a
# pre-format-freeze engine. After the autoconf tarball is on the GitHub
# release, rewrite url/sha256 and push to master.
release-homebrew:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

View replay

Medium severity Concurrent releases can miss formula updates

What failed: When two releases update the formula at the same time, one update can be rejected or missing from the final branch.

Impact · Steps · Stub / mock · Analysis · Why this is likely a bug
  • Severity: Medium Medium severity
  • Impact: When two releases run at the same time, one release may be missing or the package formula may stay stale. Users may not see the newest release until an operator checks the branch and reruns the release job.
  • Steps to Reproduce:
    1. Start two tagged releases close together so both formula-update jobs run at the same time.
    2. Let both jobs check out the master branch, calculate their version and checksum changes, and create commits.
    3. Let both jobs push their commits to master.
    4. Check the job results and confirm that one push can be rejected as non-fast-forward or that the final formula does not contain both updates.
  • Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
  • Code Analysis: The PR diff adds .github/workflows/release.yml lines 1288-1330 as the release-homebrew job. The job checks out ref master at lines 1310-1313, downloads the release asset and rewrites the formula at lines 1315-1323, then stages and commits the result at lines 1328-1329 and executes git push origin master at line 1330. The added job has no job-level concurrency group, and the workflow has no pull/rebase or retry between checkout and push. Two overlapping runs can therefore start from the same master commit; after the first push advances master, the second direct push is non-fast-forward and exits because set -e is enabled. If a later process retries from an older checkout or otherwise publishes its stale commit, it can also overwrite the other release's formula update. The smallest practical fix is to serialize this job with a concurrency group for master formula publication, or fetch and rebase master immediately before pushing with bounded retry and explicit conflict failure.
  • Why this is likely a bug: The formula is described as the source of truth for the package, so every completed release is expected to publish its matching version and checksum. The new job writes to the shared master branch from an earlier checkout but does not coordinate concurrent writers or recover from a branch update. This creates a deterministic race in a release-critical path, with a direct workaround of rerunning the failed job only after confirming which release update is already present.
Relevant code

.github/workflows/release.yml:1288-1313

release-homebrew:
  needs: release
  runs-on: ubuntu-latest
  ...
- uses: actions/checkout@v4
  with:
    ref: master
    token: ${{ secrets.RELEASE_BOT_TOKEN }}

.github/workflows/release.yml:1323-1330

bash packaging/homebrew/bump-formula.sh "${version}" "${sha256}"
git add packaging/homebrew/doltlite.rb
git commit -m "Bump Homebrew formula to ${version}"
git push origin master
Evidence Package
Copy prompt for an agent
Ito QA identified the following failure during automated PR testing. Please investigate and propose a fix.

**Medium severity — Concurrent releases can miss formula updates**

**What failed:** When two releases update the formula at the same time, one update can be rejected or missing from the final branch.

- **Impact:** When two releases run at the same time, one release may be missing or the package formula may stay stale. Users may not see the newest release until an operator checks the branch and reruns the release job.
- **Steps to reproduce:**
  1. Start two tagged releases close together so both formula-update jobs run at the same time.
  2. Let both jobs check out the master branch, calculate their version and checksum changes, and create commits.
  3. Let both jobs push their commits to master.
  4. Check the job results and confirm that one push can be rejected as non-fast-forward or that the final formula does not contain both updates.
- **Stub / mock content:** No stubs, mocks, or bypasses were applied for this test in the recorded run.
- **Code analysis:** The PR diff adds .github/workflows/release.yml lines 1288-1330 as the release-homebrew job. The job checks out ref master at lines 1310-1313, downloads the release asset and rewrites the formula at lines 1315-1323, then stages and commits the result at lines 1328-1329 and executes git push origin master at line 1330. The added job has no job-level concurrency group, and the workflow has no pull/rebase or retry between checkout and push. Two overlapping runs can therefore start from the same master commit; after the first push advances master, the second direct push is non-fast-forward and exits because set -e is enabled. If a later process retries from an older checkout or otherwise publishes its stale commit, it can also overwrite the other release's formula update. The smallest practical fix is to serialize this job with a concurrency group for master formula publication, or fetch and rebase master immediately before pushing with bounded retry and explicit conflict failure.
- **Why this is likely a bug:** The formula is described as the source of truth for the package, so every completed release is expected to publish its matching version and checksum. The new job writes to the shared master branch from an earlier checkout but does not coordinate concurrent writers or recover from a branch update. This creates a deterministic race in a release-critical path, with a direct workaround of rerunning the failed job only after confirming which release update is already present.

**Relevant code:**

`.github/workflows/release.yml:1288-1313`

~~~yaml
release-homebrew:
  needs: release
  runs-on: ubuntu-latest
  ...
- uses: actions/checkout@v4
  with:
    ref: master
    token: ${{ secrets.RELEASE_BOT_TOKEN }}
~~~

`.github/workflows/release.yml:1323-1330`

~~~yaml
bash packaging/homebrew/bump-formula.sh "${version}" "${sha256}"
git add packaging/homebrew/doltlite.rb
git commit -m "Bump Homebrew formula to ${version}"
git push origin master
~~~

esac
done

VERSION="${1:-}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

View All Evidence

Medium severity Malformed versions rewrite formula files

What failed: The helper returned success for a path-like version and changed the formula. It wrote a URL containing path traversal segments, although malformed versions should fail and leave the file unchanged.

Impact · Steps · Stub / mock · Analysis · Why this is likely a bug
  • Severity: Medium Medium severity
  • Impact: A malformed release version can be accepted as successful and leave the Homebrew formula pointing to an invalid download URL. Users may be unable to install or upgrade that release until the formula is corrected.
  • Steps to Reproduce:
    1. Copy the Homebrew formula to a temporary file and record its original contents.
    2. Run the release helper against that copy with version 9.9/../../evil and a valid 64-character checksum.
    3. Check the exit status, compare the file with its original contents, and inspect the new download URL.
    4. Repeat with a valid version such as 1.2.3 to confirm the normal path still produces matching URL components.
  • Stub / mock content: The check used a temporary copy of the Homebrew formula and a synthetic valid checksum; no network, authentication, or production release was used.
  • Code Analysis: The production defect is in packaging/homebrew/bump-formula.sh, which this PR adds. Lines 28-33 only check whether VERSION is non-empty; line 34 removes one leading v, but there is no semantic-version or character validation. Lines 36-39 validate SHA256 only. Line 49 then interpolates VERSION twice into the GitHub release path and archive name, so input such as 9.9/../../evil becomes an invalid URL. The recorded reproduction returned status 0, changed the copied formula bytes, and produced https://github.com/dolthub/doltlite/releases/download/v9.9/../../evil/doltlite-autoconf-9.9/../../evil.tar.gz. The smallest fix is to validate VERSION against the accepted release-version format immediately after the optional v is removed and before constructing URL, returning nonzero before the Python rewrite for invalid input.
  • Why this is likely a bug: This is not a browser or fixture-only problem: the helper's source path deterministically accepts any non-empty VERSION and constructs the release URL from it. The same code is called by the new release-homebrew job in .github/workflows/release.yml, so a malformed tag or operator input can turn the checked-in source-of-truth formula into an unusable one while reporting success. The valid 1.2.3 case works, which isolates the defect to missing rejection of malformed versions rather than the general rewrite logic. Adding a focused version check before line 49 preserves the intended valid-version behavior and prevents mutation on invalid input.
Relevant code

packaging/homebrew/bump-formula.sh:28-39

VERSION="${1:-}"
SHA256="${2:-}"
if [ -z "$VERSION" ] || [ -z "$SHA256" ]; then
  echo "usage: bump-formula.sh [--file PATH] VERSION SHA256" >&2
  exit 2
fi
VERSION="${VERSION#v}"

if ! printf '%s' "$SHA256" | grep -Eq '^[0-9a-f]{64}$'; then

packaging/homebrew/bump-formula.sh:41-49

if [ -z "$FORMULA" ]; then
  FORMULA="$(cd "$(dirname "$0")" && pwd)/doltlite.rb"
fi
if [ ! -f "$FORMULA" ]; then
  echo "formula not found: $FORMULA" >&2
  exit 1
fi

URL="https://github.com/dolthub/doltlite/releases/download/v${VERSION}/doltlite-autoconf-${VERSION}.tar.gz"
Evidence Package
Copy prompt for an agent
Ito QA identified the following failure during automated PR testing. Please investigate and propose a fix.

**Medium severity — Malformed versions rewrite formula files**

**What failed:** The helper returned success for a path-like version and changed the formula. It wrote a URL containing path traversal segments, although malformed versions should fail and leave the file unchanged.

- **Impact:** A malformed release version can be accepted as successful and leave the Homebrew formula pointing to an invalid download URL. Users may be unable to install or upgrade that release until the formula is corrected.
- **Steps to reproduce:**
  1. Copy the Homebrew formula to a temporary file and record its original contents.
  2. Run the release helper against that copy with version 9.9/../../evil and a valid 64-character checksum.
  3. Check the exit status, compare the file with its original contents, and inspect the new download URL.
  4. Repeat with a valid version such as 1.2.3 to confirm the normal path still produces matching URL components.
- **Stub / mock content:** The check used a temporary copy of the Homebrew formula and a synthetic valid checksum; no network, authentication, or production release was used.
- **Code analysis:** The production defect is in packaging/homebrew/bump-formula.sh, which this PR adds. Lines 28-33 only check whether VERSION is non-empty; line 34 removes one leading v, but there is no semantic-version or character validation. Lines 36-39 validate SHA256 only. Line 49 then interpolates VERSION twice into the GitHub release path and archive name, so input such as 9.9/../../evil becomes an invalid URL. The recorded reproduction returned status 0, changed the copied formula bytes, and produced https://github.com/dolthub/doltlite/releases/download/v9.9/../../evil/doltlite-autoconf-9.9/../../evil.tar.gz. The smallest fix is to validate VERSION against the accepted release-version format immediately after the optional v is removed and before constructing URL, returning nonzero before the Python rewrite for invalid input.
- **Why this is likely a bug:** This is not a browser or fixture-only problem: the helper's source path deterministically accepts any non-empty VERSION and constructs the release URL from it. The same code is called by the new release-homebrew job in .github/workflows/release.yml, so a malformed tag or operator input can turn the checked-in source-of-truth formula into an unusable one while reporting success. The valid 1.2.3 case works, which isolates the defect to missing rejection of malformed versions rather than the general rewrite logic. Adding a focused version check before line 49 preserves the intended valid-version behavior and prevents mutation on invalid input.

**Relevant code:**

`packaging/homebrew/bump-formula.sh:28-39`

~~~bash
VERSION="${1:-}"
SHA256="${2:-}"
if [ -z "$VERSION" ] || [ -z "$SHA256" ]; then
  echo "usage: bump-formula.sh [--file PATH] VERSION SHA256" >&2
  exit 2
fi
VERSION="${VERSION#v}"

if ! printf '%s' "$SHA256" | grep -Eq '^[0-9a-f]{64}$'; then
~~~

`packaging/homebrew/bump-formula.sh:41-49`

~~~bash
if [ -z "$FORMULA" ]; then
  FORMULA="$(cd "$(dirname "$0")" && pwd)/doltlite.rb"
fi
if [ ! -f "$FORMULA" ]; then
  echo "formula not found: $FORMULA" >&2
  exit 1
fi

URL="https://github.com/dolthub/doltlite/releases/download/v${VERSION}/doltlite-autoconf-${VERSION}.tar.gz"
~~~

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

DoltLite performance vs PR base

  • Baseline: 0e6af982026e5262b2fb725d6575a89491a6c6b7
  • Candidate: 705c825ac8543f786ea2fcc062078c47abddc8c9
  • Overall ratio: 1.000x
  • Gate result: PASS
  • Gates: individual > 1.50x with more than 10.00ms regression; section, suite, or overall > 1.25x with the same minimum delta
  • vc individual gate: > 2.00x with more than 50.00ms regression
  • Confirmed failed gates: none
  • Automatic retries: blobpk cleared after 2 attempts; no gate failed every time; compositepk cleared after 2 attempts; no gate failed every time; int cleared after 2 attempts; no gate failed every time
Suite Workloads Baseline total Candidate total Ratio Result
blobpk 69 10.54s 11.08s 1.052x PASS
compositepk 69 11.98s 11.55s 0.964x PASS
int 69 8.88s 8.79s 0.989x PASS
textpk 69 11.17s 11.16s 0.999x PASS
vc 13 803.26ms 803.04ms 1.000x PASS
blobpk details
Section Test Baseline Candidate Delta Ratio Result
mem_reads oltp_point_select 21.74ms 22.59ms +849us 1.039x PASS
mem_reads oltp_range_select 8.54ms 8.37ms -167us 0.980x PASS
mem_reads oltp_sum_range 8.65ms 8.38ms -275us 0.968x PASS
mem_reads oltp_order_range 2.00ms 1.98ms -14us 0.993x PASS
mem_reads oltp_distinct_range 2.45ms 2.48ms +34us 1.014x PASS
mem_reads oltp_index_scan 3.97ms 4.08ms +112us 1.028x PASS
mem_reads select_random_points 13.96ms 14.27ms +314us 1.022x PASS
mem_reads select_random_ranges 3.64ms 3.50ms -138us 0.962x PASS
mem_reads covering_index_scan 2.87ms 3.18ms +317us 1.111x PASS
mem_reads groupby_scan 19.80ms 19.91ms +107us 1.005x PASS
mem_reads index_join 6.57ms 6.63ms +63us 1.010x PASS
mem_reads index_join_scan 4.58ms 4.88ms +295us 1.064x PASS
mem_reads types_table_scan 748.82ms 741.45ms -7.37ms 0.990x PASS
mem_reads table_scan 828.54ms 822.26ms -6.28ms 0.992x PASS
mem_reads oltp_read_only 73.62ms 72.36ms -1.26ms 0.983x PASS
mem_writes oltp_bulk_insert 198.31ms 205.62ms +7.30ms 1.037x PASS
mem_writes oltp_insert 22.46ms 22.56ms +96us 1.004x PASS
mem_writes oltp_update_index 86.41ms 85.89ms -519us 0.994x PASS
mem_writes oltp_update_non_index 49.07ms 49.28ms +215us 1.004x PASS
mem_writes oltp_delete_insert 64.83ms 64.52ms -313us 0.995x PASS
mem_writes oltp_write_only 38.92ms 39.19ms +272us 1.007x PASS
mem_writes types_delete_insert 32.79ms 32.83ms +47us 1.001x PASS
mem_writes oltp_read_write 85.04ms 82.70ms -2.34ms 0.972x PASS
file_reads oltp_point_select 37.00ms 36.99ms -12us 1.000x PASS
file_reads oltp_range_select 10.12ms 9.94ms -178us 0.982x PASS
file_reads oltp_sum_range 9.96ms 9.94ms -21us 0.998x PASS
file_reads oltp_order_range 2.22ms 2.24ms +27us 1.012x PASS
file_reads oltp_distinct_range 2.69ms 2.70ms +14us 1.005x PASS
file_reads oltp_index_scan 5.71ms 5.68ms -25us 0.996x PASS
file_reads select_random_points 14.69ms 14.77ms +84us 1.006x PASS
file_reads select_random_ranges 5.02ms 5.06ms +42us 1.008x PASS
file_reads covering_index_scan 4.64ms 4.67ms +25us 1.005x PASS
file_reads groupby_scan 19.49ms 19.50ms +11us 1.001x PASS
file_reads index_join 7.50ms 7.29ms -210us 0.972x PASS
file_reads index_join_scan 4.46ms 4.44ms -19us 0.996x PASS
file_reads types_table_scan 746.41ms 753.64ms +7.22ms 1.010x PASS
file_reads table_scan 861.60ms 855.78ms -5.82ms 0.993x PASS
file_reads oltp_read_only 95.78ms 99.74ms +3.96ms 1.041x PASS
file_writes oltp_bulk_insert 266.82ms 261.26ms -5.56ms 0.979x PASS
file_writes oltp_insert 66.67ms 100.25ms +33.57ms 1.504x TRANSIENT
file_writes oltp_update_index 162.59ms 196.91ms +34.33ms 1.211x PASS
file_writes oltp_update_non_index 106.32ms 104.49ms -1.83ms 0.983x PASS
file_writes oltp_delete_insert 119.61ms 129.56ms +9.95ms 1.083x PASS
file_writes oltp_write_only 101.07ms 93.94ms -7.14ms 0.929x PASS
file_writes types_delete_insert 64.92ms 68.52ms +3.60ms 1.055x PASS
file_writes oltp_read_write 129.35ms 142.82ms +13.48ms 1.104x PASS
ac_reads oltp_point_select 39.45ms 38.27ms -1.18ms 0.970x PASS
ac_reads oltp_range_select 10.16ms 10.28ms +121us 1.012x PASS
ac_reads oltp_sum_range 10.21ms 10.23ms +19us 1.002x PASS
ac_reads oltp_order_range 2.22ms 2.19ms -30us 0.986x PASS
ac_reads oltp_distinct_range 2.78ms 2.74ms -40us 0.986x PASS
ac_reads oltp_index_scan 5.91ms 5.92ms +6us 1.001x PASS
ac_reads select_random_points 16.08ms 16.40ms +316us 1.020x PASS
ac_reads select_random_ranges 5.17ms 5.08ms -83us 0.984x PASS
ac_reads covering_index_scan 4.91ms 4.90ms -16us 0.997x PASS
ac_reads groupby_scan 20.11ms 20.34ms +224us 1.011x PASS
ac_reads index_join 7.47ms 7.45ms -15us 0.998x PASS
ac_reads index_join_scan 4.46ms 4.53ms +70us 1.016x PASS
ac_reads types_table_scan 744.70ms 751.53ms +6.84ms 1.009x PASS
ac_reads table_scan 859.38ms 855.03ms -4.35ms 0.995x PASS
ac_reads oltp_read_only 93.44ms 94.97ms +1.53ms 1.016x PASS
ac_writes oltp_bulk_insert_ac 296.07ms 281.25ms -14.83ms 0.950x PASS
ac_writes oltp_insert_ac 623.42ms 681.26ms +57.84ms 1.093x PASS
ac_writes oltp_update_index_ac 362.52ms 321.22ms -41.30ms 0.886x PASS
ac_writes oltp_update_non_index_ac 682.80ms 987.97ms +305.17ms 1.447x PASS
ac_writes oltp_delete_insert_ac 321.83ms 467.00ms +145.17ms 1.451x PASS
ac_writes oltp_write_only_ac 243.46ms 293.03ms +49.57ms 1.204x PASS
ac_writes types_delete_insert_ac 517.43ms 277.66ms -239.77ms 0.537x PASS
ac_writes oltp_read_write_ac 491.11ms 693.76ms +202.66ms 1.413x PASS
compositepk details
Section Test Baseline Candidate Delta Ratio Result
mem_reads oltp_point_select 27.47ms 27.63ms +162us 1.006x PASS
mem_reads oltp_range_select 15.79ms 15.81ms +20us 1.001x PASS
mem_reads oltp_sum_range 15.11ms 14.90ms -213us 0.986x PASS
mem_reads oltp_order_range 2.89ms 2.91ms +24us 1.008x PASS
mem_reads oltp_distinct_range 3.79ms 3.78ms -13us 0.997x PASS
mem_reads oltp_index_scan 4.32ms 4.37ms +56us 1.013x PASS
mem_reads select_random_points 24.77ms 25.08ms +304us 1.012x PASS
mem_reads select_random_ranges 6.51ms 6.54ms +29us 1.004x PASS
mem_reads covering_index_scan 2.92ms 2.96ms +41us 1.014x PASS
mem_reads groupby_scan 32.37ms 32.15ms -219us 0.993x PASS
mem_reads index_join 8.12ms 8.01ms -108us 0.987x PASS
mem_reads index_join_scan 4.78ms 4.95ms +174us 1.036x PASS
mem_reads types_table_scan 915.39ms 940.86ms +25.47ms 1.028x PASS
mem_reads table_scan 1.02s 987.43ms -31.86ms 0.969x PASS
mem_reads oltp_read_only 116.80ms 120.42ms +3.62ms 1.031x PASS
mem_writes oltp_bulk_insert 216.93ms 222.00ms +5.07ms 1.023x PASS
mem_writes oltp_insert 22.68ms 22.90ms +218us 1.010x PASS
mem_writes oltp_update_index 87.18ms 86.71ms -476us 0.995x PASS
mem_writes oltp_update_non_index 52.79ms 53.53ms +739us 1.014x PASS
mem_writes oltp_delete_insert 66.12ms 63.91ms -2.21ms 0.967x PASS
mem_writes oltp_write_only 38.04ms 38.12ms +80us 1.002x PASS
mem_writes types_delete_insert 33.57ms 33.10ms -470us 0.986x PASS
mem_writes oltp_read_write 102.33ms 101.54ms -791us 0.992x PASS
file_reads oltp_point_select 31.56ms 31.60ms +43us 1.001x PASS
file_reads oltp_range_select 16.76ms 16.49ms -273us 0.984x PASS
file_reads oltp_sum_range 15.20ms 15.47ms +270us 1.018x PASS
file_reads oltp_order_range 2.95ms 2.98ms +33us 1.011x PASS
file_reads oltp_distinct_range 3.62ms 3.78ms +157us 1.043x PASS
file_reads oltp_index_scan 5.41ms 5.53ms +111us 1.021x PASS
file_reads select_random_points 25.11ms 25.54ms +435us 1.017x PASS
file_reads select_random_ranges 7.07ms 6.91ms -162us 0.977x PASS
file_reads covering_index_scan 3.96ms 3.98ms +18us 1.005x PASS
file_reads groupby_scan 31.39ms 30.95ms -443us 0.986x PASS
file_reads index_join 9.12ms 8.89ms -230us 0.975x PASS
file_reads index_join_scan 4.86ms 4.71ms -144us 0.970x PASS
file_reads types_table_scan 901.15ms 897.51ms -3.63ms 0.996x PASS
file_reads table_scan 952.66ms 980.30ms +27.64ms 1.029x PASS
file_reads oltp_read_only 117.66ms 120.62ms +2.96ms 1.025x PASS
file_writes oltp_bulk_insert 258.48ms 260.64ms +2.16ms 1.008x PASS
file_writes oltp_insert 66.20ms 37.74ms -28.46ms 0.570x PASS
file_writes oltp_update_index 166.08ms 182.89ms +16.82ms 1.101x PASS
file_writes oltp_update_non_index 100.38ms 110.66ms +10.28ms 1.102x PASS
file_writes oltp_delete_insert 123.85ms 110.80ms -13.06ms 0.895x PASS
file_writes oltp_write_only 113.27ms 103.31ms -9.96ms 0.912x PASS
file_writes types_delete_insert 67.22ms 58.99ms -8.23ms 0.878x PASS
file_writes oltp_read_write 247.44ms 192.77ms -54.66ms 0.779x PASS
ac_reads oltp_point_select 32.17ms 31.20ms -965us 0.970x PASS
ac_reads oltp_range_select 15.44ms 15.48ms +48us 1.003x PASS
ac_reads oltp_sum_range 14.83ms 14.58ms -257us 0.983x PASS
ac_reads oltp_order_range 2.81ms 2.94ms +133us 1.047x PASS
ac_reads oltp_distinct_range 3.61ms 3.60ms -14us 0.996x PASS
ac_reads oltp_index_scan 5.20ms 5.27ms +66us 1.013x PASS
ac_reads select_random_points 24.35ms 24.79ms +437us 1.018x PASS
ac_reads select_random_ranges 6.82ms 7.11ms +298us 1.044x PASS
ac_reads covering_index_scan 3.71ms 3.85ms +136us 1.037x PASS
ac_reads groupby_scan 29.82ms 30.14ms +318us 1.011x PASS
ac_reads index_join 8.62ms 8.94ms +314us 1.036x PASS
ac_reads index_join_scan 4.78ms 4.83ms +49us 1.010x PASS
ac_reads types_table_scan 852.68ms 850.00ms -2.67ms 0.997x PASS
ac_reads table_scan 942.01ms 939.00ms -3.02ms 0.997x PASS
ac_reads oltp_read_only 116.50ms 118.17ms +1.67ms 1.014x PASS
ac_writes oltp_bulk_insert_ac 679.82ms 396.54ms -283.28ms 0.583x PASS
ac_writes oltp_insert_ac 176.37ms 198.36ms +21.98ms 1.125x PASS
ac_writes oltp_update_index_ac 233.31ms 215.36ms -17.95ms 0.923x PASS
ac_writes oltp_update_non_index_ac 458.41ms 756.95ms +298.54ms 1.651x TRANSIENT
ac_writes oltp_delete_insert_ac 1.18s 535.41ms -644.03ms 0.454x PASS
ac_writes oltp_write_only_ac 535.23ms 785.59ms +250.37ms 1.468x PASS
ac_writes types_delete_insert_ac 208.49ms 187.74ms -20.75ms 0.900x PASS
ac_writes oltp_read_write_ac 357.79ms 378.25ms +20.46ms 1.057x PASS
int details
Section Test Baseline Candidate Delta Ratio Result
mem_reads oltp_point_select 22.10ms 22.38ms +274us 1.012x PASS
mem_reads oltp_range_select 8.73ms 8.82ms +81us 1.009x PASS
mem_reads oltp_sum_range 8.48ms 8.53ms +45us 1.005x PASS
mem_reads oltp_order_range 2.25ms 2.27ms +11us 1.005x PASS
mem_reads oltp_distinct_range 3.10ms 3.14ms +40us 1.013x PASS
mem_reads oltp_index_scan 3.84ms 3.75ms -89us 0.977x PASS
mem_reads select_random_points 9.29ms 9.10ms -196us 0.979x PASS
mem_reads select_random_ranges 3.18ms 3.15ms -23us 0.993x PASS
mem_reads covering_index_scan 3.18ms 3.18ms -3us 0.999x PASS
mem_reads groupby_scan 26.81ms 26.96ms +151us 1.006x PASS
mem_reads index_join 5.88ms 5.82ms -59us 0.990x PASS
mem_reads index_join_scan 3.80ms 3.78ms -24us 0.994x PASS
mem_reads types_table_scan 922.49ms 919.82ms -2.67ms 0.997x PASS
mem_reads table_scan 1.02s 1.02s +709us 1.001x PASS
mem_reads oltp_read_only 88.58ms 87.44ms -1.14ms 0.987x PASS
mem_writes oltp_bulk_insert 189.09ms 185.69ms -3.40ms 0.982x PASS
mem_writes oltp_insert 20.47ms 20.16ms -312us 0.985x PASS
mem_writes oltp_update_index 66.89ms 66.00ms -888us 0.987x PASS
mem_writes oltp_update_non_index 38.75ms 38.45ms -306us 0.992x PASS
mem_writes oltp_delete_insert 53.48ms 53.23ms -256us 0.995x PASS
mem_writes oltp_write_only 34.16ms 33.76ms -398us 0.988x PASS
mem_writes types_delete_insert 25.78ms 25.72ms -61us 0.998x PASS
mem_writes oltp_read_write 80.24ms 81.06ms +819us 1.010x PASS
file_reads oltp_point_select 38.38ms 38.52ms +133us 1.003x PASS
file_reads oltp_range_select 10.48ms 10.57ms +93us 1.009x PASS
file_reads oltp_sum_range 10.31ms 10.19ms -122us 0.988x PASS
file_reads oltp_order_range 2.48ms 2.48ms +3us 1.001x PASS
file_reads oltp_distinct_range 3.32ms 3.33ms +11us 1.003x PASS
file_reads oltp_index_scan 5.82ms 5.85ms +28us 1.005x PASS
file_reads select_random_points 10.81ms 10.59ms -218us 0.980x PASS
file_reads select_random_ranges 4.84ms 4.83ms -17us 0.996x PASS
file_reads covering_index_scan 5.23ms 5.24ms +9us 1.002x PASS
file_reads groupby_scan 26.97ms 27.18ms +206us 1.008x PASS
file_reads index_join 7.38ms 7.40ms +28us 1.004x PASS
file_reads index_join_scan 4.03ms 4.03ms +3us 1.001x PASS
file_reads types_table_scan 919.04ms 916.98ms -2.06ms 0.998x PASS
file_reads table_scan 1.02s 1.02s -169us 1.000x PASS
file_reads oltp_read_only 111.13ms 111.15ms +19us 1.000x PASS
file_writes oltp_bulk_insert 234.93ms 232.83ms -2.10ms 0.991x PASS
file_writes oltp_insert 33.14ms 33.15ms +10us 1.000x PASS
file_writes oltp_update_index 121.31ms 121.23ms -79us 0.999x PASS
file_writes oltp_update_non_index 85.16ms 87.09ms +1.93ms 1.023x PASS
file_writes oltp_delete_insert 99.99ms 105.24ms +5.25ms 1.053x PASS
file_writes oltp_write_only 94.69ms 75.33ms -19.35ms 0.796x PASS
file_writes types_delete_insert 55.69ms 56.19ms +501us 1.009x PASS
file_writes oltp_read_write 120.59ms 121.53ms +939us 1.008x PASS
ac_reads oltp_point_select 38.64ms 38.48ms -158us 0.996x PASS
ac_reads oltp_range_select 10.46ms 10.53ms +68us 1.007x PASS
ac_reads oltp_sum_range 10.31ms 10.29ms -16us 0.998x PASS
ac_reads oltp_order_range 2.47ms 2.48ms +13us 1.005x PASS
ac_reads oltp_distinct_range 3.31ms 3.33ms +18us 1.005x PASS
ac_reads oltp_index_scan 5.93ms 5.95ms +21us 1.004x PASS
ac_reads select_random_points 10.74ms 10.97ms +230us 1.021x PASS
ac_reads select_random_ranges 4.84ms 4.88ms +35us 1.007x PASS
ac_reads covering_index_scan 5.41ms 5.29ms -126us 0.977x PASS
ac_reads groupby_scan 27.16ms 27.25ms +95us 1.003x PASS
ac_reads index_join 7.54ms 7.55ms +8us 1.001x PASS
ac_reads index_join_scan 4.04ms 4.03ms -10us 0.998x PASS
ac_reads types_table_scan 933.47ms 931.35ms -2.12ms 0.998x PASS
ac_reads table_scan 1.03s 1.03s -2.73ms 0.997x PASS
ac_reads oltp_read_only 111.86ms 112.98ms +1.12ms 1.010x PASS
ac_writes oltp_bulk_insert_ac 82.42ms 125.65ms +43.23ms 1.525x TRANSIENT
ac_writes oltp_insert_ac 99.89ms 90.08ms -9.81ms 0.902x PASS
ac_writes oltp_update_index_ac 181.10ms 208.25ms +27.15ms 1.150x PASS
ac_writes oltp_update_non_index_ac 99.44ms 80.73ms -18.71ms 0.812x PASS
ac_writes oltp_delete_insert_ac 130.84ms 130.46ms -383us 0.997x PASS
ac_writes oltp_write_only_ac 84.65ms 127.89ms +43.24ms 1.511x TRANSIENT
ac_writes types_delete_insert_ac 191.22ms 84.64ms -106.59ms 0.443x PASS
ac_writes oltp_read_write_ac 137.64ms 90.81ms -46.83ms 0.660x PASS
textpk details
Section Test Baseline Candidate Delta Ratio Result
mem_reads oltp_point_select 38.05ms 37.85ms -198us 0.995x PASS
mem_reads oltp_range_select 13.67ms 13.76ms +92us 1.007x PASS
mem_reads oltp_sum_range 13.75ms 13.77ms +15us 1.001x PASS
mem_reads oltp_order_range 3.17ms 3.18ms +7us 1.002x PASS
mem_reads oltp_distinct_range 4.34ms 4.29ms -56us 0.987x PASS
mem_reads oltp_index_scan 6.04ms 5.97ms -74us 0.988x PASS
mem_reads select_random_points 20.49ms 20.60ms +104us 1.005x PASS
mem_reads select_random_ranges 5.24ms 5.23ms -16us 0.997x PASS
mem_reads covering_index_scan 4.40ms 4.51ms +111us 1.025x PASS
mem_reads groupby_scan 34.45ms 34.10ms -346us 0.990x PASS
mem_reads index_join 8.73ms 8.80ms +65us 1.007x PASS
mem_reads index_join_scan 5.25ms 5.28ms +33us 1.006x PASS
mem_reads types_table_scan 1.18s 1.17s -2.37ms 0.998x PASS
mem_reads table_scan 1.28s 1.29s +6.01ms 1.005x PASS
mem_reads oltp_read_only 136.89ms 138.86ms +1.97ms 1.014x PASS
mem_writes oltp_bulk_insert 364.84ms 368.82ms +3.98ms 1.011x PASS
mem_writes oltp_insert 38.61ms 38.22ms -388us 0.990x PASS
mem_writes oltp_update_index 134.40ms 134.66ms +264us 1.002x PASS
mem_writes oltp_update_non_index 80.40ms 81.02ms +617us 1.008x PASS
mem_writes oltp_delete_insert 106.15ms 106.16ms +3us 1.000x PASS
mem_writes oltp_write_only 61.44ms 61.95ms +507us 1.008x PASS
mem_writes types_delete_insert 55.07ms 55.13ms +63us 1.001x PASS
mem_writes oltp_read_write 144.46ms 141.10ms -3.36ms 0.977x PASS
file_reads oltp_point_select 57.66ms 57.66ms -4us 1.000x PASS
file_reads oltp_range_select 15.93ms 15.94ms +12us 1.001x PASS
file_reads oltp_sum_range 16.30ms 15.98ms -316us 0.981x PASS
file_reads oltp_order_range 3.54ms 3.51ms -26us 0.993x PASS
file_reads oltp_distinct_range 4.69ms 4.66ms -36us 0.992x PASS
file_reads oltp_index_scan 8.31ms 8.41ms +104us 1.013x PASS
file_reads select_random_points 23.61ms 23.97ms +360us 1.015x PASS
file_reads select_random_ranges 7.31ms 7.25ms -52us 0.993x PASS
file_reads covering_index_scan 6.83ms 6.80ms -34us 0.995x PASS
file_reads groupby_scan 35.17ms 35.04ms -137us 0.996x PASS
file_reads index_join 11.08ms 10.99ms -86us 0.992x PASS
file_reads index_join_scan 5.82ms 5.87ms +48us 1.008x PASS
file_reads types_table_scan 1.18s 1.17s -5.88ms 0.995x PASS
file_reads table_scan 1.28s 1.28s -3.58ms 0.997x PASS
file_reads oltp_read_only 169.47ms 166.92ms -2.55ms 0.985x PASS
file_writes oltp_bulk_insert 378.74ms 384.68ms +5.94ms 1.016x PASS
file_writes oltp_insert 45.45ms 45.69ms +234us 1.005x PASS
file_writes oltp_update_index 152.68ms 153.28ms +600us 1.004x PASS
file_writes oltp_update_non_index 94.17ms 94.87ms +701us 1.007x PASS
file_writes oltp_delete_insert 119.21ms 120.02ms +803us 1.007x PASS
file_writes oltp_write_only 72.45ms 72.16ms -295us 0.996x PASS
file_writes types_delete_insert 62.44ms 62.45ms +7us 1.000x PASS
file_writes oltp_read_write 155.41ms 152.72ms -2.69ms 0.983x PASS
ac_reads oltp_point_select 57.05ms 56.92ms -123us 0.998x PASS
ac_reads oltp_range_select 15.91ms 15.81ms -104us 0.993x PASS
ac_reads oltp_sum_range 16.09ms 15.75ms -337us 0.979x PASS
ac_reads oltp_order_range 3.61ms 3.51ms -100us 0.972x PASS
ac_reads oltp_distinct_range 4.62ms 4.58ms -46us 0.990x PASS
ac_reads oltp_index_scan 8.61ms 8.33ms -284us 0.967x PASS
ac_reads select_random_points 24.19ms 23.82ms -371us 0.985x PASS
ac_reads select_random_ranges 7.25ms 7.26ms +10us 1.001x PASS
ac_reads covering_index_scan 6.76ms 6.81ms +44us 1.007x PASS
ac_reads groupby_scan 35.92ms 34.77ms -1.15ms 0.968x PASS
ac_reads index_join 10.80ms 10.95ms +153us 1.014x PASS
ac_reads index_join_scan 5.80ms 5.91ms +105us 1.018x PASS
ac_reads types_table_scan 1.17s 1.17s -5.61ms 0.995x PASS
ac_reads table_scan 1.28s 1.28s -9us 1.000x PASS
ac_reads oltp_read_only 168.05ms 164.14ms -3.91ms 0.977x PASS
ac_writes oltp_bulk_insert_ac 72.35ms 75.95ms +3.60ms 1.050x PASS
ac_writes oltp_insert_ac 87.17ms 85.79ms -1.38ms 0.984x PASS
ac_writes oltp_update_index_ac 99.16ms 99.19ms +26us 1.000x PASS
ac_writes oltp_update_non_index_ac 82.87ms 83.47ms +604us 1.007x PASS
ac_writes oltp_delete_insert_ac 91.90ms 90.28ms -1.62ms 0.982x PASS
ac_writes oltp_write_only_ac 90.08ms 93.86ms +3.78ms 1.042x PASS
ac_writes types_delete_insert_ac 84.30ms 81.02ms -3.28ms 0.961x PASS
ac_writes oltp_read_write_ac 98.16ms 97.85ms -311us 0.997x PASS
vc details
Section Test Baseline Candidate Delta Ratio Result
vc status_clean_many_tables 91.27ms 91.30ms +31us 1.000x PASS
vc status_dirty_many_tables 94.55ms 94.83ms +277us 1.003x PASS
vc diff_regular_working_one_table 86.23ms 86.44ms +206us 1.002x PASS
vc diff_regular_working_many_tables 100.58ms 100.07ms -514us 0.995x PASS
vc diff_stat_working_many_tables 99.89ms 99.96ms +68us 1.001x PASS
vc diff_schema_working_many_tables 100.10ms 100.28ms +177us 1.002x PASS
vc branch_list_many_branches 23.53ms 23.64ms +104us 1.004x PASS
vc branch_create_delete 26.53ms 26.35ms -186us 0.993x PASS
vc checkout_branch_clean 58.96ms 58.62ms -339us 0.994x PASS
vc merge_data_no_conflicts 30.57ms 30.51ms -53us 0.998x PASS
vc merge_schema_no_conflicts 22.35ms 22.52ms +170us 1.008x PASS
vc merge_data_conflicts 34.23ms 34.23ms +8us 1.000x PASS
vc merge_data_conflicts_with_resolve 34.47ms 34.30ms -170us 0.995x PASS

All relative performance gates passed.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

DoltLite source coverage

Metric Covered Total Coverage
Lines 53812 63216 85.12%
Branches 24937 36742 67.87%
Functions 2515 2726 92.26%

Merged 203 pooled raw profiles from the distributed Linux correctness jobs.

Per-file coverage (98 files)
File Lines Branches Functions
src/btree_orig_api.c 87.91% 77.27% 89.53%
src/chunk_file.c 100.00% 100.00% 100.00%
src/chunk_index.c 84.27% 67.11% 100.00%
src/chunk_refs.c 84.99% 69.17% 79.37%
src/chunk_staging.c 93.75% 81.15% 100.00%
src/chunk_store.c 91.11% 75.34% 100.00%
src/chunk_store_commit.c 86.75% 67.23% 100.00%
src/chunk_store_lock.c 88.69% 71.00% 100.00%
src/chunk_store_refs_api.c 91.17% 76.75% 100.00%
src/chunk_wal.c 82.48% 58.70% 87.50%
src/doltlite.c 100.00% 100.00% 100.00%
src/doltlite_add.c 86.85% 69.76% 100.00%
src/doltlite_ancestor.c 89.19% 68.92% 100.00%
src/doltlite_at.c 83.39% 64.20% 100.00%
src/doltlite_blame.c 80.86% 61.14% 96.43%
src/doltlite_branch.c 88.30% 80.66% 100.00%
src/doltlite_branches.c 93.75% 75.93% 93.75%
src/doltlite_checkout.c 77.57% 63.06% 96.43%
src/doltlite_cherry_pick.c 79.29% 60.00% 71.43%
src/doltlite_chunk_walk.c 92.23% 66.22% 100.00%
src/doltlite_clean.c 93.33% 67.19% 100.00%
src/doltlite_cmd.c 81.09% 75.19% 100.00%
src/doltlite_commit.c 94.41% 76.87% 100.00%
src/doltlite_commit_ancestors.c 92.09% 72.83% 92.31%
src/doltlite_commit_cmd.c 78.08% 67.50% 100.00%
src/doltlite_config.c 80.47% 71.74% 100.00%
src/doltlite_conflicts.c 84.27% 62.50% 93.75%
src/doltlite_constraint_violations.c 82.46% 57.54% 91.11%
src/doltlite_core.c 91.34% 70.81% 100.00%
src/doltlite_creds.c 84.09% 53.78% 92.00%
src/doltlite_dbpage.c 93.45% 78.33% 91.67%
src/doltlite_diff.c 88.56% 68.99% 96.55%
src/doltlite_diff_stat.c 94.17% 76.01% 95.65%
src/doltlite_diff_table.c 94.09% 70.60% 97.06%
src/doltlite_docs.c 83.94% 71.43% 94.74%
src/doltlite_gc.c 78.48% 58.10% 96.55%
src/doltlite_hashof.c 79.79% 70.05% 100.00%
src/doltlite_history.c 86.93% 76.40% 100.00%
src/doltlite_http_remote.c 82.22% 57.34% 92.31%
src/doltlite_ignore.c 77.57% 62.25% 82.14%
src/doltlite_log.c 96.81% 73.24% 92.86%
src/doltlite_merge.c 95.93% 70.46% 100.00%
src/doltlite_merge_cmd.c 87.39% 72.36% 100.00%
src/doltlite_merge_constraints.c 86.15% 63.64% 95.00%
src/doltlite_merge_constraints_check.c 86.81% 67.79% 100.00%
src/doltlite_merge_constraints_fk.c 78.00% 56.64% 100.00%
src/doltlite_merge_constraints_notnull.c 80.45% 67.92% 100.00%
src/doltlite_merge_constraints_strict.c 77.35% 63.04% 100.00%
src/doltlite_merge_constraints_unique.c 85.99% 62.82% 100.00%
src/doltlite_merge_pass1.c 93.16% 74.60% 100.00%
src/doltlite_merge_pass2.c 83.59% 67.46% 100.00%
src/doltlite_merge_predetect.c 86.38% 71.15% 95.45%
src/doltlite_merge_rebuild.c 99.15% 78.91% 100.00%
src/doltlite_merge_rows.c 83.48% 65.40% 100.00%
src/doltlite_merge_schema.c 91.09% 72.82% 100.00%
src/doltlite_merge_status.c 91.98% 73.53% 92.31%
src/doltlite_patch.c 94.36% 70.71% 98.15%
src/doltlite_rebase.c 85.42% 59.55% 100.00%
src/doltlite_record.c 78.67% 59.36% 95.24%
src/doltlite_ref.c 95.52% 75.36% 100.00%
src/doltlite_remote.c 85.61% 66.89% 96.00%
src/doltlite_remote_sql.c 66.60% 60.73% 93.55%
src/doltlite_remotesrv.c 75.76% 64.92% 90.77%
src/doltlite_reset.c 87.48% 72.76% 100.00%
src/doltlite_revert.c 80.51% 69.44% 100.00%
src/doltlite_schema_diff.c 94.42% 71.13% 96.97%
src/doltlite_schemas.c 70.86% 48.75% 90.91%
src/doltlite_status.c 91.49% 72.78% 97.56%
src/doltlite_tag.c 86.84% 70.31% 94.12%
src/doltlite_tests.c 84.22% 69.61% 91.11%
src/doltlite_tls.c 85.04% 62.73% 92.31%
src/doltlite_verify_constraints.c 79.15% 66.44% 100.00%
src/doltlite_workspace.c 91.84% 70.27% 100.00%
src/pager_shim.c 58.87% 67.46% 34.29%
src/prolly_btree.c 86.00% 66.45% 90.70%
src/prolly_btree_catalog.c 83.10% 69.04% 97.22%
src/prolly_btree_cursor.c 84.15% 62.09% 96.00%
src/prolly_btree_cursor_count.c 80.05% 56.74% 100.00%
src/prolly_btree_cursor_payload.c 75.69% 54.48% 95.65%
src/prolly_btree_cursor_seek.c 76.68% 66.20% 82.35%
src/prolly_btree_mutation.c 88.02% 68.30% 97.83%
src/prolly_btree_orig.c 93.75% 71.43% 93.15%
src/prolly_btree_state.c 93.51% 66.38% 100.00%
src/prolly_btree_txn.c 81.84% 69.30% 98.08%
src/prolly_cache.c 93.27% 69.12% 100.00%
src/prolly_check.c 60.36% 62.96% 100.00%
src/prolly_chunker.c 94.04% 79.73% 100.00%
src/prolly_cursor.c 89.67% 80.47% 100.00%
src/prolly_diff.c 59.59% 46.05% 71.43%
src/prolly_hash.c 93.33% 80.00% 100.00%
src/prolly_hashset.c 90.48% 80.56% 100.00%
src/prolly_mutate.c 80.78% 69.11% 100.00%
src/prolly_mutmap.c 93.87% 80.15% 100.00%
src/prolly_node.c 88.91% 73.81% 100.00%
src/prolly_three_way_diff.c 95.79% 85.37% 100.00%
src/prolly_three_way_merge.c 81.92% 67.41% 91.67%
src/prolly_xxhash.c 100.00% 100.00% 100.00%
src/sortkey.c 93.16% 81.51% 100.00%

Download HTML and LCOV artifacts from this workflow run.

timsehn and others added 2 commits September 1, 2026 18:23
packaging/homebrew/doltlite.rb pinned v0.10.6, a pre-format-freeze
engine. Homebrew-core will not take the formula yet, but the in-repo
copy is the source of truth and must not ship a stale pin.

Point url/sha256 at v0.50.2. packaging/homebrew/bump-formula.sh
rewrites those fields; the release-homebrew job runs it after the
autoconf tarball is uploaded and pushes the bump to master.

Fixes #2542

Co-Authored-By: Grok 4.6 <noreply@x.ai>
Ito QA: the autoconf tarball omitted doltlite.mk, so main.mk could not
build doltlite from the formula's source URL. Include it and grep the
archive after packing.

bump-formula.sh now requires X.Y.Z, counts url/sha256 matches before
writing, and leaves the file alone on a reject. The release job serializes
on a concurrency group and retries fetch/reset/push so overlapping tags
do not clobber a newer formula.

Co-Authored-By: Grok 4.6 <noreply@x.ai>
@timsehn
timsehn force-pushed the fix/2542-homebrew-formula-bump branch from 2877cca to a4634d2 Compare September 2, 2026 01:24
@timsehn

timsehn commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the Ito QA failures in a4634d2:

  • Install (high): main.mk includes doltlite.mk, but the autoconf tarball omitted it, so make doltlite stopped immediately. doltlite.mk is now in the tarball file list, and the source job greps the archive for it (plus configure/main.mk/auto.def/sqlite3.c/h) after packing. The v0.50.2 GitHub asset is still missing that file; the next tagged release is the one that will install from the formula.
  • Duplicate URL (medium): re.subn(..., count=1) treated "one replacement" as success. The helper now counts matches first and refuses to write unless there is exactly one url and one sha256.
  • Malformed version (medium): versions must be X.Y.Z; path-like input exits 2 and leaves the formula unchanged.
  • Concurrent bump (medium): release-homebrew uses a concurrency group, skips if master already has this version or a newer one, and retries fetch/reset/push.

test/homebrew_formula_test.sh covers the bump rejects and the tarball file-list grep.

@itoqa

itoqa Bot commented Sep 2, 2026

Copy link
Copy Markdown

Ito QA test results

History reset (rebase or force-push detected). Starting test narrative over.

Commit: a4634d2: 14 test cases ran, 13 passed ✅, 1 additional finding ⚠️.

Summary

Coverage spans release packaging and publishing, formula updates, retry and concurrency handling, credential and input validation, repeat-delivery safety, and clean archive builds. It includes normal release flows plus malformed-input, failure-recovery, race-condition, and Linux shared-library integration checks, with the change-related behavior otherwise healthy.

Safe to merge — the only failure is a medium-severity Linux packaging defect that is explicitly unrelated to this PR, while the PR’s release, formula, validation, and retry behaviors show no regressions or new failures. The Linux embedding issue is a flag for later rather than a merge blocker.

Tests run by Ito

View full run

Result Severity Type Description
Archive The release source archive was created successfully and contains all six required files exactly once at its top level.
General Missing release credentials and failed repository connections stop the release job instead of being treated as a successful no-op. Local source checks confirm the job fails before changing the formula when authentication, download, or push operations fail.
General An independent change to the main branch stays intact while the release update retries, and the formula update is applied to the current branch instead of replacing the other change.
General Sending the same release again leaves the formula unchanged, and an older release cannot replace a newer one. The update process is safe to repeat without duplicate or regressive changes.
Bump The formula copy was updated with the 9.9.9 release URL and supplied checksum, while unrelated content stayed unchanged.
Bump A path-like version was rejected with exit status 2, and the formula stayed unchanged.
Bump An invalid checksum is rejected with exit status 2, and the formula stays unchanged.
Formula The formula packaging checks pass, and the release workflow now puts the required build file in each source archive. The earlier install failure came from an already-published archive that did not match the current release workflow.
Release The release job downloads the autoconf archive for the tag, calculates its SHA-256 checksum, updates the Homebrew formula, and reports success only after pushing the change.
Release A rejected update is not reported as successful. The release job refreshes the branch, waits longer between attempts, and fails clearly if all five attempts are rejected.
Rev A fresh release archive built all three expected targets without Git metadata, and the command-line tool reported version v3.54.0.
Rev When the release credential is missing, the release job stops before checkout, leaves the main branch and formula unchanged, and prints only a safe error message.
Suite The full repository test command included the Homebrew checks and all 9 of those checks passed. The overall command also reported three failures in other, unrelated test suites.
⚠️ Medium severity Rev The command-line checks and renamed headers worked, but the compiled C consumer did not start because the installed package had libdoltlite.so without the libdoltlite.so.0 runtime link.
Additional Findings Details

These findings are unrelated to the current changes but were observed during testing.

🟡 Linux embedding program cannot start
  • Severity: Medium Medium severity
  • Description: The command-line checks and renamed headers worked, but the compiled C consumer did not start because the installed package had libdoltlite.so without the libdoltlite.so.0 runtime link.
  • Impact: Linux Homebrew users cannot start programs that use the documented shared C embedding library. The command-line tool still works, and users can use a static build or manually repair the library link.
  • Steps to Reproduce:
    1. Build and install the formula into a clean Linux prefix.
    2. Compile a small C program that includes doltlite.h and links with -L/lib -ldoltlite.
    3. Run the compiled program with the installed library directory available to the dynamic loader.
    4. Observe that the loader cannot find libdoltlite.so.0 unless a matching symlink is added manually.
  • Stub / mock content: The check used a disposable local Linux installation prefix and did not use mocks, route interception, or production services. macOS and Homebrew were unavailable in the test environment, so only the Linux formula install behavior was exercised.
  • Code Analysis: The Linux branch in packaging/homebrew/doltlite.rb:34-35 installs libdoltlite.a and copies only libdoltlite.so into the Homebrew lib directory. In main.mk:2928-2933, the Linux shared object is deliberately linked with SONAME libdoltlite.so.0. The build target creates a libdoltlite.so.0 symlink in the build directory at main.mk:2968-2972, but the formula install block does not copy that symlink or rename the installed file to the SONAME. As a result, a consumer linked with -ldoltlite records a runtime dependency on libdoltlite.so.0, while the formula prefix contains only libdoltlite.so, producing the observed loader failure. The smallest fix is to install the built SONAME file and retain the unversioned libdoltlite.so symlink, for example by copying libdoltlite.so.0 and creating or preserving libdoltlite.so -> libdoltlite.so.0 in the formula install block. This is separate from the PR's URL/livecheck changes, which is why the PR-causation decision is False.
Evidence Package

Tip

Reply with @itoqa to send us feedback on this test run.

@timsehn
timsehn merged commit 7e9ffb5 into master Sep 2, 2026
134 of 136 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Homebrew formula pins v0.10.6 (pre-format-freeze) with no release automation

1 participant