Skip to content

Commit 45a9a37

Browse files
authored
Refactor pip/JFrog integration during CI/CD (#4804)
## Changes The purpose of this PR is to adjust the way pip is configured to use JFrog during CI/CD: in particular the username/password are no longer embedded in the URL of the mirror but are instead stored separately in a `.netrc` file. This is necessary because the mirror URL isn't always handled carefully and can easily be exposed. Further to the above: - The setup for `uv` has been adjusted to use the same `.netrc` mechanism, which is slightly cleaner. - The rest of the `jfrog-auth` action has been synced with the latest version we have. Because the rest of the action has been synced, the commit to review is really a07085e. ### Linked issues Relates #4803. ### Tests - CI/CD tests
1 parent 14b54d8 commit 45a9a37

3 files changed

Lines changed: 151 additions & 15 deletions

File tree

Lines changed: 149 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
name: 'Authenticate for JFrog'
22
description: 'Authenticate with JFrog using OIDC based on the GitHub repository.'
3+
# Some things to note:
4+
# - Run this _after_ installing any tools that need to use JFrog; auth is configured for all the (supported) tools that
5+
# it detects.
6+
# - Where possible we avoid exposing tokens in environment variables, preferring to write them into files instead.
7+
# (Tokens in environment variables tend to be more exposed and easier to leak than those written into files.)
8+
#
9+
# TODO: Factor out into an external action, once releases are allowed.
310
outputs:
411
jfrog-access-token:
512
description: "Access token for JFrog"
@@ -11,36 +18,165 @@ runs:
1118
name: Authenticate against JFrog
1219
shell: bash
1320
run: |
21+
if [[ -z "${ACTIONS_ID_TOKEN_REQUEST_URL}" ]] || [[ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" ]]
22+
then
23+
printf '::error::%s\n' 'This action uses OIDC: job must have "id-token: write" permission'
24+
exit 1
25+
fi
1426
"${GITHUB_ACTION_PATH}/jfrog-auth" "${ACTIONS_ID_TOKEN_REQUEST_URL}" "${ACTIONS_ID_TOKEN_REQUEST_TOKEN}"
27+
1528
- id: detect-cmds
16-
name: Detecting python package/project managers.
29+
name: Detecting package/project managers.
1730
shell: bash
1831
run: |
19-
for cmd in pip3 uv
32+
for cmd in bun coursier mvn npm pip3 sbt uv
2033
do
21-
command -v "${cmd}" > /dev/null && found=true || found=false
22-
printf '::debug::%s\n' "Found ${cmd}: ${found}"
23-
printf '%s=%s\n' "command_${cmd}" "${found}" >> "${GITHUB_OUTPUT}"
34+
command -v "${cmd}" > /dev/null && found=true || found=false
35+
printf '::debug::%s\n' "Found ${cmd}: ${found}"
36+
printf '%s=%s\n' "command_${cmd}" "${found}" >> "${GITHUB_OUTPUT}"
2437
done
25-
- name: Configure pip for JFrog
26-
if: "${{ steps.detect-cmds.outputs.command_pip3 == 'true' }}"
38+
39+
- name: Configure bun for JFrog
40+
if: "${{ steps.detect-cmds.outputs.command_bun == 'true' }}"
41+
shell: bash
42+
env:
43+
JFROG_ACCESS_TOKEN: "${{ steps.jfrog-auth.outputs.jfrog-access-token }}"
44+
run: |
45+
umask 077
46+
cat > ~/.bunfig.toml << 'EOF'
47+
[install]
48+
registry = { url = "https://databricks.jfrog.io/artifactory/api/npm/db-npm/", token = "$jfrog_access_token" }
49+
EOF
50+
cat > "${RUNNER_TEMP}/.bun.env" << EOF
51+
# Environment variables loaded by bun.
52+
jfrog_access_token='${JFROG_ACCESS_TOKEN}'
53+
EOF
54+
printf '%s=%s\n' 'BUN_OPTIONS' "--env-file=${RUNNER_TEMP}/.bun.env" >> "${GITHUB_ENV}"
55+
printf '::debug::%s\n' 'Configured JFrog access for bun.'
56+
# There are currently the following issues with JFrog:
57+
# - The default set of CAs doesn't seem to cover the ones used by our JFrog instance.
58+
# - The JSON metadata returned for some NPM artefacts can be invalid JSON.
59+
printf '::warning::%s\n' 'JFrog has compatibility issues with bun; it will probably not work.'
60+
61+
- name: Configure coursier for JFrog
62+
# Note: SBT bootstrapping uses Coursier internally.
63+
if: "${{ steps.detect-cmds.outputs.command_coursier == 'true' ||
64+
steps.detect-cmds.outputs.command_sbt == 'true' }}"
65+
shell: bash
66+
env:
67+
JFROG_ACCESS_TOKEN: "${{ steps.jfrog-auth.outputs.jfrog-access-token }}"
68+
run: |
69+
umask 077
70+
cat > "${RUNNER_TEMP}/.coursier-credentials.properties" << EOF
71+
jfrog.host=databricks.jfrog.io
72+
jfrog.realm=Artifactory Realm
73+
jfrog.username=gha-service-account
74+
jfrog.password=${JFROG_ACCESS_TOKEN}
75+
EOF
76+
printf '%s=%s\n' 'COURSIER_CREDENTIALS' "${RUNNER_TEMP}/.coursier-credentials.properties" >> "${GITHUB_ENV}"
77+
printf '::debug::%s\n' 'Configured JFrog access for Coursier.'
78+
79+
- name: Configure Maven for JFrog
80+
if: "${{ steps.detect-cmds.outputs.command_mvn == 'true' }}"
81+
shell: bash
82+
env:
83+
JFROG_ACCESS_TOKEN: "${{ steps.jfrog-auth.outputs.jfrog-access-token }}"
84+
run: |
85+
umask 077
86+
mkdir -p ~/.m2
87+
cat > ~/.m2/settings.xml << EOF
88+
<settings>
89+
<mirrors>
90+
<mirror>
91+
<id>jfrog-central</id>
92+
<mirrorOf>*</mirrorOf>
93+
<url>https://databricks.jfrog.io/artifactory/db-maven/</url>
94+
</mirror>
95+
</mirrors>
96+
<servers>
97+
<server>
98+
<id>jfrog-central</id>
99+
<username>gha-service-account</username>
100+
<password>${JFROG_ACCESS_TOKEN}</password>
101+
</server>
102+
</servers>
103+
</settings>
104+
EOF
105+
printf '::debug::%s\n' 'Configured JFrog access for maven.'
106+
107+
- name: Configure netrc for JFrog
108+
if: "${{ steps.detect-cmds.outputs.command_pip3 == 'true' ||
109+
steps.detect-cmds.outputs.command_uv == 'true' }}"
27110
shell: bash
28111
env:
29112
JFROG_ACCESS_TOKEN: "${{ steps.jfrog-auth.outputs.jfrog-access-token }}"
30113
run: |
31114
umask 077
32-
cat > "$RUNNER_TEMP/.pip.conf" <<EOF
115+
cat > "${RUNNER_TEMP}/.netrc" << EOF
116+
machine databricks.jfrog.io
117+
login gha-service-account
118+
password ${JFROG_ACCESS_TOKEN}
119+
EOF
120+
printf '%s=%s\n' 'NETRC' "${RUNNER_TEMP}/.netrc" >> "${GITHUB_ENV}"
121+
122+
- name: Configure npm/yarn (classic) for JFrog
123+
if: "${{ steps.detect-cmds.outputs.command_npm == 'true' }}"
124+
shell: bash
125+
env:
126+
JFROG_ACCESS_TOKEN: "${{ steps.jfrog-auth.outputs.jfrog-access-token }}"
127+
run: |
128+
umask 077
129+
cat > ~/.npmrc << EOF
130+
registry=https://databricks.jfrog.io/artifactory/api/npm/db-npm/
131+
always-auth=true
132+
ignore-scripts=true
133+
//databricks.jfrog.io/artifactory/api/npm/db-npm/:_authToken=${JFROG_ACCESS_TOKEN}
134+
EOF
135+
printf '::debug::%s\n' 'Configured JFrog access for npm/yarn (classic).'
136+
137+
- name: Configure pip for JFrog
138+
if: "${{ steps.detect-cmds.outputs.command_pip3 == 'true' }}"
139+
shell: bash
140+
run: |
141+
cat > "${RUNNER_TEMP}/.pip.conf" << 'EOF'
33142
[global]
34-
index-url = https://gha-service-account:${JFROG_ACCESS_TOKEN}@databricks.jfrog.io/artifactory/api/pypi/db-pypi/simple
143+
index-url = https://databricks.jfrog.io/artifactory/api/pypi/db-pypi/simple
35144
EOF
36145
printf '%s=%s\n' 'PIP_CONFIG_FILE' "${RUNNER_TEMP}/.pip.conf" >> "${GITHUB_ENV}"
146+
printf '::debug::%s\n' 'Configured JFrog access for pip.'
147+
148+
- name: Configure sbt for JFrog
149+
if: "${{ steps.detect-cmds.outputs.command_sbt == 'true' }}"
150+
shell: bash
151+
env:
152+
JFROG_ACCESS_TOKEN: "${{ steps.jfrog-auth.outputs.jfrog-access-token }}"
153+
run: |
154+
umask 077
155+
mkdir -p ~/.sbt/1.0
156+
cat > ~/.sbt/repositories << 'EOF'
157+
[repositories]
158+
local
159+
databricks-jfrog: https://databricks.jfrog.io/artifactory/db-maven/
160+
EOF
161+
162+
cat > "${RUNNER_TEMP}/.sbt.credentials" << EOF
163+
realm=Artifactory Realm
164+
host=databricks.jfrog.io
165+
user=gha-service-account
166+
password=${JFROG_ACCESS_TOKEN}
167+
EOF
168+
169+
cat > ~/.sbt/1.0/global.sbt << 'EOF'
170+
credentials += Credentials(file(sys.env("RUNNER_TEMP")) / ".sbt.credentials")
171+
EOF
172+
printf '::debug::%s\n' 'Configured JFrog access for SBT.'
173+
37174
- name: Configure uv for JFrog
38175
if: "${{ steps.detect-cmds.outputs.command_uv == 'true' }}"
39176
shell: bash
40177
env:
41-
JFROG_ACCESS_TOKEN: "${{ steps.jfrog-auth.outputs.jfrog-access-token }}"
42178
UV_INDEX_URL: 'https://databricks.jfrog.io/artifactory/api/pypi/db-pypi/simple'
43179
run: |
44-
uv auth login "${UV_INDEX_URL}" --username gha-service-account --password "${JFROG_ACCESS_TOKEN}"
45-
printf "%s=%s\n" 'UV_INDEX_URL' "${UV_INDEX_URL}" >> "${GITHUB_ENV}"
46-
printf "%s=%s\n" 'UV_FROZEN' '1' >> "${GITHUB_ENV}"
180+
printf '%s=%s\n' 'UV_INDEX_URL' "${UV_INDEX_URL}" >> "${GITHUB_ENV}"
181+
printf '%s=%s\n' 'UV_FROZEN' '1' >> "${GITHUB_ENV}"
182+
printf '::debug::%s\n' 'Configured JFrog access for uv.'

.github/actions/jfrog-auth/jfrog-auth

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ printf '::add-mask::%s\n' "${_id_token}"
2222
# Step 2: Exchange it for the JFrog access token.
2323
#
2424
printf '::debug::%s\n' "Exchanging OIDC identifier token for JFrog access token..."
25-
_access_token=$(curl -sLS \
25+
_access_token=$(curl -fsSL \
2626
--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\"}" \
2727
"https://databricks.jfrog.io/access/api/v1/oidc/token" |
2828
jq -r .access_token)

.github/workflows/push.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ jobs:
8484

8585
- name: Install LSQL
8686
run: |
87-
databricks labs install lsql
87+
databricks labs install lsql --log-level=trace
8888
databricks labs installed
8989
env: # this is a temporary hack
9090
DATABRICKS_HOST: any

0 commit comments

Comments
 (0)