Skip to content

Commit 6ca5ad5

Browse files
silehtclaude
andauthored
feat(env): install mergify-cli from prebuilt binaries (#4)
Replace the uv + Python install path with the upstream prebuilt-binary installer (install.sh), dropping the Python/uv dependency from agents. install.sh is fetched from the same git ref as the requested release so the script and the release assets always agree on the asset-naming convention, which changed between releases (2026.6.15.1 ships unversioned assets, 2026.6.16.1 versioned ones). "latest" uses main and lets install.sh resolve the newest tag itself. - hooks/environment: curl install.sh | sh, exporting MERGIFY_VERSION and MERGIFY_INSTALL_DIR; default bumped to 2026.6.16.1 (>= 2026.6.15.1 required for prebuilt binaries). - plugin.yml: drop the now-meaningless python_version; require tar. - renovate.json: track GitHub releases instead of PyPI. - README + tests updated accordingly. mergify_cli_version keeps working (pinned version or "latest"). Fixes MRGFY-7661 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 1703236 commit 6ca5ad5

6 files changed

Lines changed: 81 additions & 51 deletions

File tree

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,7 @@ steps:
179179
| `mergify_api_url` | no | `https://api.mergify.com` | Mergify API endpoint |
180180
| `job_name` | no | Step label | Override job name (useful for matrix builds) |
181181
| `mergify_config_path` | no | — | Path to `.mergify.yml` configuration file |
182-
| `mergify_cli_version` | no | pinned default | Version of `mergify-cli` to install. Leave unset to use the plugin's pinned default (kept current by Renovate, see `hooks/environment`), pin an exact release (e.g. `2026.5.29.2`), or use `latest` to always install the newest released version. |
183-
| `python_version` | no | `3.13` | Python version for `mergify-cli`. Uses `uv` to download it if needed. Set to `system` to use the agent's system Python. |
182+
| `mergify_cli_version` | no | pinned default | Version of `mergify-cli` to install. Leave unset to use the plugin's pinned default (kept current by Renovate, see `hooks/environment`), pin an exact release (e.g. `2026.6.16.1`, must be `>= 2026.6.15.1`), or use `latest` to always install the newest released version. |
184183

185184
## Meta-data
186185

@@ -199,8 +198,13 @@ The `mergify-cli` writes Buildkite annotations (context: `mergify-ci-scopes`) sh
199198

200199
## Requirements
201200

202-
- Python 3.x on the Buildkite agent
203-
- `curl` (for installing `uv`)
201+
`mergify-cli` is installed as a prebuilt binary (no Python required), downloaded
202+
and checksum-verified by the upstream [`install.sh`](https://github.com/Mergifyio/mergify-cli/blob/main/install.sh).
203+
Supported platforms: Linux (x86_64, aarch64) and macOS (x86_64, aarch64).
204+
205+
- `curl` (to download the installer and the binary)
206+
- `tar` (to extract the binary)
207+
- `sha256sum` or `shasum` (to verify the binary checksum)
204208
- `jq` (for JSON processing in `scopes-upload`)
205209

206210
## Development

hooks/environment

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,36 @@ source "${DIR}/../lib/shared.sh"
77

88
log_info "Installing mergify-cli..."
99

10-
# Install uv if not available
11-
if ! command -v uv &>/dev/null; then
12-
echo "uv not found, installing..."
13-
curl -LsSf https://astral.sh/uv/install.sh | sh
14-
export PATH="$HOME/.local/bin:$PATH"
15-
fi
16-
1710
# Default mergify-cli release installed when the plugin config doesn't request a
18-
# specific one. Renovate keeps this pinned version current via PRs.
19-
# renovate: datasource=pypi depName=mergify-cli versioning=pep440
20-
DEFAULT_MERGIFY_CLI_VERSION="2026.5.29.2"
11+
# specific one. Must be >= 2026.6.15.1 (the first release shipping the prebuilt
12+
# binaries and the install.sh used below). Renovate keeps this pinned current.
13+
# renovate: datasource=github-releases depName=Mergifyio/mergify-cli versioning=pep440
14+
DEFAULT_MERGIFY_CLI_VERSION="2026.6.16.1"
2115

22-
# Resolve the requested release. An explicit "latest" installs the newest release
23-
# unpinned; anything else (including the default above) pins it with ==.
16+
# Resolve the requested release. An explicit "latest" installs the newest
17+
# release; anything else (including the default above) pins that exact tag.
2418
CLI_VERSION="$(plugin_config MERGIFY_CLI_VERSION "$DEFAULT_MERGIFY_CLI_VERSION")"
19+
20+
# Install from the upstream prebuilt-binary installer. We fetch install.sh from
21+
# the same git ref as the requested release so the script and the release assets
22+
# always agree on the asset-naming convention (it changed across releases). For
23+
# "latest" we use main and let install.sh resolve the newest tag itself.
2524
if [[ "$CLI_VERSION" == "latest" ]]; then
26-
CLI_PACKAGE="mergify-cli"
25+
INSTALL_REF="main"
2726
else
28-
CLI_PACKAGE="mergify-cli==$CLI_VERSION"
27+
INSTALL_REF="$CLI_VERSION"
2928
fi
29+
export MERGIFY_VERSION="$CLI_VERSION"
3030

31-
# Install (or upgrade) mergify-cli. --force --upgrade ensures we always pick up
32-
# the requested release, even when a different version is already cached on the
33-
# agent from a previous build. With a pinned version the == constraint keeps the
34-
# resolution at that exact release.
35-
PYTHON_VERSION="$(plugin_config PYTHON_VERSION "3.13")"
36-
if [[ "$PYTHON_VERSION" == "system" ]]; then
37-
uv tool install --force --upgrade "$CLI_PACKAGE"
38-
else
39-
uv tool install --force --upgrade --python "$PYTHON_VERSION" "$CLI_PACKAGE"
40-
fi
31+
# install.sh drops the binary in MERGIFY_INSTALL_DIR (default ~/.local/bin);
32+
# make sure it is resolvable for the rest of the build.
33+
export MERGIFY_INSTALL_DIR="${MERGIFY_INSTALL_DIR:-$HOME/.local/bin}"
34+
export PATH="$MERGIFY_INSTALL_DIR:$PATH"
35+
36+
curl -fsSL "https://raw.githubusercontent.com/Mergifyio/mergify-cli/${INSTALL_REF}/install.sh" | sh
4137

42-
# Verify installation
38+
# Verify installation (install.sh already printed the version above).
4339
if ! command -v mergify &>/dev/null; then
4440
log_error "mergify-cli installation failed."
45-
log_error "Try setting python_version in the plugin config (e.g., python_version: '3.13')."
4641
exit 1
4742
fi
48-
49-
mergify --version

plugin.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ description: Mergify integration for Buildkite — JUnit processing, scope detec
33
author: https://github.com/mergifyio
44
requirements:
55
- curl
6+
- tar
67
- bash
78
- jq
89
configuration:
@@ -28,8 +29,6 @@ configuration:
2829
type: string
2930
mergify_cli_version:
3031
type: string
31-
python_version:
32-
type: string
3332
required:
3433
- action
3534
additionalProperties: false

renovate.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
"osvVulnerabilityAlerts": true,
1111
"packageRules": [
1212
{
13-
"matchDatasources": ["pypi"],
14-
"matchPackageNames": ["mergify-cli"],
13+
"matchDatasources": ["github-releases"],
14+
"matchPackageNames": ["Mergifyio/mergify-cli"],
1515
"minimumReleaseAge": "2 days"
1616
}
1717
],

tests/environment.bats

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
setup() {
44
load helpers/stub
5-
# Stub uv (records its args) and mergify (so the post-install check passes).
6-
stub_command uv 0
7-
stub_command mergify 0 "mergify-cli 0.0.0-stub"
5+
# Install into a throwaway dir so the hook never touches the real ~/.local/bin.
6+
export MERGIFY_INSTALL_DIR="${BATS_TEST_TMPDIR}/bin"
7+
# Stub curl so the hook downloads a fake install.sh instead of hitting GitHub.
8+
stub_curl_install
89
}
910

1011
@test "environment: installs the pinned default when version is unset" {
@@ -14,33 +15,41 @@ setup() {
1415
# Read the default straight from the hook so this stays green when Renovate
1516
# bumps DEFAULT_MERGIFY_CLI_VERSION.
1617
default="$(grep -oE 'DEFAULT_MERGIFY_CLI_VERSION="[^"]+"' hooks/environment | cut -d'"' -f2)"
17-
grep -q -- "tool install --force --upgrade --python 3.13 mergify-cli==${default}$" "${BATS_TEST_TMPDIR}/uv.log"
18+
grep -q -- "raw.githubusercontent.com/Mergifyio/mergify-cli/${default}/install.sh$" "${BATS_TEST_TMPDIR}/curl.log"
19+
[[ "$output" == *"installed MERGIFY_VERSION=${default}"* ]]
1820
}
1921

20-
@test "environment: installs latest when version is 'latest'" {
22+
@test "environment: installs latest from main when version is 'latest'" {
2123
export BUILDKITE_PLUGIN_MERGIFY_CI_MERGIFY_CLI_VERSION="latest"
2224

2325
run bash hooks/environment
2426

2527
[ "$status" -eq 0 ]
26-
grep -q -- "tool install --force --upgrade --python 3.13 mergify-cli$" "${BATS_TEST_TMPDIR}/uv.log"
28+
grep -q -- "raw.githubusercontent.com/Mergifyio/mergify-cli/main/install.sh$" "${BATS_TEST_TMPDIR}/curl.log"
29+
[[ "$output" == *"installed MERGIFY_VERSION=latest"* ]]
2730
}
2831

29-
@test "environment: pins the exact version when one is given" {
30-
export BUILDKITE_PLUGIN_MERGIFY_CI_MERGIFY_CLI_VERSION="2026.5.5.4"
32+
@test "environment: pins install.sh and version to the requested release" {
33+
export BUILDKITE_PLUGIN_MERGIFY_CI_MERGIFY_CLI_VERSION="2026.6.15.1"
3134

3235
run bash hooks/environment
3336

3437
[ "$status" -eq 0 ]
35-
grep -q -- "tool install --force --upgrade --python 3.13 mergify-cli==2026.5.5.4$" "${BATS_TEST_TMPDIR}/uv.log"
38+
grep -q -- "raw.githubusercontent.com/Mergifyio/mergify-cli/2026.6.15.1/install.sh$" "${BATS_TEST_TMPDIR}/curl.log"
39+
[[ "$output" == *"installed MERGIFY_VERSION=2026.6.15.1"* ]]
3640
}
3741

38-
@test "environment: omits --python when python_version is 'system'" {
39-
export BUILDKITE_PLUGIN_MERGIFY_CI_PYTHON_VERSION="system"
40-
export BUILDKITE_PLUGIN_MERGIFY_CI_MERGIFY_CLI_VERSION="2026.5.5.4"
42+
@test "environment: fails when the binary is not installed" {
43+
# curl that downloads an installer doing nothing — no mergify binary appears.
44+
cat > "${BATS_TEST_TMPDIR}/stubs/curl" <<'STUB'
45+
#!/bin/bash
46+
echo ':'
47+
STUB
48+
chmod +x "${BATS_TEST_TMPDIR}/stubs/curl"
4149

42-
run bash hooks/environment
50+
# Minimal PATH so a real mergify on the dev machine can't mask the failure.
51+
PATH="${BATS_TEST_TMPDIR}/stubs:/usr/bin:/bin" run bash hooks/environment
4352

44-
[ "$status" -eq 0 ]
45-
grep -q -- "tool install --force --upgrade mergify-cli==2026.5.5.4$" "${BATS_TEST_TMPDIR}/uv.log"
53+
[ "$status" -ne 0 ]
54+
[[ "$output" == *"mergify-cli installation failed"* ]]
4655
}

tests/helpers/stub.bash

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,31 @@ STUB
2222
export PATH="${stub_dir}:${PATH}"
2323
}
2424

25+
# Stub `curl` for the environment hook. Records argv to curl.log, then prints a
26+
# fake install.sh on stdout. When the hook pipes it to `sh`, the script installs
27+
# a `mergify` stub into MERGIFY_INSTALL_DIR and echoes the MERGIFY_VERSION the
28+
# hook exported, so tests can assert both the install.sh URL and the version.
29+
stub_curl_install() {
30+
local stub_dir="${BATS_TEST_TMPDIR}/stubs"
31+
mkdir -p "$stub_dir"
32+
# Fully-quoted heredoc: nothing expands at stub-creation time. The inner
33+
# 'SCRIPT' heredoc keeps ${MERGIFY_*} literal on stdout so they expand only
34+
# in the `sh` that consumes the piped installer.
35+
cat > "${stub_dir}/curl" <<'STUB'
36+
#!/bin/bash
37+
echo "$@" >> "${BATS_TEST_TMPDIR}/curl.log"
38+
cat <<'SCRIPT'
39+
#!/bin/sh
40+
mkdir -p "${MERGIFY_INSTALL_DIR}"
41+
printf '#!/bin/bash\necho "mergify-cli 0.0.0-stub"\n' > "${MERGIFY_INSTALL_DIR}/mergify"
42+
chmod +x "${MERGIFY_INSTALL_DIR}/mergify"
43+
echo "installed MERGIFY_VERSION=${MERGIFY_VERSION}"
44+
SCRIPT
45+
STUB
46+
chmod +x "${stub_dir}/curl"
47+
export PATH="${stub_dir}:${PATH}"
48+
}
49+
2550
# Create a buildkite-agent stub that handles meta-data set/get.
2651
# Meta-data is stored in files under BATS_TEST_TMPDIR/metadata/.
2752
stub_buildkite_agent() {

0 commit comments

Comments
 (0)