diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f28d707bf..d740f09a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,9 +12,6 @@ permissions: env: PYTHONUTF8: 1 - # Windows runners can't reach public PyPI or npm; they install through JFrog. - WINDOWS_PACKAGE_INDEX: https://databricks.jfrog.io/artifactory/api/pypi/db-pypi/simple - WINDOWS_NPM_REGISTRY: https://databricks.jfrog.io/artifactory/api/npm/db-npm/ concurrency: group: ci-${{ github.event.pull_request.number || github.ref }}-${{ github.event_name }} @@ -41,16 +38,13 @@ jobs: - run: just lint test: - name: Unit tests · ${{ matrix.os == 'windows-server-latest' && 'Windows' || 'Linux' }} + name: Unit tests · ${{ matrix.os == 'windows-latest' && 'Windows' || 'Linux' }} runs-on: ${{ matrix.os }} needs: lint - permissions: - contents: read - id-token: write strategy: fail-fast: false matrix: - os: ${{ fromJSON((github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && '["ubuntu-latest", "windows-server-latest"]' || '["ubuntu-latest"]') }} + os: [ubuntu-latest, windows-latest] defaults: run: shell: bash @@ -64,23 +58,8 @@ jobs: with: persist-credentials: false - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 - - name: Authenticate to JFrog with GitHub OIDC - id: jfrog - if: ${{ runner.os == 'Windows' }} - uses: jfrog/setup-jfrog-cli@279b1f629f43dd5bc658d8361ac4802a7ef8d2d5 # v4.9.1 - env: - JF_URL: https://databricks.jfrog.io - with: - oidc-provider-name: github-actions - name: Install dependencies - env: - UV_INDEX_DATABRICKS_PYPI_USERNAME: ${{ steps.jfrog.outputs.oidc-user }} - UV_INDEX_DATABRICKS_PYPI_PASSWORD: ${{ steps.jfrog.outputs.oidc-token }} run: | - if [[ "$RUNNER_OS" == Windows ]]; then - unset UV_INDEX_URL - export UV_DEFAULT_INDEX="databricks-pypi=$WINDOWS_PACKAGE_INDEX" - fi uv lock uv sync - run: uv run --no-sync pytest --ignore=tests/test_e2e.py @@ -109,21 +88,18 @@ jobs: uv run pytest tests/native_claude/test_mod.py -q e2e-shards: - name: ${{ matrix.name }} · ${{ matrix.os == 'windows-server-latest' && 'Windows' || 'Linux' }} + name: ${{ matrix.name }} · ${{ matrix.os == 'windows-latest' && 'Windows' || 'Linux' }} runs-on: ${{ matrix.os }} needs: lint # The merge queue runs only lint and unit tests; e2e already ran on the PR. if: ${{ github.event_name != 'merge_group' }} - permissions: - contents: read - id-token: write defaults: run: shell: bash strategy: fail-fast: false matrix: - os: ${{ fromJSON((github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && '["ubuntu-latest", "windows-server-latest"]' || '["ubuntu-latest"]') }} + os: [ubuntu-latest, windows-latest] group: [gateway, claude, codex, gemini, opencode, copilot, pi] include: - group: gateway @@ -183,37 +159,11 @@ jobs: run: | test -f "$RUNNER_TEMP/.bin/databricks.exe" cygpath -w "$RUNNER_TEMP/.bin" >> "$GITHUB_PATH" - - name: Authenticate to JFrog with GitHub OIDC - id: jfrog - if: ${{ runner.os == 'Windows' }} - uses: jfrog/setup-jfrog-cli@279b1f629f43dd5bc658d8361ac4802a7ef8d2d5 # v4.9.1 - env: - JF_URL: https://databricks.jfrog.io - with: - oidc-provider-name: github-actions - name: Install the shard's agent CLI if: ${{ matrix.package != '' }} - env: - NPM_TOKEN: ${{ steps.jfrog.outputs.oidc-token }} - run: | - if [[ "$RUNNER_OS" == Windows ]]; then - npmrc="$RUNNER_TEMP/jfrog.npmrc" - printf 'registry=%s\n//%s:_authToken=%s\n' \ - "$WINDOWS_NPM_REGISTRY" "${WINDOWS_NPM_REGISTRY#https://}" "$NPM_TOKEN" > "$npmrc" - npm install -g --userconfig "$npmrc" "$AGENT_PACKAGE" - rm "$npmrc" - else - npm install -g "$AGENT_PACKAGE" - fi + run: npm install -g "$AGENT_PACKAGE" - name: Install ug and test dependencies - env: - UV_INDEX_DATABRICKS_PYPI_USERNAME: ${{ steps.jfrog.outputs.oidc-user }} - UV_INDEX_DATABRICKS_PYPI_PASSWORD: ${{ steps.jfrog.outputs.oidc-token }} run: | - if [[ "$RUNNER_OS" == Windows ]]; then - unset UV_INDEX_URL - export UV_DEFAULT_INDEX="databricks-pypi=$WINDOWS_PACKAGE_INDEX" - fi uv lock uv tool install . uv sync @@ -240,9 +190,6 @@ jobs: needs: lint # The condition runs integration tests only if lint passes and the overall workflow is not cancelled. if: ${{ github.event_name != 'merge_group' && needs.lint.result == 'success' && !cancelled() }} - permissions: - contents: read - id-token: write uses: ./.github/workflows/integration.yml secrets: inherit # zizmor: ignore[secrets-inherit] same-repo workflow that needs nearly every secret diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index df7c308ae..d7ff07e4d 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -54,9 +54,6 @@ env: CODEX_VERSION: ${{ inputs.codex_version || '0.154.0' }} OPENCODE_VERSION: ${{ inputs.opencode_version || '1.18.31' }} PACKAGE_INDEX: ${{ inputs.default_index || 'https://pypi.org/simple' }} - # Windows runners install packages through the Databricks JFrog proxies. - WINDOWS_PACKAGE_INDEX: https://databricks.jfrog.io/artifactory/api/pypi/db-pypi/simple - WINDOWS_NPM_REGISTRY: https://databricks.jfrog.io/artifactory/api/npm/db-npm/ jobs: installation: @@ -98,15 +95,8 @@ jobs: installation-windows: name: Windows installation tests - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - runs-on: windows-server-latest + runs-on: windows-latest timeout-minutes: 20 - permissions: - contents: read - id-token: write - env: - PACKAGE_INDEX: https://databricks.jfrog.io/artifactory/api/pypi/db-pypi/simple - NPM_REGISTRY: https://databricks.jfrog.io/artifactory/api/npm/db-npm/ steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: @@ -120,22 +110,8 @@ jobs: with: version: 0.9.8 enable-cache: ${{ inputs.ref == '' }} - - name: Authenticate to JFrog with GitHub OIDC - id: jfrog - uses: jfrog/setup-jfrog-cli@279b1f629f43dd5bc658d8361ac4802a7ef8d2d5 # v4.9.1 - env: - JF_URL: https://databricks.jfrog.io - with: - oidc-provider-name: github-actions - name: Test a fresh installed package without workspace credentials shell: pwsh - env: - # uv 0.9.8 resolves credentials for the named default index from - # UV_INDEX__USERNAME/PASSWORD without placing them in its URL. - UV_DEFAULT_INDEX: databricks-pypi=${{ env.PACKAGE_INDEX }} - UV_INDEX_DATABRICKS_PYPI_USERNAME: ${{ steps.jfrog.outputs.oidc-user }} - UV_INDEX_DATABRICKS_PYPI_PASSWORD: ${{ steps.jfrog.outputs.oidc-token }} - UG_INTEGRATION_NPM_TOKEN: ${{ steps.jfrog.outputs.oidc-token }} run: | $output = Join-Path $env:RUNNER_TEMP "ug-integration" $runnerArgs = @( @@ -143,8 +119,7 @@ jobs: "--ug-version", $env:UG_VERSION, "--entry-point", $env:ENTRY_POINT, "--claude-version", $env:CLAUDE_VERSION, - "--default-index", $env:UV_DEFAULT_INDEX, - "--npm-registry", $env:NPM_REGISTRY, + "--default-index", $env:PACKAGE_INDEX, "--installation-only", "--output", $output ) @@ -187,7 +162,7 @@ jobs: PY smoke: - name: Smoke journeys · ${{ matrix.agent == 'claude' && 'Claude' || 'Codex' }} · ${{ matrix.os == 'windows-server-latest' && 'Windows' || 'Linux' }} + name: Smoke journeys · ${{ matrix.agent == 'claude' && 'Claude' || 'Codex' }} · ${{ matrix.os == 'windows-latest' && 'Windows' || 'Linux' }} needs: workspace runs-on: ${{ matrix.os }} timeout-minutes: 20 @@ -195,13 +170,10 @@ jobs: # Both OS variants use the same workspace; avoid overlapping same-agent runs. group: integration-smoke-${{ github.run_id }}-${{ matrix.agent }} cancel-in-progress: false - permissions: - contents: read - id-token: write strategy: fail-fast: false matrix: - os: [ubuntu-22.04, windows-server-latest] + os: [ubuntu-22.04, windows-latest] agent: [claude, codex] env: UCODE_TEST_WORKSPACE: ${{ secrets.UCODE_TEST_WORKSPACE }} @@ -232,25 +204,9 @@ jobs: run: | test -f "$RUNNER_TEMP/.bin/databricks.exe" cygpath -w "$RUNNER_TEMP/.bin" >> "$GITHUB_PATH" - - name: Authenticate to JFrog with GitHub OIDC - id: jfrog - if: ${{ runner.os == 'Windows' }} - uses: jfrog/setup-jfrog-cli@279b1f629f43dd5bc658d8361ac4802a7ef8d2d5 # v4.9.1 - env: - JF_URL: https://databricks.jfrog.io - with: - oidc-provider-name: github-actions - name: Run the selected live cases against the e2e workspace shell: bash - env: - UV_INDEX_DATABRICKS_PYPI_USERNAME: ${{ steps.jfrog.outputs.oidc-user }} - UV_INDEX_DATABRICKS_PYPI_PASSWORD: ${{ steps.jfrog.outputs.oidc-token }} - UG_INTEGRATION_NPM_TOKEN: ${{ steps.jfrog.outputs.oidc-token }} run: | - if [[ "$RUNNER_OS" == Windows ]]; then - PACKAGE_INDEX="databricks-pypi=$WINDOWS_PACKAGE_INDEX" - registry=(--npm-registry "$WINDOWS_NPM_REGISTRY") - fi case "$AGENT" in claude) args=(--claude-version "$CLAUDE_VERSION") ;; codex) args=(--codex-version "$CODEX_VERSION") ;; @@ -262,7 +218,7 @@ jobs: uv run --no-project --python 3.12 python scripts/run_integration.py \ --python 3.12 --ug-version "$UG_VERSION" --entry-point "$ENTRY_POINT" \ --default-index "$PACKAGE_INDEX" --output "$RUNNER_TEMP/ug-integration" \ - "${registry[@]}" "${args[@]}" -- -m "$TEST_MARKER" + "${args[@]}" -- -m "$TEST_MARKER" - name: Upload live test evidence if: ${{ !cancelled() }} uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 @@ -280,14 +236,9 @@ jobs: headless-windows: name: Windows headless journey · Claude needs: workspace - runs-on: windows-server-latest + runs-on: windows-latest timeout-minutes: 20 - permissions: - contents: read - id-token: write env: - PACKAGE_INDEX: https://databricks.jfrog.io/artifactory/api/pypi/db-pypi/simple - NPM_REGISTRY: https://databricks.jfrog.io/artifactory/api/npm/db-npm/ DEPENDENCY: ${{ inputs.dependency }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -316,22 +267,11 @@ jobs: Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append & $databricks version if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } - - name: Authenticate to JFrog with GitHub OIDC - id: jfrog - uses: jfrog/setup-jfrog-cli@279b1f629f43dd5bc658d8361ac4802a7ef8d2d5 # v4.9.1 - env: - JF_URL: https://databricks.jfrog.io - with: - oidc-provider-name: github-actions - name: Configure and complete a real headless gateway task shell: pwsh env: UCODE_TEST_WORKSPACE: ${{ secrets.UCODE_TEST_WORKSPACE }} DATABRICKS_BEARER: ${{ secrets.DATABRICKS_BEARER }} - UV_DEFAULT_INDEX: databricks-pypi=${{ env.PACKAGE_INDEX }} - UV_INDEX_DATABRICKS_PYPI_USERNAME: ${{ steps.jfrog.outputs.oidc-user }} - UV_INDEX_DATABRICKS_PYPI_PASSWORD: ${{ steps.jfrog.outputs.oidc-token }} - UG_INTEGRATION_NPM_TOKEN: ${{ steps.jfrog.outputs.oidc-token }} run: | $output = Join-Path $env:RUNNER_TEMP "ug-integration" $runnerArgs = @( @@ -339,8 +279,7 @@ jobs: "--ug-version", $env:UG_VERSION, "--entry-point", $env:ENTRY_POINT, "--claude-version", $env:CLAUDE_VERSION, - "--default-index", $env:UV_DEFAULT_INDEX, - "--npm-registry", $env:NPM_REGISTRY, + "--default-index", $env:PACKAGE_INDEX, "--headless-only", "--output", $output ) @@ -362,7 +301,7 @@ jobs: ${{ runner.temp }}/ug-integration/wheels/ full: - name: Full journeys · ${{ matrix.agent == 'claude' && 'Claude' || 'Codex' }} · ${{ matrix.os == 'windows-server-latest' && 'Windows' || 'Linux' }} + name: Full journeys · ${{ matrix.agent == 'claude' && 'Claude' || 'Codex' }} · ${{ matrix.os == 'windows-latest' && 'Windows' || 'Linux' }} if: ${{ !cancelled() && needs.workspace.result == 'success' }} needs: [workspace, smoke] runs-on: ${{ matrix.os }} @@ -370,9 +309,6 @@ jobs: # Keep the previous per-agent serialization across the new OS matrix. group: integration-full-${{ github.run_id }}-${{ matrix.agent }} cancel-in-progress: false - permissions: - contents: read - id-token: write # The runner enforces its own 3600s pytest deadline; keep the job above it # so a slow run fails through the runner's report instead of a job kill. timeout-minutes: 70 @@ -381,7 +317,7 @@ jobs: # One runner per OS variant; concurrency above serializes variants per agent. max-parallel: 4 matrix: - os: [ubuntu-22.04, windows-server-latest] + os: [ubuntu-22.04, windows-latest] agent: [claude, codex] env: UCODE_TEST_WORKSPACE: ${{ secrets.UCODE_TEST_WORKSPACE }} @@ -396,18 +332,15 @@ jobs: # Non-blocking: continue-on-error and absent from the cujs `needs`, so a flaky OpenCode # leg cannot block merges. Add it to cujs once the lane is stable. opencode: - name: Full journeys · OpenCode · ${{ matrix.os == 'windows-server-latest' && 'Windows' || 'Linux' }} (non-blocking) + name: Full journeys · OpenCode · ${{ matrix.os == 'windows-latest' && 'Windows' || 'Linux' }} (non-blocking) if: ${{ !cancelled() && needs.workspace.result == 'success' }} needs: workspace continue-on-error: true runs-on: ${{ matrix.os }} - permissions: - contents: read - id-token: write strategy: fail-fast: false matrix: - os: [ubuntu-22.04, windows-server-latest] + os: [ubuntu-22.04, windows-latest] timeout-minutes: 30 env: UCODE_TEST_WORKSPACE: ${{ secrets.UCODE_TEST_WORKSPACE }} diff --git a/scripts/run_integration.py b/scripts/run_integration.py index 8300e237c..3def8c7ec 100644 --- a/scripts/run_integration.py +++ b/scripts/run_integration.py @@ -33,12 +33,6 @@ "opencode": "opencode-ai", } WINDOWS_PATHEXT = ".COM;.EXE;.BAT;.CMD" -UV_INDEX_CREDENTIAL_ENV = ( - "UV_INDEX_DATABRICKS_PYPI_USERNAME", - "UV_INDEX_DATABRICKS_PYPI_PASSWORD", -) -NPM_TOKEN_ENV = "UG_INTEGRATION_NPM_TOKEN" -INSTALLER_CREDENTIAL_ENV = (*UV_INDEX_CREDENTIAL_ENV, NPM_TOKEN_ENV) PTY_MODULES = {"pexpect", "pyte"} PTY_HELPERS = {"utils.terminal", "utils.mcp"} # Claude exports no spans on Windows, where ug writes no machine-wide Claude settings. @@ -50,38 +44,6 @@ } -def installer_environment( - base_environment: Mapping[str, str], - source_environment: Mapping[str, str], - credential_keys: Iterable[str], - npm_user_config: Path | None = None, -) -> dict[str, str]: - """Add only supported installer credentials to an isolated environment.""" - environment = dict(base_environment) - for key in credential_keys: - if value := source_environment.get(key): - environment[key] = value - if npm_user_config is not None: - environment["npm_config_userconfig"] = str(npm_user_config) - return environment - - -def npm_user_config(registry: str) -> str: - """Configure npm auth through an environment reference, never a raw token.""" - parsed = urllib.parse.urlsplit(registry) - if parsed.scheme not in {"http", "https"} or not parsed.netloc: - raise ValueError("The npm registry must be an absolute HTTP(S) URL.") - if parsed.username or parsed.password: - raise ValueError("The npm registry URL must not contain credentials.") - registry = registry.rstrip("/") + "/" - auth_path = parsed.path.rstrip("/") + "/" - return ( - f"registry={registry}\n" - f"//{parsed.netloc}{auth_path}:_authToken=${{{NPM_TOKEN_ENV}}}\n" - "always-auth=true\n" - ) - - def redact_secrets(value: str, secrets: Iterable[str]) -> str: for secret in sorted({secret for secret in secrets if secret}, key=len, reverse=True): value = value.replace(secret, "") @@ -519,16 +481,6 @@ def terminate(signum, frame): base_env["npm_config_fetch_timeout"] = "30000" base_env["UV_CACHE_DIR"] = str(output / "cache") base_env["UV_DEFAULT_INDEX"] = args.default_index - npm_token = os.environ.get(NPM_TOKEN_ENV, "") - npm_config = None - if npm_token: - npm_config = output / "installer.npmrc" - # npm expands the environment reference at request time. The short-lived - # token is never written to disk or included in an argument or URL. - npm_config.write_text(npm_user_config(args.npm_registry)) - python_install_env = installer_environment(base_env, os.environ, UV_INDEX_CREDENTIAL_ENV) - npm_install_env = installer_environment(base_env, os.environ, (NPM_TOKEN_ENV,), npm_config) - installer_secrets = tuple(os.environ.get(key, "") for key in INSTALLER_CREDENTIAL_ENV) bearer = os.environ.get("DATABRICKS_BEARER", "").strip() second_bearer = os.environ.get("DATABRICKS_SECOND_BEARER", "").strip() oauth_token = os.environ.get("CLAUDE_CODE_OAUTH_TOKEN", "").strip() @@ -542,7 +494,6 @@ def redact(value: str) -> str: second_bearer, oauth_token, *client_secrets, - *installer_secrets, ), ) @@ -631,7 +582,6 @@ def run(command, *, cwd=output, env=base_env, timeout=600) -> str: run( [uv, "build", "--wheel", "--out-dir", wheels, ROOT], cwd=ROOT, - env=python_install_env, ) (wheel,) = wheels.glob("*.whl") report["git_commit"] = run(["git", "rev-parse", "HEAD"], cwd=ROOT) @@ -664,7 +614,6 @@ def run(command, *, cwd=output, env=base_env, timeout=600) -> str: constraints.as_uri(), package, ], - env=python_install_env, ) run([uv, "pip", "check", "--python", python]) freeze = run([uv, "pip", "freeze", "--python", python]) @@ -731,7 +680,6 @@ def run(command, *, cwd=output, env=base_env, timeout=600) -> str: "--registry", args.npm_registry, ], - env=npm_install_env, ) else: run( @@ -747,7 +695,6 @@ def run(command, *, cwd=output, env=base_env, timeout=600) -> str: args.npm_registry, *[f"{AGENT_PACKAGES[a]}@{getattr(args, f'{a}_version')}" for a in agents], ], - env=npm_install_env, ) shutil.copyfile(npm_prefix / "package-lock.json", output / "npm-lock.json") report["npm_packages"] = json.loads( @@ -853,7 +800,6 @@ def run(command, *, cwd=output, env=base_env, timeout=600) -> str: args.default_index, *test_dependencies, ], - env=python_install_env, ) (output / "test-dependencies.txt").write_text( run([uv, "pip", "freeze", "--python", test_python]) + "\n" diff --git a/tests/integration/README.md b/tests/integration/README.md index c78ec6137..41a487a98 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -603,20 +603,19 @@ starting alongside unit tests and the existing agent e2e shards. Integration has no dependency on agent e2e; a failure there does not prevent integration from running. The final required `e2e` check waits for both suites and requires both to succeed. The required installation and live jobs run directly on fresh GitHub Ubuntu VMs, -not inside the optional Docker image. An advisory `windows-server-latest` job runs +not inside the optional Docker image. An advisory `windows-latest` job runs the five credential-free checks in `test_installation.py` (installation, CLI, auth-helper, and local MCP) and uploads `integration-installation-windows` evidence. It uses `continue-on-error` and is not part of `All integration tests` until the initial Windows issues are fixed. -The Windows job authenticates to the Databricks JFrog package proxy using -GitHub OIDC, following the organization's SDK CI setup. Its actual OS image is -recorded in `versions.json`; the organization can update the image behind the -runner label. The two POSIX version-floor journeys are outside this Windows subset. +It installs from public PyPI and npm, needs no credentials, and also runs on +fork PRs. Its actual OS image is recorded in `versions.json`; GitHub can update +the image behind the runner label. The two POSIX version-floor journeys are outside this Windows subset. It installs only Claude as the runner prerequisite; the five selected checks exercise ug and its local helpers, not either agent's inference path. An advisory **Windows headless journey · Claude** job installs the temporary Windows-pinned Claude version (currently 2.1.278) -on a native Windows runner using the same authenticated package proxies, +on a native Windows runner from public PyPI and npm, reuses the existing e2e workspace/bearer, and requires the unpredictable file value in the agent's structured final answer. It uploads `integration-headless-windows-claude` evidence and remains outside diff --git a/tests/test_e2e.py b/tests/test_e2e.py index c35dde42b..150061e71 100644 --- a/tests/test_e2e.py +++ b/tests/test_e2e.py @@ -780,8 +780,7 @@ def test_launch_gemini_per_model( f"({gemini.MAX_GEMINI_VERSION_TEXT}); run `ucode gemini` to downgrade." ) gemini_models: list = e2e_state.get("gemini_models") or [] - # Windows CI installs Gemini CLI from JFrog, which still serves a build under the ceiling, so - # it's the lane that launches these. Image-only models can't serve its tool calls. + # Image-only models can't serve Gemini CLI's tool calls. if os.name == "nt": gemini_models = [m for m in gemini_models if not m.endswith("-image")] if not gemini_models: diff --git a/tests/test_integration_contract.py b/tests/test_integration_contract.py index 5ca55ac3c..0cce8e181 100644 --- a/tests/test_integration_contract.py +++ b/tests/test_integration_contract.py @@ -128,7 +128,6 @@ def test_cuj_discovery_fails_for_a_file_without_a_check_name(tmp_path): ) # Fork PRs get no secrets or OIDC token, so jobs that need either must skip them. SKIPPED_ON_FORK_PRS = { - "installation-windows", "workspace", "smoke", "headless-windows", @@ -142,8 +141,8 @@ def test_cuj_discovery_fails_for_a_file_without_a_check_name(tmp_path): } -def _integration_jobs(name="integration.yml"): - workflow = (Path(__file__).parent.parent / ".github/workflows" / name).read_text() +def _integration_jobs(): + workflow = (Path(__file__).parent.parent / ".github/workflows/integration.yml").read_text() body = workflow.split("\njobs:\n", 1)[1] return dict(re.findall(r"(?ms)^ ([a-z0-9-]+):\n(.*?)(?=^ [a-z0-9-]+:\n|\Z)", body)) @@ -174,19 +173,6 @@ def test_integration_jobs_that_run_on_fork_pull_requests_need_no_credentials(): ) -def test_ci_windows_lanes_skip_fork_pull_requests(): - jfrog_jobs = { - name: job for name, job in _integration_jobs("ci.yml").items() if "setup-jfrog-cli" in job - } - - assert set(jfrog_jobs) == {"test", "e2e-shards"} - for name, job in jfrog_jobs.items(): - assert ( - f"os: ${{{{ fromJSON(({FORK_GUARD}) && " - """'["ubuntu-latest", "windows-server-latest"]' || '["ubuntu-latest"]') }}""" - ) in job, f"{name} logs in to JFrog with OIDC on Windows, which fork PRs can't do" - - MEMBER_ONLY = 'contains(fromJSON(\'["MEMBER", "OWNER"]\'), github.event.comment.author_association)' diff --git a/tests/test_integration_runner.py b/tests/test_integration_runner.py index f89b9eee4..79e1abc92 100644 --- a/tests/test_integration_runner.py +++ b/tests/test_integration_runner.py @@ -9,57 +9,11 @@ def windows_live_environment(): return {"DATABRICKS_BEARER": "bearer"} -def test_installer_environment_scopes_registry_credentials(): - base = {"PATH": "tools", "UV_DEFAULT_INDEX": "databricks-pypi=https://example/simple"} - source = { - "UV_INDEX_DATABRICKS_PYPI_USERNAME": "oidc-user", - "UV_INDEX_DATABRICKS_PYPI_PASSWORD": "python-token", - "UG_INTEGRATION_NPM_TOKEN": "npm-token", - "UNRELATED_SECRET": "must-not-cross-the-boundary", - } - - python_environment = runner.installer_environment(base, source, runner.UV_INDEX_CREDENTIAL_ENV) - npm_environment = runner.installer_environment( - base, source, (runner.NPM_TOKEN_ENV,), Path("isolated.npmrc") - ) - - assert base == { - "PATH": "tools", - "UV_DEFAULT_INDEX": "databricks-pypi=https://example/simple", - } - assert python_environment == { - **base, - "UV_INDEX_DATABRICKS_PYPI_USERNAME": "oidc-user", - "UV_INDEX_DATABRICKS_PYPI_PASSWORD": "python-token", - } - assert npm_environment == { - **base, - "UG_INTEGRATION_NPM_TOKEN": "npm-token", - "npm_config_userconfig": "isolated.npmrc", - } - - -def test_npm_user_config_references_token_environment_without_embedding_it(): - config = runner.npm_user_config("https://databricks.jfrog.io/artifactory/api/npm/db-npm/") - - assert config == ( - "registry=https://databricks.jfrog.io/artifactory/api/npm/db-npm/\n" - "//databricks.jfrog.io/artifactory/api/npm/db-npm/:_authToken=" - "${UG_INTEGRATION_NPM_TOKEN}\n" - "always-auth=true\n" - ) - - -def test_npm_user_config_rejects_credentials_in_registry_url(): - with pytest.raises(ValueError, match="must not contain credentials"): - runner.npm_user_config("https://user:token@example.invalid/npm/") - - -def test_installer_tokens_are_redacted_from_build_output(): - output = "failed for oidc-user with python-token and npm-token" +def test_secrets_are_redacted_from_build_output(): + output = "failed for bearer-a with token-b and token-c" assert ( - runner.redact_secrets(output, ("oidc-user", "python-token", "npm-token")) + runner.redact_secrets(output, ("bearer-a", "token-b", "token-c")) == "failed for with and " )