mirror of
https://git.centos.org/centos/centpkg.git
synced 2025-02-23 16:22:55 +00:00
Require --rhel-target
This commit is contained in:
parent
0271c67c5a
commit
1e7ef8239a
2 changed files with 300 additions and 3 deletions
|
@ -23,7 +23,11 @@ import centpkg.utils
|
||||||
from pyrpkg.cli import cliClient
|
from pyrpkg.cli import cliClient
|
||||||
from pyrpkg import rpkgError
|
from pyrpkg import rpkgError
|
||||||
from six.moves.urllib_parse import urlparse
|
from six.moves.urllib_parse import urlparse
|
||||||
|
import six.moves.configparser as ConfigParser
|
||||||
|
|
||||||
|
import json
|
||||||
|
import koji
|
||||||
|
import os
|
||||||
|
|
||||||
_DEFAULT_API_BASE_URL = 'https://gitlab.com'
|
_DEFAULT_API_BASE_URL = 'https://gitlab.com'
|
||||||
|
|
||||||
|
@ -126,6 +130,122 @@ class centpkgClient(cliClient):
|
||||||
msg = "Remote with name '{0}' already exists."
|
msg = "Remote with name '{0}' already exists."
|
||||||
self.log.info(msg.format(remote_name))
|
self.log.info(msg.format(remote_name))
|
||||||
|
|
||||||
|
# Overloaded build
|
||||||
|
def register_build(self):
|
||||||
|
# Do all the work from the super class
|
||||||
|
super(centpkgClient, self).register_build()
|
||||||
|
build_parser = self.subparsers.choices['build']
|
||||||
|
build_parser.formatter_class = argparse.RawDescriptionHelpFormatter
|
||||||
|
build_parser.description = textwrap.dedent('''
|
||||||
|
{0}
|
||||||
|
|
||||||
|
centpkg now sets the rhel metadata with --rhel-target.
|
||||||
|
* exception - This will build for the current in-development Y-stream release.
|
||||||
|
It is equivalent to passing latest when not in the Blocker and Exception Phase.
|
||||||
|
* zstream - If pre-GA of a y-stream release, this will build for 0day.
|
||||||
|
If post-GA of a Y-stream release, this will build for the Z-stream of that release.
|
||||||
|
* latest - This will always build for the next Y-stream release
|
||||||
|
|
||||||
|
'''.format('\n'.join(textwrap.wrap(build_parser.description))))
|
||||||
|
|
||||||
|
# Now add our additional option
|
||||||
|
build_parser.add_argument(
|
||||||
|
'--rhel-target',
|
||||||
|
choices=['exception', 'zstream', 'latest'],
|
||||||
|
default='latest',
|
||||||
|
help='Set the rhel-target metadata')
|
||||||
|
|
||||||
|
# Overloaded _build
|
||||||
|
def _build(self, sets=None):
|
||||||
|
|
||||||
|
# Only run if we have internal configuraions, or scratch
|
||||||
|
internal_config_file = "/etc/rpkg/centpkg_internal.conf"
|
||||||
|
if os.path.exists(internal_config_file):
|
||||||
|
# Get our internal only variables
|
||||||
|
cfg = ConfigParser.SafeConfigParser()
|
||||||
|
cfg.read(internal_config_file)
|
||||||
|
pp_api_url = config_get_safely(cfg, "centpkg.internal", 'pp_api_url')
|
||||||
|
gitbz_query_url = config_get_safely(cfg, "centpkg.internal", 'gitbz_query_url')
|
||||||
|
rhel_dist_git = config_get_safely(cfg, "centpkg.internal", 'rhel_dist_git')
|
||||||
|
|
||||||
|
# Find out divergent branch and stabalization
|
||||||
|
stream_version = self.cmd.target.split('-')[0]
|
||||||
|
rhel_version = centpkg.utils.stream_mapping(stream_version)
|
||||||
|
active_y, in_stabilization = centpkg.utils.determine_active_y_version(rhel_version, pp_api_url)
|
||||||
|
divergent_branch = centpkg.utils.does_divergent_branch_exist(
|
||||||
|
self.cmd.repo_name,
|
||||||
|
rhel_version,
|
||||||
|
rhel_dist_git,
|
||||||
|
pp_api_url,
|
||||||
|
"rpms")
|
||||||
|
|
||||||
|
# Good to know
|
||||||
|
if divergent_branch :
|
||||||
|
print("divergent_branch: TRUE")
|
||||||
|
else:
|
||||||
|
print("divergent_branch: FALSE")
|
||||||
|
if in_stabilization :
|
||||||
|
print("in_stabilization: TRUE")
|
||||||
|
else:
|
||||||
|
print("in_stabilization: FALSE")
|
||||||
|
|
||||||
|
# Update args.custom_user_metadata
|
||||||
|
if hasattr(self.args, 'custom_user_metadata') and self.args.custom_user_metadata:
|
||||||
|
try:
|
||||||
|
temp_custom_user_metadata = json.loads(self.args.custom_user_metadata)
|
||||||
|
# Use ValueError instead of json.JSONDecodeError for Python 2 and 3 compatibility
|
||||||
|
except ValueError as e:
|
||||||
|
self.parser.error("--custom-user-metadata is not valid JSON: %s" % e)
|
||||||
|
if not isinstance(temp_custom_user_metadata, dict):
|
||||||
|
self.parser.error("--custom-user-metadata must be a JSON object")
|
||||||
|
if hasattr(self.args, 'rhel_target') and self.args.rhel_target:
|
||||||
|
temp_custom_user_metadata["rhel-target"] = self.args.rhel_target
|
||||||
|
else:
|
||||||
|
if divergent_branch and not in_stabilization :
|
||||||
|
temp_custom_user_metadata["rhel-target"] = "latest"
|
||||||
|
elif not divergent_branch and not in_stabilization :
|
||||||
|
temp_custom_user_metadata["rhel-target"] = "zstream"
|
||||||
|
else:
|
||||||
|
print("We are currently in Stabalization mode")
|
||||||
|
print("You must either set the rhel-target (--rhel-target)")
|
||||||
|
print("or branch for the previous version.")
|
||||||
|
print("Exiting")
|
||||||
|
raise SystemExit
|
||||||
|
self.args.custom_user_metadata = json.dumps(temp_custom_user_metadata)
|
||||||
|
else:
|
||||||
|
if hasattr(self.args, 'rhel_target') and self.args.rhel_target:
|
||||||
|
temp_custom_user_metadata = {"rhel-target": self.args.rhel_target}
|
||||||
|
self.args.custom_user_metadata = json.dumps(temp_custom_user_metadata)
|
||||||
|
else:
|
||||||
|
if divergent_branch and not in_stabilization :
|
||||||
|
self.args.custom_user_metadata = '{"rhel-target": "latest"}'
|
||||||
|
elif not divergent_branch and not in_stabilization :
|
||||||
|
self.args.custom_user_metadata = '{"rhel-target": "zstream"}'
|
||||||
|
else:
|
||||||
|
print("We are currently in Stabalization mode")
|
||||||
|
print("You must either set the rhel-target (--rhel-target)")
|
||||||
|
print("or branch for the previous version.")
|
||||||
|
print("Exiting")
|
||||||
|
raise SystemExit
|
||||||
|
|
||||||
|
|
||||||
|
# Good to know, but take out for final cut
|
||||||
|
print('Metadata: %r', self.args.custom_user_metadata)
|
||||||
|
|
||||||
|
# Purposely fail during testing so we do not have so many builds
|
||||||
|
#self.args.custom_user_metadata = json.loads(self.args.custom_user_metadata)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if not self.args.scratch:
|
||||||
|
print("NO SCRATCH BUILD")
|
||||||
|
print("Only scratch builds are allowed without internal configurations")
|
||||||
|
print("Exiting")
|
||||||
|
raise SystemExit
|
||||||
|
|
||||||
|
# Proceed with build
|
||||||
|
return super(centpkgClient, self)._build(sets)
|
||||||
|
|
||||||
|
|
||||||
def register_request_gated_side_tag(self):
|
def register_request_gated_side_tag(self):
|
||||||
"""Register command line parser for subcommand request-gated-side-tag"""
|
"""Register command line parser for subcommand request-gated-side-tag"""
|
||||||
parser = self.subparsers.add_parser(
|
parser = self.subparsers.add_parser(
|
||||||
|
|
|
@ -10,16 +10,22 @@
|
||||||
# option) any later version. See http://www.gnu.org/copyleft/gpl.html for
|
# option) any later version. See http://www.gnu.org/copyleft/gpl.html for
|
||||||
# the full text of the license.
|
# the full text of the license.
|
||||||
|
|
||||||
import re
|
|
||||||
import json
|
|
||||||
|
|
||||||
import git
|
import git
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import pytz
|
||||||
|
import re
|
||||||
import requests
|
import requests
|
||||||
|
import sys
|
||||||
|
from datetime import date, datetime
|
||||||
from pyrpkg import rpkgError
|
from pyrpkg import rpkgError
|
||||||
from requests.exceptions import ConnectionError
|
from requests.exceptions import ConnectionError
|
||||||
from six.moves.configparser import NoOptionError, NoSectionError
|
from six.moves.configparser import NoOptionError, NoSectionError
|
||||||
from six.moves.urllib.parse import quote_plus, urlparse
|
from six.moves.urllib.parse import quote_plus, urlparse
|
||||||
|
|
||||||
|
import git as gitpython
|
||||||
|
|
||||||
dist_git_config = None
|
dist_git_config = None
|
||||||
|
|
||||||
def do_fork(logger, base_url, token, repo_name, namespace, cli_name):
|
def do_fork(logger, base_url, token, repo_name, namespace, cli_name):
|
||||||
|
@ -255,3 +261,174 @@ def get_repo_name(name, org='rpms'):
|
||||||
repo_name = get_canonical_repo_name(dist_git_config, name)
|
repo_name = get_canonical_repo_name(dist_git_config, name)
|
||||||
|
|
||||||
return '%s/%s' % (org, repo_name)
|
return '%s/%s' % (org, repo_name)
|
||||||
|
|
||||||
|
def stream_mapping(csname):
|
||||||
|
"""
|
||||||
|
Given a CentOS Stream name, map it to the corresponding RHEL name.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
csname: str
|
||||||
|
The CentOS Stream name.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
Correspoinding RHEL name.
|
||||||
|
"""
|
||||||
|
if csname == "c8s" or csname == "cs8" :
|
||||||
|
return "rhel-8"
|
||||||
|
if csname == "c9s" or csname == "cs9" :
|
||||||
|
return "rhel-9"
|
||||||
|
if csname == "c10s" or csname == "cs10" :
|
||||||
|
return "rhel-10"
|
||||||
|
if csname == "c11s" or csname == "cs11" :
|
||||||
|
return "rhel-11"
|
||||||
|
return None
|
||||||
|
|
||||||
|
def does_divergent_branch_exist(repo_name, rhel_version, rhel_dist_git, pp_api_url, namespace):
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Determine if the Y-1 branch exists for this repo
|
||||||
|
|
||||||
|
# Look up the Y-1 branch name
|
||||||
|
divergent_branch = determine_divergent_branch(
|
||||||
|
rhel_version,
|
||||||
|
pp_api_url,
|
||||||
|
namespace,
|
||||||
|
)
|
||||||
|
logger.debug("Divergent branch: {}".format(divergent_branch))
|
||||||
|
|
||||||
|
g = gitpython.cmd.Git()
|
||||||
|
try:
|
||||||
|
g.ls_remote(
|
||||||
|
"--exit-code",
|
||||||
|
os.path.join(rhel_dist_git, namespace, repo_name),
|
||||||
|
divergent_branch,
|
||||||
|
)
|
||||||
|
branch_exists = True
|
||||||
|
except gitpython.GitCommandError as e:
|
||||||
|
t, v, tb = sys.exc_info()
|
||||||
|
# `git ls-remote --exit-code` returns "2" if it cannot find the ref
|
||||||
|
if e.status == 2:
|
||||||
|
branch_exists = False
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
return branch_exists
|
||||||
|
|
||||||
|
def determine_divergent_branch(rhel_version, pp_api_url, namespace):
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Query the "package pages" API for the current active Y-stream release
|
||||||
|
# Phase 230 is "Planning / Development / Testing" (AKA DeveTestDoc)
|
||||||
|
request_params = {
|
||||||
|
"phase": 230,
|
||||||
|
"product__shortname": "rhel",
|
||||||
|
"relgroup__shortname": rhel_version,
|
||||||
|
"format": "json",
|
||||||
|
}
|
||||||
|
|
||||||
|
res = requests.get(
|
||||||
|
os.path.join(pp_api_url, "latest", "releases"),
|
||||||
|
params=request_params,
|
||||||
|
timeout=60,
|
||||||
|
)
|
||||||
|
res.raise_for_status()
|
||||||
|
payload = json.loads(res.text)
|
||||||
|
logger.debug(
|
||||||
|
"Response from PP API: {}".format(json.dumps(payload, indent=2))
|
||||||
|
)
|
||||||
|
if len(payload) < 1:
|
||||||
|
raise RuntimeError("Received zero potential release matches)")
|
||||||
|
|
||||||
|
active_y_version = -1
|
||||||
|
for entry in payload:
|
||||||
|
shortname = entry["shortname"]
|
||||||
|
|
||||||
|
# The shortname is in the form rhel-9-1.0
|
||||||
|
# Extract the active Y-stream version
|
||||||
|
m = re.search("(?<={}-)\d+(?=\.0)".format(rhel_version), shortname)
|
||||||
|
if not m:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Could not determine active Y-stream version from shortname"
|
||||||
|
)
|
||||||
|
y_version = int(m.group(0))
|
||||||
|
if y_version > active_y_version:
|
||||||
|
active_y_version = y_version
|
||||||
|
|
||||||
|
# The divergent branch is Y-1
|
||||||
|
return "{}.{}.0".format(rhel_version, active_y_version - 1)
|
||||||
|
|
||||||
|
def _datesplit(isodate):
|
||||||
|
date_string_tuple = isodate.split('-')
|
||||||
|
return [ int(x) for x in date_string_tuple ]
|
||||||
|
|
||||||
|
|
||||||
|
def determine_active_y_version(rhel_version, pp_api_url):
|
||||||
|
"""
|
||||||
|
Returns: A 2-tuple of the active Y-stream version(int) and whether we are
|
||||||
|
in the Exception Phase(bool)
|
||||||
|
"""
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Query the "package pages" API for the current active Y-stream release
|
||||||
|
# Phase 230 is "Planning / Development / Testing" (AKA DeveTestDoc)
|
||||||
|
request_params = {
|
||||||
|
"phase": 230,
|
||||||
|
"product__shortname": "rhel",
|
||||||
|
"relgroup__shortname": rhel_version,
|
||||||
|
"format": "json",
|
||||||
|
}
|
||||||
|
|
||||||
|
res = requests.get(
|
||||||
|
os.path.join(pp_api_url, "latest", "releases"),
|
||||||
|
params=request_params,
|
||||||
|
timeout=60,
|
||||||
|
)
|
||||||
|
res.raise_for_status()
|
||||||
|
payload = json.loads(res.text)
|
||||||
|
logger.debug(
|
||||||
|
"Response from PP API: {}".format(json.dumps(payload, indent=2))
|
||||||
|
)
|
||||||
|
if len(payload) < 1:
|
||||||
|
raise RuntimeError("Received zero potential release matches)")
|
||||||
|
|
||||||
|
release_id = -1
|
||||||
|
active_y_version = -1
|
||||||
|
for entry in payload:
|
||||||
|
shortname = entry["shortname"]
|
||||||
|
|
||||||
|
# The shortname is in the form rhel-9-1.0
|
||||||
|
# Extract the active Y-stream version
|
||||||
|
m = re.search("(?<={}-)\d+(?=\.0)".format(rhel_version), shortname)
|
||||||
|
if not m:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Could not determine active Y-stream version from shortname"
|
||||||
|
)
|
||||||
|
y_version = int(m.group(0))
|
||||||
|
if y_version > active_y_version:
|
||||||
|
active_y_version = y_version
|
||||||
|
release_id = entry["id"]
|
||||||
|
|
||||||
|
# Now look up whether we are in the Exception Phase for this Y-stream release
|
||||||
|
request_params = {
|
||||||
|
"name__regex": "Exception Phase",
|
||||||
|
"format": "json",
|
||||||
|
}
|
||||||
|
res = requests.get(os.path.join(pp_api_url, "latest", "releases", str(release_id), "schedule-tasks"), params=request_params)
|
||||||
|
res.raise_for_status()
|
||||||
|
payload = json.loads(res.text)
|
||||||
|
|
||||||
|
# This lookup *must* return exactly one value or the Product Pages are
|
||||||
|
# wrong and must be fixed.
|
||||||
|
assert len(payload) == 1
|
||||||
|
|
||||||
|
# Determine if this Y-stream release is in the exception phase
|
||||||
|
today = datetime.now(tz=pytz.utc).date()
|
||||||
|
exception_start_date = date(*_datesplit(payload[0]["date_start"]))
|
||||||
|
in_exception_phase = today >= exception_start_date
|
||||||
|
|
||||||
|
logger.debug("Active Y-stream: {}, Enforcing: {}".format(active_y_version, in_exception_phase))
|
||||||
|
|
||||||
|
return active_y_version, in_exception_phase
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue