1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
|
#!/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 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 if t.tag is not None else 0)
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()
|