1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
|
import logging
import os
import re
import json
from typing import List, Any
from datetime import datetime
from github.Issue import Issue
from github.Repository import Repository
import requests
from azure.devops.v6_0.pipelines.pipelines_client import PipelinesClient
from azure.devops.v6_0.pipelines import models
from msrest.authentication import BasicAuthentication
from packaging.version import parse as Version
from urllib3 import Retry, PoolManager
REQUEST_REPO = 'Azure/sdk-release-request'
REST_REPO = 'Azure/azure-rest-api-specs'
AUTO_ASSIGN_LABEL = 'assigned'
AUTO_PARSE_LABEL = 'auto-link'
AUTO_CLOSE_LABEL = 'auto-close'
MULTI_LINK_LABEL = 'MultiLink'
INCONSISTENT_TAG = 'Inconsistent tag'
TYPESPEC_LABEL = 'TypeSpec'
_LOG = logging.getLogger(__name__)
def get_origin_link_and_tag(issue_body_list: List[str]) -> (str, str):
link, readme_tag = '', ''
for row in issue_body_list:
if 'link' in row.lower() and 'release request' not in row.lower() and link == '':
link = row.split(":", 1)[-1].strip()
if 'readme tag' in row.lower() and readme_tag == '':
readme_tag = row.split(":", 1)[-1].strip()
if link and readme_tag:
break
if link.count('https') > 1:
link = link.split(']')[0]
link = link.replace('[', "").replace(']', "").replace('(', "").replace(')', "")
return link, readme_tag
def to_datetime(data: str) -> datetime:
return datetime.strptime(data, '%Y-%m-%dT%H:%M:%S')
def get_last_released_date(package_name: str) -> (str, datetime):
try:
pypi = PyPIClient()
latest_release, latest_stable = pypi.get_relevant_versions(package_name)
latest_release_date = pypi.project_release(package_name, latest_release)["urls"][0]["upload_time"]
latest_stable_date = pypi.project_release(package_name, latest_stable)["urls"][0]["upload_time"]
if latest_release_date > latest_stable_date:
return str(latest_release), to_datetime(latest_release_date)
return str(latest_stable), to_datetime(latest_stable_date)
except:
return '', to_datetime('1970-01-01T00:00:00')
# get python release pipeline link from web
def get_python_release_pipeline(output_folder):
pipeline_client = PipelinesClient(base_url='https://dev.azure.com/azure-sdk',
creds=BasicAuthentication(os.getenv('PIPELINE_TOKEN'), ''))
pipelines = pipeline_client.list_pipelines(project='internal')
for pipeline in pipelines:
if re.findall(r'^python - \w*$', pipeline.name):
key = pipeline.name.replace('python - ', '')
if key == output_folder:
pipeline_url = 'https://dev.azure.com/azure-sdk/internal/_build?definitionId={}'.format(pipeline.id)
return pipeline_url
else:
_LOG.info('Cannot find definitionId, Do not display pipeline_url')
return ''
# Run sdk-auto-release(main) to generate SDK
def run_pipeline(issue_link, pipeline_url, spec_readme, python_tag="", rest_repo_hash="", target_date="", issue_owner=""):
paramaters = {
"stages_to_skip": [],
"resources": {
"repositories": {
"self": {
"refName": "refs/heads/main"
}
}
},
"variables": {
"BASE_BRANCH": {
"value": "",
"isSecret": False
},
"ISSUE_LINK": {
"value": issue_link,
"isSecret": False
},
"PIPELINE_LINK": {
"value": pipeline_url,
"isSecret": False
},
"SPEC_README": {
"value": spec_readme,
"isSecret": False
},
"PYTHON_TAG": {
"value": python_tag,
"isSecret": False
},
"REST_REPO_HASH": {
"value": rest_repo_hash,
"isSecret": False
},
"TARGET_DATE": {
"value": target_date,
"isSecret": False
},
"ISSUE_OWNER": {
"value": issue_owner,
"isSecret": False
}
}
}
# Fill in with your personal access token and org URL
personal_access_token = os.getenv('PIPELINE_TOKEN')
organization_url = 'https://dev.azure.com/azure-sdk'
# Create a connection to the org
credentials = BasicAuthentication('', personal_access_token)
run_parameters = models.RunPipelineParameters(**paramaters)
client = PipelinesClient(base_url=organization_url, creds=credentials)
result = client.run_pipeline(project='internal', pipeline_id=2500, run_parameters=run_parameters)
return result.state == 'inProgress'
def record_release(package_name: str, issue_info: Any, file: str, version: str) -> None:
created_at = issue_info.created_at.strftime('%Y-%m-%d')
closed_at = issue_info.closed_at.strftime('%Y-%m-%d')
assignee = issue_info.assignee.login
author = issue_info.user.login
link = issue_info.html_url
is_stable = True if 'b' not in version else ''
closed_issue_info = f'{package_name},{author},{assignee},{created_at},{closed_at},{link},{version},{is_stable}\n'
with open(file, 'r') as file_read:
lines = file_read.readlines()
with open(file, 'w') as file_write:
lines.insert(1, closed_issue_info)
file_write.writelines(lines)
class IssuePackage:
issue = None # origin issue instance
rest_repo = None # repo instance: Azure/azure-rest-api-specs
labels_name = {} # name set of issue labels
def __init__(self, issue: Issue, rest_repo: Repository):
self.issue = issue
self.rest_repo = rest_repo
self.labels_name = {label.name for label in issue.labels}
class PyPIClient:
def __init__(self, host="https://pypi.org"):
self._host = host
self._http = PoolManager(
retries=Retry(total=3, raise_on_status=True), ca_certs=os.getenv("REQUESTS_CA_BUNDLE", None)
)
def project(self, package_name):
response = self._http.request(
"get", "{host}/pypi/{project_name}/json".format(host=self._host, project_name=package_name)
)
return json.loads(response.data.decode("utf-8"))
def project_release(self, package_name, version):
response = self._http.request(
"get",
"{host}/pypi/{project_name}/{version}/json".format(
host=self._host, project_name=package_name, version=version
),
)
return json.loads(response.data.decode("utf-8"))
def get_ordered_versions(self, package_name) -> List[Version]:
project = self.project(package_name)
versions = [Version(package_version) for package_version in project["releases"].keys()]
versions.sort()
return versions
def get_relevant_versions(self, package_name):
"""Return a tuple: (latest release, latest stable)
If there are different, it means the latest is not a stable
"""
versions = self.get_ordered_versions(package_name)
pre_releases = [version for version in versions if not version.is_prerelease]
if pre_releases:
return versions[-1], pre_releases[-1]
return versions[-1], versions[-1]
|