Files
UmbraEdit/utils/main.py
B.D. Murphy 70d74e675f chore: rename project from novelWriter to UmbraEdit
- Rename project name and package: novelWriter -> UmbraEdit, novelwriter -> umbraedit
- Rename entry script novelWriter.py -> UmbraEdit.py
- Rename package directory novelwriter/ -> umbraedit/
- Rename i18n translation sources nw_*.ts -> ue_*.ts
- Rename packaging files (appdata, project xml template)
- Update all imports, references, docs, workflows, tests and build config
- Update pyproject.toml project name, GUI script and URLs
- Preserve full upstream git history; origin repointed below
2026-11-05 07:08:12 -08:00

273 lines
11 KiB
Python
Executable File

#!/usr/bin/env python3
"""
UmbraEdit - Packaging Utils Main
==================================
This file is a part of UmbraEdit.
Copyright (C) 2019 Veronica Berglyd Olsen and UmbraEdit contributors.
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 3 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, see <https://www.gnu.org/licenses/>.
""" # noqa
from __future__ import annotations
import argparse
import datetime
import shutil
import sys
import utils.assets
import utils.build_appimage
import utils.build_binary
import utils.build_debian
import utils.build_flatpak
import utils.build_pypi
import utils.build_windows
import utils.docs
import utils.icon_themes
from utils.common import (
ROOT_DIR,
SETUP_DIR,
extractReqs,
extractVersion,
isStableVersion,
log,
readFile,
stripVersion,
writeFile,
)
OS_LINUX = sys.platform.startswith("linux")
OS_DARWIN = sys.platform.startswith("darwin")
OS_WIN = sys.platform.startswith("win32")
def printVersion(args: argparse.Namespace) -> None:
"""Print the UmbraEdit version and exit."""
print(extractVersion(beQuiet=True)[0], end=None)
def printChannel(args: argparse.Namespace) -> None:
"""Print 'stable' or 'pre' depending on the release channel, and exit."""
print("stable" if isStableVersion() else "pre", end=None)
def cleanBuildDirs(args: argparse.Namespace) -> None:
"""Recursively delete the 'build' and 'dist' folders."""
log("")
log("[b]Cleaning up build environment ...[e]")
log("")
folders = [
ROOT_DIR / ".flatpak-builder",
ROOT_DIR / "build_bin",
ROOT_DIR / "build",
ROOT_DIR / "dist_appimage",
ROOT_DIR / "dist_bin",
ROOT_DIR / "dist_deb",
ROOT_DIR / "dist_doc",
ROOT_DIR / "dist_flathub",
ROOT_DIR / "dist_flatpak",
ROOT_DIR / "dist_pypi",
ROOT_DIR / "dist_upload",
ROOT_DIR / "dist",
ROOT_DIR / "UmbraEdit.egg-info",
]
for folder in folders:
if folder.is_dir():
try:
shutil.rmtree(folder)
log(f"[cg]Deleted:[e] {folder}")
except OSError:
log(f"[cr]Failed:[e] {folder}")
else:
log(f"[cy]Missing:[e] {folder}")
log("")
def genMacOSPlist(args: argparse.Namespace) -> None:
"""Set necessary values for .plist file for MacOS build."""
outDir = SETUP_DIR / "macos"
numVers = stripVersion(extractVersion()[0])
copyrightYear = datetime.datetime.now().year
# These keys are no longer used but are present for compatibility
pkgVersMaj, pkgVersMin = numVers.split(".")[:2]
plistXML = readFile(outDir / "Info.plist.template").format(
macosBundleSVers=numVers,
macosBundleVers=numVers,
macosBundleVersMajor=pkgVersMaj,
macosBundleVersMinor=pkgVersMin,
macosBundleCopyright=f"Copyright {copyrightYear}, Veronica Berglyd Olsen",
)
log(f"[b]Writing Info.plist to {outDir}/Info.plist[e]")
writeFile(outDir / "Info.plist", plistXML)
def genReqFiles(args: argparse.Namespace) -> None:
"""Generate requirements.txt file from pyproject.toml."""
select = [s.strip().lower() for s in args.groups] if args.groups else ["app"]
(ROOT_DIR / "requirements.txt").write_text("\n".join(extractReqs(select)), encoding="utf-8")
def main() -> None:
"""Parse command line options and run the commands."""
parser = argparse.ArgumentParser(
usage="pkgutils.py [command] [--flags]",
description=(
"This tool provides setup and build commands for installing or distributing "
"UmbraEdit as a package on Linux, Mac and Windows, as well as developer tools "
"for internationalisation."
),
)
parsers = parser.add_subparsers()
# Version
cmdVersion = parsers.add_parser("version", help="Print the UmbraEdit version.")
cmdVersion.set_defaults(func=printVersion)
# Release Channel
cmdChannel = parsers.add_parser("channel", help="Print 'stable' or 'pre' depending on the release channel.")
cmdChannel.set_defaults(func=printChannel)
# Additional Builds
# =================
# Build Icons
styles = ", ".join(["all", "default", "optional", "free", "non_free", *utils.icon_themes.ICON_SOURCES.keys()])
cmdIcons = parsers.add_parser("icons", help="Build icon theme files from upstream sources.")
cmdIcons.add_argument("style", help=f"What icon style to build: {styles}")
cmdIcons.add_argument("--work-dir", help="Working directory.")
cmdIcons.set_defaults(func=utils.icon_themes.main)
# Import Translations
cmdImportTS = parsers.add_parser("qtlimport", help="Import updated i18n files from a Crowdin zip file.")
cmdImportTS.add_argument("file", help="Path to zip file from Crowdin")
cmdImportTS.add_argument("--threshold", type=float, default=75.0, help="Minimum translation percentage.")
cmdImportTS.add_argument("--credits-since", metavar="DATE", help="Print translator credits since DATE.")
cmdImportTS.set_defaults(func=utils.assets.importI18nUpdates)
# Update i18n Sources
cmdUpdateTS = parsers.add_parser("qtlupdate", help="Update translation files for internationalisation.")
cmdUpdateTS.add_argument("files", nargs="+")
cmdUpdateTS.set_defaults(func=utils.assets.updateTranslationSources)
# Build i18n Files
cmdBuildQM = parsers.add_parser("qtlrelease", help="Build the language files for internationalisation.")
cmdBuildQM.set_defaults(func=utils.assets.buildTranslationAssets)
# Update Docs i18n Sources
cmdUpdateDocsPo = parsers.add_parser("docs-lupdate", help="Update translation files for the docs.")
cmdUpdateDocsPo.add_argument("lang", nargs="+")
cmdUpdateDocsPo.set_defaults(func=utils.docs.updateDocsTranslationSources)
# Build PDF Docs
cmdBuildPdfDocs = parsers.add_parser("docs-pdf", help="Build the PDF manual files.")
cmdBuildPdfDocs.add_argument("lang", nargs="+")
cmdBuildPdfDocs.set_defaults(func=utils.docs.buildPdfDocAssets)
# Build HTML Docs
cmdBuildHtmlDocs = parsers.add_parser("docs-html", help="Build the HTML docs.")
cmdBuildHtmlDocs.add_argument("lang", nargs="+", help="Language codes to generate docs for.")
cmdBuildHtmlDocs.set_defaults(func=utils.docs.buildHtmlDocs)
# Build Sample
cmdBuildSample = parsers.add_parser("sample", help="Build the sample project zip file and add it to assets.")
cmdBuildSample.set_defaults(func=utils.assets.buildSampleZip)
# Clean Assets
cmdCleanAssets = parsers.add_parser("clean-assets", help="Delete assets built by docs-pdf, sample and qtlrelease.")
cmdCleanAssets.set_defaults(func=utils.assets.cleanBuiltAssets)
# Build Assets
cmdBuildAssets = parsers.add_parser("build-assets", help="Build all assets (except icon themes).")
cmdBuildAssets.set_defaults(func=utils.assets.buildAllAssets)
# Python Packaging
# ================
# Build Debian Package
distros = ", ".join(utils.build_debian.DISTRO_TARGETS.keys())
cmdBuildDeb = parsers.add_parser("build-deb", help="Build .deb packages for publishing.")
cmdBuildDeb.add_argument("distro", help=f"Release to build for: {distros}.")
cmdBuildDeb.add_argument("--sign", action="store_true", help="Sign the package.")
cmdBuildDeb.add_argument("--build", type=int, help="Set build number, appended to the distro suffix.")
cmdBuildDeb.add_argument("--install-source", help="Override the install source in meta.toml.")
cmdBuildDeb.set_defaults(func=utils.build_debian.debian)
# Print Debian Build Dependencies
cmdDebDepends = parsers.add_parser("build-deb-depends", help="Print the apt package dependencies.")
cmdDebDepends.add_argument("distro", help=f"Release to build for: {distros}.")
cmdDebDepends.set_defaults(func=utils.build_debian.printDebDepends)
# Build AppImage
# See https://github.com/pypa/manylinux
# See https://python-appimage.readthedocs.io/en/latest/#available-python-appimages
cmdBuildAppImage = parsers.add_parser("build-appimage", help="Build an AppImage.")
cmdBuildAppImage.add_argument("linux", help="Manylinux version, e.g. manylinux_2_28")
cmdBuildAppImage.add_argument("arch", help="Architecture, e.g. x86_64")
cmdBuildAppImage.add_argument("python", help="Python version, e.g. 3.13")
cmdBuildAppImage.set_defaults(func=utils.build_appimage.appImage)
# Build Flatpak
cmdBuildFlatpak = parsers.add_parser("build-flatpak", help="Build a Flatpak image for direct download.")
cmdBuildFlatpak.set_defaults(func=utils.build_flatpak.flatpak)
# Build Flathub Submission Files
cmdBuildFlathub = parsers.add_parser("build-flathub", help="Generate manifest and support files for a Flathub.")
cmdBuildFlathub.add_argument("path", nargs="?", help="Path to copy the generated files into.")
cmdBuildFlathub.set_defaults(func=utils.build_flatpak.flathub)
# Build PyPI Packages
cmdBuildPypi = parsers.add_parser("build-pypi", help="Build sdist and wheel packages for PyPI.")
cmdBuildPypi.set_defaults(func=utils.build_pypi.pypi)
# Build Windows Inno Setup Installer
cmdBuildSetupExe = parsers.add_parser("build-win-exe", help="Build a setup.exe installer for Windows.")
cmdBuildSetupExe.set_defaults(func=utils.build_windows.main)
# Build Binary
cmdBuildBinary = parsers.add_parser("build-bin", help="Build a standalone binary package.")
cmdBuildBinary.set_defaults(func=utils.build_binary.main)
# Build Clean
cmdBuildClean = parsers.add_parser("build-clean", help="Recursively delete all build folders.")
cmdBuildClean.set_defaults(func=cleanBuildDirs)
# Generate MacOS PList File
cmdGenMacOSPlist = parsers.add_parser("gen-plist", help="Generate an Info.plist for use in a MacOS Bundle.")
cmdGenMacOSPlist.set_defaults(func=genMacOSPlist)
# Generate Requirement File
cmdGenReq = parsers.add_parser("gen-req", help="Generate a requirements.txt file for pip.")
cmdGenReq.add_argument(
"groups",
nargs="*",
help=(
"Groups to generate for, or 'all' to generate for all groups. "
"Use 'app' to generate for just the core application. "
"Defaults to 'app' if none are specified."
),
)
cmdGenReq.set_defaults(func=genReqFiles)
args = parser.parse_args()
args.func(args)