diff --git a/.github/actions/jfrog-auth/action.yml b/.github/actions/jfrog-auth/action.yml new file mode 100644 index 0000000..301699b --- /dev/null +++ b/.github/actions/jfrog-auth/action.yml @@ -0,0 +1,170 @@ +name: 'Authenticate for JFrog' +description: 'Authenticate with JFrog using OIDC based on the GitHub repository.' +outputs: + jfrog-access-token: + description: "Access token for JFrog" + value: "${{ steps.jfrog-auth.outputs.jfrog-access-token }}" +runs: + using: "composite" + steps: + - id: jfrog-auth + name: Authenticate against JFrog + shell: bash + run: | + if [[ -z "${ACTIONS_ID_TOKEN_REQUEST_URL}" ]] || [[ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" ]] + then + printf '::error::%s\n' 'This action uses OIDC: job must have "id-token: write" permission' + exit 1 + fi + "${GITHUB_ACTION_PATH}/jfrog-auth" "${ACTIONS_ID_TOKEN_REQUEST_URL}" "${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" + + - id: detect-cmds + name: Detecting package/project managers. + shell: bash + run: | + for cmd in bun coursier mvn npm pip3 sbt uv + do + command -v "${cmd}" > /dev/null && found=true || found=false + printf '::debug::%s\n' "Found ${cmd}: ${found}" + printf '%s=%s\n' "command_${cmd}" "${found}" >> "${GITHUB_OUTPUT}" + done + + - name: Configure bun for JFrog + if: "${{ steps.detect-cmds.outputs.command_bun == 'true' }}" + shell: bash + env: + JFROG_ACCESS_TOKEN: "${{ steps.jfrog-auth.outputs.jfrog-access-token }}" + run: | + umask 077 + cat > ~/.bunfig.toml << 'EOF' + [install] + registry = { url = "https://databricks.jfrog.io/artifactory/api/npm/db-npm/", token = "$jfrog_access_token" } + EOF + cat > "${RUNNER_TEMP}/.bun.env" << EOF + jfrog_access_token='${JFROG_ACCESS_TOKEN}' + EOF + printf '%s=%s\n' 'BUN_OPTIONS' "--env-file=${RUNNER_TEMP}/.bun.env" >> "${GITHUB_ENV}" + printf '::debug::%s\n' 'Configured JFrog access for bun.' + printf '::warning::%s\n' 'JFrog has compatibility issues with bun; it will probably not work.' + + - name: Configure coursier for JFrog + if: "${{ steps.detect-cmds.outputs.command_coursier == 'true' || + steps.detect-cmds.outputs.command_sbt == 'true' }}" + shell: bash + env: + JFROG_ACCESS_TOKEN: "${{ steps.jfrog-auth.outputs.jfrog-access-token }}" + run: | + umask 077 + cat > "${RUNNER_TEMP}/.coursier-credentials.properties" << EOF + jfrog.host=databricks.jfrog.io + jfrog.realm=Artifactory Realm + jfrog.username=gha-service-account + jfrog.password=${JFROG_ACCESS_TOKEN} + EOF + printf '%s=%s\n' 'COURSIER_CREDENTIALS' "${RUNNER_TEMP}/.coursier-credentials.properties" >> "${GITHUB_ENV}" + printf '::debug::%s\n' 'Configured JFrog access for Coursier.' + + - name: Configure Maven for JFrog + if: "${{ steps.detect-cmds.outputs.command_mvn == 'true' }}" + shell: bash + env: + JFROG_ACCESS_TOKEN: "${{ steps.jfrog-auth.outputs.jfrog-access-token }}" + run: | + umask 077 + mkdir -p ~/.m2 + cat > ~/.m2/settings.xml << EOF + + + + jfrog-central + * + https://databricks.jfrog.io/artifactory/db-maven/ + + + + + jfrog-central + gha-service-account + ${JFROG_ACCESS_TOKEN} + + + + EOF + printf '::debug::%s\n' 'Configured JFrog access for maven.' + + - name: Configure netrc for JFrog + if: "${{ steps.detect-cmds.outputs.command_pip3 == 'true' || + steps.detect-cmds.outputs.command_uv == 'true' }}" + shell: bash + env: + JFROG_ACCESS_TOKEN: "${{ steps.jfrog-auth.outputs.jfrog-access-token }}" + run: | + umask 077 + cat > "${RUNNER_TEMP}/.netrc" << EOF + machine databricks.jfrog.io + login gha-service-account + password ${JFROG_ACCESS_TOKEN} + EOF + printf '%s=%s\n' 'NETRC' "${RUNNER_TEMP}/.netrc" >> "${GITHUB_ENV}" + + - name: Configure npm/yarn (classic) for JFrog + if: "${{ steps.detect-cmds.outputs.command_npm == 'true' }}" + shell: bash + env: + JFROG_ACCESS_TOKEN: "${{ steps.jfrog-auth.outputs.jfrog-access-token }}" + run: | + umask 077 + cat > ~/.npmrc << EOF + registry=https://databricks.jfrog.io/artifactory/api/npm/db-npm/ + always-auth=true + ignore-scripts=true + //databricks.jfrog.io/artifactory/api/npm/db-npm/:_authToken=${JFROG_ACCESS_TOKEN} + EOF + printf '::debug::%s\n' 'Configured JFrog access for npm/yarn (classic).' + + - name: Configure pip for JFrog + if: "${{ steps.detect-cmds.outputs.command_pip3 == 'true' }}" + shell: bash + run: | + cat > "${RUNNER_TEMP}/.pip.conf" << 'EOF' + [global] + index-url = https://databricks.jfrog.io/artifactory/api/pypi/db-pypi/simple + EOF + printf '%s=%s\n' 'PIP_CONFIG_FILE' "${RUNNER_TEMP}/.pip.conf" >> "${GITHUB_ENV}" + printf '::debug::%s\n' 'Configured JFrog access for pip.' + + - name: Configure sbt for JFrog + if: "${{ steps.detect-cmds.outputs.command_sbt == 'true' }}" + shell: bash + env: + JFROG_ACCESS_TOKEN: "${{ steps.jfrog-auth.outputs.jfrog-access-token }}" + run: | + umask 077 + mkdir -p ~/.sbt/1.0 + cat > ~/.sbt/repositories << 'EOF' + [repositories] + local + databricks-jfrog: https://databricks.jfrog.io/artifactory/db-maven/ + EOF + + cat > "${RUNNER_TEMP}/.sbt.credentials" << EOF + realm=Artifactory Realm + host=databricks.jfrog.io + user=gha-service-account + password=${JFROG_ACCESS_TOKEN} + EOF + + cat > ~/.sbt/1.0/global.sbt << 'EOF' + credentials += Credentials(file(sys.env("RUNNER_TEMP")) / ".sbt.credentials") + EOF + printf '::debug::%s\n' 'Configured JFrog access for SBT.' + + - name: Configure uv for JFrog + if: "${{ steps.detect-cmds.outputs.command_uv == 'true' }}" + shell: bash + env: + UV_INDEX_URL: 'https://databricks.jfrog.io/artifactory/api/pypi/db-pypi/simple' + run: | + printf '%s=%s\n' 'UV_INDEX_URL' "${UV_INDEX_URL}" >> "${GITHUB_ENV}" + printf '%s=%s\n' 'UV_FROZEN' '1' >> "${GITHUB_ENV}" + printf '::debug::%s\n' 'Configured JFrog access for uv.' diff --git a/.github/actions/jfrog-auth/jfrog-auth b/.github/actions/jfrog-auth/jfrog-auth new file mode 100755 index 0000000..9a05b43 --- /dev/null +++ b/.github/actions/jfrog-auth/jfrog-auth @@ -0,0 +1,28 @@ +#!/bin/sh +set -eu + +_request_url="$1" +_request_token="$2" + +printf '::debug::%s\n' "Fetching OIDC identifier token from GitHub..." +_id_token="$(curl -sLS \ + -H 'User-Agent: actions/oidc-client' \ + -H "Authorization: Bearer ${_request_token}" \ + "${_request_url}&audience=jfrog-github" | + jq -r .value)" +printf '::add-mask::%s\n' "${_id_token}" + +printf '::debug::%s\n' "Exchanging OIDC identifier token for JFrog access token..." +_access_token=$(curl -fsSL \ + --json "{\"grant_type\": \"urn:ietf:params:oauth:grant-type:token-exchange\", \"subject_token_type\":\"urn:ietf:params:oauth:token-type:id_token\", \"subject_token\": \"${_id_token}\", \"provider_name\": \"github-actions\"}" \ + "https://databricks.jfrog.io/access/api/v1/oidc/token" | + jq -r .access_token) +printf '::add-mask::%s\n' "${_access_token}" + +if [ -z "${_access_token}" ] || [ "${_access_token}" = 'null' ] +then + printf '::error::%s\n' "Could not fetch JFrog access token." + exit 1 +fi + +printf '%s=%s\n' 'jfrog-access-token' "${_access_token}" >> "${GITHUB_OUTPUT}" diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 527b6c4..53d654f 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -19,7 +19,11 @@ concurrency: jobs: lint: name: Lint and format check - runs-on: ubuntu-latest + runs-on: + group: databrickslabs-protected-runner-group + labels: linux-ubuntu-latest + permissions: + id-token: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -29,6 +33,9 @@ jobs: version: "0.11.2" checksum: "7ac2ca0449c8d68dae9b99e635cd3bc9b22a4cb1de64b7c43716398447d42981" + - name: Setup for JFrog + uses: ./.github/actions/jfrog-auth + - name: Install dependencies run: make dev @@ -37,7 +44,9 @@ jobs: test: name: Testing - runs-on: ubuntu-latest + runs-on: + group: databrickslabs-protected-runner-group + labels: linux-ubuntu-latest needs: [ lint ] permissions: id-token: write @@ -55,6 +64,9 @@ jobs: version: "0.11.2" checksum: "7ac2ca0449c8d68dae9b99e635cd3bc9b22a4cb1de64b7c43716398447d42981" + - name: Setup for JFrog + uses: ./.github/actions/jfrog-auth + - name: Install dependencies run: make dev diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7fe3031..d983fb2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,12 @@ permissions: jobs: build: name: Build distribution - runs-on: ubuntu-latest + runs-on: + group: databrickslabs-protected-runner-group + labels: linux-ubuntu-latest + permissions: + contents: read + id-token: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -22,6 +27,9 @@ jobs: version: "0.11.2" checksum: "7ac2ca0449c8d68dae9b99e635cd3bc9b22a4cb1de64b7c43716398447d42981" + - name: Setup for JFrog + uses: ./.github/actions/jfrog-auth + - name: Build package run: make build @@ -34,7 +42,9 @@ jobs: github-release: name: Create GitHub Release needs: build - runs-on: ubuntu-latest + runs-on: + group: databrickslabs-protected-runner-group + labels: linux-ubuntu-latest environment: name: release permissions: