Update tests and clean up packaging util
This commit is contained in:
+89
-95
@@ -23,6 +23,7 @@ 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/>.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
@@ -42,7 +43,7 @@ OS_DARWIN = 3
|
||||
# Utilities
|
||||
# =============================================================================================== #
|
||||
|
||||
def extractVersion(beQuiet=False):
|
||||
def extractVersion(beQuiet: bool = False) -> tuple[str, str, str]:
|
||||
"""Extract the novelWriter version number without having to import
|
||||
anything else from the main package.
|
||||
"""
|
||||
@@ -85,29 +86,19 @@ def stripVersion(version: str) -> str:
|
||||
return version
|
||||
|
||||
|
||||
def sysCall(callArgs, cwd=None):
|
||||
"""Wrapper function for system calls."""
|
||||
sysP = subprocess.Popen(
|
||||
callArgs, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
shell=True, cwd=cwd
|
||||
)
|
||||
stdOut, stdErr = sysP.communicate()
|
||||
return stdOut.decode("utf-8"), stdErr.decode("utf-8"), sysP.returncode
|
||||
|
||||
|
||||
def readFile(fileName):
|
||||
def readFile(fileName: str) -> str:
|
||||
"""Read an entire file and return as a string."""
|
||||
with open(fileName, mode="r", encoding="utf-8") as inFile:
|
||||
return inFile.read()
|
||||
|
||||
|
||||
def writeFile(fileName, writeText):
|
||||
def writeFile(fileName: str, writeText: str) -> None:
|
||||
"""Write string to file."""
|
||||
with open(fileName, mode="w+", encoding="utf-8") as outFile:
|
||||
outFile.write(writeText)
|
||||
|
||||
|
||||
def toUpload(srcPath, dstName=None):
|
||||
def toUpload(srcPath: str, dstName: str | None = None) -> None:
|
||||
"""Copy a file produced by one of the build functions to the upload
|
||||
directory. The file can optionally be given a new name.
|
||||
"""
|
||||
@@ -120,7 +111,7 @@ def toUpload(srcPath, dstName=None):
|
||||
return
|
||||
|
||||
|
||||
def makeCheckSum(sumFile, cwd=None):
|
||||
def makeCheckSum(sumFile: str, cwd: str | None = None) -> str:
|
||||
"""Create a SHA256 checksum file."""
|
||||
try:
|
||||
if cwd is None:
|
||||
@@ -146,7 +137,7 @@ def makeCheckSum(sumFile, cwd=None):
|
||||
# Package Installer (pip)
|
||||
##
|
||||
|
||||
def installPackages(hostOS):
|
||||
def installPackages(hostOS: int) -> None:
|
||||
"""Install package dependencies both for this script and for running
|
||||
novelWriter itself.
|
||||
"""
|
||||
@@ -179,7 +170,7 @@ def installPackages(hostOS):
|
||||
# Clean Build and Dist Folders (build-clean)
|
||||
##
|
||||
|
||||
def cleanBuildDirs():
|
||||
def cleanBuildDirs() -> None:
|
||||
"""Recursively delete the 'build' and 'dist' folders."""
|
||||
print("")
|
||||
print("Cleaning up build environment ...")
|
||||
@@ -215,7 +206,7 @@ def cleanBuildDirs():
|
||||
# Build PDF Manual (manual)
|
||||
##
|
||||
|
||||
def buildPdfManual():
|
||||
def buildPdfManual() -> None:
|
||||
"""This function will build the documentation as manual.pdf."""
|
||||
print("")
|
||||
print("Building PDF Manual")
|
||||
@@ -269,7 +260,7 @@ def buildPdfManual():
|
||||
# Qt Linguist QM Builder (qtlrelease)
|
||||
##
|
||||
|
||||
def buildQtI18n():
|
||||
def buildQtI18n() -> None:
|
||||
"""Build the lang.qm files for Qt Linguist."""
|
||||
print("")
|
||||
print("Building Qt Localisation Files")
|
||||
@@ -322,7 +313,7 @@ def buildQtI18n():
|
||||
# Qt Linguist TS Builder (qtlupdate)
|
||||
##
|
||||
|
||||
def buildQtI18nTS(sysArgs):
|
||||
def buildQtI18nTS(sysArgs: list[str]) -> None:
|
||||
"""Build the lang.ts files for Qt Linguist."""
|
||||
print("")
|
||||
print("Building Qt Translation Files")
|
||||
@@ -402,7 +393,7 @@ def buildQtI18nTS(sysArgs):
|
||||
# Generate MacOS PList
|
||||
##
|
||||
|
||||
def genMacOSPlist():
|
||||
def genMacOSPlist() -> None:
|
||||
"""Set necessary values for .plist file for MacOS build."""
|
||||
outDir = "setup/macos"
|
||||
numVers = stripVersion(extractVersion()[0])
|
||||
@@ -429,7 +420,7 @@ def genMacOSPlist():
|
||||
# Sample Project ZIP File Builder (sample)
|
||||
##
|
||||
|
||||
def buildSampleZip():
|
||||
def buildSampleZip() -> None:
|
||||
"""Bundle the sample project into a single zip file to be saved into
|
||||
the novelwriter/assets folder for further bundling into builds.
|
||||
"""
|
||||
@@ -466,7 +457,7 @@ def buildSampleZip():
|
||||
return
|
||||
|
||||
|
||||
def cleanBuiltAssets():
|
||||
def cleanBuiltAssets() -> None:
|
||||
"""Remove assets built by this script."""
|
||||
print("")
|
||||
print("Removing Built Assets")
|
||||
@@ -495,7 +486,7 @@ def cleanBuiltAssets():
|
||||
return
|
||||
|
||||
|
||||
def checkAssetsExist():
|
||||
def checkAssetsExist() -> bool:
|
||||
"""Check that the necessary compiled assets exist ahead of a build.
|
||||
"""
|
||||
hasSample = False
|
||||
@@ -530,7 +521,7 @@ def checkAssetsExist():
|
||||
# Import Translations (import-i18n)
|
||||
##
|
||||
|
||||
def importI18nUpdates(sysArgs):
|
||||
def importI18nUpdates(sysArgs: list[str]) -> None:
|
||||
"""Import new translation files from a zip file."""
|
||||
print("")
|
||||
print("Import Updated Translations")
|
||||
@@ -570,7 +561,7 @@ def importI18nUpdates(sysArgs):
|
||||
# Make Minimal Package (minimal-zip)
|
||||
##
|
||||
|
||||
def makeMinimalPackage(targetOS):
|
||||
def makeMinimalPackage(targetOS: int) -> None:
|
||||
"""Pack the core source file in a single zip file."""
|
||||
from zipfile import ZipFile, ZIP_DEFLATED
|
||||
|
||||
@@ -685,7 +676,8 @@ def makeMinimalPackage(targetOS):
|
||||
# Make Debian Package (build-deb)
|
||||
##
|
||||
|
||||
def makeDebianPackage(signKey=None, sourceBuild=False, distName="unstable", buildName=""):
|
||||
def makeDebianPackage(signKey: str | None = None, sourceBuild: bool = False,
|
||||
distName: str = "unstable", buildName: str = "") -> str:
|
||||
"""Build a Debian package."""
|
||||
print("")
|
||||
print("Build Debian Package")
|
||||
@@ -858,14 +850,14 @@ def makeDebianPackage(signKey=None, sourceBuild=False, distName="unstable", buil
|
||||
# Make Launchpad Package (build-ubuntu)
|
||||
##
|
||||
|
||||
def makeForLaunchpad(doSign=False, isFirst=False, isSnapshot=False):
|
||||
def makeForLaunchpad(sign: bool = False, first: bool = False, snapshot: bool = False) -> None:
|
||||
"""Wrapper for building Debian packages for Launchpad."""
|
||||
print("")
|
||||
print("Launchpad Packages")
|
||||
print("==================")
|
||||
print("")
|
||||
|
||||
if isFirst or isSnapshot:
|
||||
if first or snapshot:
|
||||
bldNum = "0"
|
||||
else:
|
||||
bldNum = input("Build number [0]: ")
|
||||
@@ -880,7 +872,7 @@ def makeForLaunchpad(doSign=False, isFirst=False, isSnapshot=False):
|
||||
]
|
||||
|
||||
tStamp = datetime.datetime.now().strftime("%Y%m%d~%H%M%S")
|
||||
if isSnapshot:
|
||||
if snapshot:
|
||||
print(f"Building Ununtu SNAPSHOT~{tStamp} for:")
|
||||
print("")
|
||||
else:
|
||||
@@ -890,7 +882,7 @@ def makeForLaunchpad(doSign=False, isFirst=False, isSnapshot=False):
|
||||
print(f" * Ubuntu {distNum} {codeName.title()}")
|
||||
print("")
|
||||
|
||||
if doSign:
|
||||
if sign:
|
||||
signKey = "D6A9F6B8F227CF7C6F6D1EE84DBBE4B734B0BD08"
|
||||
else:
|
||||
signKey = None
|
||||
@@ -900,7 +892,7 @@ def makeForLaunchpad(doSign=False, isFirst=False, isSnapshot=False):
|
||||
|
||||
dputCmd = []
|
||||
for distNum, codeName in distLoop:
|
||||
if isSnapshot:
|
||||
if snapshot:
|
||||
buildName = f"+SNAPSHOT~{tStamp}~ubuntu{distNum}.0"
|
||||
else:
|
||||
buildName = f"~ubuntu{distNum}.{bldNum}"
|
||||
@@ -927,7 +919,7 @@ def makeForLaunchpad(doSign=False, isFirst=False, isSnapshot=False):
|
||||
# Make AppImage (build-appimage)
|
||||
##
|
||||
|
||||
def makeAppImage(sysArgs):
|
||||
def makeAppImage(sysArgs: list[str]) -> list[str]:
|
||||
"""Build an AppImage."""
|
||||
import glob
|
||||
import argparse
|
||||
@@ -1143,7 +1135,7 @@ def makeAppImage(sysArgs):
|
||||
# Make Windows Setup EXE (build-win-exe)
|
||||
##
|
||||
|
||||
def makeWindowsEmbedded(sysArgs):
|
||||
def makeWindowsEmbedded(sysArgs: list[str]) -> None:
|
||||
"""Set up a package with embedded Python and dependencies for
|
||||
Windows installation.
|
||||
"""
|
||||
@@ -1375,7 +1367,7 @@ def makeWindowsEmbedded(sysArgs):
|
||||
# XDG Installation (xdg-install)
|
||||
##
|
||||
|
||||
def xdgInstall():
|
||||
def xdgInstall() -> None:
|
||||
"""Will attempt to install icons and make a launcher."""
|
||||
print("")
|
||||
print("XDG Install")
|
||||
@@ -1511,7 +1503,7 @@ def xdgInstall():
|
||||
# XDG Uninstallation (xdg-uninstall)
|
||||
##
|
||||
|
||||
def xdgUninstall():
|
||||
def xdgUninstall() -> None:
|
||||
"""Will attempt to uninstall icons and the launcher."""
|
||||
print("")
|
||||
print("XDG Uninstall")
|
||||
@@ -1581,7 +1573,7 @@ def xdgUninstall():
|
||||
# WIN Installation (win-install)
|
||||
##
|
||||
|
||||
def winInstall():
|
||||
def winInstall() -> None:
|
||||
"""Will attempt to install icons and make a launcher for Windows."""
|
||||
import winreg
|
||||
try:
|
||||
@@ -1708,7 +1700,7 @@ def winInstall():
|
||||
# WIN Uninstallation (win-uninstall)
|
||||
##
|
||||
|
||||
def winUninstall():
|
||||
def winUninstall() -> None:
|
||||
"""Will attempt to uninstall icons previously installed."""
|
||||
import winreg
|
||||
try:
|
||||
@@ -1809,36 +1801,38 @@ if __name__ == "__main__":
|
||||
else:
|
||||
hostOS = OS_NONE
|
||||
|
||||
sysArgs = sys.argv.copy()
|
||||
|
||||
# Set Target OS
|
||||
if "--target-linux" in sys.argv:
|
||||
sys.argv.remove("--target-linux")
|
||||
if "--target-linux" in sysArgs:
|
||||
sysArgs.remove("--target-linux")
|
||||
targetOS = OS_LINUX
|
||||
elif "--target-darwin" in sys.argv:
|
||||
sys.argv.remove("--target-darwin")
|
||||
elif "--target-darwin" in sysArgs:
|
||||
sysArgs.remove("--target-darwin")
|
||||
targetOS = OS_DARWIN
|
||||
elif "--target-win" in sys.argv:
|
||||
sys.argv.remove("--target-win")
|
||||
elif "--target-win" in sysArgs:
|
||||
sysArgs.remove("--target-win")
|
||||
targetOS = OS_WIN
|
||||
else:
|
||||
targetOS = hostOS
|
||||
|
||||
# Sign package
|
||||
if "--sign" in sys.argv:
|
||||
sys.argv.remove("--sign")
|
||||
if "--sign" in sysArgs:
|
||||
sysArgs.remove("--sign")
|
||||
doSign = True
|
||||
else:
|
||||
doSign = False
|
||||
|
||||
# First build
|
||||
if "--first" in sys.argv:
|
||||
sys.argv.remove("--first")
|
||||
if "--first" in sysArgs:
|
||||
sysArgs.remove("--first")
|
||||
isFirstBuild = True
|
||||
else:
|
||||
isFirstBuild = False
|
||||
|
||||
# Build snapshot
|
||||
if "--snapshot" in sys.argv:
|
||||
sys.argv.remove("--snapshot")
|
||||
if "--snapshot" in sysArgs:
|
||||
sysArgs.remove("--snapshot")
|
||||
isSnapshot = True
|
||||
else:
|
||||
isSnapshot = False
|
||||
@@ -1909,66 +1903,66 @@ if __name__ == "__main__":
|
||||
# General
|
||||
# =======
|
||||
|
||||
if "help" in sys.argv:
|
||||
sys.argv.remove("help")
|
||||
if "help" in sysArgs:
|
||||
sysArgs.remove("help")
|
||||
print("\n".join(helpMsg))
|
||||
sys.exit(0)
|
||||
|
||||
if "version" in sys.argv:
|
||||
sys.argv.remove("version")
|
||||
if "version" in sysArgs:
|
||||
sysArgs.remove("version")
|
||||
print(extractVersion(beQuiet=True)[0], end=None)
|
||||
sys.exit(0)
|
||||
|
||||
if "pip" in sys.argv:
|
||||
sys.argv.remove("pip")
|
||||
if "pip" in sysArgs:
|
||||
sysArgs.remove("pip")
|
||||
installPackages(hostOS)
|
||||
|
||||
if "build-clean" in sys.argv:
|
||||
sys.argv.remove("build-clean")
|
||||
if "build-clean" in sysArgs:
|
||||
sysArgs.remove("build-clean")
|
||||
cleanBuildDirs()
|
||||
|
||||
# Additional Builds
|
||||
# =================
|
||||
|
||||
if "manual" in sys.argv:
|
||||
sys.argv.remove("manual")
|
||||
if "manual" in sysArgs:
|
||||
sysArgs.remove("manual")
|
||||
buildPdfManual()
|
||||
|
||||
if "qtlrelease" in sys.argv:
|
||||
sys.argv.remove("qtlrelease")
|
||||
if "qtlrelease" in sysArgs:
|
||||
sysArgs.remove("qtlrelease")
|
||||
buildQtI18n()
|
||||
|
||||
if "qtlupdate" in sys.argv:
|
||||
sys.argv.remove("qtlupdate")
|
||||
buildQtI18nTS(sys.argv)
|
||||
if "qtlupdate" in sysArgs:
|
||||
sysArgs.remove("qtlupdate")
|
||||
buildQtI18nTS(sysArgs)
|
||||
sys.exit(0) # Don't continue execution
|
||||
|
||||
if "sample" in sys.argv:
|
||||
sys.argv.remove("sample")
|
||||
if "sample" in sysArgs:
|
||||
sysArgs.remove("sample")
|
||||
buildSampleZip()
|
||||
|
||||
if "clean-assets" in sys.argv:
|
||||
sys.argv.remove("clean-assets")
|
||||
if "clean-assets" in sysArgs:
|
||||
sysArgs.remove("clean-assets")
|
||||
cleanBuiltAssets()
|
||||
|
||||
if "gen-plist" in sys.argv:
|
||||
sys.argv.remove("gen-plist")
|
||||
if "gen-plist" in sysArgs:
|
||||
sysArgs.remove("gen-plist")
|
||||
genMacOSPlist()
|
||||
|
||||
# Python Packaging
|
||||
# ================
|
||||
|
||||
if "import-i18n" in sys.argv:
|
||||
sys.argv.remove("import-i18n")
|
||||
importI18nUpdates(sys.argv)
|
||||
if "import-i18n" in sysArgs:
|
||||
sysArgs.remove("import-i18n")
|
||||
importI18nUpdates(sysArgs)
|
||||
sys.exit(0) # Don't continue execution
|
||||
|
||||
if "minimal-zip" in sys.argv:
|
||||
sys.argv.remove("minimal-zip")
|
||||
if "minimal-zip" in sysArgs:
|
||||
sysArgs.remove("minimal-zip")
|
||||
makeMinimalPackage(targetOS)
|
||||
|
||||
if "build-deb" in sys.argv:
|
||||
sys.argv.remove("build-deb")
|
||||
if "build-deb" in sysArgs:
|
||||
sysArgs.remove("build-deb")
|
||||
if hostOS == OS_LINUX:
|
||||
if doSign:
|
||||
signKey = "D6A9F6B8F227CF7C6F6D1EE84DBBE4B734B0BD08"
|
||||
@@ -1979,23 +1973,23 @@ if __name__ == "__main__":
|
||||
print("ERROR: Command 'build-deb' can only be used on Linux")
|
||||
sys.exit(1)
|
||||
|
||||
if "build-ubuntu" in sys.argv:
|
||||
sys.argv.remove("build-ubuntu")
|
||||
if "build-ubuntu" in sysArgs:
|
||||
sysArgs.remove("build-ubuntu")
|
||||
if hostOS == OS_LINUX:
|
||||
makeForLaunchpad(doSign=doSign, isFirst=isFirstBuild, isSnapshot=isSnapshot)
|
||||
makeForLaunchpad(sign=doSign, first=isFirstBuild, snapshot=isSnapshot)
|
||||
else:
|
||||
print("ERROR: Command 'build-ubuntu' can only be used on Linux")
|
||||
sys.exit(1)
|
||||
|
||||
if "build-win-exe" in sys.argv:
|
||||
sys.argv.remove("build-win-exe")
|
||||
makeWindowsEmbedded(sys.argv)
|
||||
if "build-win-exe" in sysArgs:
|
||||
sysArgs.remove("build-win-exe")
|
||||
makeWindowsEmbedded(sysArgs)
|
||||
sys.exit(0) # Don't continue execution
|
||||
|
||||
if "build-appimage" in sys.argv:
|
||||
sys.argv.remove("build-appimage")
|
||||
if "build-appimage" in sysArgs:
|
||||
sysArgs.remove("build-appimage")
|
||||
if hostOS == OS_LINUX:
|
||||
sys.argv = makeAppImage(sys.argv)
|
||||
sysArgs = makeAppImage(sysArgs)
|
||||
else:
|
||||
print("ERROR: Command 'build-appimage' can only be used on Linux")
|
||||
sys.exit(1)
|
||||
@@ -2003,32 +1997,32 @@ if __name__ == "__main__":
|
||||
# General Installers
|
||||
# ==================
|
||||
|
||||
if "xdg-install" in sys.argv:
|
||||
sys.argv.remove("xdg-install")
|
||||
if "xdg-install" in sysArgs:
|
||||
sysArgs.remove("xdg-install")
|
||||
if hostOS == OS_WIN:
|
||||
print("ERROR: Command 'xdg-install' cannot be used on Windows")
|
||||
sys.exit(1)
|
||||
else:
|
||||
xdgInstall()
|
||||
|
||||
if "xdg-uninstall" in sys.argv:
|
||||
sys.argv.remove("xdg-uninstall")
|
||||
if "xdg-uninstall" in sysArgs:
|
||||
sysArgs.remove("xdg-uninstall")
|
||||
if hostOS == OS_WIN:
|
||||
print("ERROR: Command 'xdg-uninstall' cannot be used on Windows")
|
||||
sys.exit(1)
|
||||
else:
|
||||
xdgUninstall()
|
||||
|
||||
if "win-install" in sys.argv:
|
||||
sys.argv.remove("win-install")
|
||||
if "win-install" in sysArgs:
|
||||
sysArgs.remove("win-install")
|
||||
if hostOS == OS_WIN:
|
||||
winInstall()
|
||||
else:
|
||||
print("ERROR: Command 'win-install' can only be used on Windows")
|
||||
sys.exit(1)
|
||||
|
||||
if "win-uninstall" in sys.argv:
|
||||
sys.argv.remove("win-uninstall")
|
||||
if "win-uninstall" in sysArgs:
|
||||
sysArgs.remove("win-uninstall")
|
||||
if hostOS == OS_WIN:
|
||||
winUninstall()
|
||||
else:
|
||||
|
||||
@@ -35,7 +35,7 @@ from PyQt5.QtCore import QUrl
|
||||
from novelwriter.common import (
|
||||
checkBool, checkFloat, checkHandle, checkInt, checkIntTuple, checkPath,
|
||||
checkString, checkStringNone, checkUuid, formatInt, formatTime,
|
||||
formatTimeStamp, fuzzyTime, getFileSize, hexToInt, isHandle, isItemClass,
|
||||
formatTimeStamp, formatVersion, fuzzyTime, getFileSize, hexToInt, isHandle, isItemClass,
|
||||
isItemLayout, isItemType, isTitleTag, jsonEncode, makeFileNameSafe, minmax,
|
||||
numberToRoman, NWConfigParser, openExternalPath, readTextFile, simplified,
|
||||
transferCase, xmlIndent, yesNo
|
||||
@@ -348,6 +348,17 @@ def testBaseCommon_formatTime():
|
||||
# END Test testBaseCommon_formatTime
|
||||
|
||||
|
||||
@pytest.mark.base
|
||||
def testBaseCommon_formatVersion():
|
||||
"""Test the formatVersion function."""
|
||||
assert formatVersion("1.2") == "1.2"
|
||||
assert formatVersion("1.2a1") == "1.2 Alpha 1"
|
||||
assert formatVersion("1.2b2") == "1.2 Beta 2"
|
||||
assert formatVersion("1.2rc3") == "1.2 RC 3"
|
||||
|
||||
# END Test testBaseCommon_formatVersion
|
||||
|
||||
|
||||
@pytest.mark.base
|
||||
def testBaseCommon_simplified():
|
||||
"""Test the simplified function."""
|
||||
|
||||
Reference in New Issue
Block a user