Update tests and clean up packaging util

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