From 2d56f8dbb891a33e1d9349e71a5aa7fa95021075 Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Wed, 12 Aug 2026 00:49:19 +0200 Subject: [PATCH 1/3] CI: pin every third-party action to a commit zenoh-flat-jni#36 applied this rule there, from @diogomatsubara's review, and this repository is the other half of the same release: a tag is mutable, so a moved tag runs code nobody reviewed - on jobs that hold the organization GPG key and the Central token. Every third-party `uses:` across the four workflows now names a commit, with the version in a trailing comment. Actions under eclipse-zenoh/ are ours and stay on a branch, deliberately. The pins are the same commits zenoh-flat-jni landed, so the two repositories run the same action code. Two of them are also bumps, and each says why in place: - peaceiris/actions-gh-pages v3 -> v4.1.0. v3 runs on node16, a runtime the Actions runner has retired. Its inputs are unchanged. - gradle/actions/setup-gradle v4 -> v5, not v6: v6 moved caching into a proprietary component under Gradle's own terms of use, which is not ours to accept for an Eclipse project. markdownlint-cli2-action is pinned where it stands, at v18. It is on node20, which is not retired, and a newer major changes markdownlint's rules - that is an upgrade to make on its own, with whatever README edits it asks for, not inside a pinning change. checkout, setup-java and upload-artifact land on current majors as a consequence of pinning to those commits. Every input these workflows pass still exists in the pinned versions, and checkout still defaults persist-credentials to true, which the release branch push in ci/scripts/bump-and-tag.bash depends on. --- .github/workflows/ci.yml | 18 +++++++++++++----- .github/workflows/publish-dokka.yml | 11 ++++++++--- .github/workflows/publish.yml | 14 ++++++++++---- .github/workflows/release.yml | 6 +++++- PUBLISHING.md | 7 +++++++ 5 files changed, 43 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad2d6037..876533d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,9 @@ on: env: CARGO_TERM_COLOR: always +# Third-party actions are pinned to a commit, with the version in a trailing +# comment: a tag is mutable, and a moved tag runs code nobody reviewed. Actions +# under eclipse-zenoh/ are ours and stay on a branch. jobs: build: name: Build on ${{ matrix.os }} @@ -23,11 +26,11 @@ jobs: steps: - name: Check out zenoh-java - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: path: zenoh-java - - uses: actions/setup-java@v4 + - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0 with: distribution: temurin java-version: 11 @@ -44,8 +47,10 @@ jobs: working-directory: zenoh-java run: rustup show + # v5, not v6: v6 moved caching into a proprietary component under Gradle's + # own terms of use, which is not ours to accept for an Eclipse project. - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2 - name: Gradle Test working-directory: zenoh-java @@ -63,8 +68,11 @@ jobs: markdown_lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: DavidAnson/markdownlint-cli2-action@v18 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + # Pinned where it stands. v18 runs on node20, which is not retired, and a + # newer major changes markdownlint's rules - an upgrade to make on its own, + # not inside a pinning change. + - uses: DavidAnson/markdownlint-cli2-action@eb5ca3ab411449c66620fe7f1b3c9e10547144b0 # v18 with: config: '.markdownlint.yaml' globs: '**/README.md' diff --git a/.github/workflows/publish-dokka.yml b/.github/workflows/publish-dokka.yml index 4964557b..650cab17 100644 --- a/.github/workflows/publish-dokka.yml +++ b/.github/workflows/publish-dokka.yml @@ -16,24 +16,29 @@ on: env: CARGO_TERM_COLOR: always +# Third-party actions are pinned to a commit, with the version in a trailing +# comment: a tag is mutable, and a moved tag runs code nobody reviewed. jobs: build_doc_and_deploy: name: Build and Deploy Documentation runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ inputs.branch }} + # v5, not v6: v6 moved caching into a proprietary component under Gradle's + # own terms of use, which is not ours to accept for an Eclipse project. - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2 - name: Build doc run: ./gradlew dokkaGenerate + # v4, not the v3 this was on: v3 runs on node16, a retired Actions runtime. - name: Deploy doc if: ${{ inputs.live-run || false }} - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453 # v4.1.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./zenoh-java/build/dokka/javadoc diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9937124f..56ca32f8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -27,6 +27,10 @@ on: # org.eclipse.zenoh:zenoh-flat-jni artifact this SDK depends on, already # cross-compiled and verified by that repository's release. This workflow only # compiles Kotlin and publishes. +# +# Third-party actions are pinned to a commit, with the version in a trailing +# comment: a tag is mutable, and a moved tag runs code nobody reviewed. Actions +# under eclipse-zenoh/ are ours and stay on a branch. jobs: publish_package: name: Publish to Maven Central @@ -36,17 +40,19 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ inputs.branch }} - - uses: actions/setup-java@v4 + - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0 with: distribution: temurin java-version: 11 + # v5, not v6: v6 moved caching into a proprietary component under Gradle's + # own terms of use, which is not ours to accept for an Eclipse project. - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2 # Assembles the artifact and generates its POM without uploading, so a # `maven_publish: false` rehearsal actually proves something. Only the @@ -82,7 +88,7 @@ jobs: - name: "Upload gradle problems report" if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: problem-reports-${{ github.job }}.zip path: ${{ github.workspace }}/build/reports/problems/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0052018a..e6548299 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,8 +57,12 @@ jobs: branch: ${{ inputs.branch }} github-token: ${{ secrets.BOT_TOKEN_WORKFLOW }} + # Third-party actions are pinned to a commit, with the version in a + # trailing comment: a tag is mutable, and a moved tag runs code nobody + # reviewed. The eclipse-zenoh/ci actions above and below are ours, and stay + # on a branch. - name: Checkout this repository - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ steps.create-release-branch.outputs.branch }} diff --git a/PUBLISHING.md b/PUBLISHING.md index 69ffcdce..c249a40b 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -258,6 +258,13 @@ in the README. Publishing goes through `io.github.gradle-nexus.publish-plugin` to the Central Portal, signed with the organization GPG key, exactly as in zenoh-flat-jni. +Every third-party action these workflows use is pinned to a **commit SHA**, with +the version in a trailing comment — a tag is mutable, and a moved tag would run +code nobody reviewed on a job that holds the signing key and the Central token. +The `eclipse-zenoh/ci` actions are ours and stay on `@main` deliberately: the +tagging and GitHub-release steps track whatever that branch holds at run time. +Bumping a pin is an ordinary pull request; read the diff of the action first. + ## Building against zenoh-flat-jni source A build can be pointed at zenoh-flat-jni's *source* through a Gradle composite From 9fd7586ef64b24a2b2c41848d63a59c8f77608c8 Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Wed, 12 Aug 2026 00:49:36 +0200 Subject: [PATCH 2/3] CI: fix the triggers - no double runs, release branches, no nightly Three things in the `on:` block, following the main zenoh repository as zenoh-flat-jni#36 did. `push` and `pull_request` were both on `["**"]`, so every push to a branch with an open pull request ran the whole job set twice - two builds on two runners for one commit, and two entries in the checks list to read. `push` now covers main and `release/*`, minus the dry-run branches. A release is built from a branch the shared create-release-branch action creates, and CI has never run on it - `push: ["**"]` did cover that, but only as a side effect of covering everything. `pull_request` stays on every branch, so a backport targeting a release branch still gets CI, and nothing loses coverage: a branch under review is covered there, and a branch that is not under review had no reader for its result. The weekday nightly goes. The obvious argument for it is that it would catch zenoh-flat-jni moving under us, and it does not: Cargo.lock pins that dependency to a commit, source = "git+https://github.com/eclipse-zenoh/zenoh-flat-jni.git?branch=main#" and Cargo re-resolves a git dependency only on `cargo update` or a missing lock entry, so a timed build rebuilds exactly what the last merge built. Upstream drift arrives here as a lockfile-sync pull request, which runs CI like anything else. What a nightly would still catch is an expired Central token or GPG key, and only during a week with no merges at all - every merge already exercises them. That is a thin canary against a daily publication of two coordinates, and under #525 it also risks a half-hour zenoh-flat-jni rebuild for no new commit. workflow_dispatch still runs the path on demand. zenoh-flat-jni#36 dropped its own nightly for the same reason and recorded it in PUBLISHING.md; its description said the other JVM repositories keep theirs, which this makes stale - only zenoh-kotlin does now. Concurrency is deliberately not touched here - #525 adds it, with cancel-in-progress false, because the pair of publications that branch introduces must not be cancelled between the two uploads. --- .github/workflows/ci.yml | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 876533d1..024d8cd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,26 @@ name: CI on: + # `release/*` because that is where a release is actually built from: the + # release workflow creates the branch through the shared + # eclipse-zenoh/ci/create-release-branch action, and without this CI never runs + # on it. The dry-run branches that same action produces are excluded - they are + # throwaway. push: - branches: ["**"] + branches: ["main", "release/*", "!release/dry-run/*"] + # Every branch, not just main: a backport pull request targets a release + # branch, and it needs CI as much as any other. Not `push` on every branch as + # well - with this on, that runs the whole matrix twice per branch. pull_request: branches: ["**"] - schedule: - - cron: "0 6 * * 1-5" + # No schedule. The obvious argument for a nightly is that it would catch + # zenoh-flat-jni moving under us, and it would not: Cargo.lock pins that + # dependency to a commit, and Cargo re-resolves a git dependency only on + # `cargo update`, so a timed build rebuilds exactly what the last merge built. + # Upstream drift arrives here as a lockfile-sync pull request, which runs CI + # like anything else. What is left is an expired Central token or GPG key, + # caught only in a week with no merges at all - a thin canary against a daily + # publication. workflow_dispatch runs the path on demand. workflow_dispatch: env: From 1b0c5597db161be2bb663ee3f8fabae0534850c1 Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Wed, 12 Aug 2026 01:05:21 +0200 Subject: [PATCH 3/3] CI: dispatch-only releases, no nightly dry run either release.yml ran on `0 0 * * 1-5` as well as on dispatch. A scheduled run passes no inputs, so it passes no `zenoh-flat-jni-version`, so it fell back to `zenohFlatJniVersion` in gradle.properties - a version not on Maven Central - and died while compiling. Every weeknight, for as long as that has been true. PUBLISHING.md documented the failure rather than the schedule being wrong. Nothing is lost by removing it. A dry-run release rehearses the release path, and rehearsing is a deliberate act with a version in the box, exactly like the release it rehearses. Actions -> Release -> Run workflow does both. Same reasoning as the CI nightly in the previous commit, one workflow over. --- .github/workflows/release.yml | 7 +++++-- PUBLISHING.md | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e6548299..2d5448ef 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,8 +14,11 @@ name: Release on: - schedule: - - cron: "0 0 * * 1-5" + # Dispatch only. The weekday schedule this had ran a dry-run release every + # night with no inputs, which means no `zenoh-flat-jni-version` - so it + # resolved the unreleased fallback in gradle.properties and died compiling, + # every night. A release is a deliberate act, and rehearsing one is a + # deliberate act too; both are `Run workflow` here. See PUBLISHING.md. workflow_dispatch: inputs: live-run: diff --git a/PUBLISHING.md b/PUBLISHING.md index c249a40b..acf9546c 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -208,8 +208,11 @@ Could not find org.eclipse.zenoh:zenoh-flat-jni:1.9.0 That is the conditional repository below doing its job, not a broken build: a non-snapshot version never gets the snapshot repository on its resolution path. -The same applies to the nightly scheduled run of `release.yml`, which passes no -inputs and so fails this way until zenoh-flat-jni is released for real. + +`release.yml` used to run on a weekday schedule as well, and a scheduled run +passes no inputs — so it failed exactly this way every night. The schedule is +gone: the workflow is `workflow_dispatch` only, and a rehearsal is something you +start on purpose, with the version filled in. The Central snapshot repository is declared **conditionally** in `build.gradle.kts`, and this is the part worth understanding: