☄️ deploy: github releases - whatsNew text as note

Use whatsNew text (if available) as release notes text when deploying to
Github releases. This feature will always use 'en-US' locale texts,
since English is the lingua franka on GitHub. Additionally this change
also adds a config option to preprend a static text to those release
notes.
This commit is contained in:
Michael Pöhn 2024-04-17 21:04:45 +02:00
parent a079f9d85f
commit c6598f2835
No known key found for this signature in database
GPG key ID: 725F386C05529A5A
4 changed files with 33 additions and 21 deletions

View file

@ -218,12 +218,13 @@
# an example for this deployment automation: # an example for this deployment automation:
# https://github.com/f-droid/fdroidclient/releases/ # https://github.com/f-droid/fdroidclient/releases/
# #
# It is highly recommended to use a "Fine-grained personal access token", which # In the examble below tokens are read from environment variables. Putting
# is restriced to the minimum required permissions, which are: # tokens directly into the config file is also supported but discouraged. It is
# highly recommended to use a "Fine-grained personal access token", which is
# restriced to the minimum required permissions, which are:
# * Metadata - read # * Metadata - read
# * Contents - read/write # * Contents - read/write
# Also make sure to limit access only to the GitHub repository you're deploying # (https://github.com/settings/personal-access-tokens/new)
# to. (https://github.com/settings/personal-access-tokens/new)
# #
# github_token: {env: GITHUB_TOKEN} # github_token: {env: GITHUB_TOKEN}
# github_releases: # github_releases:
@ -231,6 +232,8 @@
# packages: # packages:
# - org.fdroid.basic # - org.fdroid.basic
# - org.fdroid.fdroid # - org.fdroid.fdroid
# release_notes_prepend: |
# Re-post of official F-Droid App release from https://f-droid.org
# - repo: example/app # - repo: example/app
# token: {env: GITHUB_TOKEN_EXAMPLE} # token: {env: GITHUB_TOKEN_EXAMPLE}
# packages: # packages:

View file

