summaryrefslogtreecommitdiffstats
path: root/hacking/build_library/build_ansible/announce.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 16:04:21 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 16:04:21 +0000
commit8a754e0858d922e955e71b253c139e071ecec432 (patch)
tree527d16e74bfd1840c85efd675fdecad056c54107 /hacking/build_library/build_ansible/announce.py
parentInitial commit. (diff)
downloadansible-core-8a754e0858d922e955e71b253c139e071ecec432.tar.xz
ansible-core-8a754e0858d922e955e71b253c139e071ecec432.zip
Adding upstream version 2.14.3.upstream/2.14.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'hacking/build_library/build_ansible/announce.py')
-rw-r--r--hacking/build_library/build_ansible/announce.py293
1 files changed, 293 insertions, 0 deletions
diff --git a/hacking/build_library/build_ansible/announce.py b/hacking/build_library/build_ansible/announce.py
new file mode 100644
index 0000000..c245bfb
--- /dev/null
+++ b/hacking/build_library/build_ansible/announce.py
@@ -0,0 +1,293 @@
+# coding: utf-8
+# Copyright: (c) 2019, Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+import asyncio
+import datetime
+import hashlib
+
+import aiohttp
+from jinja2 import Environment, DictLoader
+
+
+VERSION_FRAGMENT = """
+{%- if versions | length > 1 %}
+ {% for version in versions %}
+ {% if loop.last %}and {{ pretty_version(version) }}{% else %}
+ {% if versions | length == 2 %}{{ pretty_version(version) }} {% else %}{{ pretty_version(version) }}, {% endif -%}
+ {% endif -%}
+ {% endfor -%}
+{%- else %}{{ pretty_version(versions[0]) }}{% endif -%}
+"""
+
+LONG_TEMPLATE = """
+{% set plural = False if versions | length == 1 else True %}
+{% set latest_ver = (versions | sort(attribute='ver_obj'))[-1] %}
+
+To: ansible-releases@redhat.com, ansible-devel@googlegroups.com, ansible-project@googlegroups.com, ansible-announce@googlegroups.com
+Subject: New release{% if plural %}s{% endif %}: {{ version_str }}
+
+{% filter wordwrap %}
+Hi all- we're happy to announce that the general release of {{ version_str }}{% if plural %} are{%- else %} is{%- endif %} now available!
+{% endfilter %}
+
+
+
+How to get it
+-------------
+
+{% for version in versions %}
+$ pip install ansible{% if is_ansible_base(version) %}-base{% endif %}=={{ version }} --user
+{% if not loop.last %}
+or
+{% endif %}
+{% endfor %}
+
+The tar.gz of the release{% if plural %}s{% endif %} can be found here:
+
+{% for version in versions %}
+* {{ pretty_version(version) }}
+{% if is_ansible_base(version) %}
+ https://pypi.python.org/packages/source/a/ansible-base/ansible-base-{{ version }}.tar.gz
+{% else %}
+ https://pypi.python.org/packages/source/a/ansible/ansible-{{ version }}.tar.gz
+{% endif %}
+ SHA256: {{ hashes[version] }}
+{% endfor %}
+
+
+What's new in {{ version_str }}
+{{ '-' * (14 + version_str | length) }}
+
+{% filter wordwrap %}
+{% if plural %}These releases are{% else %}This release is a{% endif %} maintenance release{% if plural %}s{% endif %} containing numerous bugfixes. The full {% if plural %} changelogs are{% else %} changelog is{% endif %} at:
+{% endfilter %}
+
+
+{% for version in versions %}
+* {{ version }}
+ https://github.com/ansible/ansible/blob/stable-{{ version.split('.')[:2] | join('.') }}/changelogs/CHANGELOG-v{{ version.split('.')[:2] | join('.') }}.rst
+{% endfor %}
+
+
+What's the schedule for future maintenance releases?
+----------------------------------------------------
+
+{% filter wordwrap %}
+Future maintenance releases will occur approximately every 3 weeks. So expect the next one around {{ next_release.strftime('%Y-%m-%d') }}.
+{% endfilter %}
+
+
+
+Porting Help
+------------
+
+{% filter wordwrap %}
+We've published a porting guide at
+https://docs.ansible.com/ansible/devel/porting_guides/porting_guide_{{ latest_ver.split('.')[:2] | join('.') }}.html to help migrate your content to {{ latest_ver.split('.')[:2] | join('.') }}.
+{% endfilter %}
+
+
+
+{% filter wordwrap %}
+If you discover any errors or if any of your working playbooks break when you upgrade to {{ latest_ver }}, please use the following link to report the regression:
+{% endfilter %}
+
+
+ https://github.com/ansible/ansible/issues/new/choose
+
+{% filter wordwrap %}
+In your issue, be sure to mention the version that works and the one that doesn't.
+{% endfilter %}
+
+
+Thanks!
+
+-{{ name }}
+
+""" # noqa for E501 (line length).
+# jinja2 is horrid about getting rid of extra newlines so we have to have a single per paragraph for
+# proper wrapping to occur
+
+SHORT_TEMPLATE = """
+{% set plural = False if versions | length == 1 else True %}
+{% set version = (versions|sort(attribute='ver_obj'))[-1] %}
+@ansible
+{{ version_str }}
+{% if plural %}
+ have
+{% else %}
+ has
+{% endif %}
+been released! Get
+{% if plural %}
+them
+{% else %}
+it
+{% endif %}
+on PyPI: pip install ansible{% if is_ansible_base(version) %}-base{% endif %}=={{ version }},
+the Ansible PPA on Launchpad, or GitHub. Happy automating!
+""" # noqa for E501 (line length).
+# jinja2 is horrid about getting rid of extra newlines so we have to have a single per paragraph for
+# proper wrapping to occur
+
+JINJA_ENV = Environment(
+ loader=DictLoader({'long': LONG_TEMPLATE,
+ 'short': SHORT_TEMPLATE,
+ 'version_string': VERSION_FRAGMENT,
+ }),
+ extensions=['jinja2.ext.i18n'],
+ trim_blocks=True,
+ lstrip_blocks=True,
+)
+
+
+async def calculate_hash_from_tarball(session, version):
+ tar_url = f'https://pypi.python.org/packages/source/a/ansible-base/ansible-base-{version}.tar.gz'
+ tar_task = asyncio.create_task(session.get(tar_url))
+ tar_response = await tar_task
+
+ tar_hash = hashlib.sha256()
+ while True:
+ chunk = await tar_response.content.read(1024)
+ if not chunk:
+ break
+ tar_hash.update(chunk)
+
+ return tar_hash.hexdigest()
+
+
+async def parse_hash_from_file(session, version):
+ filename = f'ansible-base-{version}.tar.gz'
+ hash_url = f'https://releases.ansible.com/ansible-base/{filename}.sha'
+ hash_task = asyncio.create_task(session.get(hash_url))
+ hash_response = await hash_task
+
+ hash_content = await hash_response.read()
+ precreated_hash, precreated_filename = hash_content.split(None, 1)
+ if filename != precreated_filename.strip().decode('utf-8'):
+ raise ValueError(f'Hash file contains hash for a different file: {precreated_filename}')
+
+ return precreated_hash.decode('utf-8')
+
+
+async def get_hash(session, version):
+ calculated_hash = await calculate_hash_from_tarball(session, version)
+ precreated_hash = await parse_hash_from_file(session, version)
+
+ if calculated_hash != precreated_hash:
+ raise ValueError(f'Hash in file ansible-base-{version}.tar.gz.sha {precreated_hash} does not'
+ f' match hash of tarball from pypi {calculated_hash}')
+
+ return calculated_hash
+
+
+async def get_hashes(versions):
+ hashes = {}
+ requestors = {}
+ async with aiohttp.ClientSession() as aio_session:
+ for version in versions:
+ requestors[version] = asyncio.create_task(get_hash(aio_session, version))
+
+ for version, request in requestors.items():
+ await request
+ hashes[version] = request.result()
+
+ return hashes
+
+
+def next_release_date(weeks=3):
+ days_in_the_future = weeks * 7
+ today = datetime.datetime.now()
+ numeric_today = today.weekday()
+
+ # We release on Thursdays
+ if numeric_today == 3:
+ # 3 is Thursday
+ pass
+ elif numeric_today == 4:
+ # If this is Friday, we can adjust back to Thursday for the next release
+ today -= datetime.timedelta(days=1)
+ elif numeric_today < 3:
+ # Otherwise, slide forward to Thursday
+ today += datetime.timedelta(days=(3 - numeric_today))
+ else:
+ # slightly different formula if it's past Thursday this week. We need to go forward to
+ # Thursday of next week
+ today += datetime.timedelta(days=(10 - numeric_today))
+
+ next_release = today + datetime.timedelta(days=days_in_the_future)
+ return next_release
+
+
+def is_ansible_base(version):
+ '''
+ Determines if a version is an ansible-base version or not, by checking
+ if it is >= 2.10.0. Stops comparing when it gets to the first non-numeric
+ component to allow for .dev and .beta suffixes.
+ '''
+ # Ignore .beta/.dev suffixes
+ ver_split = []
+ for component in version.split('.'):
+ if not component.isdigit():
+ if 'rc' in component:
+ ver_split.append(int(component.split('rc')[0]))
+ if 'b' in component:
+ ver_split.append(int(component.split('b')[0]))
+ continue
+ ver_split.append(int(component))
+ return tuple(ver_split) >= (2, 10, 0)
+
+
+# Currently only use with a single element list, but left general for later
+# in case we need to refer to the releases collectively.
+def release_variants(versions):
+ if all(is_ansible_base(v) for v in versions):
+ return 'ansible-base'
+
+ if all(not is_ansible_base(v) for v in versions):
+ return 'Ansible'
+
+ return 'Ansible and ansible-base'
+
+
+def pretty_version(version):
+ return '{0} {1}'.format(
+ release_variants([version]),
+ version,
+ )
+
+
+def create_long_message(versions, name):
+ hashes = asyncio.run(get_hashes(versions))
+
+ version_template = JINJA_ENV.get_template('version_string')
+ version_str = version_template.render(versions=versions,
+ pretty_version=pretty_version).strip()
+
+ next_release = next_release_date()
+
+ template = JINJA_ENV.get_template('long')
+ message = template.render(versions=versions, version_str=version_str,
+ name=name, hashes=hashes, next_release=next_release,
+ is_ansible_base=is_ansible_base,
+ pretty_version=pretty_version)
+ return message
+
+
+def create_short_message(versions):
+ version_template = JINJA_ENV.get_template('version_string')
+ version_str = version_template.render(versions=versions,
+ pretty_version=pretty_version).strip()
+
+ template = JINJA_ENV.get_template('short')
+ message = template.render(versions=versions, version_str=version_str,
+ is_ansible_base=is_ansible_base,
+ pretty_version=pretty_version)
+ message = ' '.join(message.split()) + '\n'
+ return message