ci: dev → main pipeline with tokenless publishing to crates.io - #455
Open
MattJackson wants to merge 1 commit into
Open
MattJackson wants to merge 1 commit into
MattJackson wants to merge 1 commit into
Conversation
Replace test.yml, security.yml and pr-code-security.yml with three workflows: - ci.yml (CI): the fast tier on every PR push and in the merge queue: rustfmt, clippy, lib tests for four feature sets, MSRV 1.88, docs, cargo-deny (also weekly), gitleaks, advisory semver-checks and a SQL Server 2022 smoke test. The ci-ok job aggregates them into the single required check. - qa.yml (QA): the heavy tier in the merge queue: the Linux SQL Server matrix (2017/2019/2022/azure-sql-edge x 7 feature sets), macOS via colima, Windows SQL 2019 with integrated auth, and strict semver-checks against the latest crates.io release. qa-ok aggregates them. It also runs on pull_request with every job skipped so the required check reports before a PR enters the queue. - release.yml (Release): every PR into main is a release from dev, merged with a merge commit. On such a PR the release gate requires a version above crates.io (lower or invalid versions fail), a CHANGELOG heading, a free tag, a published or co-released tiberius-macros requirement, an unchanged tiberius-macros package unless it is bumped, a head on dev, and a merged tree equal to a dev commit whose qa.yml run succeeded. A PR that introduces release.yml (the target tip has none) passes once as a bootstrap. On push to main, a bumped version is published via crates.io Trusted Publishing, checked against the index checksum, then tagged with a GitHub Release. An unbumped push is a no-op. A dispatch retries a failed release or dry-runs publishing. The gate lives in .github/scripts/release_gate.py with offline tests that CI runs. Strict clippy is on for --features=all. The rustls clippy lane and the rustdoc warnings stay advisory until the 0.13.1 stack lands, because upstream main still has lints there. The Windows lane skips the three special-character password tests: they build a SQL login string on top of an IntegratedSecurity=true one, so they only run on Linux and macOS until the test also sets IntegratedSecurity=false. Also add dependabot for actions, CODEOWNERS for .github/ and CONTRIBUTING.md, point the README badge at tiberius-rs/tiberius ci.yml, and update the deny.toml comment.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
@aqrln, this is the GitHub Actions setup you asked for in #454, so every release can go out
from CI through Trusted Publishing with no stored token. While I was at it I went through all
of our CI end to end, and I'd like to propose a small change to how code flows into
main.The goal is to make things faster for maintainers and safer at the same time. Here's the
what and the why.
What we have today
main. The ruleset requires a PR but no approvals and no passingchecks, so a PR can be merged while CI is red.
main, "Cargo tests" only runs fmt, the MSRV check and the rustls unit tests.Clippy is advisory. The real SQL Server tests run on PRs only, and nothing requires the PR to
be up to date with
main. So the commit that actually lands onmainmay never have beentested as-is.
qa, which is stale, and the smokejob's
devcondition refers to a branch that doesn't exist. Those lanes never run.0.13.0: tagged, but not published until you did it by hand.
What this PR sets up
devdevbecomes the default branch)ci.yml: fmt, strict clippy, lib tests for four feature sets, MSRV, docs, cargo-deny, gitleaks, advisory semver, one real-server smoke run. About 10 minutes.devqa.ymlruns the full suite on the exact commit that will land: SQL Server 2017/2019/2022/Edge × 7 feature sets, macOS, Windows, and strict semver against the last published version. If it's red, the PR bounces back anddevis never broken.dev→maindevalready has the version and CHANGELOG bumprelease.ymlgate: the merged tree must be identical to adevtree that passed QA, and it checks the version, changelog, tag and macro-crate versionmainrelease.ymlpublishestiberius-macros(if bumped) andtiberiusvia Trusted Publishing, checks the published checksum, and creates the tag and GitHub Releasemainalways equals the latest crates.io release, and it keepsdev's real commits, becausereleases are merge commits. That means
git log mainshows exactly what shipped in eachrelease, and the next
dev→mainPR is always a clean merge.Why this is better
Faster for maintainers
testing and merges it for you. No waiting on a 45-minute matrix before clicking merge, and
no watching CI afterwards.
dev→main, and one click. No tagging, no localcargo publish,no tokens.
and not again at release time. PR feedback stays around 10 minutes.
instead of a broken branch that someone has to notice and fix.
Safer
devwithout the full suite passing on the exact merge result.main's tree is byte-for-byte a tree that passed thatsuite. A change that bypasses
devmakes the release gate fail.mainis arelease), a version going backwards, a macro crate changed without its version changing, a
missing CHANGELOG entry, a clashing tag, or an unpublished dependency version. After
publishing, it checks the crates.io checksum matches what was built.
and this workflow, and every action is pinned by SHA (Dependabot keeps the pins current).
.github/changes need a maintainer's review (CODEOWNERS), so the release path can't changeunnoticed.
Nothing is lost. Every job in
test.yml,security.ymlandpr-code-security.ymliskept, and the lanes that never ran now do:
clippy(advisory)ci.ymlclippy, strict (--all-targets --features=all -D warnings), plus a rustls-webpki-roots laneformatci.ymlrustfmtmsrvci.ymlMSRV (1.88)rustls-featuresci.ymllib tests, 4 feature setssemver(advisory)ci.ymladvisory semver, plus new strict semver inqa.ymlagainst crates.iosmoke(4×7 on every PR)ci.ymlSQL 2022 smoke on every PR, plus the full 4×7 inqa.ymlin the merge queueintegration-macos(never ran)qa.ymlmacOS, now greenintegration-windows(never ran)qa.ymlWindows, now greensecurity.ymlcargo-denyci.ymlcargo-deny (PRs, queue, weekly)pr-code-security.ymlgitleaksci.ymlgitleaks (every PR and the queue)Proof
I ran the whole pipeline on my fork before opening this, including two back-to-back
releases (all runs at
https://github.com/MattJackson/tiberius/actions/runs/<id>):ci.ymlon this PRrelease.ymlon the base yet)mainmainwithout a version bumpdev(35/35 jobs, incl. macOS + Windows)dev→mainPR gatemain, publish dry rundevdev→mainPR gate (clean merge)main, publish dry runThe release-gate logic also has 29 unit tests, which run in CI.
Not proven on a fork: the real crates.io token exchange, a real publish with index
verification, tag and GitHub Release creation, and the merge queue (personal repos don't have
one). 0.13.1 will be the first end-to-end run. If anything fails part-way, the Release
workflow can be re-run on
main, and crates already published are skipped.Known items: three Windows tests are skipped (with a TODO) because they add a SQL login to
an integrated-auth connection string, which is a test bug and not a CI one. The macOS lanes
take about 45 minutes, so the queue timeout should be 120 minutes or more.
Who does what
Me, once you're happy with this PR (repo admin, about 10 minutes):
mainallows today).devfrom the resultingmain.The rulesets decide which branch may use which method.
devthe default branch.silently move to
dev:refs/heads/main;release gate;dev:ci-okandqa-ok;v*andtiberius-macros-v*: block updates, deletions and forcepushes. Creation stays open, because the Release workflow creates the tags.
You (needs a crates.io owner login, about 5 minutes):
Add a Trusted Publisher on crates.io for each crate (Settings → Trusted Publishing →
Add → GitHub):
tiberiustiberius-rstiberiusrelease.ymltiberius-macrostiberius-rstiberiusrelease.ymltiberius-macrosis owned by you alone, so that one has to be you. Once the first releasehas gone out through CI, you can switch both crates to require Trusted Publishing and
revoke any old tokens.
Nothing else is needed from you: no secrets, no workflow changes. The day-to-day is review,
"Merge when ready", and a
dev→mainPR when it's time to ship.CONTRIBUTING.mdin thisPR describes the flow for contributors. Once #8 is done I'll also delete the stale
qabranch and the
sync/*leftovers.Next
My 0.13.1 PR (the stacked fixes, rebased on current
main) would be the first release throughthis: into
devvia the queue, then adev→mainPR, and 0.13.1 publishes itself.Closes #454
Thoughts on this?