diff --git a/.github/workflows/build-nightly.yaml b/.github/workflows/build-nightly.yaml index f7c76289fd..350a5b6834 100644 --- a/.github/workflows/build-nightly.yaml +++ b/.github/workflows/build-nightly.yaml @@ -210,28 +210,3 @@ jobs: DATACORDER_USER: ${{ secrets.DATACORDER_USER }} DATACORDER_SSHPASS: ${{ secrets.DATACORDER_PASSWORD }} run: $GITHUB_WORKSPACE/ci/linux/upload_dist_package.sh nightly - - post_builds: - # Post nightlies if at least one job was successful - if: ${{ needs.linux_zip == "success" || needs.windows_zip == "success" }} - name: Post builds on Nebula and the forums - needs: [linux_zip, windows_zip] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - name: Checkout - with: - fetch-depth: '0' - - name: Install Python dependencies - run: pip install -r ci/post/requirements.txt - - name: Post builds - env: - INDIEGAMES_USER: ${{ secrets.INDIEGAMES_USER }} - INDIEGAMES_SSHPASS: ${{ secrets.INDIEGAMES_PASSWORD }} - NEBULA_USER: ${{ secrets.NEBULA_USER }} - NEBULA_PASSWORD: ${{ secrets.NEBULA_PASSWORD }} - HLP_API: ${{ secrets.HLP_API }} - HLP_KEY: ${{ secrets.HLP_KEY }} - LINUX_RESULT: ${{ needs.linux_zip.result }} - WINDOWS_RESULT: ${{ needs.windows_zip.result }} - run: python ci/post/main.py nightly diff --git a/.github/workflows/build-release.yaml b/.github/workflows/build-release.yaml index 66a7734e5d..24d047c325 100644 --- a/.github/workflows/build-release.yaml +++ b/.github/workflows/build-release.yaml @@ -263,26 +263,3 @@ jobs: asset_path: ${{ steps.generate_package.outputs.debug_path }} asset_name: ${{ steps.generate_package.outputs.debug_name }} asset_content_type: ${{ steps.generate_package.outputs.debug_mime }} - - post_builds: - name: Post builds on Nebula and the forums - needs: [linux_zip, windows_zip] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - name: Checkout - with: - fetch-depth: '0' - - name: Install Python dependencies - run: pip install -r ci/post/requirements.txt - - name: Post builds - env: - INDIEGAMES_USER: ${{ secrets.INDIEGAMES_USER }} - INDIEGAMES_SSHPASS: ${{ secrets.INDIEGAMES_PASSWORD }} - NEBULA_USER: ${{ secrets.NEBULA_USER }} - NEBULA_PASSWORD: ${{ secrets.NEBULA_PASSWORD }} - HLP_API: ${{ secrets.HLP_API }} - HLP_KEY: ${{ secrets.HLP_KEY }} - LINUX_RESULT: ${{ needs.linux_zip.result }} - WINDOWS_RESULT: ${{ needs.windows_zip.result }} - run: python ci/post/main.py release diff --git a/.github/workflows/nightly-trigger.yml b/.github/workflows/nightly-trigger.yml deleted file mode 100644 index 85f79ce3b7..0000000000 --- a/.github/workflows/nightly-trigger.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Push a new nightly tag - -on: - schedule: - - cron: '0 6 * * *' - -jobs: - tag: - if: github.repository == 'scp-fs2open/fs2open.github.com' - runs-on: ubuntu-latest - steps: - - name: Prepare environment - run: | - git config --global user.email 'SirKnightlySCP@gmail.com' - git config --global user.name SirKnightly - - name: Checkout repo - uses: actions/checkout@v2 - - name: Create tag - run: git tag "nightly_$(date '+%Y%m%d')_$(git rev-parse --short HEAD)" - - name: Push tag - run: git push --tags diff --git a/ci/post/file_list.py b/ci/post/file_list.py deleted file mode 100644 index ec05ef3462..0000000000 --- a/ci/post/file_list.py +++ /dev/null @@ -1,128 +0,0 @@ -import re -from ftplib import FTP, error_perm -from typing import List, Tuple, Dict - -import requests - -from util import retry_multi, GLOBAL_TIMEOUT - -class ReleaseFile: - def __init__(self, name, url, group, subgroup=None, mirrors=None): - if mirrors is None: - mirrors = [] - self.mirrors = mirrors - self.subgroup = subgroup - self.group = group - self.url = url - self.name = name - - self.base_url = "/".join(url.split('/')[0:-1]) + "/" - self.filename = url.split('/')[-1] - - # A list of tuples of (filename, hash) - self.content_hashes = None - - self.hash = None - self.size = 0 - - -class SourceFile: - def __init__(self, name, url, group): - self.group = group - self.url = url - self.name = name - - -def get_release_files(tag_name, config) -> Tuple[List[ReleaseFile], Dict[str, SourceFile]]: - @retry_multi(5) - def execute_request(path): - headers = { - "Accept": "application/vnd.github.v3+json" - } - url = "https://api.github.com" + path - - response = requests.get(url, headers=headers, timeout=GLOBAL_TIMEOUT) - - response.raise_for_status() - - return response.json() - - build_group_regex = re.compile("fs2_open_.*-builds-([^.-]*)(-([^.]*))?.*") - source_file_regex = re.compile("fs2_open_.*-source-([^.]*)?.*") - - response = execute_request( - "/repos/{}/{}/releases/tags/{}".format(config["github"]["user"], config["github"]["repo"], tag_name)) - - binary_files = [] - source_files = {} - for asset in response["assets"]: - url = asset["browser_download_url"] - name = asset["name"] - - group_match = build_group_regex.match(name) - - if group_match is not None: - platform = group_match.group(1) - # x64 is the Visual Studio name but for consistency we need Win64 - if platform == "x64": - platform = "Win64" - - binary_files.append(ReleaseFile(name, url, platform, group_match.group(3))) - else: - group_match = source_file_regex.match(name) - - if group_match is None: - continue - - group = group_match.group(1) - - source_files[group] = SourceFile(name, url, group) - - return binary_files, source_files - - -def get_ftp_files(build_type, tag_name, config): - tag_regex = re.compile("nightly_(.*)") - build_group_regex = re.compile("nightly_.*-builds-([^.]+).*") - - files = [] - try: - with FTP(config["ftp"]["host"], config["ftp"]["user"], config["ftp"]["pass"]) as ftp: - version_str = tag_regex.match(tag_name).group(1) - - path_template = config["ftp"]["path"] - path = path_template.format(type=build_type, version=version_str) - file_entries = list(ftp.mlsd(path, ["type"])) - - for entry in file_entries: - if entry[1]["type"] == "file": - files.append(entry[0]) - except error_perm: - print("Received permanent FTP error!") - return [] - - out_data = [] - for file in files: - file_match = build_group_regex.match(file) - if file_match is None: - print("Ignoring non nightly file '{}'".format(file)) - continue - - group_match = file_match.group(1) - primary_url = None - mirrors = [] - - # x64 is the name Visual Studio uses but Win64 works better for us since that gets displayed in the nightly post - if "x64" in group_match: - group_match = group_match.replace("x64", "Win64") - - for mirror in config["ftp"]["mirrors"]: - download_url = mirror.format(type=build_type, version=version_str, file=file) - if primary_url is None: - primary_url = download_url - else: - mirrors.append(download_url) - - out_data.append(ReleaseFile(file, primary_url, group_match, None, mirrors)) - - return out_data \ No newline at end of file diff --git a/ci/post/forum.py b/ci/post/forum.py deleted file mode 100644 index 47ab499e1a..0000000000 --- a/ci/post/forum.py +++ /dev/null @@ -1,82 +0,0 @@ -from itertools import groupby -from typing import List - -import requests -import semantic_version -from mako.template import Template - -from file_list import ReleaseFile - - -class FileGroup: - def __init__(self, name, files: List[ReleaseFile]): - self.files = files - self.name = name - - if len(files) == 1: - self.mainFile = files[0] - self.subFiles = {} - else: - self.mainFile = None - subFiles = [] - for file in files: - # We only have subcategories for Windows where SSE2 is the main group - if file.subgroup == "SSE2": - self.mainFile = file - else: - subFiles.append(file) - - self.subFiles = dict(((x[0], next(x[1])) for x in groupby(subFiles, lambda f: f.subgroup))) - - - -class ForumAPI: - def __init__(self, config): - self.config = config - - def create_post(self, title, content, board): - resp = requests.post(self.config["hlp"]["api"], data={ - "api_key": self.config["hlp"]["key"], - "board": str(board), - "subject": title, - "body": content - }) - - if resp.text != "OK": - print("Post failed!") - - def post_nightly(self, date, revision, files, log, success): - print("Posting nightly thread...") - - title = "Nightly: {} - Revision {}".format(date, revision) - - template = Template(filename=self.config["templates"]["nightly"]) - rendered = template.render_unicode(**{ - "date": date, - "revision": revision, - "files": files, - "log": log, - "success": success - }) - - print("Creating post...") - self.create_post(title, rendered, self.config["nightly"]["hlp_board"]) - - def post_release(self, date, version: semantic_version.Version, groups, sources): - print("Posting release thread...") - - title = "Release: {}".format(version) - - template = Template( - filename=self.config["templates"]["release"].format(major=version.major, minor=version.minor), - module_directory='/tmp/mako_modules') - rendered = template.render_unicode(**{ - "date": date, - "version": version, - "groups": groups, - "sources": sources - }).strip("\n") - - print("Creating post...") - - self.create_post(title, rendered, self.config["release"]["hlp_board"]) diff --git a/ci/post/installer.py b/ci/post/installer.py deleted file mode 100644 index 5473f98073..0000000000 --- a/ci/post/installer.py +++ /dev/null @@ -1,88 +0,0 @@ -import os -import hashlib -import tarfile -import traceback -import zipfile -from tempfile import NamedTemporaryFile -from zipfile import ZipFile - -import requests -import sys -from mako.template import Template - -from file_list import ReleaseFile -from util import GLOBAL_TIMEOUT - - -def _download_file(url, dest_file, session): - print("Downloading " + url) - # NOTE the stream=True parameter - r = session.get(url, stream=True, timeout=GLOBAL_TIMEOUT) - for chunk in r.iter_content(chunk_size=1024): - if chunk: # filter out keep-alive new chunks - dest_file.write(chunk) - dest_file.flush() - - -def _gen_hash(fileobj, hash_alg): - h = hashlib.new(hash_alg) - while True: - data = fileobj.read(4096) - - if not data: - break - - h.update(data) - - return h.hexdigest() - - -def get_hashed_file_list(file: ReleaseFile, session: requests.Session = None, hash_alg: str = "sha256"): - if session is None: - session = requests.Session() - - with NamedTemporaryFile('w+b', suffix=file.filename) as local_file: - _download_file(file.url, local_file, session) - - filename = local_file.name - hash_list = [] - - local_file.seek(0) - file.hash = _gen_hash(local_file, hash_alg) - file.size = os.stat(filename).st_size - - try: - if tarfile.is_tarfile(filename): - with tarfile.open(filename) as archive: - for entry in archive: - if not entry.isfile(): - continue - - print("Computing hash for " + entry.name) - fileobj = archive.extractfile(entry) - hash_list.append((entry.path, _gen_hash(fileobj, hash_alg))) - elif zipfile.is_zipfile(filename): - with ZipFile(filename) as archive: - for entry in archive.infolist(): - # Python 3.6 has is_dir but that version is relatively new so we'll use the "safe" version here - if entry.filename.endswith('/'): - continue - - print("Computing hash for " + entry.filename) - with archive.open(entry) as fileobj: - hash_list.append((entry.filename, _gen_hash(fileobj, hash_alg))) - else: - raise NotImplementedError("Unsupported archive type!") - except: - traceback.print_exception(*sys.exc_info()) - return - - file.content_hashes = hash_list - - -def render_installer_config(version, groups, config): - template = Template(filename=config["templates"]["installer"], module_directory='/tmp/mako_modules') - return template.render(**{ - "version": version, - "groups": groups, - }).strip("\n") diff --git a/ci/post/main.py b/ci/post/main.py deleted file mode 100644 index 032ff9a066..0000000000 --- a/ci/post/main.py +++ /dev/null @@ -1,152 +0,0 @@ -import sys -import os -import re -from subprocess import check_output -from datetime import datetime -from itertools import groupby - -import semantic_version - -import file_list -import installer -import nebula -import forum - - -MAJOR_VERSION_PATTERN = re.compile("set_if_not_defined\(FSO_VERSION_MAJOR (\d+)\)") -MINOR_VERSION_PATTERN = re.compile("set_if_not_defined\(FSO_VERSION_MINOR (\d+)\)") -BUILD_VERSION_PATTERN = re.compile("set_if_not_defined\(FSO_VERSION_BUILD (\d+)\)") -LOG_FORMAT = """ -------------------------------------------------------------------------%n -commit %h%nAuthor: %an <%ad>%n -Commit: %cn <%cd>%n%n %s -""" -DATEFORMAT_VERSION = "%Y%m%d" -DATEFORMAT_FORUM = "%d %B %Y" - - -def _match_version_number(text, regex): - match = regex.search(text) - return int(match.group(1)) - - -def get_source_version(date_version): - with open(os.path.join("..", "..", "cmake", "version.cmake"), "r") as f: - filetext = f.read() - - major = _match_version_number(filetext, MAJOR_VERSION_PATTERN) - minor = _match_version_number(filetext, MINOR_VERSION_PATTERN) - build = _match_version_number(filetext, BUILD_VERSION_PATTERN) - - return semantic_version.Version("{}.{}.{}-{}".format(major, minor, build, date_version)) - - -def main(): - os.chdir(os.path.dirname(__file__)) - - config = { - "github": { - "user": "scp-fs2open", - "repo": "fs2open.github.com", - }, - "ftp": { - "host": "scp.indiegames.us", - "user": os.environ["INDIEGAMES_USER"], - "pass": os.environ["INDIEGAMES_SSHPASS"], - "path": "public_html/builds/{type}/{version}/", - "mirrors": ( - "https://porphyrion.feralhosting.com/datacorder/builds/{type}/{version}/{file}", - "http://scp.indiegames.us/builds/{type}/{version}/{file}", - ), - }, - "nebula": { - "user": os.environ["NEBULA_USER"], - "password": os.environ["NEBULA_PASSWORD"], - }, - "hlp": { - "api": os.environ["HLP_API"], - "key": os.environ["HLP_KEY"], - }, - "templates": { - "nightly": "nightly.mako", - "release": "release.mako", - }, - "nightly": { - "hlp_board": 173, - }, - "release": { - "hlp_board": 50, - }, - } - - if sys.argv[1] not in ("release", "nightly"): - print("ERROR: Invalid release mode %s passed. Expected release or nightly!" % sys.argv[1]) - sys.exit(1) - - tag_name = os.path.basename(os.environ["GITHUB_REF"]) - date = datetime.now() - version = get_source_version(date.strftime(DATEFORMAT_VERSION)) - success = os.environ["LINUX_RESULT"] == "success" and os.environ["WINDOWS_RESULT"] == "success" - - tags = check_output(("git", "for-each-ref", "--sort=-taggerdate", "--format", "%(tag)", "refs/tags")).splitlines() - previous_tag = None - found = False - - for tag in tags: - if found: - if tag.startswith(sys.argv[1] + "_"): - previous_tag = tag - break - elif tag == tag_name: - found = True - - if not found: - print("ERROR: Couldn't find tag %s in repo!" % tag_name) - sys.exit(1) - - if not previous_tag: - print("ERROR: Could not find a previous tag for %s!" % tag_name) - sys.exit(1) - - if sys.argv[1] == "release": - if not tag_name.startswith("release_"): - print("ERROR: Invalid tag name detected. Expected release_ prefix for release builds but found %s (full ref = %s)" % tag_name, os.environ["GITHUB_REF"]) - sys.exit(1) - - files, sources = file_list.get_release_files(tag_name, config) - - print("Hashing files") - for file in files: - installer.get_hashed_file_list(file) - - # Construct the file groups - groups = dict(((x[0], file_list.FileGroup(x[0], list(x[1]))) for x in groupby(files, lambda g: g.group))) - - print(installer.render_installer_config(version, groups, config)) - - nebula.submit_release( - nebula.render_nebula_release(version, "rc" if version.prerelease else "stable", "files", config), - config) - - fapi = forum.ForumAPI(config) - fapi.post_release(date.strftime(DATEFORMAT_FORUM), version, groups, sources) - else: - if not tag_name.startswith("nightly_"): - print("ERROR: Invalid tag name detected. Expected nightly_ prefix for nightly builds but found %s (full ref = %s)" % tag_name, os.environ["GITHUB_REF"]) - sys.exit(1) - - print("Retrieving file list") - files = file_list.get_ftp_files("nightly", tag_name, config) - - print("Hashing files") - for file in files: - installer.get_hashed_file_list(file) - - nebula.submit_release(nebula.render_nebula_release(version, "nightly", files, config), config) - - log = check_output(("git", "log", "%s^..%s^" % (previous_tag, tag_name), "--no-merges", "--stat", "--pretty=format:\"%s\"" % LOG_FORMAT.strip())) - - fapi = forum.ForumAPI(config) - fapi.post_nightly(date.strftime(DATEFORMAT_FORUM), os.environ["GITHUB_SHA"], files, log, success) - -main() diff --git a/ci/post/nebula.py b/ci/post/nebula.py deleted file mode 100644 index 91a3c515e1..0000000000 --- a/ci/post/nebula.py +++ /dev/null @@ -1,240 +0,0 @@ -import os.path -import sys -import traceback - -import requests - -from util import retry_multi, GLOBAL_TIMEOUT - -metadata = { - 'type': 'engine', - 'title': 'FSO', - 'notes': '', - 'banner': 'https://fsnebula.org/storage/0d/e7/bf64bcdea9a9c115969cfb784e1ca457d24a7c2da4fc6f213521c3bb6abb.png', - 'screenshots': [], - 'videos': [], - 'first_release': '2017-09-24', - 'cmdline': '', - 'mod_flag': ['FSO'], - 'tile': 'https://fsnebula.org/storage/ec/cc/0bf23e028c26d5175ff52d003bff85b0a17b0ddfc1130d65bdf6d36f6324.png', - 'logo': None, - 'release_thread': None, - 'description': '', - 'id': 'FSO', - 'packages': [], -} - -platforms = { - 'Linux': 'linux', - 'MacOSX': 'macosx', - 'Win32-SSE2': 'windows', - 'Win64-SSE2': 'windows', - 'Win32-AVX': 'windows', - 'Win64-AVX': 'windows' -} - -envs = { - 'Linux': 'linux && x86_64', # Linux only has 64bit builds - 'MacOSX': 'macosx', - 'Win32-SSE2': 'windows', - 'Win64-SSE2': 'windows && x86_64', - 'Win32-AVX': 'windows && avx', - 'Win64-AVX': 'windows && avx && x86_64' -} - -subdirs = { - 'Win32-SSE2': 'x86', - 'Win64-SSE2': 'x64', - 'Win32-AVX': 'x86_avx', - 'Win64-AVX': 'x64_avx' -} - - -def render_nebula_release(version, stability, files, config): - meta = metadata.copy() - meta['version'] = str(version) - meta['stability'] = stability # This can be one of ('stable', 'rc', 'nightly') - - for file in files: - if file.content_hashes is None: - # The extraction probably failed which is why we don't have any hashes - continue - - group = file.group - - # release.py sets subgroup but nightly.py doesn't which means we have to normalize group here. - if file.subgroup: - group += '-' + file.subgroup - - pkg = { - 'name': group, - 'notes': '', - 'is_vp': False, - 'files': [{ - 'dest': platforms[group] + '/' + subdirs.get(group, ''), - 'filesize': file.size, - 'checksum': ['sha256', file.hash], - 'urls': [file.url] + file.mirrors, - 'filename': file.name - }], - 'environment': envs.get(group), - 'filelist': [], - 'status': 'required', - 'folder': None, - 'dependencies': [], - 'executables': [] - } - - for fn, checksum in file.content_hashes: - if group in subdirs: - dest_fn = subdirs[group] + '/' + fn - else: - dest_fn = fn - - # If the group is a known platform then we put the binaries into a separate subfolder for each platform - if group in platforms: - dest_fn = platforms[group] + '/' + dest_fn - - pkg['filelist'].append({ - 'orig_name': fn, - 'checksum': ('sha256', checksum), - 'archive': file.name, - 'filename': dest_fn - }) - - if group == 'Linux' and fn.endswith('.AppImage'): - if 'qtfred' in fn: - if '-FASTDBG' in fn: - label = 'QtFRED Debug' - else: - label = 'QtFRED' - - else: - if '-FASTDBG' in fn: - label = 'Fast Debug' - else: - label = None - - props = { - "x64": True, # All Linux builds are 64-bit - "sse2": True, # Linux builds are forced to compile with SSE2 but not AVX - "avx": False, - "avx2": False, - } - - pkg['executables'].append({ - 'file': dest_fn, - 'label': label, - 'properties': props, - }) - elif group == 'MacOSX' and fn.startswith(os.path.basename(fn) + '.app/'): - if 'qtfred' in fn: - if '-FASTDBG' in fn: - label = 'QtFRED Debug' - else: - label = 'QtFRED' - else: - if '-FASTDBG' in fn: - label = 'Fast Debug' - else: - label = None - - props = { - "x64": True, # All Mac builds are 64-bit - "sse2": True, # There are no forced options on mac but 64-bit always implies SSE2 so we set that here - "avx": False, - "avx2": False, - } - - pkg['executables'].append({ - 'file': dest_fn, - 'label': label, - 'properties': props, - }) - elif group.startswith('Win') and fn.endswith('.exe'): - if 'fred2' in fn: - if '-FASTDBG' in fn: - label = 'FRED2 Debug' - else: - label = 'FRED2' - - elif 'qtfred' in fn: - if '-FASTDBG' in fn: - label = 'QtFRED Debug' - else: - label = 'QtFRED' - - else: - if '-FASTDBG' in fn: - label = 'Fast Debug' - else: - label = None - - props = { - "x64": "x64" in fn, - "sse2": "SSE2" in fn or "AVX" in fn, # AVX implies SSE2 - "avx": "AVX" in fn, # This conveniently also covers the AVX2 case since AVX2 implies AVX - "avx2": "AVX2" in fn, - } - - pkg['executables'].append({ - 'file': dest_fn, - 'label': label, - 'properties': props, - }) - - meta["packages"].append(pkg) - - return meta - - -@retry_multi(5) -def nebula_request(session, kind, path, **kwargs): - uri = "https://fsnebula.org/api/1/{}".format(path) - - request_args = { - "timeout": GLOBAL_TIMEOUT - } - request_args.update(kwargs) - - return session.request(kind, uri, **request_args) - - -def submit_release(meta, config): - try: - with requests.Session() as session: - print('Logging into Nebula...') - result = nebula_request(session, 'post', 'login', data={ - 'user': config['nebula']['user'], - 'password': config['nebula']['password'] - }) - - if result.status_code != 200: - print('Login failed!') - return False - - data = result.json() - if not data['result']: - print('Login failed!') - return False - - print('Submitting release...') - result = nebula_request(session, 'post', 'mod/release', headers={ - 'X-KN-TOKEN': data['token'] - }, json=meta) - - if result.status_code != 200: - print('Request failed!') - return False - - data = result.json() - if not data['result']: - print('ERROR: ' + data['reason']) - return False - - print('Success!') - return True - except Exception: - exc_type, exc_value, exc_traceback = sys.exc_info() - traceback.print_exception(exc_type, exc_value, exc_traceback) - return False diff --git a/ci/post/nightly.mako b/ci/post/nightly.mako deleted file mode 100644 index 23c10ea726..0000000000 --- a/ci/post/nightly.mako +++ /dev/null @@ -1,27 +0,0 @@ -Here is the nightly for ${date} - Revision ${revision} - -% if not success: -[b][color=red]At least one of the nightly builds failed![/color][/b] -% endif -% for file in files: - - -Group: ${file.group} -[url=${file.url}]${file.name}[/url] \ - %for mirror in file.mirrors: - %if loop.first: -(\ - %endif -[url=${mirror}]Mirror[/url]\ - %if loop.last: -)\ - %else: -, \ - %endif - %endfor - -% endfor - -[code] -${log} -[/code] diff --git a/ci/post/release.mako b/ci/post/release.mako deleted file mode 100644 index 186231cb56..0000000000 --- a/ci/post/release.mako +++ /dev/null @@ -1,68 +0,0 @@ - -<%def name="build(file)">[url=${file.url}]${file.filename}[/url] - -[b]Change log:[/b] (chronologically ordered) -PLACEHOLDER - -[b]Deprecations:[/b] -PLACEHOLDER - -Deprecations are a mechanism in FSO where a certain feature or aspect of the engine has changed or is no longer supported. Since this would normally break existing mods we have the mod table feature "[tt]$Target Version:[/tt]" with which a mod can specify what version of FSO it was developed with. The features listed above will be removed or changed when the target version of a mod is at least the version released in this post. - -[size=5pt]Previous [url=https://www.hard-light.net/forums/index.php?topic=97293.0]21.0 Release Thread[/url][/size] - -Launchers, if you don't have one already: -[b]All platforms: [/b] For every day use, we recommend [url=https://www.hard-light.net/forums/index.php?topic=94068.0]Knossos[/url], an integrated solution for downloading and launching mods. - -[hidden=Alternative Launchers] -Cross-platform: [url=http://www.hard-light.net/forums/index.php?topic=89162]wxLauncher 0.12.x Test Build[/url] (ongoing project for a unified launcher, you should upgrade to the latest RC/test build if you have not yet) -[b]Important:[/b] For best compatibility with FSO 3.8 and later you should use at least wxLauncher 0.12. - -Windows: [url=http://scp.fsmods.net/files/Launcher55g.zip]Launcher 5.5g[/url] ([url=http://scp.indiegames.us/builds/Launcher55g.zip]Mirror[/url]) ([url=http://www.mediafire.com/?wdvzn7hhhzh418m]Mirror[/url]) Not compatible with Windows 8+, use wxLauncher above -OS X: Soulstorm's [url=http://www.hard-light.net/forums/index.php/topic,51391.0.html]OS X Launcher 3.0[/url] -Linux: [url=http://www.hard-light.net/forums/index.php/topic,53206.0.html]YAL[/url] or [url=http://www.hard-light.net/wiki/index.php/Fs2_open_on_Linux/Graphics_Settings]by hand[/url] or whatever you can figure out.[/hidden] - -[img]http://scp.indiegames.us/img/windows-icon.png[/img] [color=green][size=12pt]Windows (32/64-bit)[/size][/color] -[size=8pt]Compiled using GitHub Actions on Windows Server 2019 (10.0.17763), Visual Studio Enterprise 2019[/size] - -[b]64-bit:[/b] ${build(groups["Win64"].mainFile)} - -[b]32-bit:[/b] ${build(groups["Win32"].mainFile)} -[size=8pt]This one is based on the SSE2 Optimizations from the MSVC Compiler.[/size] - -[hidden=Alternative builds] - -[b]64-bit AVX:[/b] ${build(groups["Win64"].subFiles["AVX"])} -[size=8pt]This one is based on the AVX Optimizations from the MSVC Compiler (fastest build if your CPU supports AVX instructions).[/size] - - -[b]32-bit AVX:[/b] ${build(groups["Win32"].subFiles["AVX"])} -[size=8pt]This one is based on the AVX Optimizations from the MSVC Compiler.[/size] - -[b]What are those SSE, SSE2 and AVX builds I keep seeing everywhere?[/b] -[url=http://www.hard-light.net/forums/index.php?topic=65628.0]Your answer is in this topic.[/url] -Don't want to deal with that? Use [url=https://www.hard-light.net/forums/index.php?topic=94068.0]Knossos[/url] and it will download the best build specifically for your PC! -[/hidden] - -[img]http://scp.indiegames.us/img/linux-icon.png[/img] [color=green][size=12pt]Linux 64-bit[/size][/color] -[size=8pt]Compiled with Ubuntu 16.04 LTS 64-bit, GCC 5[/size] -${build(groups["Linux"].mainFile)} - -These builds use a mechanism called [url=http://appimage.org/]AppImage[/url] which should allow these builds to run on most Linux distributions. However, we recommend that you compile your own builds which will result in less issues. -Alternatively, if there is a package in your software repository then you should use that. If you are the maintainer of such a package for a distribution then let us know and we will include that here. - - -[img]http://scp.indiegames.us/img/mac-icon.png[/img] [color=green][size=12pt]OS X[/size][/color] -[b][color=red]Not available[/color][/b] We recently lost access to our Mac CI environment which we usually used for compiling these builds so for the time being, there will be no builds for this OS. - -[hidden=TrackIR Users] -[size=12pt]Important!![/size] -An external DLL is required for FSO to use TrackIR functions. The following DLL is simply unpacked in to your main FreeSpace2 root dir. -TrackIR is only supported on Windows. -[url=http://www.mediafire.com/download.php?4zw024zrh44etse]TrackIR SCP DLL[/url] ([url=http://scp.fsmods.net/builds/scptrackir.zip]Mirror[/url]) ([url=http://scp.indiegames.us/builds/scptrackir.zip]Mirror[/url])[/hidden] - -Known issues: -[list] -[li][url=https://github.com/scp-fs2open/fs2open.github.com/issues]Github issues[/url] and [url=https://github.com/scp-fs2open/fs2open.github.com/pulls]pending pull requests[/url][/li] -[li]See the list of [url=http://scp.indiegames.us/mantis/search.php?project_id=1&status_id%5B%5D=10&status_id%5B%5D=20&status_id%5B%5D=30&status_id%5B%5D=40&status_id%5B%5D=50&priority_id%5B%5D=40&priority_id%5B%5D=50&priority_id%5B%5D=60&sticky_issues=on&sortby=last_updated&dir=DESC&per_page=200&hide_status_id=-2]Fix for next release[/url] bugs - mark a bug as an elevated priority (high, urgent, immediate) to get it included in that filter.[/li] -[/list] diff --git a/ci/post/requirements.txt b/ci/post/requirements.txt deleted file mode 100644 index 491e6a8cb5..0000000000 --- a/ci/post/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -Mako==1.1.5 -requests==2.26.0 -semantic-version==2.8.5 diff --git a/ci/post/util.py b/ci/post/util.py deleted file mode 100644 index c2522772cb..0000000000 --- a/ci/post/util.py +++ /dev/null @@ -1,37 +0,0 @@ -import os -import time -from functools import wraps - -from requests import RequestException - -GLOBAL_TIMEOUT = 30 - - -def retry_multi(max_retries): - """ - Retry a function `max_retries` times. - This has been copied from https://stackoverflow.com/a/23892489 - """ - - def retry(func): - @wraps(func) - def wrapper(*args, **kwargs): - num_retries = 0 - while num_retries <= max_retries: - try: - ret = func(*args, **kwargs) - break - except RequestException: - if num_retries == max_retries: - raise - num_retries += 1 - time.sleep(5) - return ret - - return wrapper - - return retry - - -def expand_config_vars(config): - config["git"]["repo"] = os.path.expandvars(config["git"]["repo"])