#!/usr/bin/env -S python3 -u """ ADOdb announcements script. Posts release announcements to - Gitter - Twitter This file is part of ADOdb, a Database Abstraction Layer library for PHP. @package ADOdb @link https://adodb.org Project's web site and documentation @link https://github.com/ADOdb/ADOdb Source code and issue tracker The ADOdb Library is dual-licensed, released under both the BSD 3-Clause and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, any later version. This means you can use it in proprietary products. See the LICENSE.md file distributed with this source code for details. @license BSD-3-Clause @license LGPL-2.1-or-later @copyright 2022 Damien Regad, Mark Newnham and the ADOdb community @author Damien Regad """ import argparse from datetime import date import json import re from pathlib import Path import tweepy # https://www.tweepy.org/ from git import Repo # https://gitpython.readthedocs.io # https://github.com/PyGithub/PyGithub from github import Github, GithubException, Milestone from adodbutil import env, Matrix def process_command_line(): """ Parse command-line options :return: Namespace """ # Get most recent Git tag repo = Repo(path=Path(__file__).parents[1]) tags = sorted(repo.tags, key=lambda t: t.tag.tagged_date) latest_tag = str(tags[-1]) parser = argparse.ArgumentParser( description="Post ADOdb release announcement messages to Gitter." ) parser.add_argument('version', nargs='?', default=latest_tag, help="Version number to announce; if not specified, " "the latest tag will be used.") parser.add_argument('-m', '--message', help="Additional text to add to announcement message") parser.add_argument('-b', '--batch', action="store_true", help="Batch mode - do not ask for confirmation " "before posting") only = parser.add_mutually_exclusive_group() only.add_argument('-g', '--gitter-only', action="store_true", help="Only post the announcement to Gitter") only.add_argument('-t', '--twitter-only', action="store_true", help="Only post the announcement to Twitter") only.add_argument('-u', '--github-only', action="store_true", help="Only post the announcement to GitHub") return parser.parse_args() def github_close_milestone(repo, version): print(f"Closing Milestone '{version}'") # Search Milestone for version milestone_found = False milestone: Milestone.Milestone for milestone in repo.get_milestones(): if milestone.title == version: milestone_found = True break # Milestone not found, check if already closed if not milestone_found: # Process closed Milestones in reverse order of due_on, to minimize # number of iterations for milestone in repo.get_milestones(state='closed', sort='due_on', direction='desc'): if milestone.title == version: print(f"Already closed {milestone.raw_data['html_url']}") return raise Exception(f"Milestone '{version}' not found") # Close the milestone # noinspection PyUnboundLocalVariable milestone.edit(title=milestone.title, state='closed', due_on=date.today()) def post_github(version, message, changelog_link): print(f"GitHub Release for repository '{env.github_repo}'") gh = Github(env.github_token) repo = gh.get_repo(env.github_repo) # Check if Release already exists version = 'v' + version try: rel = repo.get_release(version) print(f"Existing release '{version}' found", rel.html_url) # Discard the message provided on command-line, and use the one from # the Release's description, inform user to update it on GitHub. if message: print(f"Your message will be discarded; " "the Release's description will be used instead.\n" "Edit it on GitHub if needed") else: print("Retrieving the Release's description for the " "announcement message") # Remove the changelog link to keep only the release's message message = re.sub(r"[,.]?\s*(Please )?See .*$", "", rel.body, flags=re.IGNORECASE).strip() if message: message += ".\n" except GithubException as err: if err.status != 404: raise err print(f"Release '{version}' does not exist yet") # Make sure the version has been tagged try: repo.get_git_ref('tags/' + version) print(f"Tag '{version}' found") except GithubException: print(f"ERROR: Tag '{version}' does not exist on GitHub, " "please push it first") exit(1) # Create the release rel = repo.create_git_release(version, version, message + changelog_link) print("Release created successfully", rel.html_url) print() # Closing the Milestone try: github_close_milestone(repo, version) except Exception as e: print("WARNING: " + str(e) + "\n") # Return message to be used for remaining announcements return message def post_gitter(message): print(f"Posting to Gitter ({env.matrix_room})... ") matrix = Matrix(env.matrix_domain, env.matrix_token, env.matrix_room) message_id = matrix.post('# ' + message) print("Message posted successfully\n" f"https://matrix.to/#/{matrix.room_id}/{message_id}") print() def post_twitter(message): print(f"Posting to Twitter ({env.twitter_account})... ",) twitter = tweepy.Client( consumer_key=env.twitter_api_key, consumer_secret=env.twitter_api_secret, access_token=env.twitter_access_token, access_token_secret=env.twitter_access_secret ) try: r = twitter.create_tweet(text=message) except tweepy.errors.HTTPException as e: print("ERROR:", e) return print("Tweeted successfully\n" f"https://twitter.com/{env.twitter_account}/status/{r.data['id']}") print() def main(): args = process_command_line() post_everywhere = not args.gitter_only \ and not args.github_only \ and not args.twitter_only version = args.version.lstrip('v') changelog_url = f"https://github.com/ADOdb/ADOdb/blob/v{version}" \ "/docs/changelog.md" message = args.message.rstrip(".") + ".\n" if args.message else "" # Tell user where the release will be announced print(f"Posting to: ", end='') if post_everywhere or args.github_only: print(f"GitHub ({env.github_repo})", end=', ' if post_everywhere else '\n') if post_everywhere or args.gitter_only: print(f"Gitter ({env.matrix_room})", end=', ' if post_everywhere else '\n') if post_everywhere or args.twitter_only: print(f"Twitter ({env.twitter_account})") print() # Create GitHub release, retrieve message from it if it already exists if post_everywhere or args.github_only: message = post_github(version, message, f"See [Changelog]({changelog_url}) for details") if args.github_only: return # Build announcement message msg_announce = f"ADOdb Version {version} released\n{message}" \ "See Changelog " + changelog_url # Get confirmation if not args.batch: print("Review ", end='') print("Announcement message") print("-" * 27) print(msg_announce) print("-" * 27) if not args.batch: reply = input("Proceed with posting ? ") if not reply.casefold() == 'y': print("Aborting") exit(1) print() if post_everywhere or args.gitter_only: post_gitter(msg_announce) if post_everywhere or args.twitter_only: post_twitter(msg_announce) if __name__ == "__main__": main()