Skip to content

DEVOPS-6131: route sbt resolution through virtana-zing AR mirror (PR pipeline) - #16

Merged
romanbakaleyko merged 7 commits into
masterfrom
DEVOPS-6131-sbt-central-mirror
Aug 17, 2026
Merged

DEVOPS-6131: route sbt resolution through virtana-zing AR mirror (PR pipeline)#16
romanbakaleyko merged 7 commits into
masterfrom
DEVOPS-6131-sbt-central-mirror

Conversation

@romanbakaleyko

@romanbakaleyko romanbakaleyko commented Aug 17, 2026

Copy link
Copy Markdown

Problem

DEVOPS-6131 — the delta-sharing sbt build fails on jenkins-eng with HTTP 429 from Maven Central (repo1.maven.org). Central's Cloudflare front-end rate-limits jenkins-eng's shared egress IP, so sbt cannot download its own launcher (org.scala-sbt:sbt:1.9.9), plugins, or dependencies. Same root cause as DEVOPS-6083 (Maven), but via sbt.

Fix

This is the sbt analog of the mirrorMavenCentral shared step. sbt doesn't read Maven's settings.xml, so we configure sbt's own resolution to go through the virtana-zing Artifact Registry Maven proxy (us-maven.pkg.dev/zing-registry-188222/virtana-zing), authenticated with a short-lived OAuth token minted from gcr_push_key (SA zing-gcr-push, which has artifactregistry read on zing-registry-188222).

Before make build in ci/Jenkinsfile (the pull-request pipeline):

  • ~/.sbt/repositoriesmaven-central points at the mirror
  • ~/.config/coursier/credentials.properties → authenticates the launcher (boot) resolution
  • credentials.sbt (build) + project/credentials.sbt (meta) → authenticate library and plugin resolution (lm-coursier only honors sbt's native credentials, not the coursier file/env)
  • pre-fetch sbt-launch.jar through the mirror (bearer header; the wrapper's bare curl can't auth)
  • -Dsbt.override.build.repos=true

The token is minted in a throwaway CLOUDSDK_CONFIG (agent identity untouched) and never printed (set +x); the coursier properties file is removed in the finally block.

Verification

Proven green end-to-end on a trimmed test clone (micro-services/delta-sharing/rbak-test-build): 0 429, 0 unauthorized, 0 token leaks, Image built: deltaio/delta-sharing-server:1.3.11-SNAPSHOT. This PR's own pull-request build exercises the change directly.

Follow-up

The build job (inline pipeline) needs the same block — handled separately.

🤖 Generated with Claude Code

romanbakaleyko and others added 7 commits August 17, 2026 13:21
…pipeline)

The delta-sharing sbt build fails on jenkins-eng: Maven Central (repo1.maven.org)
429-rate-limits the shared egress IP, so sbt can't download its own launcher
(org.scala-sbt:sbt:1.9.9) or its plugins/deps.

Fix (sbt analog of the mirrorMavenCentral shared step): before `make build`,
point sbt's Central resolution at the virtana-zing Artifact Registry Maven proxy,
authenticated with a short-lived OAuth token minted from gcr_push_key:
  - ~/.sbt/repositories            maven-central -> us-maven.pkg.dev/.../virtana-zing
  - ~/.config/coursier/...         launcher (boot) auth
  - credentials.sbt / project/credentials.sbt   build + plugin (meta) auth
  - pre-fetch sbt-launch.jar through the mirror (bearer header)
  - -Dsbt.override.build.repos=true

Verified green end-to-end on a trimmed clone (rbak-test-build): no 429, no
unauthorized, no token leak, image builds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Refactor the inline pipeline sh into repo-side config templates and one setup
script, so ci/Jenkinsfile just binds the credential and calls it:

  ci/sbt-mirror/
    repositories                 static repo list (maven-central -> mirror)
    credentials.properties.tmpl  coursier boot creds (token populated on the fly)
    credentials.sbt              lm-coursier creds for deps + plugins (reads ~/.sbt/.ar-token)
    setup.sh                     mints token, renders the above, pre-fetches sbt-launch.jar,
                                 writes .sbtopts (-Dsbt.override.build.repos=true)

Build stage is now:
  withCredentials([file(credentialsId:'gcr_push_key', variable:'AR_KEY_FILE')]) {
      sh 'ci/sbt-mirror/setup.sh && make -f ci/Makefile build'
  }

Same behavior as the previous commit; the shared script lets the inline `build`
job reuse identical logic. Generated copies (credentials.sbt, project/credentials.sbt,
.sbtopts) are gitignored.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ot file read)

The previous refactor had project/credentials.sbt read the token via IO.read of a
file; that didn't reach the meta build, so plugin resolution 401'd. Revert to the
proven mechanism: credentials.sbt reads AR_TOKEN from the environment. setup.sh
writes a sourceable .ar-token.env, and the pipeline does
`ci/sbt-mirror/setup.sh && . ./.ar-token.env && make build` so $AR_TOKEN reaches
sbt (setup.sh runs in its own process). No secret is committed; .ar-token.env is
gitignored and removed in the finally block.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ls.sbt

- Jenkins runs sh with -x, so sourcing .ar-token.env echoed the token; prefix the
  build step with `set +x`.
- Revert credentials.sbt to the exact Credentials form proven to authenticate
  plugin + dependency resolution against the mirror.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nd credentials"

sbt's Ivy code path (makePom/metadata) matches credentials by realm, not just host.
With an empty realm it logged 4x "Unable to find credentials for
[https://us-maven.pkg.dev @ us-maven.pkg.dev]" (non-fatal; coursier still resolved
by host). Set the realm to exactly what AR advertises
(WWW-Authenticate: Basic realm="https://us-maven.pkg.dev") so Ivy matches too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…y warnings)

delta-sharing is multi-project (server/client/spark). A plain `credentials += ...`
is root-project-only, so Ivy resolution for the subprojects logged "Unable to find
credentials for [https://us-maven.pkg.dev @ us-maven.pkg.dev]" (non-fatal; coursier
still downloaded everything build-wide). Scope to `ThisBuild / credentials` so all
subprojects have the credential.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n, self-cleaning)

Replace the setup.sh + .ar-token.env + pipeline-finally-cleanup dance with a single
wrapper that runs the build with the mirror configured:

    ci/sbt-mirror/with-mirror.sh make -f ci/Makefile build

- Token stays IN-PROCESS (exported to the child build only); no token is written to
  a file that gets passed around. The one transient file that must hold it (coursier
  boot creds) plus the rendered sbt config are removed by an EXIT trap, pass or fail.
- gcloud auth already uses a throwaway CLOUDSDK_CONFIG dir, so the agent's own gcloud
  account is never activated/replaced (it is not `gcloud auth login`).
- repositories now written workspace-local (.sbt-mirror-repositories) so we don't
  clobber a shared ~/.sbt/repositories on the agent.

Pipeline Build stage is now a single line; no finally cleanup of auth files needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@romanbakaleyko
romanbakaleyko merged commit 126b467 into master Aug 17, 2026
8 checks passed
@romanbakaleyko
romanbakaleyko deleted the DEVOPS-6131-sbt-central-mirror branch August 17, 2026 11:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants