mirror of
https://github.com/hykilpikonna/hyfetch.git
synced 2026-06-20 14:09:57 -05:00
272 lines
8.8 KiB
Python
Executable File
272 lines
8.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import argparse
|
|
import json
|
|
import os
|
|
import re
|
|
import shlex
|
|
import stat
|
|
import subprocess
|
|
import urllib.error
|
|
import urllib.parse
|
|
import urllib.request
|
|
from pathlib import Path
|
|
|
|
from packaging import version as pv
|
|
|
|
from tools.list_distros import generate_help
|
|
from tools.reformat_readme import reformat_readme
|
|
|
|
NEOFETCH_NEW_VERSION = ""
|
|
FASTFETCH_RELEASE_API = 'https://api.github.com/repos/fastfetch-cli/fastfetch/releases'
|
|
FASTFETCH_ASSETS = [
|
|
'fastfetch-windows-amd64.zip',
|
|
'fastfetch-linux-amd64.zip',
|
|
'fastfetch-linux-aarch64.zip',
|
|
'fastfetch-linux-armv7l.zip',
|
|
'fastfetch-musl-amd64.zip',
|
|
'fastfetch-macos-amd64.zip',
|
|
'fastfetch-macos-aarch64.zip',
|
|
]
|
|
RELEASE_FILES = [
|
|
'Cargo.lock',
|
|
'Cargo.toml',
|
|
'README.md',
|
|
'docs/hyfetch.1',
|
|
'docs/neofetch.1',
|
|
'hyfetch/__version__.py',
|
|
'neofetch',
|
|
'package.json',
|
|
'tools/build_pkg.sh',
|
|
]
|
|
|
|
|
|
def pre_check():
|
|
"""
|
|
Check source code status before releasing.
|
|
"""
|
|
assert os.path.isfile('./neofetch'), './neofetch doesn\'t exist, you are running this script in the wrong directory'
|
|
assert os.stat('./neofetch').st_mode & stat.S_IEXEC, 'neofetch is not executable'
|
|
assert os.path.islink('./hyfetch/scripts/neowofetch'), 'neowofetch is not a symbolic link'
|
|
assert not subprocess.check_output(['git', 'status', '--porcelain']).strip(), \
|
|
'Please commit or stash all changes before release'
|
|
|
|
print('Running shellcheck... (This may take a while)')
|
|
subprocess.check_call(shlex.split('shellcheck neofetch'))
|
|
|
|
|
|
def edit_versions(version: str):
|
|
"""
|
|
Edit version numbers in hyfetch/constants.py, package.json, and README.md
|
|
|
|
Also edits version number of neofetch, but the neofetch version number is separate.
|
|
|
|
:param version: Version to release
|
|
"""
|
|
# 1. package.json
|
|
print('Editing package.json...')
|
|
path = Path('package.json')
|
|
content = json.loads(path.read_text())
|
|
cur = pv.parse(content['version'])
|
|
assert cur < pv.parse(version), 'Version did not increase'
|
|
content['version'] = version
|
|
path.write_text(json.dumps(content, ensure_ascii=False, indent=2))
|
|
|
|
# 2. hyfetch/constants.py
|
|
print('Editing hyfetch/__version__.py...')
|
|
path = Path('hyfetch/__version__.py')
|
|
content = [f"VERSION = '{version}'" if l.startswith('VERSION = ') else l for l in path.read_text().split('\n')]
|
|
path.write_text('\n'.join(content))
|
|
|
|
# 3. Cargo.toml
|
|
print('Editing Cargo.toml...')
|
|
path = Path('Cargo.toml')
|
|
content = path.read_text()
|
|
content = re.sub(r'(?<=^version = ")[^"]+(?="$)', version, content, flags=re.MULTILINE)
|
|
path.write_text(content)
|
|
|
|
print('Updating Cargo.lock...')
|
|
subprocess.check_call(['cargo', 'metadata', '--format-version', '1'], stdout=subprocess.DEVNULL)
|
|
|
|
# 4. README.md
|
|
print('Editing README.md...')
|
|
path = Path('README.md')
|
|
content = path.read_text()
|
|
changelog_token = '<!-- CHANGELOG STARTS HERE --->'
|
|
changelog_i = content.index(changelog_token) + len(changelog_token)
|
|
content = content[:changelog_i] + f'\n\n### {version}' + content[changelog_i:]
|
|
path.write_text(content)
|
|
|
|
# 5. neofetch script
|
|
print('Editing neofetch...')
|
|
path = Path('neofetch')
|
|
lines = path.read_text().replace("\t", " ").split('\n')
|
|
version_i = next(i for i, l in enumerate(lines) if l.startswith('version='))
|
|
nf = pv.parse(lines[version_i].replace('version=', ''))
|
|
new = pv.parse(version)
|
|
nf = f'{nf.major + new.major - cur.major}.{nf.minor + new.minor - cur.minor}.{nf.micro + new.micro - cur.micro}'
|
|
lines[version_i] = f"version={nf}"
|
|
path.write_text('\n'.join(lines))
|
|
|
|
global NEOFETCH_NEW_VERSION
|
|
NEOFETCH_NEW_VERSION = nf
|
|
|
|
|
|
def fetch_fastfetch_release(release: str) -> dict:
|
|
"""
|
|
Fetch fastfetch release metadata from GitHub.
|
|
"""
|
|
if release == 'latest':
|
|
url = f'{FASTFETCH_RELEASE_API}/latest'
|
|
else:
|
|
tag = urllib.parse.quote(release, safe='')
|
|
url = f'{FASTFETCH_RELEASE_API}/tags/{tag}'
|
|
|
|
request = urllib.request.Request(url, headers={
|
|
'Accept': 'application/vnd.github+json',
|
|
'User-Agent': 'hyfetch-release-script',
|
|
})
|
|
|
|
try:
|
|
with urllib.request.urlopen(request, timeout=30) as response:
|
|
return json.loads(response.read().decode())
|
|
except urllib.error.HTTPError as e:
|
|
if e.code == 404 and release.startswith('v'):
|
|
return fetch_fastfetch_release(release[1:])
|
|
raise
|
|
|
|
|
|
def update_fastfetch_version(release: str) -> str:
|
|
"""
|
|
Update the fastfetch binary version embedded in Python wheels.
|
|
"""
|
|
print(f'Checking fastfetch {release}...')
|
|
metadata = fetch_fastfetch_release(release)
|
|
tag = metadata['tag_name']
|
|
assets = {asset['name'] for asset in metadata['assets']}
|
|
missing = sorted(set(FASTFETCH_ASSETS) - assets)
|
|
assert not missing, f'Fastfetch {tag} is missing required release assets: {", ".join(missing)}'
|
|
|
|
print(f'Editing tools/build_pkg.sh fastfetch version to {tag}...')
|
|
path = Path('tools/build_pkg.sh')
|
|
content = path.read_text()
|
|
content = re.sub(r'(?<=^FASTFETCH_VERSION=")[^"]+(?="$)', tag, content, flags=re.MULTILINE)
|
|
path.write_text(content)
|
|
return tag
|
|
|
|
|
|
def finalize_neofetch():
|
|
"""
|
|
Finalize current version
|
|
"""
|
|
# 1. Update distro list
|
|
print('Updating distro list in neofetch...')
|
|
path = Path('neofetch')
|
|
content = path.read_text()
|
|
content = re.compile(r'(?<=# Flag: --ascii_distro\n#\n).*?(?=ascii_distro=)', re.DOTALL)\
|
|
.sub(generate_help(100, '# ') + '\n', content)
|
|
content = re.compile(r"""(?<=Which Distro's ascii art to print\n\n).*?{distro}_small to use them\.""", re.DOTALL)\
|
|
.sub(generate_help(100, ' ' * 32), content)
|
|
path.write_text(content)
|
|
|
|
# 2. Regenerate man page
|
|
print('Regenerating neofetch man page...')
|
|
Path('docs/neofetch.1').write_text(subprocess.check_output(['help2man', './neofetch']).decode())
|
|
Path('docs/hyfetch.1').write_text(subprocess.check_output(['help2man', 'cargo run --']).decode())
|
|
|
|
# 3. Reformat readme links
|
|
print('Reformatting readme links...')
|
|
reformat_readme()
|
|
|
|
|
|
def post_check():
|
|
"""
|
|
Check after changes are made
|
|
"""
|
|
print('Running shellcheck... (This may take a while)')
|
|
subprocess.check_call(shlex.split('shellcheck neofetch'))
|
|
|
|
|
|
def create_release(v: str):
|
|
"""
|
|
Create release commit and tag
|
|
"""
|
|
print('Committing changes...')
|
|
|
|
# 1. Add files
|
|
subprocess.check_call(['git', 'add', *RELEASE_FILES])
|
|
|
|
# 2. Commit
|
|
subprocess.check_call(['git', 'commit', '-m', f'[U] Release {v}'])
|
|
|
|
# 3. Create tag
|
|
subprocess.check_call(['git', 'tag', v])
|
|
subprocess.check_call(['git', 'tag', f'neofetch-{NEOFETCH_NEW_VERSION}'])
|
|
|
|
i = input('Please check the commit is correct. Press y to continue or any other key to cancel.')
|
|
if i.lower() != 'y':
|
|
print('Aborting...')
|
|
subprocess.check_call(['git', 'tag', '-d', v])
|
|
subprocess.check_call(['git', 'tag', '-d', f'neofetch-{NEOFETCH_NEW_VERSION}'])
|
|
subprocess.check_call(['git', 'reset', '--soft', 'HEAD~1'])
|
|
print('Release commit was undone with changes preserved in the index.')
|
|
exit(1)
|
|
|
|
# 4. Push
|
|
print('Pushing commits...')
|
|
subprocess.check_call(['git', 'push'])
|
|
subprocess.check_call(['git', 'push', 'origin', v, f'neofetch-{NEOFETCH_NEW_VERSION}'])
|
|
|
|
|
|
def deploy():
|
|
"""
|
|
Deploy release to pip and npm
|
|
"""
|
|
print('Deploying to pypi...')
|
|
subprocess.check_call(['bash', 'tools/deploy.sh'])
|
|
print('Done!')
|
|
|
|
print('Deploying to crates.io...')
|
|
subprocess.check_call(['bash', 'tools/deploy-crate.sh'])
|
|
print('Done!')
|
|
|
|
print('Deploying to npm...')
|
|
otp = input('Please provide 2FA OTP for NPM: ')
|
|
subprocess.check_call(['npm', 'publish', '--otp', otp])
|
|
print('Done!')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser(description='HyFetch Release Utility')
|
|
parser.add_argument('version', help='Version to release')
|
|
parser.add_argument(
|
|
'--fastfetch-version',
|
|
default='latest',
|
|
help='Fastfetch release tag to embed in Python wheels, or "latest" (default).',
|
|
)
|
|
parser.add_argument(
|
|
'--skip-fastfetch-update',
|
|
action='store_true',
|
|
help='Keep the existing FASTFETCH_VERSION in tools/build_pkg.sh.',
|
|
)
|
|
parser.add_argument(
|
|
'--local-deploy',
|
|
action='store_true',
|
|
help='Publish from this machine after pushing tags. By default, GitHub Actions publishes the release.',
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
pre_check()
|
|
edit_versions(args.version)
|
|
if not args.skip_fastfetch_update:
|
|
update_fastfetch_version(args.fastfetch_version)
|
|
|
|
finalize_neofetch()
|
|
post_check()
|
|
create_release(args.version)
|
|
|
|
if args.local_deploy:
|
|
deploy()
|
|
else:
|
|
print('Release tag pushed. GitHub Actions will create the GitHub Release and publish packages.')
|