Skip to content

Commit e17ba14

Browse files
authored
Merge pull request #402 from duckinator/trusted-publishing6
Finish Trusted Publishing
2 parents 3264aa6 + 5f98a7f commit e17ba14

6 files changed

Lines changed: 52 additions & 40 deletions

File tree

.github/workflows/main.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
name: Release
1+
name: Main
22

33
on:
44
push:
55

66
jobs:
77
lint:
88
runs-on: ubuntu-slim
9+
permissions:
10+
contents: read
911
steps:
1012
- uses: actions/checkout@v6
1113
- uses: actions/setup-python@v5
@@ -22,6 +24,8 @@ jobs:
2224
os: [ubuntu-latest, windows-latest, macos-latest]
2325
python-version: ["3.11", "3.12", "3.13", "3.14"]
2426
runs-on: ${{ matrix.os }}
27+
permissions:
28+
contents: read
2529
steps:
2630
- uses: actions/checkout@v6
2731
with:
@@ -35,6 +39,8 @@ jobs:
3539

3640
#test_freebsd:
3741
# runs-on: ubuntu-latest
42+
# permissions:
43+
# contents: read
3844
# steps:
3945
# - uses: actions/checkout@v6
4046
# - uses: vmactions/freebsd-vm@v1
@@ -51,12 +57,16 @@ jobs:
5157
ci_success:
5258
name: "CI Success"
5359
runs-on: ubuntu-slim
60+
permissions:
61+
contents: read
5462
needs: [lint, test]
5563
steps:
5664
- run: true
5765

5866
changes:
5967
runs-on: ubuntu-latest
68+
permissions:
69+
contents: read
6070
outputs:
6171
version_changed: ${{ steps.project.VERSION_CHANGED == '1' }}
6272
steps:

bork/creds.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from os import getenv
22
from typing import Optional
3+
from . import trusted_publishing
4+
from .log import logger
35

46
from pydantic.dataclasses import dataclass
57

@@ -9,11 +11,11 @@ class Credentials:
911
pypi: Optional['Credentials.PyPI'] = None
1012

1113
@classmethod
12-
def from_env(cls) -> 'Credentials':
14+
def from_env(cls, pypi_repository=None) -> 'Credentials':
1315
# TODO: support specifying another env than os.environ?
1416
return cls(
1517
github = getenv("BORK_GITHUB_TOKEN"),
16-
pypi = cls.PyPI.from_env(),
18+
pypi = cls.PyPI.from_env(pypi_repository),
1719
)
1820

1921
@dataclass(frozen = True)
@@ -22,14 +24,27 @@ class PyPI:
2224
password: str
2325

2426
@classmethod
25-
def from_env(cls) -> Optional['Credentials.PyPI']:
27+
def from_trusted_publishing(cls, repository) -> Optional['Credentials.PyPI']:
28+
if repository is None:
29+
return None
30+
31+
logger().debug("trying to get token via Trusted Publishing")
32+
token = trusted_publishing.get_token(repository)
33+
if token is not None:
34+
return cls("__token__", token)
35+
36+
return None
37+
38+
# FIXME: Avoid needing to pass around repository, BUT ALSO respect --pypi-repository/--test-pypi
39+
@classmethod
40+
def from_env(cls, repository=None) -> Optional['Credentials.PyPI']:
2641
match getenv("BORK_PYPI_USERNAME"), getenv("BORK_PYPI_PASSWORD"), getenv("BORK_PYPI_TOKEN"):
2742
case username, password, None if username and password:
2843
return cls(username, password)
2944
case None, None, token if token:
3045
return cls("__token__", token)
3146
case None, None, None:
32-
return None
47+
return cls.from_trusted_publishing(repository)
3348

3449
# Error cases
3550
case _, password, token if password and token:

bork/http.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,10 @@
22
import urllib3
33

44
from . import version
5-
from .log import logger
65

76
MAX_RETRIES = False
87

98
def request(method, url, fields, auth):
10-
log = logger()
11-
129
user_agent = f"bork/{version.__version__} (+https://github.com/duckinator/bork)"
1310

1411
http = urllib3.PoolManager()
@@ -18,10 +15,6 @@ def request(method, url, fields, auth):
1815
if 399 < response.status < 500:
1916
raise RuntimeError(response.data.decode())
2017

21-
log.debug(response.getheaders())
22-
23-
log.debug("%s %s returned %i", method, response.geturl(), response.status)
24-
2518
return response
2619

2720
def get(url, auth=None):

bork/pypi.py

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import hashlib
2-
import os
32
from pathlib import Path
43

5-
from . import builder, trusted_publishing
4+
from . import builder
5+
from .creds import Credentials
66
from .filesystem import find_files, wheel_file_info
77
from .log import logger
88
from .http import post
@@ -37,14 +37,6 @@ def __init__(self, files, repository=None):
3737
self.files = files
3838
self.repository = repository
3939

