Skip to content
Closed
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
1 change: 0 additions & 1 deletion .bazeliskrc

This file was deleted.

12 changes: 10 additions & 2 deletions .bazelrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
test --test_output=all
test --verbose_failures

build --java_runtime_version=local_jdk
build --tool_java_runtime_version=local_jdk
# remotejdk rather than local_jdk: the build is then hermetic, CI needs no setup-java step,
# and bazel-contrib's release workflow — which runs `bazel test //...` on a runner whose
# default JDK we do not control — cannot fail on the JDK version.
#
# 21 is the floor: rules_clojure.fs uses java.util.HexFormat, and the TOOL runtime is what
# runs the persistent worker, so both have to be set.
build --java_language_version=21
build --java_runtime_version=remotejdk_21
build --tool_java_language_version=21
build --tool_java_runtime_version=remotejdk_21
1 change: 1 addition & 0 deletions .bazelversion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
9.2.0
13 changes: 13 additions & 0 deletions .bcr/metadata.template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"homepage": "https://github.com/bpalermo/rules_clojure",
"maintainers": [
{
"name": "Bruno Palermo",
"github": "bpalermo",
"github_user_id": 4821983
}
],
"repository": ["github:bpalermo/rules_clojure"],
"versions": [],
"yanked_versions": {}
}
44 changes: 44 additions & 0 deletions .bcr/presubmit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Bazel 8.x and 9.x, because both are supported and the whole point of this fork is that
# 9 works — a matrix that tested only one of them would let the other rot silently.
#
# Linux and macOS arm64 only. Windows has never been supported (the toolchain shells out
# to POSIX installers and the worker's path handling is untested there), and claiming it
# would fail here rather than merely disappoint.
matrix:
platform: ["ubuntu2404", "macos_arm64"]
bazel: ["8.x", "9.x"]

tasks:
verify_targets:
name: the worker builds as a dependency
platform: ${{ platform }}
bazel: ${{ bazel }}
# A buildable target, not a .bzl file, and specifically this one: building the worker
# runs the bootstrap genrules, which proves the module's own maven_deps install
# resolves when rules_clojure is NOT the root module. That is the single most likely
# way publication breaks.
build_flags:
# The bootstrap genrules run Clojure on the TOOL JVM, and rules_clojure.fs needs
# java.util.HexFormat (Java 17+). The anonymous module BCR generates has no
# .bazelrc, so state the tool runtime here.
- "--tool_java_language_version=21"
- "--tool_java_runtime_version=remotejdk_21"
build_targets:
- "@rules_clojure//src/rules_clojure:worker"

# The real test: a module that depends on this one, brings its own Clojure from Maven,
# and AOT-compiles and tests Clojure code. Its own .bazelrc carries the JDK settings.
bcr_test_module:
module_path: "examples/bzlmod"
matrix:
platform: ["ubuntu2404", "macos_arm64"]
bazel: ["8.x", "9.x"]
tasks:
run_tests:
name: build and test Clojure through the published module
platform: ${{ platform }}
bazel: ${{ bazel }}
build_targets:
- "//src/example:hello"
test_targets:
- "//test/example:core_test"
5 changes: 5 additions & 0 deletions .bcr/source.template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"url": "https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/{REPO}-{TAG}.tar.gz",
"integrity": "",
"strip_prefix": "{REPO}-{VERSION}"
}
33 changes: 0 additions & 33 deletions .circleci/config.yml

This file was deleted.

87 changes: 72 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,80 @@
name: CI
name: ci

on:
push:
branches:
- master
branches: [main]
pull_request:
branches:
- master
workflow_dispatch:

