Skip to content

Commit

Permalink
NXDRIVE-1931: Check the auto-updater against the latest release befor…
Browse files Browse the repository at this point in the history
…e building a new version

It is not yet enabled as the current GA (4.2.0)
doest not handle auto-update without account
and, on macOS only, cannot be started from
$HOME/Applications.

Let's wait for the next GA to come first.
  • Loading branch information
Mickaël Schoentgen authored and BoboTiG committed Dec 5, 2019
1 parent 49a02da commit 31bd90b
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 63 deletions.
2 changes: 1 addition & 1 deletion docs/changes/4.3.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Release date: `2019-xx-xx`

## Packaging / Build

- [NXDRIVE-](https://jira.nuxeo.com/browse/NXDRIVE-):
- [NXDRIVE-1931](https://jira.nuxeo.com/browse/NXDRIVE-1931): Check the auto-updater against the latest release before building a new version

## Tests

Expand Down
221 changes: 159 additions & 62 deletions tools/scripts/check_update_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,13 @@
from os.path import expanduser, expandvars
from pathlib import Path

__version__ = "1.1.0"
import requests
import yaml

# Alter the lookup path to be able to find Nuxeo Drive sources
sys.path.insert(0, os.getcwd())

__version__ = "2.0.0"

EXT = {"darwin": "dmg", "linux": "appimage", "win32": "exe"}[sys.platform]
Server = http.server.SimpleHTTPRequestHandler
Expand Down Expand Up @@ -78,6 +84,28 @@ def create_versions(dst, version):
versions.write(yml)


def download_last_ga_release(output_dir, version):
""" Download the latest GA release from the update website. """

file = f"nuxeo-drive-{version}"
if EXT == "appimage":
file += "-x86_64.AppImage"
else:
file += f".{EXT}"

url = f"https://community.nuxeo.com/static/drive-updates/release/{file}"
output = os.path.join(output_dir, "alpha", file)
print(">>> Downloading", url, "->", output, flush=True)

with requests.get(url) as req, open(output, "wb") as dst:
dst.write(req.content)

# Adjust execution rights
subprocess.check_call(["chmod", "a+x", output])

return output


def gen_exe():
""" Generate an executable to install. """

Expand All @@ -97,6 +125,20 @@ def gen_exe():
subprocess.check_call(cmd.split())


def get_last_version_number():
""" Get the lastest GA release version from the update website. """

from nxdrive.updater.utils import get_latest_version

url = "https://community.nuxeo.com/static/drive-updates/versions.yml"
print(">>> Donwloading", url, flush=True)
with requests.get(url) as req:
data = req.content

versions = yaml.safe_load(data)
return get_latest_version(versions, "release")


def get_version():
""" Get the current version from the auto-generated VERSION file. """

Expand Down Expand Up @@ -161,13 +203,18 @@ def launch_drive(executable, args=None):
]

print(">>> Command:", cmd, flush=True)
return subprocess.check_output(cmd).decode("utf-8").strip()
try:
subprocess.check_call(cmd)
except subprocess.CalledProcessError:
# Either this is bad and OK the process will handle the error later;
# either this is an error at the end of the app exit, and it is OK too.
pass


def save_log(output):
def save_log(output, name):
"""Save log files for job archives."""

dst = os.path.join(output, f"nxdrive-{EXT}.log")
dst = os.path.join(output, f"nxdrive-{EXT}-{name}.log")

if EXT == "exe":
src = expandvars("C:\\Users\\%username%\\.nuxeo-drive\\logs\\nxdrive.log")
Expand All @@ -176,12 +223,12 @@ def save_log(output):

try:
os.remove(dst)
print(">>> Deleting", repr(dst), flush=True)
print(">>> Deleted", repr(dst), flush=True)
except FileNotFoundError:
pass
try:
shutil.copyfile(src, dst)
print(">>> Copying", repr(src), "->", repr(dst), flush=True)
print(">>> Copied", repr(src), "->", repr(dst), flush=True)
except FileNotFoundError:
pass

Expand Down Expand Up @@ -308,7 +355,9 @@ def version_find():
with open(path, encoding="utf-8") as handler:
for lineno, line in enumerate(handler.readlines()):
if line.startswith("__version__"):
return re.findall(r'"(.+)"', line)[0], lineno
version = re.findall(r'"(.+)"', line)[0]
print(">>> Current version is", version, "at line", lineno, flush=True)
return version, lineno


def version_update(version, lineno):
Expand Down Expand Up @@ -350,78 +399,95 @@ def stop(server):
pass


def main():
""" Main logic. """
#
# Functions used by main()
# (or put like this: this is main() splited into smaller functions :)
#

# Cleanup
if os.path.isdir("dist"):
shutil.rmtree("dist")

# Remove previous installation
uninstall_drive()
def check_against_me(root):
"""Check the auto-updater against itself."""
version, lineno = version_find()

src = os.getcwd()
# Guess the anterior version
previous = version_decrement(version)

# Server tree
root = tempfile.mkdtemp()
path = os.path.join(root, "alpha")
os.makedirs(path)
try:
# Update the version in Drive code source to emulate an old version
version_update(previous, lineno)
assert version_find() == (previous, lineno)

# Set the sync-and-stop option to let Drive update and quit without manual action
set_options()
exe = gen_and_move(root, previous)

# Generate the current version executable
version, lineno = version_find()
print(">>> Current version is", version, "at line", lineno, flush=True)
# And gooo!
job(root, version, exe, previous, "dev")
finally:
# Restore the original version
version_update(version, lineno)


def check_against_last_release(root):
"""Check the auto-updater against the latest GA release."""

version, _ = version_find()

# Get the version number
ga_version = get_last_version_number()

# Get the latest GA release file
last_ga = download_last_ga_release(root, ga_version)

# Append the versions.yml file
create_versions(root, ga_version)

# And gooo!
job(root, version, last_ga, ga_version, "ga")


def gen_and_move(root, version):
""" Generate the installer for a given version and move it to the web server root. """

# Generate the installer
gen_exe()

# Move the file to the webserver
ext = "-x86_64.AppImage" if EXT == "appimage" else f".{EXT}"
file = f"dist/nuxeo-drive-{version}{ext}"
dst_file = os.path.join(path, os.path.basename(file))
print(">>> Moving", file, "->", path, flush=True)
dst_file = os.path.join(root, "alpha", os.path.basename(file))
print(">>> Moving", file, "->", dst_file, flush=True)
shutil.move(file, dst_file)

version_forced = os.getenv("FORCE_USE_LATEST_VERSION", False)
if not version_forced:
# Where the account will be bound
local_folder = os.path.join(root, "folder")

# Guess the anterior version
previous = version_decrement(version)

# Create the versions.yml file
# Create, or append to, the versions.yml file
create_versions(root, version)

try:
# Update the version in Drive code source to emulate an old version
version_update(previous, lineno)
assert version_find() == (previous, lineno)
return dst_file

# Generate the executable
gen_exe()

# Move the file to test to the webserver
ext = "-x86_64.AppImage" if EXT == "appimage" else f".{EXT}"
src_file = f"dist/nuxeo-drive-{previous}{ext}"
installer = os.path.basename(src_file)
dst_file = os.path.join(path, installer)
print(">>> Moving", src_file, "->", path, flush=True)
shutil.move(src_file, dst_file)
def job(root, version, executable, previous_version, name):
"""Repetive tasks.
*name* is a string to customize the log file to archive.
"""

# Append the versions.yml file
create_versions(root, previous)
src = os.getcwd()

try:
# Install Drive on the computer
install_drive(dst_file)
install_drive(executable)

# Set the sync-and-stop option to let Drive update and quit without manual action
set_options()

version_forced = os.getenv("FORCE_USE_LATEST_VERSION", "0") == "1"
if not version_forced:
# Where the account will be bound
local_folder = os.path.join(root, "folder")

# Add an account to be able to auto-update
url = os.getenv("NXDRIVE_TEST_NUXEO_URL", "http://localhost:8080/nuxeo")
username = os.getenv("NXDRIVE_TEST_USERNAME", "Administrator")
password = os.getenv("NXDRIVE_TEST_PASSWORD", "Administrator")
launch_drive(
dst_file,
executable,
[
"bind-server",
username,
Expand All @@ -432,14 +498,14 @@ def main():
)

# Launch Drive in its own thread
print(">>> Testing upgrade", previous, "->", version, flush=True)
threading.Thread(target=launch_drive, args=(dst_file,)).start()
print(">>> Testing upgrade", previous_version, "->", version, flush=True)
threading.Thread(target=launch_drive, args=(executable,)).start()

# Start the web server
webserver(root)

# Save the log file for job archives
save_log(src)
save_log(src, name)

# And assert the version is the good one
current_ver = get_version()
Expand All @@ -450,25 +516,56 @@ def main():
finally:
os.chdir(src)

# Restore the original version
version_update(version, lineno)

if not version_forced:
# Remove the account
try:
launch_drive(dst_file, ["clean-folder", f"--local-folder={root}"])
launch_drive(executable, ["clean-folder", f"--local-folder={root}"])
except Exception as exc:
print(" !! ERROR:", exc, flush=True)

# Remove the installation
uninstall_drive()


def setup():
""" Setup and cleanup. """

# Cleanup
if os.path.isdir("dist"):
shutil.rmtree("dist")

# Remove previous installation
uninstall_drive()

# Server tree
root = tempfile.mkdtemp()
path = os.path.join(root, "alpha")
os.makedirs(path)

return root


def main():
""" Main logic. """

root = setup()

# Generate the current version executable
version, _ = version_find()
gen_and_move(root, version)

try:
check_against_me(root)
# To enable on all OS when 4.4.0 is GA
# check_against_last_release(root)
finally:
# Cleanup
try:
shutil.rmtree(root)
except Exception as exc:
print(" !! ERROR:", exc, flush=True)

uninstall_drive()


if __name__ == "__main__":
tests()
exit(main())
sys.exit(main())

0 comments on commit 31bd90b

Please sign in to comment.