40-
self.username = os.environ.get("BORK_PYPI_USERNAME", None)
41-
self.password = os.environ.get("BORK_PYPI_PASSWORD", None)
42-
43-
token = os.environ.get("BORK_PYPI_TOKEN", None)
44-
if self.username is None and token is not None:
45-
self.username = "__token__"
46-
self.password = token
47-
4840
def _upload_file(self, url, file, metadata):
4941
file_contents = Path(file).read_bytes()
5042
file_digest = hashlib.sha256(file_contents).hexdigest()
@@ -119,20 +111,15 @@ def _upload_file(self, url, file, metadata):
119111
*other_fields
120112
]
121113

122-
# If we've still have no credentials, try Trusted Publishing.
123-
if self.username is None and self.password is None:
124-
token = trusted_publishing.get_token(self.repository)
125-
if token is not None:
126-
self.username = "__token__"
127-
self.password = token
114+
username, password = self._get_credentials()
128115

129-
if self.username is None and self.password is None:
116+
if username is None and password is None:
130117
raise RuntimeError(
131118
"BORK_PYPI_USERNAME and BORK_PYPI_PASSWORD environment variables are undefined.\n\n"
132119
"If you used Bork prior to v9.0.0, these variables used to be TWINE_USERNAME and "
133120
"TWINE_PASSWORD. You can use the same values.")
134121

135-
response = post(url, form, auth=(self.username, self.password))
122+
response = post(url, form, auth=(username, password))
136123
return response
137124

138125
def upload(self, *, dry_run = True, metadata = None):
@@ -162,6 +149,17 @@ def upload(self, *, dry_run = True, metadata = None):
162149
log.info("FAILED - %s couldn't be uploaded to %s", filename, self.repository)
163150
log.info(response.data.decode().strip())
164151

152+
def _get_credentials(self):
153+
username = None
154+
password = None
155+
156+
credentials = Credentials.from_env(self.repository)
157+
if credentials.pypi:
158+
username = credentials.pypi.username
159+
password = credentials.pypi.password
160+
161+
return (username, password)
162+
165163

166164
def upload(repository_name, *globs, **kwargs):
167165
files = find_files(globs)

bork/trusted_publishing.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22
from .log import logger
33
import json
44
import os
5-
import urllib
5+
import urllib.request
66
from urllib.parse import urlsplit
77

88

99
# FIXME: Dedupe request/get/post with bork/github_api.py.
1010

1111
def request(method, url, data, headers):
12-
log = logger()
13-
1412
if headers is None:
1513
headers = {}
1614

@@ -21,10 +19,8 @@ def request(method, url, data, headers):
2119

2220
req = urllib.request.Request(url, data=data,
2321
headers=headers, method=method)
24-
log.debug('%s %s', req.method, req.full_url)
2522

2623
with urllib.request.urlopen(req) as res:
27-
log.debug('-> %s returned %i %s', res.url, res.status, res.reason)
2824
response = res.read().decode()
2925

3026
return response
@@ -54,7 +50,7 @@ def detected():
5450

5551
def get_token(self, repository):
5652
"""Perform the whole song and dance to get a token."""
57-
url = f"{repository}/_/oidc/mint-token"
53+
url = urlsplit(repository)._replace(path="/_/oidc/mint-token").geturl()
5854
data = json.loads(post(url, {"token": self.get_ambient_credential()}))
5955
return data["token"]
6056

@@ -66,11 +62,11 @@ class GithubTrustedPublishing(TrustedPublishingProvider):
6662
@staticmethod
6763
def detected():
6864
"""Are we running on GitHub CI?"""
69-
return bool(os.environ.get("GITHUB_CI"))
65+
return bool(os.environ.get("CI") and os.environ.get("GITHUB_ACTION"))
7066

7167
def get_ambient_credential(self):
72-
token = os.environ("ACTIONS_ID_TOKEN_REQUEST_TOKEN")
73-
url = os.environ("ACTIONS_ID_TOKEN_REQUEST_URL")
68+
token = os.environ.get("ACTIONS_ID_TOKEN_REQUEST_TOKEN")
69+
url = os.environ.get("ACTIONS_ID_TOKEN_REQUEST_URL")
7470

7571
if not token:
7672
raise TrustedPublishingError("Expected ACTIONS_ID_TOKEN_REQUEST_TOKEN environment variable to be defined.")

bork/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# This file should only ever be modified to change the version.
22
# This will automatically prepare and create a release.
33

4-
__version__ = '11.0.0b5'
4+
__version__ = '11.0.0'

0 commit comments

Comments
 (0)