mirror of
https://github.com/sabnzbd/sabnzbd.git
synced 2025-12-24 00:00:12 -05:00
273 lines
9.9 KiB
Python
273 lines
9.9 KiB
Python
#!/usr/bin/python3 -OO
|
|
# Copyright 2008-2025 by The SABnzbd-Team (sabnzbd.org)
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import re
|
|
import xml.etree.ElementTree as ET
|
|
|
|
import github
|
|
import praw
|
|
|
|
from constants import (
|
|
RELEASE_VERSION,
|
|
RELEASE_VERSION_BASE,
|
|
PRERELEASE,
|
|
RELEASE_SRC,
|
|
RELEASE_WIN_BIN_X64,
|
|
RELEASE_WIN_BIN_ARM64,
|
|
RELEASE_WIN_INSTALLER,
|
|
RELEASE_MACOS,
|
|
RELEASE_README,
|
|
RELEASE_THIS,
|
|
RELEASE_TITLE,
|
|
APPDATA_FILE,
|
|
ON_GITHUB_ACTIONS,
|
|
)
|
|
|
|
# Verify we have all assets
|
|
files_to_check = (
|
|
RELEASE_SRC,
|
|
RELEASE_WIN_BIN_X64,
|
|
RELEASE_WIN_BIN_ARM64,
|
|
RELEASE_WIN_INSTALLER,
|
|
RELEASE_MACOS,
|
|
RELEASE_README,
|
|
)
|
|
for file_to_check in files_to_check:
|
|
if not os.path.exists(file_to_check):
|
|
raise RuntimeError("Not all release files are present!")
|
|
print("All release files are present")
|
|
|
|
# Verify that appdata file is updated
|
|
if not isinstance(ET.parse(APPDATA_FILE).find(f"./releases/release[@version='{RELEASE_VERSION_BASE}']"), ET.Element):
|
|
release_missing = f"Could not find {RELEASE_VERSION_BASE} in {APPDATA_FILE}"
|
|
if RELEASE_THIS:
|
|
raise RuntimeError(release_missing)
|
|
elif ON_GITHUB_ACTIONS:
|
|
print(f"::warning file={APPDATA_FILE},title=Missing release::{release_missing}")
|
|
else:
|
|
print(release_missing)
|
|
|
|
# Calculate hashes for Synology release
|
|
with open(RELEASE_SRC, "rb") as inp_file:
|
|
source_data = inp_file.read()
|
|
|
|
print("---- Synology spksrc digest hashes ---- ")
|
|
print(RELEASE_SRC, "SHA1", hashlib.sha1(source_data).hexdigest())
|
|
print(RELEASE_SRC, "SHA256", hashlib.sha256(source_data).hexdigest())
|
|
print(RELEASE_SRC, "MD5", hashlib.md5(source_data).hexdigest())
|
|
print("----")
|
|
|
|
# Check if tagged as release and check for token
|
|
gh_token = os.environ.get("AUTOMATION_GITHUB_TOKEN", "")
|
|
if RELEASE_THIS and gh_token:
|
|
gh_obj = github.Github(auth=github.Auth.Token(gh_token))
|
|
gh_repo = gh_obj.get_repo("sabnzbd/sabnzbd")
|
|
|
|
# Read the release notes
|
|
with open(RELEASE_README, "r") as readme_file:
|
|
readme_data = readme_file.read()
|
|
|
|
# We have to manually check if we already created this release
|
|
for release in gh_repo.get_releases():
|
|
if release.tag_name == RELEASE_VERSION:
|
|
gh_release = release
|
|
print("Found existing release %s" % gh_release.name)
|
|
break
|
|
else:
|
|
# Did not find it, so create the release, use the GitHub tag we got as input
|
|
print("Creating GitHub release SABnzbd %s" % RELEASE_VERSION)
|
|
gh_release = gh_repo.create_git_release(
|
|
tag=RELEASE_VERSION,
|
|
name=RELEASE_TITLE,
|
|
message=readme_data,
|
|
draft=True,
|
|
prerelease=PRERELEASE,
|
|
)
|
|
|
|
# Fetch existing assets, as overwriting is not allowed by GitHub
|
|
gh_assets = gh_release.get_assets()
|
|
|
|
# Upload the assets
|
|
for file_to_check in files_to_check:
|
|
if os.path.exists(file_to_check):
|
|
# Check if this file was previously uploaded
|
|
if gh_assets.totalCount:
|
|
for gh_asset in gh_assets:
|
|
if gh_asset.name == file_to_check:
|
|
print("Removing existing asset %s " % gh_asset.name)
|
|
gh_asset.delete_asset()
|
|
# Upload the new one
|
|
print("Uploading %s to release %s" % (file_to_check, gh_release.name))
|
|
gh_release.upload_asset(file_to_check)
|
|
|
|
# Check if we now have all files
|
|
gh_new_assets = gh_release.get_assets()
|
|
if gh_new_assets.totalCount:
|
|
all_assets = [gh_asset.name for gh_asset in gh_new_assets]
|
|
|
|
# Check if we have all files, using set-comparison
|
|
if set(files_to_check) == set(all_assets):
|
|
print("All assets present, releasing %s" % RELEASE_VERSION)
|
|
# Publish release
|
|
gh_release.update_release(
|
|
tag_name=RELEASE_VERSION,
|
|
name=RELEASE_TITLE,
|
|
message=readme_data,
|
|
draft=False,
|
|
prerelease=PRERELEASE,
|
|
)
|
|
|
|
# Update the website
|
|
gh_repo_web = gh_obj.get_repo("sabnzbd/sabnzbd.github.io")
|
|
# Check if the branch already exists, only create one if it doesn't
|
|
skip_website_update = False
|
|
try:
|
|
gh_repo_web.get_branch(RELEASE_VERSION)
|
|
print("Branch %s on sabnzbd/sabnzbd.github.io already exists, skipping update" % RELEASE_VERSION)
|
|
skip_website_update = True
|
|
except github.GithubException:
|
|
# Create a new branch to have the changes
|
|
sb = gh_repo_web.get_branch("master")
|
|
print("Creating branch %s on sabnzbd/sabnzbd.github.io" % RELEASE_VERSION)
|
|
new_branch = gh_repo_web.create_git_ref(ref="refs/heads/" + RELEASE_VERSION, sha=sb.commit.sha)
|
|
|
|
# Update the files
|
|
if not skip_website_update:
|
|
# We need bytes version to interact with GitHub
|
|
RELEASE_VERSION_BYTES = RELEASE_VERSION.encode()
|
|
|
|
# Get all the version files
|
|
latest_txt = gh_repo_web.get_contents("latest.txt")
|
|
latest_txt_items = latest_txt.decoded_content.split()
|
|
new_latest_txt_items = latest_txt_items[:2]
|
|
config_yml = gh_repo_web.get_contents("_config.yml")
|
|
if PRERELEASE:
|
|
# If it's a pre-release, we append to current version in latest.txt
|
|
new_latest_txt_items.extend([RELEASE_VERSION_BYTES, latest_txt_items[1]])
|
|
# And replace in _config.yml
|
|
new_config_yml = re.sub(
|
|
b"latest_testing: '[^']*'",
|
|
b"latest_testing: '%s'" % RELEASE_VERSION_BYTES,
|
|
config_yml.decoded_content,
|
|
)
|
|
else:
|
|
# New stable release, replace the version
|
|
new_latest_txt_items[0] = RELEASE_VERSION_BYTES
|
|
# And replace in _config.yml
|
|
new_config_yml = re.sub(
|
|
b"latest_testing: '[^']*'",
|
|
b"latest_testing: ''",
|
|
config_yml.decoded_content,
|
|
)
|
|
new_config_yml = re.sub(
|
|
b"latest_stable: '[^']*'",
|
|
b"latest_stable: '%s'" % RELEASE_VERSION_BYTES,
|
|
new_config_yml,
|
|
)
|
|
# Also update the wiki-settings, these only use x.x notation
|
|
new_config_yml = re.sub(
|
|
b"wiki_version: '[^']*'",
|
|
b"wiki_version: '%s'" % RELEASE_VERSION_BYTES[:3],
|
|
new_config_yml,
|
|
)
|
|
|
|
# Update the files
|
|
print("Updating latest.txt")
|
|
gh_repo_web.update_file(
|
|
"latest.txt",
|
|
"Release %s: latest.txt" % RELEASE_VERSION,
|
|
b"\n".join(new_latest_txt_items),
|
|
latest_txt.sha,
|
|
RELEASE_VERSION,
|
|
)
|
|
print("Updating _config.yml")
|
|
gh_repo_web.update_file(
|
|
"_config.yml",
|
|
"Release %s: _config.yml" % RELEASE_VERSION,
|
|
new_config_yml,
|
|
config_yml.sha,
|
|
RELEASE_VERSION,
|
|
)
|
|
|
|
# Create pull-request
|
|
print("Creating pull request in sabnzbd/sabnzbd.github.io for the update")
|
|
update_pr = gh_repo_web.create_pull(
|
|
title="Release %s" % RELEASE_VERSION,
|
|
base="master",
|
|
body="Automated update of release files",
|
|
head=RELEASE_VERSION,
|
|
)
|
|
|
|
# Merge pull-request
|
|
print("Merging pull request in sabnzbd/sabnzbd.github.io for the update")
|
|
update_pr.merge(merge_method="squash")
|
|
|
|
# Only with GitHub success we proceed to Reddit
|
|
if reddit_token := os.environ.get("REDDIT_TOKEN", ""):
|
|
# Token format (without whitespace):
|
|
# {
|
|
# "client_id":"XXX",
|
|
# "client_secret":"XXX",
|
|
# "user_agent":"SABnzbd release script",
|
|
# "username":"Safihre",
|
|
# "password":"XXX"
|
|
# }
|
|
credentials = json.loads(reddit_token)
|
|
reddit = praw.Reddit(**credentials)
|
|
|
|
subreddit_sabnzbd = reddit.subreddit("sabnzbd")
|
|
subreddit_usenet = reddit.subreddit("usenet")
|
|
|
|
# Read the release notes
|
|
with open(RELEASE_README, "r") as readme_file:
|
|
readme_lines = readme_file.readlines()
|
|
|
|
# Put the download link after the title
|
|
readme_lines[2] = "## https://sabnzbd.org/downloads\n\n"
|
|
|
|
# Use the header in the readme as title
|
|
title = readme_lines[0]
|
|
release_notes_text = "".join(readme_lines[2:])
|
|
print("Posting release notes to Reddit")
|
|
|
|
# Only stable releases to r/usenet
|
|
if not PRERELEASE:
|
|
# Get correct flair-id (required by r/usenet)
|
|
for flair in subreddit_usenet.flair.link_templates.user_selectable():
|
|
if flair["flair_text"] == "News":
|
|
print("Posting to r/usenet")
|
|
submission = subreddit_usenet.submit(
|
|
title, selftext=release_notes_text, flair_id=flair["flair_template_id"]
|
|
)
|
|
break
|
|
else:
|
|
raise ValueError("Could not locate flair_text for posting to r/usenet")
|
|
|
|
# Post always to r/SABnzbd
|
|
print("Posting to r/sabnzbd")
|
|
subreddit_sabnzbd.submit(title, selftext=release_notes_text)
|
|
|
|
else:
|
|
print("Missing REDDIT_TOKEN")
|
|
|
|
else:
|
|
print("To push release to GitHub, first tag the commit.")
|
|
print("Or missing the AUTOMATION_GITHUB_TOKEN, cannot push to GitHub without it.")
|