Use a read-only PAT for read only operations
We now use a dummy PAT for read-only requests and the GHA token for any write operations. This should give us 5x more ready-only API calls per hour.
This commit is contained in:
parent
177fa71ff2
commit
3f115655b3
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -39,6 +39,7 @@ jobs:
|
|||||||
id: check
|
id: check
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GITHUB_TOKEN_READONLY: ${{ secrets.GITHUBTOKENREADONLY }}
|
||||||
run: |
|
run: |
|
||||||
python autobuild.py write-build-plan build_plan.json
|
python autobuild.py write-build-plan build_plan.json
|
||||||
$buildPlan = Get-Content build_plan.json -Raw
|
$buildPlan = Get-Content build_plan.json -Raw
|
||||||
@ -48,6 +49,7 @@ jobs:
|
|||||||
if: steps.check.outputs.build-plan != '[]'
|
if: steps.check.outputs.build-plan != '[]'
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GITHUB_TOKEN_READONLY: ${{ secrets.GITHUBTOKENREADONLY }}
|
||||||
run: |
|
run: |
|
||||||
python autobuild.py clean-assets
|
python autobuild.py clean-assets
|
||||||
|
|
||||||
@ -55,6 +57,7 @@ jobs:
|
|||||||
if: steps.check.outputs.build-plan != '[]'
|
if: steps.check.outputs.build-plan != '[]'
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GITHUB_TOKEN_READONLY: ${{ secrets.GITHUBTOKENREADONLY }}
|
||||||
run: |
|
run: |
|
||||||
python autobuild.py show
|
python autobuild.py show
|
||||||
|
|
||||||
@ -120,6 +123,7 @@ jobs:
|
|||||||
- name: Process build queue
|
- name: Process build queue
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GITHUB_TOKEN_READONLY: ${{ secrets.GITHUBTOKENREADONLY }}
|
||||||
run: |
|
run: |
|
||||||
$env:PACKAGER='CI (msys2-autobuild/' + $env:GITHUB_SHA.Substring(0, 8) + '/' + $env:GITHUB_RUN_ID + ')'
|
$env:PACKAGER='CI (msys2-autobuild/' + $env:GITHUB_SHA.Substring(0, 8) + '/' + $env:GITHUB_RUN_ID + ')'
|
||||||
$BUILD_ROOT='C:\_'
|
$BUILD_ROOT='C:\_'
|
||||||
|
|||||||
45
autobuild.py
45
autobuild.py
@ -6,6 +6,7 @@ import argparse
|
|||||||
import glob
|
import glob
|
||||||
from os import environ
|
from os import environ
|
||||||
from github import Github
|
from github import Github
|
||||||
|
from github.GithubObject import GithubObject
|
||||||
from github.GithubException import GithubException
|
from github.GithubException import GithubException
|
||||||
from github.GitRelease import GitRelease
|
from github.GitRelease import GitRelease
|
||||||
from github.GitReleaseAsset import GitReleaseAsset
|
from github.GitReleaseAsset import GitReleaseAsset
|
||||||
@ -252,6 +253,18 @@ def download_text_asset(asset: GitReleaseAsset) -> str:
|
|||||||
return r.text
|
return r.text
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def make_writable(obj: GithubObject) -> Generator:
|
||||||
|
# XXX: This switches the read-only token with a potentially writable one
|
||||||
|
old_requester = obj._requester # type: ignore
|
||||||
|
repo = get_repo(readonly=False)
|
||||||
|
try:
|
||||||
|
obj._requester = repo._requester # type: ignore
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
obj._requester = old_requester # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def upload_asset(release: GitRelease, path: _PathLike, replace: bool = False,
|
def upload_asset(release: GitRelease, path: _PathLike, replace: bool = False,
|
||||||
text: bool = False, content: bytes = None) -> None:
|
text: bool = False, content: bytes = None) -> None:
|
||||||
path = Path(path)
|
path = Path(path)
|
||||||
@ -265,6 +278,7 @@ def upload_asset(release: GitRelease, path: _PathLike, replace: bool = False,
|
|||||||
# We want to treat incomplete assets as if they weren't there
|
# We want to treat incomplete assets as if they weren't there
|
||||||
# so replace them always
|
# so replace them always
|
||||||
if replace or not asset_is_complete(asset):
|
if replace or not asset_is_complete(asset):
|
||||||
|
with make_writable(asset):
|
||||||
asset.delete_asset()
|
asset.delete_asset()
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
@ -273,6 +287,7 @@ def upload_asset(release: GitRelease, path: _PathLike, replace: bool = False,
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def upload() -> None:
|
def upload() -> None:
|
||||||
|
with make_writable(release):
|
||||||
if content is None:
|
if content is None:
|
||||||
release.upload_asset(str(path), label=asset_label, name=asset_name)
|
release.upload_asset(str(path), label=asset_label, name=asset_name)
|
||||||
else:
|
else:
|
||||||
@ -603,7 +618,7 @@ def get_release_assets(release: GitRelease, include_incomplete=False) -> List[Gi
|
|||||||
|
|
||||||
|
|
||||||
def get_buildqueue_with_status(full_details: bool = False) -> List[Package]:
|
def get_buildqueue_with_status(full_details: bool = False) -> List[Package]:
|
||||||
repo = get_repo(optional_credentials=True)
|
repo = get_repo()
|
||||||
assets = []
|
assets = []
|
||||||
for name in ["msys", "mingw"]:
|
for name in ["msys", "mingw"]:
|
||||||
release = repo.get_release('staging-' + name)
|
release = repo.get_release('staging-' + name)
|
||||||
@ -883,9 +898,11 @@ def update_status(pkgs: List[Package]):
|
|||||||
asset_name = "status.json"
|
asset_name = "status.json"
|
||||||
for asset in release.get_assets():
|
for asset in release.get_assets():
|
||||||
if asset.name == asset_name:
|
if asset.name == asset_name:
|
||||||
|
with make_writable(asset):
|
||||||
asset.delete_asset()
|
asset.delete_asset()
|
||||||
break
|
break
|
||||||
with io.BytesIO(content) as fileobj:
|
with io.BytesIO(content) as fileobj:
|
||||||
|
with make_writable(release):
|
||||||
new_asset = release.upload_asset_from_memory( # type: ignore
|
new_asset = release.upload_asset_from_memory( # type: ignore
|
||||||
fileobj, len(content), asset_name)
|
fileobj, len(content), asset_name)
|
||||||
except GithubException as e:
|
except GithubException as e:
|
||||||
@ -1001,7 +1018,7 @@ def upload_assets(args: Any) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def fetch_assets(args: Any) -> None:
|
def fetch_assets(args: Any) -> None:
|
||||||
repo = get_repo(optional_credentials=True)
|
repo = get_repo()
|
||||||
target_dir = args.targetdir
|
target_dir = args.targetdir
|
||||||
fetch_all = args.fetch_all
|
fetch_all = args.fetch_all
|
||||||
|
|
||||||
@ -1111,46 +1128,50 @@ def clean_gha_assets(args: Any) -> None:
|
|||||||
for asset in assets:
|
for asset in assets:
|
||||||
print(f"Deleting {get_asset_filename(asset)}...")
|
print(f"Deleting {get_asset_filename(asset)}...")
|
||||||
if not args.dry_run:
|
if not args.dry_run:
|
||||||
|
with make_writable(asset):
|
||||||
asset.delete_asset()
|
asset.delete_asset()
|
||||||
|
|
||||||
if not assets:
|
if not assets:
|
||||||
print("Nothing to delete")
|
print("Nothing to delete")
|
||||||
|
|
||||||
|
|
||||||
def get_credentials(optional: bool = False) -> Dict[str, Any]:
|
def get_credentials(readonly: bool = True) -> Dict[str, Any]:
|
||||||
if "GITHUB_TOKEN" in environ:
|
if readonly and "GITHUB_TOKEN_READONLY" in environ:
|
||||||
|
return {'login_or_token': environ["GITHUB_TOKEN_READONLY"]}
|
||||||
|
elif "GITHUB_TOKEN" in environ:
|
||||||
return {'login_or_token': environ["GITHUB_TOKEN"]}
|
return {'login_or_token': environ["GITHUB_TOKEN"]}
|
||||||
elif "GITHUB_USER" in environ and "GITHUB_PASS" in environ:
|
elif "GITHUB_USER" in environ and "GITHUB_PASS" in environ:
|
||||||
return {'login_or_token': environ["GITHUB_USER"], 'password': environ["GITHUB_PASS"]}
|
return {'login_or_token': environ["GITHUB_USER"], 'password': environ["GITHUB_PASS"]}
|
||||||
else:
|
else:
|
||||||
if optional:
|
if readonly:
|
||||||
print("[Warning] 'GITHUB_TOKEN' or 'GITHUB_USER'/'GITHUB_PASS' env vars "
|
print("[Warning] 'GITHUB_TOKEN' or 'GITHUB_TOKEN_READONLY' or "
|
||||||
|
"'GITHUB_USER'/'GITHUB_PASS' env vars "
|
||||||
"not set which might lead to API rate limiting", file=sys.stderr)
|
"not set which might lead to API rate limiting", file=sys.stderr)
|
||||||
return {}
|
return {}
|
||||||
else:
|
else:
|
||||||
raise Exception("'GITHUB_TOKEN' or 'GITHUB_USER'/'GITHUB_PASS' env vars not set")
|
raise Exception("'GITHUB_TOKEN' or 'GITHUB_USER'/'GITHUB_PASS' env vars not set")
|
||||||
|
|
||||||
|
|
||||||
def get_github(optional_credentials: bool = False) -> Github:
|
def get_github(readonly: bool = True) -> Github:
|
||||||
kwargs = get_credentials(optional=optional_credentials)
|
kwargs = get_credentials(readonly=readonly)
|
||||||
has_creds = bool(kwargs)
|
has_creds = bool(kwargs)
|
||||||
# 100 is the maximum allowed
|
# 100 is the maximum allowed
|
||||||
kwargs['per_page'] = 100
|
kwargs['per_page'] = 100
|
||||||
kwargs['retry'] = Retry(total=3, backoff_factor=1)
|
kwargs['retry'] = Retry(total=3, backoff_factor=1)
|
||||||
gh = Github(**kwargs)
|
gh = Github(**kwargs)
|
||||||
if not has_creds and optional_credentials:
|
if not has_creds and readonly:
|
||||||
print(f"[Warning] Rate limit status: {gh.get_rate_limit().core}", file=sys.stderr)
|
print(f"[Warning] Rate limit status: {gh.get_rate_limit().core}", file=sys.stderr)
|
||||||
return gh
|
return gh
|
||||||
|
|
||||||
|
|
||||||
def get_repo(optional_credentials: bool = False) -> Repository:
|
def get_repo(readonly: bool = True) -> Repository:
|
||||||
gh = get_github(optional_credentials=optional_credentials)
|
gh = get_github(readonly=readonly)
|
||||||
return gh.get_repo(REPO, lazy=True)
|
return gh.get_repo(REPO, lazy=True)
|
||||||
|
|
||||||
|
|
||||||
def wait_for_api_limit_reset(
|
def wait_for_api_limit_reset(
|
||||||
min_remaining: int = 50, min_sleep: float = 60, max_sleep: float = 300) -> None:
|
min_remaining: int = 50, min_sleep: float = 60, max_sleep: float = 300) -> None:
|
||||||
gh = get_github(optional_credentials=True)
|
gh = get_github()
|
||||||
while True:
|
while True:
|
||||||
core = gh.get_rate_limit().core
|
core = gh.get_rate_limit().core
|
||||||
reset = core.reset.replace(tzinfo=timezone.utc)
|
reset = core.reset.replace(tzinfo=timezone.utc)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user