def check(self): """ checks remote api availability & version """ # TODO: implement version check url = '{api_base_url}status/'.format( api_base_url=API_BASE_URL ) api_version = None try: r = requests.get(url, timeout=REQUEST_TIMEOUT, headers=self.headers) if r.status_code == 200: data = r.json() api_version = data.get('version') except RequestException as e: log.warning('unable to call api: {}'.format(e)) # print(api_version) if api_version: semver.parse_version_info(api_version)
def bump(self, major=False, minor=False, patch=False, status=None, build=None): # Get the current version as an instance. current_info = semver.parse_version_info(self.identifier) # Get the new version. if major: new_version = semver.bump_major(self.identifier) elif minor: new_version = semver.bump_minor(self.identifier) elif patch: new_version = semver.bump_patch(self.identifier) else: new_version = self.identifier # Update the status. if status: status = status elif current_info.prerelease: status = current_info.prerelease else: status = None if status: info = semver.parse_version_info(new_version) new_version = "%s.%s.%s-%s" % ( info.major, info.minor, info.patch, status ) # Update the build. if build: info = semver.parse_version_info(new_version) new_version = "%s.%s.%s-%s+%s" % ( info.major, info.minor, info.patch, info.prerelease, build ) # Return the new version. new_version = new_version.strip() return new_version
def _parse_version(self, value): if isinstance(value, semver.VersionInfo): version_info = value else: version_info = semver.parse_version_info(value) version = semver.format_version(*tuple(version_info)) return version, version_info
# you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import pkg_resources import semver def get_version_string(): version = pkg_resources.get_distribution('boundary_layer').version # Pip seems to replace '-' with '.' in the version strings, for some reason. # this makes semver unhappy, so we must replace .dev0 with -dev0 if not version.endswith('.dev0'): return version dev0_position = version.rindex('.dev0') return version[:dev0_position] + '-' + version[1 + dev0_position:] VERSION_STRING = get_version_string() VERSION = semver.parse_version_info(VERSION_STRING) MIN_SUPPORTED_VERSION = semver.parse_version_info('0.9.9')
def test_should_versioninfo_bump_patch_and_prerelease(): v = parse_version_info("3.4.5-rc.1") expected = parse_version_info("3.4.6-rc.1") assert v.bump_patch().bump_prerelease() == expected
def install_os(self, dev, image_name): vendor_dir = os.path.join(self.cli_args.topdir, 'vendor_images', _OS_NAME) image_fpath = os.path.join(vendor_dir, image_name) if not os.path.exists(image_fpath): errmsg = 'image file does not exist: %s' % image_fpath self.log.error(errmsg) self.exit_results(dev=dev, results=dict(ok=False, error_type='install', message=errmsg)) msg = 'Installing Cumulus image=[%s] ... this can take up to 30 min.' % image_name self.log.info(msg) self.post_device_status(dev=dev, state='OS-INSTALL', message=msg) os_semver = semver.parse_version_info(dev.facts['os_version']) # Cumulus 2.x upgrade command is removed in Cumulus 3.x, so two upgrade methods are required # Cumulus 2.x upgrade if os_semver.major == 2: install_command = 'sudo /usr/cumulus/bin/cl-img-install -sf http://{server}/images/{os_name}/{image_name}'.format( server=self.cli_args.server, os_name=_OS_NAME, image_name=image_name) all_good, results = dev.api.execute([install_command]) if not all_good: errmsg = 'Unable to run command: {}. Error message: {}'.format( install_command, results) self.exit_results(dev=dev, results=dict(ok=False, error_type='install', message=errmsg)) # Cumulus 3.x upgrade else: install_command = 'sudo onie-select -rf' all_good, results = dev.api.execute([install_command]) if not all_good: errmsg = 'Unable to run command: {}. Error message: {}'.format( install_command, results) self.exit_results(dev=dev, results=dict(ok=False, error_type='install', message=errmsg)) dev.api.execute(['sudo reboot']) time.sleep(60) # Boot into ONIE rescue mode self.wait_for_onie_rescue(countdown=300, poll_delay=10, user='******') # Download and verify OS self.onie_install(dev, image_name) # Wait for onie-rescue shell to terminate time.sleep(60) # Wait for actual install to occur. This takes up to 30 min. self.wait_for_device(countdown=1800, poll_delay=30)
def test_parse_version_info_str_hash(): s_version = "1.2.3-alpha.1.2+build.11.e0f985a" v = parse_version_info(s_version) assert v.__str__() == s_version d = {} d[v] = "" # to ensure that VersionInfo are hashable
def validate_version(version): try: semver.parse_version_info(version) except ValueError as ex: print(str(ex)) sys.exit(1)
def create_version_branch(command_context: CommandContext, operation: Callable[[VersionConfig, Optional[str], Optional[int]], Result]) -> Result: result = Result() context: Context = command_context.context if command_context.selected_ref.name not in [ repotools.create_ref_name(const.LOCAL_BRANCH_PREFIX, context.config.release_branch_base), repotools.create_ref_name(const.REMOTES_PREFIX, context.config.remote_name, context.config.release_branch_base)]: result.fail(os.EX_USAGE, _("Failed to create release branch based on {branch}.") .format(branch=repr(command_context.selected_ref.name)), _("Release branches (major.minor) can only be created off {branch}") .format(branch=repr(context.config.release_branch_base)) ) existing_release_branches = list(repotools.git_list_refs(context.repo, repotools.ref_name([ const.REMOTES_PREFIX, context.config.remote_name, 'release']))) release_branch_merge_bases = dict() for release_branch in context.get_release_branches(): merge_base = repotools.git_merge_base(context.repo, context.config.release_branch_base, release_branch) if merge_base is None: result.fail(os.EX_DATAERR, "Failed to resolve merge base.", None) branch_refs = release_branch_merge_bases.get(merge_base) if branch_refs is None: release_branch_merge_bases[merge_base] = branch_refs = list() branch_refs.append(release_branch) latest_branch = None branch_points_on_same_commit = list() subsequent_branches = list() for history_commit in repotools.git_list_commits( context=context.repo, start=None, end=command_context.selected_commit, options=const.BRANCH_COMMIT_SCAN_OPTIONS): branch_refs = release_branch_merge_bases.get(history_commit.obj_name) if branch_refs is not None and len(branch_refs): branch_refs = list( filter(lambda tag_ref: context.release_branch_matcher.format(tag_ref.name) is not None, branch_refs)) if not len(branch_refs): continue branch_refs.sort( reverse=True, key=utils.cmp_to_key( lambda tag_ref_a, tag_ref_b: semver.compare( context.release_branch_matcher.format(tag_ref_a.name), context.release_branch_matcher.format(tag_ref_b.name) ) ) ) if latest_branch is None: latest_branch = branch_refs[0] if history_commit.obj_name == command_context.selected_commit: branch_points_on_same_commit.extend(branch_refs) # for tag_ref in tag_refs: # print('<<' + tag_ref.name) break for history_commit in repotools.git_list_commits(context.repo, command_context.selected_commit, command_context.selected_ref): branch_refs = release_branch_merge_bases.get(history_commit.obj_name) if branch_refs is not None and len(branch_refs): branch_refs = list( filter(lambda tag_ref: context.release_branch_matcher.format(tag_ref.name) is not None, branch_refs)) if not len(branch_refs): continue branch_refs.sort( reverse=True, key=utils.cmp_to_key( lambda tag_ref_a, tag_ref_b: semver.compare( context.release_branch_matcher.format(tag_ref_a.name), context.release_branch_matcher.format(tag_ref_b.name) ) ) ) # for tag_ref in tag_refs: # print('>>' + tag_ref.name) subsequent_branches.extend(branch_refs) if context.verbose: cli.print("Branches on same commit:\n" + '\n'.join(' - ' + repr(tag_ref.name) for tag_ref in branch_points_on_same_commit)) cli.print("Subsequent branches:\n" + '\n'.join(' - ' + repr(tag_ref.name) for tag_ref in subsequent_branches)) if latest_branch is not None: latest_branch_version = context.release_branch_matcher.format(latest_branch.name) latest_branch_version_info = semver.parse_version_info(latest_branch_version) else: latest_branch_version = None latest_branch_version_info = None if latest_branch_version is not None: version_result = operation(context.config.version_config, latest_branch_version, get_global_sequence_number(context)) result.add_subresult(version_result) new_version = version_result.value new_version_info = semver.parse_version_info(new_version) else: new_version_info = semver.parse_version_info(context.config.version_config.initial_version) new_version = version.format_version_info(new_version_info) scheme_procedures.get_sequence_number(context.config.version_config, new_version_info) if context.config.sequential_versioning: new_sequential_version = create_sequence_number_for_version(context, new_version) else: new_sequential_version = None try: config_in_selected_commit = read_config_in_commit(context.repo, command_context.selected_commit) except FileNotFoundError: config_in_selected_commit = dict() try: properties_in_selected_commit = read_properties_in_commit(context, context.repo, config_in_selected_commit, command_context.selected_commit) except FileNotFoundError: properties_in_selected_commit = dict() if not context.config.allow_shared_release_branch_base and len(branch_points_on_same_commit): result.fail(os.EX_USAGE, _("Branch creation failed."), _("Release branches cannot share a common ancestor commit.\n" "Existing branches on commit {commit}:\n" "{listing}") .format(commit=command_context.selected_commit, listing='\n'.join(' - ' + repr(tag_ref.name) for tag_ref in branch_points_on_same_commit))) if len(subsequent_branches): result.fail(os.EX_USAGE, _("Branch creation failed."), _("Subsequent release branches in history: %s\n") % '\n'.join(' - ' + repr(tag_ref.name) for tag_ref in subsequent_branches)) if context.config.tie_sequential_version_to_semantic_version \ and len(existing_release_branches): prompt_result = prompt_for_confirmation( context=context, fail_title=_("Failed to create release branch based on {branch}.") .format(branch=repr(command_context.selected_ref.name)), message=_("This operation disables version increments " "on all existing release branches.\n" "Affected branches are:\n" "{listing}") .format(listing=os.linesep.join(repr(branch.name) for branch in existing_release_branches)) if not context.config.commit_version_property else _("This operation disables version increments on all existing branches.\n" "Affected branches are:\n" "{listing}") .format(listing=os.linesep.join(repr(branch.name) for branch in existing_release_branches)), prompt=_("Continue?"), ) result.add_subresult(prompt_result) if result.has_errors() or not prompt_result.value: return result if not result.has_errors(): if new_version is None: result.error(os.EX_SOFTWARE, _("Internal error."), _("Missing result version.") ) if latest_branch_version is not None and semver.compare(latest_branch_version, new_version) >= 0: result.error(os.EX_DATAERR, _("Failed to increment version from {current_version} to {new_version}.") .format(current_version=repr(latest_branch_version), new_version=repr(new_version)), _("The new version is lower than or equal to the current version.") ) result.abort_on_error() branch_name = get_branch_name_for_version(context, new_version_info) tag_name = get_tag_name_for_version(context, new_version_info) clone_result = clone_repository(context, context.config.release_branch_base) cloned_repo: RepoContext = clone_result.value # run version change hooks on new release branch git_or_fail(cloned_repo, result, ['checkout', '--force', '-b', branch_name, command_context.selected_commit], _("Failed to check out release branch.")) clone_context: Context = create_temp_context(context, result, cloned_repo.dir) clone_context.config.remote_name = 'origin' commit_info = CommitInfo() commit_info.add_message("#version: " + cli.if_none(new_version)) if (context.config.commit_version_property and new_version is not None) \ or (context.config.commit_sequential_version_property and new_sequential_version is not None): update_result = update_project_property_file(clone_context, properties_in_selected_commit, new_version, new_sequential_version, commit_info) result.add_subresult(update_result) if result.has_errors(): result.fail(os.EX_DATAERR, _("Property update failed."), _("An unexpected error occurred.") ) if new_version is not None: execute_version_change_actions(clone_context, latest_branch_version, new_version) if commit_info is not None: if command_context.selected_commit != command_context.selected_ref.target.obj_name: result.fail(os.EX_USAGE, _("Failed to commit version update."), _("The selected parent commit {commit} does not represent the tip of {branch}.") .format(commit=command_context.selected_commit, branch=repr(command_context.selected_ref.name)) ) # commit changes commit_info.add_parent(command_context.selected_commit) object_to_tag = create_commit(clone_context, result, commit_info) else: object_to_tag = command_context.selected_commit # show info and prompt for confirmation cli.print("ref : " + cli.if_none(command_context.selected_ref.name)) cli.print("ref_" + const.DEFAULT_VERSION_VAR_NAME + " : " + cli.if_none(latest_branch_version)) cli.print("new_branch : " + cli.if_none(branch_name)) cli.print("new_" + const.DEFAULT_VERSION_VAR_NAME + " : " + cli.if_none(new_version)) cli.print("selected object : " + cli.if_none(command_context.selected_commit)) cli.print("tagged object : " + cli.if_none(object_to_tag)) prompt_result = prompt_for_confirmation( context=context, fail_title=_("Failed to create release branch based on {branch}.") .format(branch=repr(command_context.selected_ref.name)), message=_("The branch and tags are about to be pushed."), prompt=_("Continue?"), ) result.add_subresult(prompt_result) if result.has_errors() or not prompt_result.value: return result # push atomically push_command = ['push', '--atomic'] if context.dry_run: push_command.append('--dry-run') if context.verbose: push_command.append('--verbose') push_command.append(context.config.remote_name) # push the base branch commit # push_command.append(commit + ':' + const.LOCAL_BRANCH_PREFIX + selected_ref.local_branch_name) # push the new branch or fail if it exists push_command.extend( ['--force-with-lease=' + repotools.create_ref_name(const.LOCAL_BRANCH_PREFIX, branch_name) + ':', repotools.ref_target(object_to_tag) + ':' + repotools.create_ref_name(const.LOCAL_BRANCH_PREFIX, branch_name)]) # push the new version tag or fail if it exists push_command.extend(['--force-with-lease=' + repotools.create_ref_name(const.LOCAL_TAG_PREFIX, tag_name) + ':', repotools.ref_target(object_to_tag) + ':' + repotools.create_ref_name( const.LOCAL_TAG_PREFIX, tag_name)]) git_or_fail(cloned_repo, result, push_command, _("Failed to push.")) return result
def release(ctx, version, skip_release_notes=False): """Tag a new release.""" status = run("git status --porcelain", hide=True).stdout.strip() if status != "": raise Exit(message="git checkout not clean, cannot release") version = semver.parse_version_info(version) is_patch_release = version.patch != 0 # Check that we have release notes for the desired version. run("git checkout main", echo=True) if not skip_release_notes: with open("website/content/release-notes/_index.md") as release_notes: if "## Version {}".format(version) not in release_notes.read(): raise Exit(message="no release notes for v{}".format(version)) # Move HEAD to the correct release branch - either a new one, or # an existing one. if is_patch_release: run("git checkout v{}.{}".format(version.major, version.minor), echo=True) else: run("git checkout -b v{}.{}".format(version.major, version.minor), echo=True) # Copy over release notes from main. if not skip_release_notes: run("git checkout main -- website/content/release-notes/_index.md", echo=True) # Update links on the website to point to files at the version # we're creating. if is_patch_release: previous_version = "v{}.{}.{}".format(version.major, version.minor, version.patch-1) else: previous_version = "main" def _replace(pattern): oldpat = pattern.format(previous_version) newpat = pattern.format("v{}").format(version) run("perl -pi -e 's#{}#{}#g' website/content/*.md website/content/*/*.md".format(oldpat, newpat), echo=True) _replace("/metallb/metallb/{}") _replace("/metallb/metallb/tree/{}") _replace("/metallb/metallb/blob/{}") # Update the version listed on the website sidebar run("perl -pi -e 's/MetalLB .*/MetalLB v{}/g' website/content/_header.md".format(version), echo=True) # Update the manifests with the new version run("perl -pi -e 's,image: quay.io/metallb/speaker:.*,image: quay.io/metallb/speaker:v{},g' manifests/metallb.yaml".format(version), echo=True) run("perl -pi -e 's,image: quay.io/metallb/controller:.*,image: quay.io/metallb/controller:v{},g' manifests/metallb.yaml".format(version), echo=True) # Update the versions in the helm chart (version and appVersion are always the same) # helm chart versions follow Semantic Versioning, and thus exclude the leading 'v' run("perl -pi -e 's,^version: .*,version: {},g' charts/metallb/Chart.yaml".format(version), echo=True) run("perl -pi -e 's,^appVersion: .*,appVersion: v{},g' charts/metallb/Chart.yaml".format(version), echo=True) run("perl -pi -e 's,^Current chart version is: .*,Current chart version is: `{}`,g' charts/metallb/README.md".format(version), echo=True) # Update the version in kustomize instructions # # TODO: Check if kustomize instructions really need the version in the # website or if there is a simpler way. For now, though, we just replace the # only page that mentions the version on release. run("perl -pi -e 's,github.com/metallb/metallb//manifests\?ref=.*,github.com/metallb/metallb//manifests\?ref=v{},g' website/content/installation/_index.md".format(version), echo=True) # Update the version embedded in the binary run("perl -pi -e 's/version\s+=.*/version = \"{}\"/g' internal/version/version.go".format(version), echo=True) run("gofmt -w internal/version/version.go", echo=True) run("git commit -a -m 'Automated update for release v{}'".format(version), echo=True) run("git tag v{} -m 'See the release notes for details:\n\nhttps://metallb.universe.tf/release-notes/#version-{}-{}-{}'".format(version, version.major, version.minor, version.patch), echo=True) run("git checkout main", echo=True)
def make(ctx, check, version): """Perform a set of operations needed to release a single check: \b * update the version in __about__.py * update the changelog * update the requirements-agent-release.txt file * commit the above changes """ valid_checks = get_valid_checks() if check != 'all' and check not in valid_checks: abort('Check `{}` is not an Agent-based Integration'.format(check)) # don't run the task on the master branch if get_current_branch() == 'master': abort( 'This task will commit, you do not want to add commits to master directly' ) if check == 'all': if version: abort('You cannot bump every check to the same version') checks = sorted(valid_checks) else: checks = [check] for check in checks: echo_success('Check `{}`'.format(check)) if version: # sanity check on the version provided cur_version = get_version_string(check) p_version = parse_version_info(version) p_current = parse_version_info(cur_version) if p_version <= p_current: abort('Current version is {}, cannot bump to {}'.format( cur_version, version)) else: cur_version, changelog_types = ctx.invoke(changes, check=check, dry_run=True) if not changelog_types: echo_warning('No changes for {}, skipping...'.format(check)) continue bump_function = get_bump_function(changelog_types) version = bump_function(cur_version) # update the version number echo_info('Current version of check {}: {}, bumping to: {}'.format( check, cur_version, version)) update_version_module(check, cur_version, version) # update the CHANGELOG echo_waiting('Updating the changelog...') # TODO: Avoid double GitHub API calls when bumping all checks at once ctx.invoke(changelog, check=check, version=version, old_version=cur_version, quiet=True, dry_run=False) if check == 'datadog_checks_dev': commit_targets = [check] # update the global requirements file else: commit_targets = [check, AGENT_REQ_FILE] req_file = os.path.join(get_root(), AGENT_REQ_FILE) echo_waiting( 'Updating the requirements file {}...'.format(req_file)) update_agent_requirements( req_file, check, get_agent_requirement_line(check, version)) # commit the changes. # do not use [ci skip] so releases get built https://docs.gitlab.com/ee/ci/yaml/#skipping-jobs msg = '[Release] Bumped {} version to {}'.format(check, version) git_commit(commit_targets, msg) # Reset version version = None # done echo_success( 'All done, remember to push to origin and open a PR to merge these changes on master' )
def test_should_versioninfo_bump_minor_and_patch(): v = parse_version_info("3.4.5") expected = parse_version_info("3.5.1") assert v.bump_minor().bump_patch() == expected
def write_version_py(filename='wradlib/version.py'): cnt = """ # THIS FILE IS GENERATED FROM WRADLIB SETUP.PY short_version = '%(short_version)s' version = '%(version)s' full_version = '%(full_version)s' git_revision = '%(git_revision)s' release = %(isrelease)s """ # Adding the git rev number needs to be done inside write_version_py(), # otherwise the import of wradlib.version messes up the build under # Python 3. SHORT_VERSION = VERSION FULL_VERSION = VERSION GIT_REVISION = VERSION + '-unknown' GIT_HASH = 'unknown' ISRELEASED = "'unknown'" if os.path.exists('.git'): GIT_REVISION, GIT_HASH = git_version() elif os.path.exists('wradlib/version.py'): # must be a source distribution, use existing version file try: from wradlib.version import full_version as GIT_REVISION from wradlib.version import git_revision as GIT_HASH except ImportError: raise ImportError('Unable to import git_revision. Try removing ' 'wradlib/version.py and the build directory ' 'before building.') # parse version using semver ver = semver.parse_version_info(GIT_REVISION) # get commit count, dev0 means tagged commit -> release try: ISRELEASED = ver.prerelease == 'dev0' if not ISRELEASED: if not ver.patch: patch = 0 minor = 1 else: patch = 1 minor = 0 SHORT_VERSION = semver.format_version(ver.major, ver.minor + minor, ver.patch + patch, ver.prerelease) FULL_VERSION = GIT_REVISION except ValueError: warnings.warn("wradlib source does not contain detailed version info " "via git or version.py, exact version can't be " "retrieved.", UserWarning) print(SHORT_VERSION, FULL_VERSION, GIT_REVISION, GIT_HASH, ISRELEASED) a = open(filename, 'w') try: a.write(cnt % {'short_version': SHORT_VERSION, 'version': FULL_VERSION, 'full_version': GIT_REVISION, 'git_revision': GIT_HASH, 'isrelease': str(ISRELEASED)}) finally: a.close() return SHORT_VERSION
def test_should_versioninfo_bump_major_and_minor(): v = parse_version_info("3.4.5") expected = parse_version_info("4.1.0") assert v.bump_major().bump_minor() == expected
tags = {} with Repo(join(script_dir, 'ClassicPress-nightly')) as r: tags.update(r.refs.as_dict('refs/tags')) with Repo(join(script_dir, 'ClassicPress-release')) as r: tags.update(r.refs.as_dict('refs/tags')) dump('tags', tags) vers = {} for tag in tags: try: ver = parse_version_info(tag) # we only care about release and nightly builds if not ver.build or ver.build[:7] == 'nightly': if ver.major in vers: vers[ver.major].append(ver) else: vers[ver.major] = [ver] except ValueError: # ignore non-semver tags pass dump( 'vers', dict((major, sorted(str(v) for v in arr)) for (major, arr) in vers.iteritems()))
def normalize_version(version): version_info = semver.parse_version_info(version) version_string = str(version_info) return version_string
def test_semver_version(): """Ensure the package version is semver compliant.""" from tokendito.__version__ import __version__ as version assert semver.parse_version_info(version)
def get_short_version(version): version = semver.parse_version_info(version) if version.major >= 1 and version.minor >= 1: return ".".join([str(version.major), str(version.minor)]) else: return version
def __init__(self, string): self._current = None self._original = string self.name = None self.object = semver.parse_version_info(string)
def test_should_versioninfo_bump_patch_and_prerelease_with_token(): v = parse_version_info("3.4.5-dev.1") expected = parse_version_info("3.4.6-dev.1") assert v.bump_patch().bump_prerelease("dev") == expected
def make(ctx, check, version, initial_release, skip_sign, sign_only): """Perform a set of operations needed to release a single check: \b * update the version in __about__.py * update the changelog * update the requirements-agent-release.txt file * update in-toto metadata * commit the above changes You can release everything at once by setting the check to `all`. \b If you run into issues signing: \b - Ensure you did `gpg --import <YOUR_KEY_ID>.gpg.pub` """ # Import lazily since in-toto runs a subprocess to check for gpg2 on load from ..signing import update_link_metadata, YubikeyException root = get_root() releasing_all = check == 'all' valid_checks = get_valid_checks() if not releasing_all and check not in valid_checks: abort('Check `{}` is not an Agent-based Integration'.format(check)) # don't run the task on the master branch if get_current_branch() == 'master': abort( 'This task will commit, you do not want to add commits to master directly' ) if releasing_all: if version: abort('You cannot bump every check to the same version') checks = sorted(valid_checks) else: checks = [check] if initial_release: version = '1.0.0' for check in checks: if sign_only: break elif initial_release and check in BETA_PACKAGES: continue # Initial releases will only bump if not already 1.0.0 so no need to always output if not initial_release: echo_success('Check `{}`'.format(check)) if version: # sanity check on the version provided cur_version = get_version_string(check) if version == 'final': # Remove any pre-release metadata version = finalize_version(cur_version) else: # Keep track of intermediate version bumps prev_version = cur_version for method in version.split(','): # Apply any supported version bumping methods. Chaining is required for going # from mainline releases to development releases since e.g. x.y.z > x.y.z-rc.A. # So for an initial bug fix dev release you can do `fix,rc`. if method in VERSION_BUMP: version = VERSION_BUMP[method](prev_version) prev_version = version p_version = parse_version_info(version) p_current = parse_version_info(cur_version) if p_version <= p_current: if initial_release: continue else: abort('Current version is {}, cannot bump to {}'.format( cur_version, version)) else: cur_version, changelog_types = ctx.invoke(changes, check=check, dry_run=True) if not changelog_types: echo_warning('No changes for {}, skipping...'.format(check)) continue bump_function = get_bump_function(changelog_types) version = bump_function(cur_version) if initial_release: echo_success('Check `{}`'.format(check)) # update the version number echo_info('Current version of check {}: {}'.format(check, cur_version)) echo_waiting('Bumping to {}... '.format(version), nl=False) update_version_module(check, cur_version, version) echo_success('success!') # update the CHANGELOG echo_waiting('Updating the changelog... ', nl=False) # TODO: Avoid double GitHub API calls when bumping all checks at once ctx.invoke(changelog, check=check, version=version, old_version=cur_version, initial=initial_release, quiet=True, dry_run=False) echo_success('success!') commit_targets = [check] # update the list of integrations to be shipped with the Agent if check not in NOT_CHECKS: commit_targets.append(AGENT_REQ_FILE) req_file = os.path.join(root, AGENT_REQ_FILE) echo_waiting('Updating the Agent requirements file... ', nl=False) update_agent_requirements( req_file, check, get_agent_requirement_line(check, version)) echo_success('success!') echo_waiting('Committing files...') # commit the changes. # do not use [ci skip] so releases get built https://docs.gitlab.com/ee/ci/yaml/#skipping-jobs msg = '[Release] Bumped {} version to {}'.format(check, version) git_commit(commit_targets, msg) if not initial_release: # Reset version version = None if sign_only or not skip_sign: echo_waiting('Updating release metadata...') echo_info( 'Please touch your Yubikey immediately after entering your PIN!') try: commit_targets = update_link_metadata(checks) git_commit(commit_targets, '[Release] Update metadata', force=True) except YubikeyException as e: abort('A problem occurred while signing metadata: {}'.format(e)) # done echo_success( 'All done, remember to push to origin and open a PR to merge these changes on master' )
def test_should_versioninfo_bump_prerelease_and_build_with_token(): v = parse_version_info("3.4.5-rc.1+b.1") expected = parse_version_info("3.4.5-rc.2+b.2") assert v.bump_prerelease().bump_build("b") == expected
def changelog(ctx, check, version, old_version, quiet, dry_run): """Perform the operations needed to update the changelog. This method is supposed to be used by other tasks and not directly. """ if check not in get_valid_checks(): abort('Check `{}` is not an Agent-based Integration'.format(check)) # sanity check on the version provided cur_version = old_version or get_version_string(check) if parse_version_info(version) <= parse_version_info(cur_version): abort('Current version is {}, cannot bump to {}'.format( cur_version, version)) if not quiet: echo_info('Current version of check {}: {}, bumping to: {}'.format( check, cur_version, version)) # get the name of the current release tag target_tag = get_release_tag_string(check, cur_version) # get the diff from HEAD diff_lines = get_diff(check, target_tag) # for each PR get the title, we'll use it to populate the changelog pr_numbers = parse_pr_numbers(diff_lines) if not quiet: echo_info('Found {} PRs merged since tag: {}'.format( len(pr_numbers), target_tag)) user_config = ctx.obj entries = [] for pr_num in pr_numbers: try: payload = get_pr(pr_num, user_config) except Exception as e: echo_failure('Unable to fetch info for PR #{}: {}'.format( pr_num, e)) continue changelog_labels = get_changelog_types(payload) if not changelog_labels: abort( 'No valid changelog labels found attached to PR #{}, please add one!' .format(pr_num)) elif len(changelog_labels) > 1: abort( 'Multiple changelog labels found attached to PR #{}, please only use one!' .format(pr_num)) changelog_type = changelog_labels[0] if changelog_type == CHANGELOG_TYPE_NONE: if not quiet: # No changelog entry for this PR echo_info('Skipping PR #{} from changelog due to label'.format( pr_num)) continue author = payload.get('user', {}).get('login') author_url = payload.get('user', {}).get('html_url') title = '[{}] {}'.format(changelog_type, payload.get('title')) entry = ChangelogEntry(pr_num, title, payload.get('html_url'), author, author_url, from_contributor(payload)) entries.append(entry) # store the new changelog in memory new_entry = StringIO() # the header contains version and date header = '## {} / {}\n'.format(version, datetime.now().strftime('%Y-%m-%d')) new_entry.write(header) # one bullet point for each PR new_entry.write('\n') for entry in entries: thanks_note = '' if entry.from_contributor: thanks_note = ' Thanks [{}]({}).'.format(entry.author, entry.author_url) new_entry.write('* {}. See [#{}]({}).{}\n'.format( entry.title, entry.number, entry.url, thanks_note)) new_entry.write('\n') # read the old contents changelog_path = os.path.join(get_root(), check, 'CHANGELOG.md') old = list(stream_file_lines(changelog_path)) # write the new changelog in memory changelog_buffer = StringIO() # preserve the title changelog_buffer.write(''.join(old[:2])) # prepend the new changelog to the old contents # make the command idempotent if header not in old: changelog_buffer.write(new_entry.getvalue()) # append the rest of the old changelog changelog_buffer.write(''.join(old[2:])) # print on the standard out in case of a dry run if dry_run: echo_info(changelog_buffer.getvalue()) else: # overwrite the old changelog write_file(changelog_path, changelog_buffer.getvalue())
def create_version_tag(command_context: CommandContext, operation: Callable[[VersionConfig, Optional[str], Optional[int]], Result]) -> Result: result = Result() context: Context = command_context.context release_branches = command_context.context.get_release_branches(reverse=True) # TODO configuration allow_merge_base_tags = True # context.config.allow_shared_release_branch_base selected_branch = command_context.selected_ref selected_branch_base_version = context.release_branch_matcher.format(command_context.selected_ref.name) if selected_branch_base_version is not None: selected_branch_base_version_info = semver.parse_version_info(selected_branch_base_version) else: selected_branch_base_version_info = None if selected_branch_base_version is None: result.fail(os.EX_USAGE, _("Cannot bump version."), _("{branch} is not a release branch.") .format(branch=repr(command_context.selected_ref.name))) latest_version_tag = None preceding_version_tag = None preceding_branch_version_tag = None version_tags_on_same_commit = list() subsequent_version_tags = list() enclosing_versions = set() # abort scan, when a preceding commit for each tag type has been processed. # enclosing_versions now holds enough information for operation validation, # assuming the branch has not gone haywire in earlier commits # TODO evaluate upper and lower bound version for efficiency abort_version_scan = False on_selected_branch = False before_commit = False before_selected_branch = False for release_branch in release_branches: # fork_point = repotools.git_merge_base(context.repo, context.config.release_branch_base, # command_context.selected_commit) # if fork_point is None: # result.fail(os.EX_USAGE, # _("Cannot bump version."), # _("{branch} has no fork point on {base_branch}.") # .format(branch=repr(command_context.selected_ref.name), # base_branch=repr(context.config.release_branch_base))) fork_point = None branch_base_version = context.release_branch_matcher.format(release_branch.name) if branch_base_version is not None: branch_base_version_info = semver.parse_version_info(branch_base_version) else: branch_base_version_info = None on_selected_branch = not before_selected_branch and release_branch.name == selected_branch.name for history_commit in repotools.git_list_commits( context=context.repo, start=fork_point, end=release_branch.obj_name, options=const.BRANCH_COMMIT_SCAN_OPTIONS): at_commit = not before_commit and on_selected_branch and history_commit.obj_name == command_context.selected_commit version_tag_refs = None assert not at_commit if before_commit else not before_commit for tag_ref in repotools.git_get_tags_by_referred_object(context.repo, history_commit.obj_name): version_info = context.version_tag_matcher.to_version_info(tag_ref.name) if version_info is not None: tag_matches = version_info.major == branch_base_version_info.major \ and version_info.minor == branch_base_version_info.minor if tag_matches: if version_tag_refs is None: version_tag_refs = list() version_tag_refs.append(tag_ref) else: if fork_point is not None: # fail stray tags on exclusive branch commits result.fail(os.EX_DATAERR, _("Cannot bump version."), _("Found stray version tag: {version}.") .format(version=repr(version.format_version_info(version_info))) ) else: # when no merge base is used, abort at the first mismatching tag break if not abort_version_scan and version_tag_refs is not None and len(version_tag_refs): version_tag_refs.sort( reverse=True, key=utils.cmp_to_key( lambda tag_ref_a, tag_ref_b: semver.compare( context.version_tag_matcher.format(tag_ref_a.name), context.version_tag_matcher.format(tag_ref_b.name) ) ) ) if latest_version_tag is None: latest_version_tag = version_tag_refs[0] if at_commit: version_tags_on_same_commit.extend(version_tag_refs) if at_commit or before_commit: if preceding_version_tag is None: preceding_version_tag = version_tag_refs[0] if on_selected_branch and preceding_branch_version_tag is None: preceding_branch_version_tag = version_tag_refs[0] else: subsequent_version_tags.extend(version_tag_refs) for tag_ref in version_tag_refs: enclosing_versions.add(context.version_tag_matcher.format(tag_ref.name)) if before_commit: abort_version_scan = True if at_commit: before_commit = True if on_selected_branch: before_commit = True before_selected_branch = True if abort_version_scan: break if context.config.sequential_versioning and preceding_version_tag is not None: match = context.version_tag_matcher.fullmatch(preceding_version_tag.name) preceding_sequential_version = match.group(context.version_tag_matcher.group_unique_code) else: preceding_sequential_version = None if preceding_sequential_version is not None: preceding_sequential_version = int(preceding_sequential_version) if context.verbose: cli.print("Tags on selected commit:\n" + '\n'.join(' - ' + repr(tag_ref.name) for tag_ref in version_tags_on_same_commit)) cli.print("Tags in subsequent history:\n" + '\n'.join(' - ' + repr(tag_ref.name) for tag_ref in subsequent_version_tags)) if preceding_branch_version_tag is not None: latest_branch_version = context.version_tag_matcher.format(preceding_branch_version_tag.name) else: latest_branch_version = None global_sequence_number = get_global_sequence_number(context) if latest_branch_version is not None: version_result = operation(context.config.version_config, latest_branch_version, global_sequence_number) result.add_subresult(version_result) new_version = version_result.value if result.has_errors(): return result else: template_version_info = semver.parse_version_info(context.config.version_config.initial_version) new_version = semver.format_version( major=selected_branch_base_version_info.major, minor=selected_branch_base_version_info.minor, patch=template_version_info.patch, prerelease=str(global_sequence_number + 1) if context.config.tie_sequential_version_to_semantic_version and global_sequence_number is not None else template_version_info.prerelease, build=template_version_info.build, ) new_version_info = semver.parse_version_info(new_version) new_sequential_version = scheme_procedures.get_sequence_number(context.config.version_config, new_version_info) if new_version_info.major != selected_branch_base_version_info.major or new_version_info.minor != selected_branch_base_version_info.minor: result.fail(os.EX_USAGE, _("Tag creation failed."), _("The major.minor part of the new version {new_version}" " does not match the branch version {branch_version}.") .format(new_version=repr(new_version), branch_version=repr( "%d.%d" % (selected_branch_base_version_info.major, selected_branch_base_version_info.minor))) ) try: config_in_selected_commit = read_config_in_commit(context.repo, command_context.selected_commit) except FileNotFoundError: config_in_selected_commit = dict() try: properties_in_selected_commit = read_properties_in_commit(context, context.repo, config_in_selected_commit, command_context.selected_commit) except FileNotFoundError: properties_in_selected_commit = dict() if context.verbose: print("properties in selected commit:") print(json.dumps(obj=properties_in_selected_commit, indent=2)) valid_tag = False # validate the commit if len(version_tags_on_same_commit): if config_in_selected_commit is None: result.fail(os.EX_DATAERR, _("Tag creation failed."), _("The selected commit does not contain a configuration file.") ) version_property_name = config_in_selected_commit.get(const.CONFIG_VERSION_PROPERTY) if version_property_name is not None \ and properties_in_selected_commit.get(version_property_name) is None: result.warn(_("Missing version info."), _("The selected commit does not contain a version in property '{property_name}'.") .format(property_name=version_property_name) ) if len(version_tags_on_same_commit): if context.config.allow_qualifier_increments_within_commit: preceding_commit_version = context.version_tag_matcher.format( version_tags_on_same_commit[0].name) prerelease_keywords_list = [context.config.version_config.qualifiers, 1] preceding_commit_version_ = version.parse_version(preceding_commit_version) new_commit_version_ = version.parse_version(new_version) version_delta = version.determine_version_delta(preceding_commit_version_, new_commit_version_, prerelease_keywords_list ) version_increment_eval_result = version.evaluate_version_increment(preceding_commit_version_, new_commit_version_, context.config.strict_mode, prerelease_keywords_list) result.add_subresult(version_increment_eval_result) if result.has_errors(): return result if not version_delta.prerelease_field_only(0, False): result.fail(os.EX_USAGE, _("Tag creation failed."), _("The selected commit already has version tags.\n" "Operations on such a commit are limited to pre-release type increments.") ) valid_tag = True else: result.fail(os.EX_USAGE, _("Tag creation failed."), _("There are version tags pointing to the selected commit {commit}.\n" "Consider reusing these versions or bumping them to stable." "{listing}") .format(commit=command_context.selected_commit, listing='\n'.join( ' - ' + repr(tag_ref.name) for tag_ref in subsequent_version_tags)) ) if not valid_tag: if len(subsequent_version_tags): result.fail(os.EX_USAGE, _("Tag creation failed."), _("There are version tags in branch history following the selected commit {commit}:\n" "{listing}") .format(commit=command_context.selected_commit, listing='\n'.join( ' - ' + repr(tag_ref.name) for tag_ref in subsequent_version_tags)) ) global_seq_number = global_sequence_number if context.config.tie_sequential_version_to_semantic_version \ and global_seq_number is not None \ and new_sequential_version is not None \ and preceding_sequential_version != global_seq_number: result.fail(os.EX_USAGE, _("Tag creation failed."), _( "The preceding sequential version {seq_val} " "does not equal the global sequential version {global_seq_val}.") .format(seq_val=preceding_sequential_version if preceding_sequential_version is not None else '<none>', global_seq_val=global_seq_number) ) if not result.has_errors(): if new_version is None: result.fail(os.EX_SOFTWARE, _("Internal error."), _("Missing result version.") ) if latest_branch_version is not None and semver.compare(latest_branch_version, new_version) >= 0: result.fail(os.EX_DATAERR, _("Failed to increment version from {current} to {new}.") .format(current=repr(latest_branch_version), new=repr(new_version)), _("The new version is lower than or equal to the current version.") ) if context.config.push_to_local \ and command_context.current_branch.short_name == command_context.selected_ref.short_name: if context.verbose: cli.print( _('Checking out {base_branch} in order to avoid failing the push to a checked-out release branch') .format(base_branch=repr(context.config.release_branch_base))) git_or_fail(context.repo, result, ['checkout', context.config.release_branch_base]) original_current_branch = command_context.current_branch else: original_current_branch = None branch_name = get_branch_name_for_version(context, new_version_info) tag_name = get_tag_name_for_version(context, new_version_info) clone_result = clone_repository(context, context.config.release_branch_base) cloned_repo = clone_result.value commit_info = CommitInfo() commit_info.add_message("#version: " + cli.if_none(new_version)) # run version change hooks on release branch checkout_command = ['checkout', '--force', '--track', '-b', branch_name, repotools.create_ref_name(const.REMOTES_PREFIX, context.config.remote_name, branch_name)] returncode, out, err = repotools.git(cloned_repo, *checkout_command) if returncode != os.EX_OK: result.fail(os.EX_DATAERR, _("Failed to check out release branch."), _("An unexpected error occurred.") ) clone_context: Context = create_temp_context(context, result, cloned_repo.dir) clone_context.config.remote_name = 'origin' if (context.config.commit_version_property and new_version is not None) \ or (context.config.commit_sequential_version_property and new_sequential_version is not None): update_result = update_project_property_file(clone_context, properties_in_selected_commit, new_version, new_sequential_version, commit_info) result.add_subresult(update_result) if result.has_errors(): result.fail(os.EX_DATAERR, _("Property update failed."), _("An unexpected error occurred.") ) if new_version is not None: execute_version_change_actions(clone_context, latest_branch_version, new_version) if commit_info is not None: if command_context.selected_commit != command_context.selected_ref.target.obj_name: result.fail(os.EX_USAGE, _("Failed to commit version update."), _("The selected parent commit {commit} does not represent the tip of {branch}.") .format(commit=command_context.selected_commit, branch=repr(command_context.selected_ref.name)) ) # commit changes commit_info.add_parent(command_context.selected_commit) object_to_tag = create_commit(clone_context, result, commit_info) new_branch_ref_object = object_to_tag else: object_to_tag = command_context.selected_commit new_branch_ref_object = None # if command_context.selected_branch not in repotools.git_list_refs(context.repo, # '--contains', object_to_tag, # command_context.selected_branch.ref): # show info and prompt for confirmation cli.print("ref : " + cli.if_none(command_context.selected_ref.name)) cli.print("ref_" + const.DEFAULT_VERSION_VAR_NAME + " : " + cli.if_none(latest_branch_version)) cli.print("new_tag : " + cli.if_none(tag_name)) cli.print("new_" + const.DEFAULT_VERSION_VAR_NAME + " : " + cli.if_none(new_version)) cli.print("selected object : " + cli.if_none(command_context.selected_commit)) cli.print("tagged object : " + cli.if_none(object_to_tag)) prompt_result = prompt_for_confirmation( context=context, fail_title=_("Failed to create release tag based on {branch}.") .format(branch=repr(command_context.selected_ref.name)), message=_("The tags are about to be pushed."), prompt=_("Continue?"), ) result.add_subresult(prompt_result) if result.has_errors() or not prompt_result.value: return result # push atomically push_command = ['push', '--atomic'] if context.dry_run: push_command.append('--dry-run') if context.verbose: push_command.append('--verbose') push_command.append(context.config.remote_name) # push the release branch commit or its version increment commit if new_branch_ref_object is not None: push_command.append( new_branch_ref_object + ':' + repotools.create_ref_name(const.LOCAL_BRANCH_PREFIX, branch_name)) # check, if preceding tags exist on remote if preceding_version_tag is not None: push_command.append('--force-with-lease=' + preceding_version_tag.name + ':' + preceding_version_tag.name) # push the new version tag or fail if it exists push_command.extend(['--force-with-lease=' + repotools.create_ref_name(const.LOCAL_TAG_PREFIX, tag_name) + ':', repotools.ref_target(object_to_tag) + ':' + repotools.create_ref_name( const.LOCAL_TAG_PREFIX, tag_name)]) returncode, out, err = repotools.git(clone_context.repo, *push_command) if returncode != os.EX_OK: result.fail(os.EX_DATAERR, _("Failed to push."), _("git push exited with " + str(returncode)) ) if original_current_branch is not None: if context.verbose: cli.print( _('Switching back to {original_branch} ') .format(original_branch=repr(original_current_branch.name))) git_or_fail(context.repo, result, ['checkout', original_current_branch.short_name]) return result
def get_latest_release_version(finder: PackageFinder, package: str) -> VersionInfo: results = finder.find_all_candidates(package) versions = sorted(set([p.version for p in results]), reverse=True) return semver.parse_version_info(str(versions[0]).replace('rc', '-rc'))
def call(context: Context, operation: Callable[[VersionConfig, Optional[str], Optional[int]], Result]) -> Result: command_context = get_command_context( context=context, object_arg=context.args['<object>'] ) check_in_repo(command_context) # determine the type of operation to be performed and run according subroutines if operation == scheme_procedures.version_bump_major \ or operation == scheme_procedures.version_bump_minor: check_requirements(command_context=command_context, ref=command_context.selected_ref, branch_classes=[BranchClass.DEVELOPMENT_BASE], modifiable=True, with_upstream=True, # not context.config.push_to_local in_sync_with_upstream=True, fail_message=_("Version creation failed.") ) tag_result = create_version_branch(command_context, operation) command_context.add_subresult(tag_result) elif operation == scheme_procedures.version_bump_patch \ or operation == scheme_procedures.version_bump_qualifier \ or operation == scheme_procedures.version_bump_prerelease \ or operation == scheme_procedures.version_bump_to_release: check_requirements(command_context=command_context, ref=command_context.selected_ref, branch_classes=[BranchClass.RELEASE], modifiable=True, with_upstream=True, # not context.config.push_to_local in_sync_with_upstream=True, fail_message=_("Version creation failed.") ) tag_result = create_version_tag(command_context, operation) command_context.add_subresult(tag_result) elif isinstance(operation, scheme_procedures.VersionSet): check_requirements(command_context=command_context, ref=command_context.selected_ref, branch_classes=None, modifiable=True, with_upstream=True, # not context.config.push_to_local in_sync_with_upstream=True, fail_message=_("Version creation failed.") ) version_result = operation(context.config.version_config, None, get_global_sequence_number(context)) command_context.add_subresult(version_result) new_version = version_result.value if new_version is None: command_context.fail(os.EX_USAGE, _("Illegal argument."), _("Failed to parse version.") ) new_version_info = semver.parse_version_info(new_version) branch_name = get_branch_name_for_version(context, new_version_info) release_branch = repotools.get_branch_by_name(context.repo, {context.config.remote_name}, branch_name, BranchSelection.BRANCH_PREFER_LOCAL) if release_branch is None: tag_result = create_version_branch(command_context, operation) command_context.add_subresult(tag_result) else: selected_ref = release_branch tag_result = create_version_tag(command_context, operation) command_context.add_subresult(tag_result) if not command_context.has_errors() \ and context.config.pull_after_bump \ and not context.config.push_to_local: fetch_all_and_ff(context.repo, command_context.result, context.config.remote_name) return context.result
def app_version(): if _STUB_FOR_TEST: return parse_version_info('0.0.0-local+0') return _VERSION_INFO
branch = os.environ.get('READTHEDOCS_VERSION') if not branch or branch == 'latest': branch = 'HEAD' archive_name = 'master' chart_release = './cilium' image_tag = 'latest' elif branch == 'stable': branch = release archive_name = release chart_release = 'cilium/cilium --version ' + release tags.add('stable') else: archive_name = branch chart_release = 'cilium/cilium --version ' + release tags.add('stable') relinfo = semver.parse_version_info(release) current_release = '%d.%d' % (relinfo.major, relinfo.minor) if relinfo.patch == 90: next_release = '%d.%d' % (relinfo.major, relinfo.minor + 1) else: next_release = current_release githubusercontent = 'https://raw.githubusercontent.com/cilium/cilium/' scm_web = githubusercontent + branch jenkins_branch = 'https://jenkins.cilium.io/view/Cilium-v' + current_release archive_filename = archive_name + '.tar.gz' archive_link = 'https://github.com/cilium/cilium/archive/' + archive_filename archive_name = 'cilium-' + archive_name.strip('v') project_link = 'https://github.com/cilium/cilium/projects?query=is:open+' + next_release backport_format = 'https://github.com/cilium/cilium/pulls?q=is:open+is:pr+label:%s/' + current_release # Store variables in the epilogue so they are globally available.
def parse_version(version: str) -> semver.VersionInfo: try: return semver.parse_version_info( version[1:] if version.startswith("v") else version) except ValueError: pass
# -*- coding: utf-8 -*- # @author: vuolter from pkg_resources import get_distribution from semver import parse_version_info __package__ = 'pyload' __package_name__ = 'pyload-ng' __version__ = get_distribution(__package_name__).version __version_info__ = parse_version_info(__version__) __credits__ = (('Walter Purcaro', '*****@*****.**', '2015-2017'), ('pyLoad Team', '*****@*****.**', '2009-2015')) del get_distribution, parse_version_info
def from_db_value(self, value, expression, connection, context): if value is None: return value return semver.parse_version_info(value)
APPID = "pyload" PKGNAME = "pyload-ng" PKGDIR = pkg_resources.resource_filename(__name__, None) USERHOMEDIR = os.path.expanduser("~") DATADIR = os.path.join( os.getenv("APPDATA") if os.name == "nt" else USERHOMEDIR, "pyLoad" ) TMPDIR = os.path.join(tempfile.gettempdir(), "pyLoad") os.makedirs(DATADIR, exist_ok=True) os.makedirs(TMPDIR, exist_ok=True) os.chdir(USERHOMEDIR) __version__ = pkg_resources.get_distribution(PKGNAME).parsed_version.base_version __version_info__ = semver.parse_version_info(__version__) ### Locale ############################################################################ locale.setlocale(locale.LC_ALL, "") if os.name == "nt": _locale._getdefaultlocale = lambda *args: ["en_US", "utf_8_sig"] ### Exception logger ################################################################## exc_logger = logging.getLogger("exception") def excepthook(exc_type, exc_value, exc_traceback):
def to_python(self, value): if isinstance(value, semver.VersionInfo): return value if value is None: return value return semver.parse_version_info(value)
def bump_version_command(): """Increment the version number immediately after checking out a release branch.""" __author__ = "Shawn Davis <*****@*****.**>" __date__ = "2018-01-29" __help__ = """NOTES This command is based upon [Semantic Versioning](http://semver.org) If you omit the ``project_name`` then ``bumpversion`` will attempt to locate the ``VERSION.txt`` file to determine the current project name. The ``version.py`` file will also be updated if you specify the ``--path``. Or you may configure this by adding a ``.bumpversion.cfg`` file to project root: version_path = source/main/version.py This would cause version info to be written to the given path without using the ``--path`` switch. When ``--path`` or ``.bumpversion.cfg``, the file is created if it does not exist. However, the full path to the file must be present; it is not created for you. """ __version__ = "0.17.0-d" # Define options and arguments. parser = ArgumentParser(description=__doc__, epilog=__help__, formatter_class=RawDescriptionHelpFormatter) parser.add_argument( "-b=", "--build=", dest="build_name", help="An optional build name." ) parser.add_argument( "-M", "--major", action="store_true", dest="major_version", help="Bumping the major version resets the minor and patch level." ) parser.add_argument( "-m", "--minor", action="store_true", dest="minor_version", help="Bumping the minor version resets the patch level." ) parser.add_argument( "-p", "--patch", action="store_true", dest="patch_level", help="Bump the patch level." ) parser.add_argument( "--path=", dest="version_path", help="Path to version.py file. See NOTES." ) parser.add_argument( "--preview", action="store_true", dest="preview", help="Preview the changes." ) parser.add_argument( "-s=", "--status=", dest="status", help="Set the status." ) # Access to the version number requires special consideration, especially # when using sub parsers. The Python 3.3 behavior is different. See this # answer: http://stackoverflow.com/questions/8521612/argparse-optional-subparser-for-version # parser.add_argument('--version', action='version', version='%(prog)s 2.0') parser.add_argument( "-v", action="version", help="Show version number and exit.", version=__version__ ) parser.add_argument( "--version", action="version", help="Show verbose version information and exit.", version="%(prog)s" + " %s %s by %s" % (__version__, __date__, __author__) ) # This will display help or input errors as needed. args = parser.parse_args() # print args # Load the current version. try: with open("VERSION.txt", "rb") as f: current_version = f.read() current_version = current_version.strip() f.close() except IOError: current_version = "0.1.0-d" # Just display the current version if no changes are requested. actions = [args.build_name or False, args.major_version, args.minor_version, args.patch_level, args.status or False] count = 0 for a in actions: if a: count += 1 if count == 0: print("Current Version: %s" % current_version) sys.exit(EXIT_OK) # Get the version instance. version = Version(current_version) # Use args to update the version. new_version = version.bump( major=args.major_version, minor=args.minor_version, patch=args.patch_level, status=args.status, build=args.build_name ) # Preview and write out the new version. print("%-30s %-30s" % ("Bump", "To")) print("%-30s %-30s" % (current_version, new_version)) if not args.preview: with open("VERSION.txt", "wb") as f: f.write(new_version) f.close() if args.version_path: version_py = args.version_path elif os.path.exists(".bumpversion.cfg"): config = load_simple_config(".bumpversion.cfg") version_py = config.get("version_path") else: version_py = None if version_py is not None: info = semver.parse_version_info(new_version) try: with open(version_py, "wb") as f: f.write("# created by bumpversion from python-projectutils\n") if info.build: f.write('build = "%s"\n' % info.build) else: f.write('build = None\n') f.write('major = %s\n' % info.major) f.write('minor = %s\n' % info.minor) f.write('patch = %s\n' % info.patch) if info.prerelease: f.write('status = "%s"\n' % info.prerelease) else: f.write('status = None\n') f.write('version = "%s"\n' % new_version) f.close() except IOError as e: print(e.message) # Quit. sys.exit(EXIT_OK)
def _get_expected_versions(self, components): """ This method assigns a version to each component. That version can be used later to construct URIs for containers in GCR, locations of cloud function code, etc. If a released version is specified (e.g. 0.0.1) that is validated in github and, if present, used. If not found in github, it will not be used. If "latest" is specified then github is interrogated and the tag with the highest version number is extracted and used. If a prerelease version string (as defined by semver) is used, that is allowed through without github validation :param components: The list of components to assign versions to :return: A list of (component, version) tuples. version will None if none could be retrieved or validated """ result = [] available_versions = self._get_available_versions(components) for component in components: if not component.has_code_version: result.append((component, constants.NO_CODE_VERSION)) continue tag_name = component.git_tag_name if tag_name not in available_versions: logging.warn( 'Component type %s (tag name %s) not found in %s/%s/%s!', component.type, tag_name, git.ETSY_GITHUB_HOST, component.code_repo_owner, component.code_repo) result.append((component, None)) continue component_versions = available_versions[tag_name] if component.code_version in component_versions: result.append((component, component.code_version)) continue if component.code_version_is_latest: result.append((component, component_versions[-1])) continue try: if semver.parse_version_info( component.code_version).prerelease: result.append((component, component.code_version)) continue except ValueError: # Just warn here to let the deploy process continue. The component won't have a version and therefore # won't be deployable, but other components can continue. This check must be after the check for # `latest` because that is a valid, non-semver identifier. logging.warn('Component %s has an invalid version', component) logging.warn( 'Component %s (tag name %s) not available in %s/%s/%s!', component, tag_name, git.ETSY_GITHUB_HOST, component.code_repo_owner, component.code_repo) result.append((component, None)) return result
def parse(self, folder, pattern=False, home={}): """ returns dict with information home contains parsed plugins from pyload. { name : {path, version, config, (pattern, re), (plugin, class)} } """ plugins = {} if home: pfolder = os.path.join(self.pyload.userdir, "plugins", folder) os.makedirs(pfolder, exist_ok=True) try: file = open(os.path.join(pfolder, "__init__.py"), mode="wb") file.close() except Exception: pass else: pfolder = os.path.join(PKGDIR, "plugins", folder) configs = {} for entry in os.listdir(pfolder): if ( os.path.isfile(os.path.join(pfolder, entry)) and entry.endswith(".py") ) and not entry.startswith("_"): with open(os.path.join(pfolder, entry)) as data: content = data.read() name = entry[:-3] if name[-1] == ".": name = name[:-4] m_pyver = self._PYLOAD_VERSION.search(content) if m_pyver is None: self.pyload.log.debug( f"__pyload_version__ not found in plugin {name}" ) else: pyload_version = m_pyver.group(1) requires_version = f"{pyload_version}.0" requires_version_info = semver.parse_version_info(requires_version) if self.pyload.version_info.major: core_version = self.pyload.version_info.major plugin_version = requires_version_info.major else: core_version = self.pyload.version_info.minor plugin_version = requires_version_info.minor if core_version > plugin_version: self.pyload.log.warning( self._( "Plugin {} not compatible with current pyLoad version" ).format(name) ) continue m_ver = self._VERSION.search(content) if m_ver is None: self.pyload.log.debug(f"__version__ not found in plugin {name}") version = 0 else: version = float(m_ver.group(1)) # home contains plugins from pyload root if isinstance(home, dict) and name in home: if home[name]["v"] >= version: continue plugins[name] = {} plugins[name]["v"] = version module = entry.replace(".pyc", "").replace(".py", "") # the plugin is loaded from user directory plugins[name]["user"] = True if home else False plugins[name]["name"] = module plugins[name]["folder"] = folder if pattern: m_pat = self._PATTERN.search(content) pattern = r"^unmachtable$" if m_pat is None else m_pat.group(1) plugins[name]["pattern"] = pattern try: plugins[name]["re"] = re.compile(pattern) except Exception: self.pyload.log.error( self._("{} has a invalid pattern").format(name) ) # internals have no config if folder == "base": self.pyload.config.delete_config(name) continue m_desc = self._DESC.search(content) desc = "" if m_desc is None else m_desc.group(1) config = self._CONFIG.findall(content) if not config: new_config = {"enabled": ["bool", "Activated", False], "desc": desc} configs[name] = new_config continue config = literal_eval( config[0].strip().replace("\n", "").replace("\r", "") ) if isinstance(config, list) and all( isinstance(c, tuple) for c in config ): config = {x[0]: x[1:] for x in config} else: self.pyload.log.error( self._("Invalid config in {}: {}").format(name, config) ) continue if folder == "addons" and "enabled" not in config: config["enabled"] = ["bool", "Activated", False] config["desc"] = desc configs[name] = config if not home: temp_plugins, temp_configs = self.parse(folder, pattern, plugins or True) plugins.update(temp_plugins) configs.update(temp_configs) return plugins, configs
def changelog(ctx, check, version, old_version, initial, quiet, dry_run, output_file, tag_prefix): """Perform the operations needed to update the changelog. This method is supposed to be used by other tasks and not directly. """ if check and check not in get_valid_checks(): abort(f'Check `{check}` is not an Agent-based Integration') # sanity check on the version provided cur_version = old_version or get_version_string(check, tag_prefix=tag_prefix) if parse_version_info(version.replace( tag_prefix, '', 1)) <= parse_version_info( cur_version.replace(tag_prefix, '', 1)): abort(f'Current version is {cur_version}, cannot bump to {version}') if not quiet: echo_info( f'Current version of check {check}: {cur_version}, bumping to: {version}' ) # get the name of the current release tag target_tag = get_release_tag_string(check, cur_version) # get the diff from HEAD diff_lines = get_commits_since(check, None if initial else target_tag) # for each PR get the title, we'll use it to populate the changelog pr_numbers = parse_pr_numbers(diff_lines) if not quiet: echo_info( f'Found {len(pr_numbers)} PRs merged since tag: {target_tag}') if initial: # Only use the first one del pr_numbers[:-1] user_config = ctx.obj entries = [] for pr_num in pr_numbers: try: payload = get_pr(pr_num, user_config) except Exception as e: echo_failure(f'Unable to fetch info for PR #{pr_num}: {e}') continue changelog_labels = get_changelog_types(payload) if not changelog_labels: abort( f'No valid changelog labels found attached to PR #{pr_num}, please add one!' ) elif len(changelog_labels) > 1: abort( f'Multiple changelog labels found attached to PR #{pr_num}, please only use one!' ) changelog_type = changelog_labels[0] if changelog_type == CHANGELOG_TYPE_NONE: if not quiet: # No changelog entry for this PR echo_info(f'Skipping PR #{pr_num} from changelog due to label') continue author = payload.get('user', {}).get('login') author_url = payload.get('user', {}).get('html_url') title = f"[{changelog_type}] {payload.get('title')}" entry = ChangelogEntry(pr_num, title, payload.get('html_url'), author, author_url, from_contributor(payload)) entries.append(entry) # store the new changelog in memory new_entry = StringIO() # the header contains version and date header = f"## {version} / {datetime.utcnow().strftime('%Y-%m-%d')}\n" new_entry.write(header) # one bullet point for each PR new_entry.write('\n') for entry in entries: thanks_note = '' if entry.from_contributor: thanks_note = f' Thanks [{entry.author}]({entry.author_url}).' new_entry.write( f'* {entry.title}. See [#{entry.number}]({entry.url}).{thanks_note}\n' ) new_entry.write('\n') # read the old contents if check: changelog_path = os.path.join(get_root(), check, output_file) else: changelog_path = os.path.join(get_root(), output_file) old = list(stream_file_lines(changelog_path)) # write the new changelog in memory changelog_buffer = StringIO() # preserve the title changelog_buffer.write(''.join(old[:2])) # prepend the new changelog to the old contents # make the command idempotent if header not in old: changelog_buffer.write(new_entry.getvalue()) # append the rest of the old changelog changelog_buffer.write(''.join(old[2:])) # print on the standard out in case of a dry run if dry_run: echo_info(changelog_buffer.getvalue()) else: # overwrite the old changelog write_file(changelog_path, changelog_buffer.getvalue())