@@ -101,12 +101,43 @@ runs:
101101 shell : bash
102102 run : hatch build
103103
104- # Publish using Trusted Publishing - no password required
104+ # Publish using Trusted Publishing - manual approach for composite action compatibility
105105 # See: https://docs.pypi.org/trusted-publishers/using-a-publisher/
106+ - name : Mint PyPI API token via OIDC
107+ id : mint-token
108+ shell : bash
109+ run : |
110+ # Retrieve the ambient OIDC token
111+ resp=$(curl -sS -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
112+ "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=pypi")
113+ oidc_token=$(jq -r '.value' <<< "${resp}")
114+
115+ if [ -z "$oidc_token" ] || [ "$oidc_token" = "null" ]; then
116+ echo "::error::Failed to retrieve OIDC token"
117+ exit 1
118+ fi
119+
120+ # Exchange the OIDC token for a PyPI API token
121+ resp=$(curl -sS -X POST https://pypi.org/_/oidc/mint-token -d "{\"token\": \"${oidc_token}\"}")
122+ api_token=$(jq -r '.token' <<< "${resp}")
123+
124+ if [ -z "$api_token" ] || [ "$api_token" = "null" ]; then
125+ echo "::error::Failed to mint PyPI API token. Response: $resp"
126+ exit 1
127+ fi
128+
129+ # Mask the token to prevent leaking
130+ echo "::add-mask::${api_token}"
131+ echo "api-token=${api_token}" >> "${GITHUB_OUTPUT}"
132+
106133 - name : Publish package distributions to PyPI
107- uses : pypa/gh-action-pypi-publish@release/v1
108- with :
109- verbose : true
134+ shell : bash
135+ env :
136+ TWINE_USERNAME : __token__
137+ TWINE_PASSWORD : ${{ steps.mint-token.outputs.api-token }}
138+ run : |
139+ pip install --quiet twine
140+ twine upload --verbose dist/*
110141
111142 - name : Push version bump commit to main
112143 if : ${{ env.SKIP_BUMP == '0' }}
@@ -115,7 +146,7 @@ runs:
115146 git push
116147
117148 - name : Create Release
118- uses : ncipollo/release-action@v1
149+ uses : ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0
119150 env :
120151 NEW_VERSION : ${{ env.NEW_VERSION }}
121152 GITHUB_TOKEN : ${{ inputs.github_token }}
0 commit comments