Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,26 @@ build:macos --host_cxxopt=-std=c++17
build:windows --cxxopt=/std:c++17
build:windows --host_cxxopt=/std:c++17

# Release artifacts: `bazel build //release:bazel-diff-rust --config=release`.
# CI uses nothing but this config, so the flags a published binary is built with
# live here rather than in the workflow files. The C++ standard flags come from
# the platform-specific configs above.
#
# rules_rust ignores Cargo profiles, so `-c opt` alone (opt-level=3,
# debuginfo=0, strip=debuginfo) builds a *weaker* binary than
# `cargo build --release` does under [profile.release] in Cargo.toml. The two
# settings below restore the difference; keep them in sync with that profile.
build:release -c opt

# lto = "thin" in Cargo.toml. Cross-crate inlining/DCE at link time. rules_rust
# skips it for build scripts and proc-macros, matching Cargo.
build:release --@rules_rust//rust/settings:lto=thin

# strip = true in Cargo.toml (`-c opt` only strips debuginfo, not the symbol
# table). Passed as an extra flag because the strip level is otherwise a
# property of the toolchain, which would change every build, not just releases.
build:release --@rules_rust//rust/settings:extra_rustc_flags=-Cstrip=symbols

# Avoid cache thrashing, but allow integration tests to find "bazel" on the PATH.
common --incompatible_strict_action_env
common --test_env=PATH
Expand Down
72 changes: 67 additions & 5 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -261,14 +261,44 @@ jobs:
.\bazel-diff-example.ps1 -WorkspacePath "$env:GITHUB_WORKSPACE" -BazelPath "$env:USERPROFILE\go\bin\bazelisk.exe" -PreviousRevision $prevRev -FinalRevision $currRev
deploy:
needs: [test-jre21]
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
permissions:
contents: read
id-token: write
attestations: write
strategy:
fail-fast: false
matrix:
java: [ '11' ]
# rust_asset is the name //release:bazel-diff-rust gives the binary on
# that platform -- it is asserted, not applied (see the upload step).
#
# The two Windows-only Bazel flags can't move into --config=release:
# startup flags can't live in a --config at all, and .bazelrc expands
# platform-specific config for the bare `build` config name only.
# bazel_startup_flags shortens the output root because MSVC's link.exe
# is MAX_PATH-bound (260): under the default root, the Rust stdlib path
# ...rust_toolchain\lib\rustlib\x86_64-pc-windows-msvc\lib\
# librustc_std_workspace_alloc-<hash>.rlib comes to 263 characters and
# the link fails with LNK1181. C:/b buys back 35.
include:
- os: ubuntu-latest
java: '11'
rust_asset: bazel-diff-rust-linux-amd64
bazel_startup_flags: ''
bazel_extra_flags: ''
upload_jar_and_archive: true
- os: macos-latest
java: '11'
rust_asset: bazel-diff-rust-macos-arm64
bazel_startup_flags: ''
bazel_extra_flags: ''
upload_jar_and_archive: false
- os: windows-latest
java: '11'
rust_asset: bazel-diff-rust-windows-amd64.exe
bazel_startup_flags: '--output_user_root=C:/b'
bazel_extra_flags: '--legacy_external_runfiles'
upload_jar_and_archive: false
steps:
- name: Setup Java JDK
uses: actions/setup-java@v4
Expand All @@ -281,20 +311,52 @@ jobs:
with:
go-version: ^1.17
id: go
- name: Setup Bazelisk
run: go install github.com/bazelbuild/bazelisk@latest && export PATH=$PATH:$(go env GOPATH)/bin
- name: Setup Bazelisk (Linux/macOS)
if: runner.os != 'Windows'
run: |
go install github.com/bazelbuild/bazelisk@latest
echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
- name: Setup Bazelisk (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
go install github.com/bazelbuild/bazelisk@latest
echo "$(go env GOPATH)\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- uses: actions/checkout@v4
- name: Build deployable JAR
run: ~/go/bin/bazelisk build //cli:bazel-diff_deploy.jar
if: matrix.upload_jar_and_archive
run: bazelisk build //cli:bazel-diff_deploy.jar
- uses: actions/upload-artifact@v4
if: matrix.upload_jar_and_archive
with:
name: bazel-diff_deploy.jar
path: bazel-bin/cli/bazel-diff_deploy.jar
if-no-files-found: error
- name: Build release source archive
if: matrix.upload_jar_and_archive
run: make release_source_archive
- uses: actions/upload-artifact@v4
if: matrix.upload_jar_and_archive
with:
name: release.tar.gz
path: archives/release.tar.gz
if-no-files-found: error
# //release:bazel-diff-rust names the binary for the platform Bazel built
# it for, so this step neither renames nor relocates anything: whatever
# lands in bazel-bin/release/ is the published asset. --config=release
# carries the flags (see .bazelrc). `if-no-files-found: error` below is
# the assertion that Bazel's name still matches matrix.rust_asset.
- name: Build Rust binary
shell: bash
env:
# Windows runs this under Git bash, whose MSYS runtime rewrites any
# argument starting with `//package` into a `/package` Windows path --
# Bazel then rejects "invalid package name '/release'". (`//:target`
# survived only because it has no path-like segment to convert.)
MSYS2_ARG_CONV_EXCL: '//'
run: bazelisk ${{ matrix.bazel_startup_flags }} build //release:bazel-diff-rust --config=release ${{ matrix.bazel_extra_flags }}
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.rust_asset }}
path: bazel-bin/release/${{ matrix.rust_asset }}
if-no-files-found: error
32 changes: 20 additions & 12 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,23 @@ jobs:
strategy:
fail-fast: false
matrix:
# bazel_startup_flags shortens the output root on Windows because MSVC's
# link.exe is MAX_PATH-bound (260): under the default root the Rust
# stdlib rlib ...\librustc_std_workspace_alloc-<hash>.rlib comes to 263
# characters and the link fails with LNK1181. Keep in sync with the
# `deploy` job in ci.yaml, which is where this gets exercised per-PR.
include:
- os: ubuntu-latest
asset: bazel-diff-rust-linux-amd64
bazel_startup_flags: ""
bazel_extra_flags: ""
- os: macos-latest
asset: bazel-diff-rust-macos-arm64
bazel_startup_flags: ""
bazel_extra_flags: ""
- os: windows-latest
asset: bazel-diff-rust-windows-amd64.exe
bazel_startup_flags: "--output_user_root=C:/b"
bazel_extra_flags: "--legacy_external_runfiles"
steps:
- name: Checkout
Expand All @@ -79,26 +87,26 @@ jobs:
run: |
go install github.com/bazelbuild/bazelisk@latest
echo "$(go env GOPATH)\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
# //release:bazel-diff-rust names the binary for the platform Bazel built
# it for, so nothing here renames or relocates it: bazel-bin/release/ holds
# the published asset, and `gh release upload` keeps that file name. The
# build flags live in .bazelrc under --config=release.
- name: Build Rust binary
shell: bash
run: |
bazelisk build //:bazel-diff-rust -c opt \
--cxxopt=-std=c++17 \
--host_cxxopt=-std=c++17 \
${{ matrix.bazel_extra_flags }}
if [[ "${{ runner.os }}" == "Windows" ]]; then
src="bazel-bin/src/bazel-diff.exe"
else
src="bazel-bin/src/bazel-diff"
fi
cp "$src" "${{ matrix.asset }}"
env:
# Windows runs this under Git bash, whose MSYS runtime rewrites any
# argument starting with `//package` into a `/package` Windows path --
# Bazel then rejects "invalid package name '/release'". (`//:target`
# survived only because it has no path-like segment to convert.)
MSYS2_ARG_CONV_EXCL: '//'
run: bazelisk ${{ matrix.bazel_startup_flags }} build //release:bazel-diff-rust --config=release ${{ matrix.bazel_extra_flags }}
- name: Upload release asset
shell: bash
env:
TAG: ${{ inputs.tag_name || github.ref_name }}
GH_TOKEN: ${{ github.token }}
run: |
gh release upload "$TAG" "${{ matrix.asset }}" \
gh release upload "$TAG" "bazel-bin/release/${{ matrix.asset }}" \
--clobber \
--repo "$GITHUB_REPOSITORY"
finalize:
Expand Down
4 changes: 4 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ bazel_dep(name = "buildifier_prebuilt", version = "8.5.1.2", dev_dependency = Tr
bazel_dep(name = "aspect_bazel_lib", version = "2.22.5", dev_dependency = True)

bazel_dep(name = "bazel_skylib", version = "1.9.0")

# Direct dep because //release selects on @platforms//os and @platforms//cpu to
# name the release binaries.
bazel_dep(name = "platforms", version = "1.1.0")
bazel_dep(name = "rules_proto", version = "7.1.0")
bazel_dep(name = "rules_java", version = "9.7.0")
bazel_dep(name = "rules_kotlin", version = "2.4.0")
Expand Down
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ release_deploy_jar:
//cli:bazel-diff_deploy.jar \
-c opt

# Builds the same artifact CI publishes, named the same way:
# bazel-bin/release/bazel-diff-rust-<os>-<arch>[.exe].
.PHONY: release_rust_binary
release_rust_binary:
bazel \
build \
//:bazel-diff-rust \
-c opt
//release:bazel-diff-rust \
--config=release

.PHONY: build_rust
build_rust:
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,14 @@ Windows amd64: download
`bazel-diff-rust-windows-amd64.exe` from the
[latest release](https://github.com/Tinder/bazel-diff/releases/latest).

Those assets are produced by Bazel alone -- CI runs nothing but the command below and uploads
whatever lands in `bazel-bin/release/`, so `//release:bazel-diff-rust` names the binary for the
platform it was built on (`bazel-diff-rust-<os>-<arch>`, plus `.exe` on Windows):

```terminal
make release_rust_binary # bazel build //release:bazel-diff-rust --config=release
```

### Performance gate

The Rust candidate is expected to be faster than Kotlin, and CI enforces it. `make perf-gate`
Expand Down
11 changes: 11 additions & 0 deletions release/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
load("//release:defs.bzl", "RELEASE_ASSET_NAME", "release_binary")

package(default_visibility = ["//visibility:public"])

# Built by the `deploy` job in ci.yaml and the `rust-binaries` job in
# release.yaml, which upload the file it writes to bazel-bin/release/ as-is.
release_binary(
name = "bazel-diff-rust",
asset_name = RELEASE_ASSET_NAME,
binary = "//:bazel-diff-rust",
)
64 changes: 64 additions & 0 deletions release/defs.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""Bazel-side naming for the Rust CLI binaries published to GitHub Releases.

`bazel build //release:bazel-diff-rust --config=release` writes
`bazel-bin/release/bazel-diff-rust-<os>-<arch>[.exe]` for whatever platform Bazel
is building for. Release automation therefore only has to run Bazel and upload
what lands in `bazel-bin/release/`: no per-runner `cp`, no shell-side knowledge
of where rules_rust drops the binary, and no way for the published asset name to
disagree with the platform it was actually built on.
"""

_OS_NAME = select(
{
"@platforms//os:linux": "linux",
"@platforms//os:macos": "macos",
"@platforms//os:windows": "windows",
},
no_match_error = "bazel-diff does not publish release binaries for this OS",
)

_ARCH_NAME = select(
{
"@platforms//cpu:aarch64": "arm64",
"@platforms//cpu:x86_64": "amd64",
},
no_match_error = "bazel-diff does not publish release binaries for this CPU",
)

_EXTENSION = select({
"@platforms//os:windows": ".exe",
"//conditions:default": "",
})

# The asset name GitHub Releases publishes, e.g. "bazel-diff-rust-linux-amd64".
RELEASE_ASSET_NAME = "bazel-diff-rust-" + _OS_NAME + "-" + _ARCH_NAME + _EXTENSION

def _release_binary_impl(ctx):
asset = ctx.actions.declare_file(ctx.attr.asset_name)

# Bazel copies instead of symlinking on platforms without symlink support
# (Windows, unless --windows_enable_symlinks), so the output is always a
# file CI can upload directly.
ctx.actions.symlink(
output = asset,
target_file = ctx.executable.binary,
progress_message = "Naming release asset %{output}",
)
return [DefaultInfo(files = depset([asset]))]

release_binary = rule(
implementation = _release_binary_impl,
doc = "Republishes `binary` under the file name used for release assets.",
attrs = {
"asset_name": attr.string(
mandatory = True,
doc = "File name to give the binary. Use `RELEASE_ASSET_NAME`.",
),
"binary": attr.label(
mandatory = True,
executable = True,
cfg = "target",
doc = "The binary to publish.",
),
},
)
8 changes: 8 additions & 0 deletions tools/readme_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,14 @@ Windows amd64: download
`bazel-diff-rust-windows-amd64.exe` from the
[latest release](https://github.com/Tinder/bazel-diff/releases/latest).

Those assets are produced by Bazel alone -- CI runs nothing but the command below and uploads
whatever lands in `bazel-bin/release/`, so `//release:bazel-diff-rust` names the binary for the
platform it was built on (`bazel-diff-rust-<os>-<arch>`, plus `.exe` on Windows):

```terminal
make release_rust_binary # bazel build //release:bazel-diff-rust --config=release
```

### Performance gate

The Rust candidate is expected to be faster than Kotlin, and CI enforces it. `make perf-gate`
Expand Down
Loading