# The default token permissions are write-heavy for every job; grant least privilege.
permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
verify:
# The ruleset itself, plus the module a stranger would write, on both supported Bazel
# majors and both supported platforms. This is what .bcr/presubmit.yml claims, so it is
# what CI has to cover.
bazel:
name: bazel ${{ matrix.bazel }} (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
bazel: ["8.6.0", "9.2.0"]
env:
# Takes precedence over .bazelversion, which is how one checkout covers both majors.
USE_BAZEL_VERSION: ${{ matrix.bazel }}
steps:
- uses: actions/checkout@v7

- uses: bazel-contrib/setup-bazel@0.19.0
with:
bazelisk-cache: true
disk-cache: ${{ github.workflow }}-${{ matrix.bazel }}
repository-cache: true
# Pull requests RESTORE caches but never SAVE them, so only reviewed code can
# populate a cache a later trusted build consumes — the standard Actions
# cache-poisoning shape — and PR caches stop evicting main's from the shared
# repository quota. Gated on the ref, not the event: workflow_dispatch can
# target any branch.
cache-save: ${{ github.ref == 'refs/heads/main' }}

- name: the ruleset
run: bazel test //...

# examples/ is in .bazelignore — each example is its own module and its own run.
- name: examples/bzlmod (the BCR test module)
working-directory: examples/bzlmod
run: bazel test //...

# The tools.deps path: clojure_tools_deps downloads the Clojure CLI and resolves
# deps.edn, and gen_srcs writes the BUILD files these examples build from. Linux only
# for now — the macOS branch of rules/tools_deps.bzl needs HOMEBREW_RUBY_PATH and is the
# least exercised code in the repo. Promote it once it is seen to pass.
tools-deps-examples:
name: examples/${{ matrix.example }} (bazel ${{ matrix.bazel }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example: [simple, stress]
bazel: ["8.6.0", "9.2.0"]
env:
USE_BAZEL_VERSION: ${{ matrix.bazel }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Mount bazel cache
uses: actions/cache@v2
- uses: actions/checkout@v7

- uses: bazel-contrib/setup-bazel@0.19.0
with:
path: ~/.cache/bazel
key: bazel
- name: Run bazel build
run: bazel build //...
- name: Run bazel test
bazelisk-cache: true
disk-cache: ${{ github.workflow }}-${{ matrix.example }}-${{ matrix.bazel }}
repository-cache: true
cache-save: ${{ github.ref == 'refs/heads/main' }}

- working-directory: examples/${{ matrix.example }}
run: bazel test //...
130 changes: 130 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: release

on:
# `*` matches any character except `/`, so the last one absorbs the suffix in
# v0.6.0-rc1: prerelease tags trigger this too, and the `prerelease` input below marks
# them accordingly. Rehearse with a prerelease tag rather than a dry run — the source
# archive must come from bazel-contrib/.github's release_ruleset workflow or BCR rejects
# its attestation, and that workflow always releases.
push:
tags: ["v*.*.*"]
workflow_dispatch:
inputs:
tag_name:
description: "Tag to release, e.g. v0.6.0"
required: true
type: string

permissions:
contents: read

jobs:
# Fail in seconds on a misconfigured release rather than after the whole pipeline.
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- name: MODULE.bazel must agree with the tag
env:
TAG: ${{ inputs.tag_name || github.ref_name }}
run: |
version="${TAG#v}"
echo "releasing $version"
modver=$(grep -oE 'version = "[^"]+"' MODULE.bazel | head -1 | cut -d'"' -f2)
if [ "$modver" != "$version" ]; then
echo "::error::MODULE.bazel declares '$modver' but the tag is '$version'" >&2
exit 1
fi

# The BCR entry commit's AUTHOR must be the address the Google CLA is signed under:
# bazelbuild/bazel-central-registry runs a cla/google check that reads the commit
# email, and publish-to-bcr otherwise defaults to a noreply address, which fails it
# AFTER the release is published and the tag can no longer move.
#
# A repository variable rather than a literal, because this repo is public. Only the
# length is printed; the address is not echoed into a public log.
- name: the CLA author address must be configured
env:
BCR_AUTHOR_EMAIL: ${{ vars.BCR_AUTHOR_EMAIL }}
run: |
if [ -z "${BCR_AUTHOR_EMAIL}" ]; then
echo "::error::repository variable BCR_AUTHOR_EMAIL is unset. Set it to the address the Google CLA is signed under: gh variable set BCR_AUTHOR_EMAIL" >&2
exit 1
fi
case "${BCR_AUTHOR_EMAIL}" in
*@*.*) echo "BCR_AUTHOR_EMAIL is set (${#BCR_AUTHOR_EMAIL} chars)" ;;
*) echo "::error::BCR_AUTHOR_EMAIL does not look like an email address" >&2; exit 1 ;;
esac

# Through bazel-contrib's workflow rather than `gh release create`: BCR verifies that a
# module's source-archive attestation was signed by release_ruleset and rejects
# attestations produced any other way.
#
# draft: true is required for attestations to coexist with immutable releases — assets
# must be attached while the release is still mutable. `finalize` publishes it.
release:
needs: check
permissions:
contents: write
id-token: write
attestations: write
uses: bazel-contrib/.github/.github/workflows/release_ruleset.yaml@v7.7.0
with:
bazel_test_command: "bazel test //..."
draft: true
# Derived from the tag: anything with a -suffix (v0.6.0-rc1) is a prerelease.
prerelease: ${{ contains(inputs.tag_name || github.ref_name, '-') }}
release_files: rules_clojure-*.tar.gz
tag_name: ${{ inputs.tag_name || github.ref_name }}

# Called DIRECTLY, not through a local wrapper workflow. A wrapper that relies on
# `secrets: inherit` to satisfy this workflow's REQUIRED publish_token fails startup
# validation for the entire release workflow — an inherited secret set is not statically
# known, so validation cannot prove the secret is present.
publish-to-bcr:
needs: release
permissions:
attestations: write
contents: write
id-token: write
uses: bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml@v1.5.0
with:
tag_name: ${{ inputs.tag_name || github.ref_name }}
# A fork of bazelbuild/bazel-central-registry that the token can push a branch to;
# the pull request is opened from there against upstream.
registry_fork: bpalermo/bazel-central-registry
# A prerelease pushes the entry branch to the fork but stops short of the upstream
# pull request. BCR is add-only — a merged release-candidate entry is permanent —
# and rehearsing must not spend BCR reviewers' attention. Everything before the pull
# request (token auth, asset download, attestation, entry generation) still runs.
open_pull_request: ${{ !contains(inputs.tag_name || github.ref_name, '-') }}
# A `uses:` job cannot declare `environment:` itself (actions/runner#1490), so it is
# passed as an input for the callee's job to declare — which is also the only place
# the environment secret can be resolved. The `bcr-publish` environment's policy
# allows only v* tags, so a pull request cannot reach the token.
environment: bcr-publish
# Required with immutable releases: the release is still a draft while this runs, so
# downloading assets by release URL would 404.
download_default_release_artifacts: true
author_name: Bruno Palermo
author_email: ${{ vars.BCR_AUTHOR_EMAIL }}
# Explicitly mapped, NOT `inherit`: PUBLISH_TOKEN is an ENVIRONMENT secret, and
# `inherit` passes only repository and organization secrets. This expression is empty
# at this level — which is all the static required-secret check needs — and the
# environment secret shadows it inside the callee's job. Do not "simplify" it.
secrets:
publish_token: ${{ secrets.PUBLISH_TOKEN }}

# Publishing the release is the LAST thing that happens: it is the only irreversible,
# publicly visible step, and everything before it can be re-run.
finalize:
needs: [check, publish-to-bcr]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- env:
TAG: ${{ inputs.tag_name || github.ref_name }}
GH_TOKEN: ${{ github.token }}
run: gh release edit "$TAG" --draft=false --repo "$GITHUB_REPOSITORY"
52 changes: 52 additions & 0 deletions .github/workflows/release_prep.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# Prepare the release archive. Called by bazel-contrib/.github's release_ruleset workflow
# with the tag as its only argument; stdout becomes the release notes.
#
# The path is hard-coded on purpose upstream: release_ruleset has no release_prep_command
# input, because a dispatch-supplied command would not be covered by the attestation. That
# is also why BCR only trusts source archives produced this way — it verifies the signing
# workflow, so an archive attested any other way is rejected.
#
# This is a source-only ruleset, so the archive is just the tagged tree. The prefix matches
# what GitHub generates for source archives, so switching between the two costs nothing.

set -o errexit -o nounset -o pipefail

TAG="${1:?tag, e.g. v0.6.0}"
PREFIX="rules_clojure-${TAG#v}"
ARCHIVE="rules_clojure-${TAG}.tar.gz"

git archive --format=tar --prefix="${PREFIX}/" "${TAG}" | gzip > "${ARCHIVE}"

# Sanity check rather than trust: an archive whose MODULE.bazel disagrees with the tag
# would be rejected by BCR much later, with a far less obvious message.
declared=$(tar -xzOf "${ARCHIVE}" "${PREFIX}/MODULE.bazel" \
| grep -oE 'version = "[^"]+"' | head -1 | cut -d'"' -f2)
if [ "${declared}" != "${TAG#v}" ]; then
echo "archived MODULE.bazel declares '${declared}', tag is '${TAG#v}'" >&2
exit 1
fi

cat <<NOTES
## Install

\`\`\`starlark
bazel_dep(name = "rules_clojure", version = "${TAG#v}")
\`\`\`

## JDK

The \`clojure_library\` worker runs on Bazel's **tool** JVM and needs Java 21
(\`rules_clojure.fs\` uses \`java.util.HexFormat\`). Set both runtimes in your \`.bazelrc\`:

\`\`\`
build --java_language_version=21
build --java_runtime_version=remotejdk_21
build --tool_java_language_version=21
build --tool_java_runtime_version=remotejdk_21
\`\`\`

Tested with Bazel 8.x and 9.x on Linux and macOS. This is a fork of
[griffinbank/rules_clojure](https://github.com/griffinbank/rules_clojure); see \`NOTICE\`
for what differs.
NOTES
Loading