@ -1116,27 +1116,27 @@ def push_binary_transparency(git_repo_path, git_remote):
raise FDroidException(_("Pushing to remote server failed!")) raise FDroidException(_("Pushing to remote server failed!"))
def find_release_files(index_v2_path, repo_dir, package_names): def find_release_infos(index_v2_path, repo_dir, package_names):
""" """
Find files for uploading to a release page. Find files, texts, etc. for uploading to a release page.
This function parses index-v2.json for file-paths elegible for deployment This function parses index-v2.json for file-paths elegible for deployment
to release pages. (e.g. GitHub releases) It also groups these files by to release pages. (e.g. GitHub releases) It also groups these files by
packageName and versionName. e.g. to get a list of files for all specific packageName and versionName. e.g. to get a list of files for all specific
release of fdroid client you may call: release of fdroid client you may call:
find_binary_release_files()['org.fdroid.fdroid']['0.19.2'] find_binary_release_infos()['org.fdroid.fdroid']['0.19.2']
All paths in the returned data-structure are of type pathlib.Path. All paths in the returned data-structure are of type pathlib.Path.
""" """
release_files = {} release_infos = {}
with open(index_v2_path, 'r') as f: with open(index_v2_path, 'r') as f:
idx = json.load(f) idx = json.load(f)
for package_name in package_names: for package_name in package_names:
package = idx.get('packages', {}).get(package_name, {}) package = idx.get('packages', {}).get(package_name, {})
for version in package.get('versions', {}).values(): for version in package.get('versions', {}).values():
if package_name not in release_files: if package_name not in release_infos:
release_files[package_name] = {} release_infos[package_name] = {}
ver_name = version['manifest']['versionName'] ver_name = version['manifest']['versionName']
apk_path = repo_dir / version['file']['name'][1:] apk_path = repo_dir / version['file']['name'][1:]
files = [apk_path] files = [apk_path]
@ -1146,8 +1146,11 @@ def find_release_files(index_v2_path, repo_dir, package_names):
idsig_path = pathlib.Path(str(apk_path) + '.idsig') idsig_path = pathlib.Path(str(apk_path) + '.idsig')
if idsig_path.is_file(): if idsig_path.is_file():
files.append(idsig_path) files.append(idsig_path)
release_files[package_name][ver_name] = files release_infos[package_name][ver_name] = {
return release_files 'files': files,
'whatsNew': version.get('whatsNew', {}).get("en-US"),
}
return release_infos
def upload_to_github_releases(repo_section, gh_config, global_gh_token): def upload_to_github_releases(repo_section, gh_config, global_gh_token):
@ -1167,13 +1170,13 @@ def upload_to_github_releases(repo_section, gh_config, global_gh_token):
for package_name in repo_conf.get('packages', []): for package_name in repo_conf.get('packages', []):
package_names.append(package_name) package_names.append(package_name)
release_files = find_release_files(index_v2_path, repo_dir, package_names) release_infos = find_release_infos(index_v2_path, repo_dir, package_names)
for repo_conf in gh_config: for repo_conf in gh_config:
upload_to_github_releases_repo(repo_conf, release_files, global_gh_token) upload_to_github_releases_repo(repo_conf, release_infos, global_gh_token)
def upload_to_github_releases_repo(repo_conf, release_files, global_gh_token): def upload_to_github_releases_repo(repo_conf, release_infos, global_gh_token):
repo = repo_conf.get('repo') repo = repo_conf.get('repo')
if not repo: if not repo:
logging.warning(_("One of the 'github_releases' config items is missing the 'repo' value. skipping ...")) logging.warning(_("One of the 'github_releases' config items is missing the 'repo' value. skipping ..."))
@ -1191,7 +1194,7 @@ def upload_to_github_releases_repo(repo_conf, release_files, global_gh_token):
# local fdroid repo # local fdroid repo
all_local_versions = set() all_local_versions = set()
for package_name in repo_conf['packages']: for package_name in repo_conf['packages']:
for version in release_files.get(package_name, {}).keys(): for version in release_infos.get(package_name, {}).keys():
all_local_versions.add(version) all_local_versions.add(version)
gh = fdroidserver.github.GithubApi(token, repo) gh = fdroidserver.github.GithubApi(token, repo)
@ -1202,9 +1205,14 @@ def upload_to_github_releases_repo(repo_conf, release_files, global_gh_token):
# collect files associated with this github release # collect files associated with this github release
files = [] files = []
for package in packages: for package in packages:
files.extend(release_files.get(package, {}).get(version, [])) files.extend(release_infos.get(package, {}).get(version, {}).get('files', []))
# always use the whatsNew text from the first app listed in
# config.qml github_releases.packages
text = release_infos.get(packages[0], {}).get(version, {}).get('whatsNew') or ''
if 'release_notes_prepend' in repo_conf:
text = repo_conf['release_notes_prepend'] + "\n\n" + text
# create new release on github and upload all associated files # create new release on github and upload all associated files
gh.create_release(version, files) gh.create_release(version, files, text)
def main(): def main():

View file

@ -90,7 +90,7 @@ class GithubApi:
tags.append(r[10:]) tags.append(r[10:])
return tags return tags
def create_release(self, tag, files): def create_release(self, tag, files, body=''):
""" """
Create a new release on github. Create a new release on github.
@ -113,6 +113,7 @@ class GithubApi:
data=json.dumps( data=json.dumps(
{ {
"tag_name": tag, "tag_name": tag,
"body": body,
} }
).encode("utf-8"), ).encode("utf-8"),
) )

View file

@ -94,7 +94,7 @@ class GithubApiTest(unittest.TestCase):
api._create_release_asset = unittest.mock.Mock() api._create_release_asset = unittest.mock.Mock()
with unittest.mock.patch("urllib.request.urlopen", uomock): with unittest.mock.patch("urllib.request.urlopen", uomock):
success = api.create_release('faketag', ['file_a', 'file_b']) success = api.create_release('faketag', ['file_a', 'file_b'], body="bdy")
self.assertTrue(success) self.assertTrue(success)
req = uomock.call_args_list[0][0][0] req = uomock.call_args_list[0][0][0]
@ -105,7 +105,7 @@ class GithubApiTest(unittest.TestCase):
req.full_url, req.full_url,
'https://api.github.com/repos/fakerepopath/releases', 'https://api.github.com/repos/fakerepopath/releases',
) )
self.assertEqual(req.data, b'{"tag_name": "faketag"}') self.assertEqual(req.data, b'{"tag_name": "faketag", "body": "bdy"}')
self.assertListEqual( self.assertListEqual(
api._create_release_asset.call_args_list, api._create_release_asset.call_args_list,
[ [