From 18952c57ff649a52a8f6eee9bebac5f48842eedc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 14 May 2014 12:59:57 +0200 Subject: [PATCH 1/4] Added automatic migration of old event subscription format to the new one (cherry picked from commit e7088ef) --- src/octoprint/events.py | 101 ++++++++++++++++-------------- src/octoprint/server/__init__.py | 5 +- src/octoprint/settings.py | 103 ++++++++++++++++++++++++++++--- 3 files changed, 150 insertions(+), 59 deletions(-) diff --git a/src/octoprint/events.py b/src/octoprint/events.py index f9216b06..6b332454 100644 --- a/src/octoprint/events.py +++ b/src/octoprint/events.py @@ -203,36 +203,42 @@ class DebugEventListener(GenericEventListener): class CommandTrigger(GenericEventListener): - def __init__(self, triggerType, printer): + def __init__(self, printer): GenericEventListener.__init__(self) self._printer = printer self._subscriptions = {} - self._initSubscriptions(triggerType) + self._initSubscriptions() - def _initSubscriptions(self, triggerType): + def _initSubscriptions(self): """ Subscribes all events as defined in "events > $triggerType > subscriptions" in the settings with their respective commands. """ - if not settings().get(["events", triggerType]): + if not settings().get(["events"]): return - if not settings().getBoolean(["events", triggerType, "enabled"]): + if not settings().getBoolean(["events", "enabled"]): return eventsToSubscribe = [] - for subscription in settings().get(["events", triggerType, "subscriptions"]): - if not "event" in subscription.keys() or not "command" in subscription.keys(): - self._logger.info("Invalid %s, missing either event or command: %r" % (triggerType, subscription)) + for subscription in settings().get(["events", "subscriptions"]): + if not "event" in subscription.keys() or not "command" in subscription.keys() \ + or not "type" in subscription.keys() or not subscription["type"] in ["system", "gcode"]: + self._logger.info("Invalid command trigger, missing either event, type or command or type is invalid: %r" % subscription) + continue + + if "enabled" in subscription.keys() and not subscription["enabled"]: + self._logger.info("Disabled command trigger: %r" % subscription) continue event = subscription["event"] command = subscription["command"] + commandType = subscription["type"] if not event in self._subscriptions.keys(): self._subscriptions[event] = [] - self._subscriptions[event].append(command) + self._subscriptions[event].append((command, commandType)) if not event in eventsToSubscribe: eventsToSubscribe.append(event) @@ -250,18 +256,48 @@ class CommandTrigger(GenericEventListener): if not event in self._subscriptions: return - for command in self._subscriptions[event]: + for command, commandType in self._subscriptions[event]: try: - processedCommand = self._processCommand(command, payload) - self.executeCommand(processedCommand) + if isinstance(command, (tuple, list, set)): + processedCommand = [] + for c in command: + processedCommand.append(self._processCommand(c, payload)) + else: + processedCommand = self._processCommand(command, payload) + self.executeCommand(processedCommand, commandType) except KeyError, e: self._logger.warn("There was an error processing one or more placeholders in the following command: %s" % command) - def executeCommand(self, command): - """ - Not implemented, override in child classes - """ - pass + def executeCommand(self, command, commandType): + if commandType == "system": + self._executeSystemCommand(command) + elif commandType == "gcode": + self._executeGcodeCommand(command) + + def _executeSystemCommand(self, command): + def commandExecutioner(command): + self._logger.info("Executing system command: %s" % command) + subprocess.Popen(command, shell=True) + + try: + if isinstance(command, (list, tuple, set)): + for c in command: + commandExecutioner(c) + else: + commandExecutioner(command) + except subprocess.CalledProcessError, e: + self._logger.warn("Command failed with return code %i: %s" % (e.returncode, e.message)) + except Exception, ex: + self._logger.exception("Command failed") + + def _executeGcodeCommand(self, command): + commands = [command] + if isinstance(command, (list, tuple, set)): + self.logger.debug("Executing GCode commands: %r" % command) + commands = list(command) + else: + self._logger.debug("Executing GCode command: %s" % command) + self._printer.commands(commands) def _processCommand(self, command, payload): """ @@ -302,34 +338,3 @@ class CommandTrigger(GenericEventListener): params.update(payload) return command.format(**params) - - -class SystemCommandTrigger(CommandTrigger): - """ - Performs configured system commands for configured events. - """ - - def __init__(self, printer): - CommandTrigger.__init__(self, "systemCommandTrigger", printer) - - def executeCommand(self, command): - try: - self._logger.info("Executing system command: %s" % command) - subprocess.Popen(command, shell=True) - except subprocess.CalledProcessError, e: - self._logger.warn("Command failed with return code %i: %s" % (e.returncode, e.message)) - except Exception, ex: - self._logger.exception("Command failed") - - -class GcodeCommandTrigger(CommandTrigger): - """ - Sends configured GCODE commands to the printer for configured events. - """ - - def __init__(self, printer): - CommandTrigger.__init__(self, "gcodeCommandTrigger", printer) - - def executeCommand(self, command): - self._logger.debug("Executing GCode command: %s" % command) - self._printer.commands(command.split(",")) diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 28f186cc..692bc5f0 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -135,9 +135,8 @@ class Server(): # configure timelapse octoprint.timelapse.configureTimelapse() - # setup system and gcode command triggers - events.SystemCommandTrigger(printer) - events.GcodeCommandTrigger(printer) + # setup command triggers + events.CommandTrigger(printer) if self._debug: events.DebugEventListener() diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index e116f652..4a724138 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -125,12 +125,8 @@ default_settings = { "config": "/default/path/to/your/cura/config.ini" }, "events": { - "systemCommandTrigger": { - "enabled": False - }, - "gcodeCommandTrigger": { - "enabled": False - } + "enabled": False, + "subscriptions": [] }, "api": { "enabled": False, @@ -173,7 +169,7 @@ class Settings(object): self._configfile = configfile else: self._configfile = os.path.join(self.settings_dir, "config.yaml") - self.load() + self.load(migrate=True) def _init_settings_dir(self, basedir): if basedir is not None: @@ -189,7 +185,7 @@ class Settings(object): #~~ load and save - def load(self): + def load(self, migrate=False): if os.path.exists(self._configfile) and os.path.isfile(self._configfile): with open(self._configfile, "r") as f: self._config = yaml.safe_load(f) @@ -197,6 +193,97 @@ class Settings(object): if not self._config: self._config = {} + if migrate: + self._migrateConfig() + + def _migrateConfig(self): + if not self._config: + return + + if "events" in self._config.keys() and ("gcodeCommandTrigger" in self._config["events"] or "systemCommandTrigger" in self._config["events"]): + self._logger.info("Migrating config (event subscriptions)...") + + # migrate event hooks to new format + placeholderRe = re.compile("%\((.*?)\)s") + + eventNameReplacements = { + "ClientOpen": "ClientOpened", + "TransferStart": "TransferStarted" + } + payloadDataReplacements = { + "Upload": {"data": "{file}", "filename": "{file}"}, + "Connected": {"data": "{port} at {baudrate} baud"}, + "FileSelected": {"data": "{file}", "filename": "{file}"}, + "TransferStarted": {"data": "{remote}", "filename": "{remote}"}, + "TransferDone": {"data": "{remote}", "filename": "{remote}"}, + "ZChange": {"data": "{new}"}, + "CaptureStart": {"data": "{file}"}, + "CaptureDone": {"data": "{file}"}, + "MovieDone": {"data": "{movie}", "filename": "{gcode}"}, + "Error": {"data": "{error}"}, + "PrintStarted": {"data": "{file}", "filename": "{file}"}, + "PrintDone": {"data": "{file}", "filename": "{file}"}, + } + + def migrateEventHook(event, command): + # migrate placeholders + command = placeholderRe.sub("{__\\1}", command) + + # migrate event names + if event in eventNameReplacements: + event = eventNameReplacements["event"] + + # migrate payloads to more specific placeholders + if event in payloadDataReplacements: + for key in payloadDataReplacements[event]: + command = command.replace("{__%s}" % key, payloadDataReplacements[event][key]) + + # return processed tuple + return event, command + + disableSystemCommands = False + if "systemCommandTrigger" in self._config["events"] and "enabled" in self._config["events"]["systemCommandTrigger"]: + disableSystemCommands = not self._config["events"]["systemCommandTrigger"]["enabled"] + + disableGcodeCommands = False + if "gcodeCommandTrigger" in self._config["events"] and "enabled" in self._config["events"]["gcodeCommandTrigger"]: + disableGcodeCommands = not self._config["events"]["gcodeCommandTrigger"]["enabled"] + + disableAllCommands = disableSystemCommands and disableGcodeCommands + newEvents = { + "enabled": not disableAllCommands, + "subscriptions": [] + } + + if "systemCommandTrigger" in self._config["events"] and "subscriptions" in self._config["events"]["systemCommandTrigger"]: + for trigger in self._config["events"]["systemCommandTrigger"]["subscriptions"]: + if not ("event" in trigger and "command" in trigger): + continue + + newTrigger = {"type": "system"} + if disableSystemCommands and not disableAllCommands: + newTrigger["enabled"] = False + + newTrigger["event"], newTrigger["command"] = migrateEventHook(trigger["event"], trigger["command"]) + newEvents["subscriptions"].append(newTrigger) + + if "gcodeCommandTrigger" in self._config["events"] and "subscriptions" in self._config["events"]["gcodeCommandTrigger"]: + for trigger in self._config["events"]["gcodeCommandTrigger"]["subscriptions"]: + if not ("event" in trigger and "command" in trigger): + continue + + newTrigger = {"type": "gcode"} + if disableGcodeCommands and not disableAllCommands: + newTrigger["enabled"] = False + + newTrigger["event"], newTrigger["command"] = migrateEventHook(trigger["event"], trigger["command"]) + newTrigger["command"] = newTrigger["command"].split(",") + newEvents["subscriptions"].append(newTrigger) + + self._config["events"] = newEvents + self.save(force=True) + self._logger.info("Migrated %d event subscriptions to new format and structure" % len(newEvents["subscriptions"])) + def save(self, force=False): if not self._dirty and not force: return From e51d9439c53a50ac7e11780a391d52c17dceb6ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 2 Sep 2014 11:35:42 +0200 Subject: [PATCH 2/4] Modified versioneer to allow specification of manual versions based on branches You can now define a lookup file explicitely mapping virtual version tags to branches via regular expressions and a reference commit from which the commit distance will be calculated. Format of the file is The file is processed from top to bottom, the first matching line wins. If or are left out, the lookup table does not apply to the matched branches and the regular versioneer resolution (via git describe) takes place. Current configuration makes "master", "staging" and any branch starting with "fix/" make use the default behaviour, all other branches (so basically all development branches) are defined as "1.2.0-dev" (cherry picked from commit 212f40c) --- .versioneer-lookup | 18 ++++ setup.py | 1 + src/octoprint/_version.py | 101 +++++++++++++++++-- versioneer.py | 206 +++++++++++++++++++++++++++++++++++--- 4 files changed, 303 insertions(+), 23 deletions(-) create mode 100644 .versioneer-lookup diff --git a/.versioneer-lookup b/.versioneer-lookup new file mode 100644 index 00000000..8e1218d7 --- /dev/null +++ b/.versioneer-lookup @@ -0,0 +1,18 @@ +# Configuration file for the versioneer lookup, manually mapping tags based on branches +# +# Format is +# +# +# +# The file is processed from top to bottom, the first matching line wins. If or are left out, +# the lookup table does not apply to the matched branches + +# master and staging shall not use the lookup table +master +staging + +# fix/ branches are fixes for master, so we don't handle those either +fix/.* + +# every other branch is a development branch and thus gets resolved to 1.2.0-dev for now +.* 1.2.0-dev 50cf776e70b9 diff --git a/setup.py b/setup.py index 471e546f..d06296ea 100644 --- a/setup.py +++ b/setup.py @@ -7,6 +7,7 @@ versioneer.versionfile_source = 'src/octoprint/_version.py' versioneer.versionfile_build = 'octoprint/_version.py' versioneer.tag_prefix = '' versioneer.parentdir_prefix = '' +versioneer.lookupfile = '.versioneer-lookup' from setuptools import setup, find_packages, Command import os diff --git a/src/octoprint/_version.py b/src/octoprint/_version.py index f9a41364..52aefd49 100644 --- a/src/octoprint/_version.py +++ b/src/octoprint/_version.py @@ -54,6 +54,18 @@ import sys import re import os.path +def get_gits(root, verbose=False): + if not os.path.exists(os.path.join(root, ".git")): + if verbose: + print("no .git in %s" % root) + return None + + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + return GITS + + def get_expanded_variables(versionfile_abs): # the code embedded in _version.py can just fetch the value of these # variables. When used from setup.py, we don't want to import @@ -114,20 +126,60 @@ def versions_from_expanded_variables(variables, tag_prefix, verbose=False): return { "version": variables["full"].strip(), "full": variables["full"].strip() } +def versions_from_lookup(lookup, root, verbose=False): + GITS = get_gits(root, verbose=verbose) + if GITS is None: + return {} + + stdout = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], + cwd=root) + if stdout is None: + return {} + + current_branch = stdout.strip() + for matcher, tag, ref_commit in lookup: + if matcher.match(current_branch): + if tag is None or ref_commit is None: + return {} + + stdout = run_command(GITS, ["rev-list", "%s..HEAD" % ref_commit, "--count"], cwd=root) + if stdout is None: + return {} + num_commits = stdout.strip() + + stdout =run_command(GITS, ["rev-parse", "--short", "HEAD"], cwd=root) + if stdout is None: + return {} + short_hash = stdout.strip() + + stdout = run_command(GITS, ["describe", "--tags", "--dirty", "--always"], cwd=root) + if stdout is None: + return {} + dirty = stdout.strip().endswith("-dirty") + + stdout = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if stdout is None: + return {} + full = stdout.strip() + + version = "%s-%s-g%s" % (tag, num_commits, short_hash) + if dirty: + version += "-dirty" + full += "-dirty" + return {"version": version, "full": full} + + return {} + def versions_from_vcs(tag_prefix, root, verbose=False): # this runs 'git' from the root of the source tree. This only gets called # if the git-archive 'subst' variables were *not* expanded, and # _version.py hasn't already been rewritten with a short version string, # meaning we're inside a checked out source tree. - if not os.path.exists(os.path.join(root, ".git")): - if verbose: - print("no .git in %s" % root) + GITS = get_gits(root, verbose=verbose) + if GITS is None: return {} - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] stdout = run_command(GITS, ["describe", "--tags", "--dirty", "--always"], cwd=root) if stdout is None: @@ -158,10 +210,39 @@ def versions_from_parentdir(parentdir_prefix, root, verbose=False): return {"version": dirname[len(parentdir_prefix):], "full": ""} tag_prefix = "" -parentdir_prefix = "octoprint-" +parentdir_prefix = "" versionfile_source = "src/octoprint/_version.py" +lookupfile = ".versioneer-lookup" -def get_versions(default={"version": "unknown", "full": ""}, verbose=False): +def parse_lookup_file(root, lookup_path=None): + if not lookup_path: + lookup_path = lookupfile + if not lookup_path: + return [] + + path = os.path.join(root, lookup_path) + if not os.path.exists(path): + return [] + + import re + lookup = [] + with open(os.path.join(root, lookup_path), "r") as f: + for line in f: + if '#' in line: + line = line[:line.rindex('#')] + line = line.strip() + try: + split_line = line.split() + if len(split_line) == 3: + pattern, tag, ref_commit = split_line + lookup.append([re.compile(pattern), tag, ref_commit]) + elif len(split_line) >= 1: + lookup.append([re.compile(split_line[0]), None, None]) + except: + break + return lookup + +def get_versions(default={"version": "unknown", "full": ""}, lookup_path=None, verbose=False): # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have # __file__, we can work backwards from there to the root. Some # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which @@ -182,7 +263,9 @@ def get_versions(default={"version": "unknown", "full": ""}, verbose=False): except NameError: return default - return (versions_from_vcs(tag_prefix, root, verbose) + lookup = parse_lookup_file(root, lookup_path=lookup_path) + return (versions_from_lookup(lookup, root, verbose) + or versions_from_vcs(tag_prefix, root, verbose) or versions_from_parentdir(parentdir_prefix, root, verbose) or default) diff --git a/versioneer.py b/versioneer.py index a01ab1ce..237968f1 100644 --- a/versioneer.py +++ b/versioneer.py @@ -252,6 +252,7 @@ versionfile_source = None versionfile_build = None tag_prefix = None parentdir_prefix = None +lookupfile = None VCS = "git" @@ -312,6 +313,18 @@ import sys import re import os.path +def get_gits(root, verbose=False): + if not os.path.exists(os.path.join(root, ".git")): + if verbose: + print("no .git in %%s" %% root) + return None + + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + return GITS + + def get_expanded_variables(versionfile_abs): # the code embedded in _version.py can just fetch the value of these # variables. When used from setup.py, we don't want to import @@ -372,20 +385,60 @@ def versions_from_expanded_variables(variables, tag_prefix, verbose=False): return { "version": variables["full"].strip(), "full": variables["full"].strip() } +def versions_from_lookup(lookup, root, verbose=False): + GITS = get_gits(root, verbose=verbose) + if GITS is None: + return {} + + stdout = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], + cwd=root) + if stdout is None: + return {} + + current_branch = stdout.strip() + for matcher, tag, ref_commit in lookup: + if matcher.match(current_branch): + if tag is None or ref_commit is None: + return {} + + stdout = run_command(GITS, ["rev-list", "%%s..HEAD" %% ref_commit, "--count"], cwd=root) + if stdout is None: + return {} + num_commits = stdout.strip() + + stdout =run_command(GITS, ["rev-parse", "--short", "HEAD"], cwd=root) + if stdout is None: + return {} + short_hash = stdout.strip() + + stdout = run_command(GITS, ["describe", "--tags", "--dirty", "--always"], cwd=root) + if stdout is None: + return {} + dirty = stdout.strip().endswith("-dirty") + + stdout = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if stdout is None: + return {} + full = stdout.strip() + + version = "%%s-%%s-g%%s" %% (tag, num_commits, short_hash) + if dirty: + version += "-dirty" + full += "-dirty" + return {"version": version, "full": full} + + return {} + def versions_from_vcs(tag_prefix, root, verbose=False): # this runs 'git' from the root of the source tree. This only gets called # if the git-archive 'subst' variables were *not* expanded, and # _version.py hasn't already been rewritten with a short version string, # meaning we're inside a checked out source tree. - if not os.path.exists(os.path.join(root, ".git")): - if verbose: - print("no .git in %%s" %% root) + GITS = get_gits(root, verbose=verbose) + if GITS is None: return {} - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] stdout = run_command(GITS, ["describe", "--tags", "--dirty", "--always"], cwd=root) if stdout is None: @@ -418,8 +471,37 @@ def versions_from_parentdir(parentdir_prefix, root, verbose=False): tag_prefix = "%(TAG_PREFIX)s" parentdir_prefix = "%(PARENTDIR_PREFIX)s" versionfile_source = "%(VERSIONFILE_SOURCE)s" +lookupfile = %(LOOKUPFILE)s -def get_versions(default={"version": "unknown", "full": ""}, verbose=False): +def parse_lookup_file(root, lookup_path=None): + if not lookup_path: + lookup_path = lookupfile + if not lookup_path: + return [] + + path = os.path.join(root, lookup_path) + if not os.path.exists(path): + return [] + + import re + lookup = [] + with open(os.path.join(root, lookup_path), "r") as f: + for line in f: + if '#' in line: + line = line[:line.rindex('#')] + line = line.strip() + try: + split_line = line.split() + if len(split_line) == 3: + pattern, tag, ref_commit = split_line + lookup.append([re.compile(pattern), tag, ref_commit]) + elif len(split_line) >= 1: + lookup.append([re.compile(split_line[0]), None, None]) + except: + break + return lookup + +def get_versions(default={"version": "unknown", "full": ""}, lookup_path=None, verbose=False): # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have # __file__, we can work backwards from there to the root. Some # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which @@ -440,7 +522,9 @@ def get_versions(default={"version": "unknown", "full": ""}, verbose=False): except NameError: return default - return (versions_from_vcs(tag_prefix, root, verbose) + lookup = parse_lookup_file(root, lookup_path=lookup_path) + return (versions_from_lookup(lookup, root, verbose) + or versions_from_vcs(tag_prefix, root, verbose) or versions_from_parentdir(parentdir_prefix, root, verbose) or default) @@ -488,6 +572,18 @@ import sys import re import os.path +def get_gits(root, verbose=False): + if not os.path.exists(os.path.join(root, ".git")): + if verbose: + print("no .git in %s" % root) + return None + + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + return GITS + + def get_expanded_variables(versionfile_abs): # the code embedded in _version.py can just fetch the value of these # variables. When used from setup.py, we don't want to import @@ -548,20 +644,62 @@ def versions_from_expanded_variables(variables, tag_prefix, verbose=False): return { "version": variables["full"].strip(), "full": variables["full"].strip() } + +def versions_from_lookup(lookup, root, verbose=False): + GITS = get_gits(root, verbose=verbose) + if GITS is None: + return {} + + stdout = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], + cwd=root) + if stdout is None: + return {} + + current_branch = stdout.strip() + for matcher, tag, ref_commit in lookup: + if matcher.match(current_branch): + if tag is None or ref_commit is None: + return {} + + stdout = run_command(GITS, ["rev-list", "%s..HEAD" % ref_commit, "--count"], cwd=root) + if stdout is None: + return {} + num_commits = stdout.strip() + + stdout =run_command(GITS, ["rev-parse", "--short", "HEAD"], cwd=root) + if stdout is None: + return {} + short_hash = stdout.strip() + + stdout = run_command(GITS, ["describe", "--tags", "--dirty", "--always"], cwd=root) + if stdout is None: + return {} + dirty = stdout.strip().endswith("-dirty") + + stdout = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if stdout is None: + return {} + full = stdout.strip() + + version = "%s-%s-g%s" % (tag, num_commits, short_hash) + if dirty: + version += "-dirty" + full += "-dirty" + return {"version": version, "full": full} + + return {} + + def versions_from_vcs(tag_prefix, root, verbose=False): # this runs 'git' from the root of the source tree. This only gets called # if the git-archive 'subst' variables were *not* expanded, and # _version.py hasn't already been rewritten with a short version string, # meaning we're inside a checked out source tree. - if not os.path.exists(os.path.join(root, ".git")): - if verbose: - print("no .git in %s" % root) + GITS = get_gits(root, verbose=verbose) + if GITS is None: return {} - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] stdout = run_command(GITS, ["describe", "--tags", "--dirty", "--always"], cwd=root) if stdout is None: @@ -684,11 +822,38 @@ def get_root(): except NameError: return os.path.dirname(os.path.abspath(sys.argv[0])) +def parse_lookup_file(root, lookup_path=None): + if not lookup_path: + lookup_path = lookupfile + + path = os.path.join(root, lookup_path) + if not os.path.exists(path): + return [] + + import re + lookup = [] + with open(os.path.join(root, lookup_path), "r") as f: + for line in f: + if '#' in line: + line = line[:line.rindex("#")] + line = line.strip() + try: + split_line = line.split() + if len(split_line) == 3: + pattern, tag, ref_commit = split_line + lookup.append([re.compile(pattern), tag, ref_commit]) + elif len(split_line) >= 1: + lookup.append([re.compile(split_line[0]), None, None]) + except: + break + return lookup + def get_versions(default=DEFAULT, verbose=False): # returns dict with two keys: 'version' and 'full' assert versionfile_source is not None, "please set versioneer.versionfile_source" assert tag_prefix is not None, "please set versioneer.tag_prefix" assert parentdir_prefix is not None, "please set versioneer.parentdir_prefix" + # I am in versioneer.py, which must live at the top of the source tree, # which we use to compute the root directory. py2exe/bbfreeze/non-CPython # don't have __file__, in which case we fall back to sys.argv[0] (which @@ -697,6 +862,11 @@ def get_versions(default=DEFAULT, verbose=False): root = get_root() versionfile_abs = os.path.join(root, versionfile_source) + if lookupfile: + lookup = parse_lookup_file(root, lookup_path = lookupfile) + else: + lookup = None + # extract version from first of _version.py, 'git describe', parentdir. # This is meant to work for developers using a source checkout, for users # of a tarball created by 'setup.py sdist', and for users of a @@ -715,6 +885,12 @@ def get_versions(default=DEFAULT, verbose=False): if verbose: print("got version from file %s %s" % (versionfile_abs,ver)) return ver + if lookup: + ver = versions_from_lookup(lookup, root, verbose=verbose) + if ver: + if verbose: print("got version from lookup %s" % ver) + return ver + ver = versions_from_vcs(tag_prefix, root, verbose) if ver: if verbose: print("got version from git %s" % ver) @@ -792,6 +968,7 @@ if 'cx_Freeze' in sys.modules: # cx_freeze enabled? "TAG_PREFIX": tag_prefix, "PARENTDIR_PREFIX": parentdir_prefix, "VERSIONFILE_SOURCE": versionfile_source, + "LOOKUPFILE": '"%s"' % lookupfile if lookupfile is not None else "None", }) f.close() @@ -835,6 +1012,7 @@ class cmd_update_files(Command): "TAG_PREFIX": tag_prefix, "PARENTDIR_PREFIX": parentdir_prefix, "VERSIONFILE_SOURCE": versionfile_source, + "LOOKUPFILE": '"%s"' % lookupfile if lookupfile is not None else "None", }) f.close() From 45c1958d8368084090c0bdd3706cf59a0d15e239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 2 Sep 2014 12:06:02 +0200 Subject: [PATCH 3/4] Versioneer now also returns and persists the branch from which OctoPrint was installed Changed version output accordingly to now display "{version} ({branch} branch)" if branch information is available (which should be the case if installation was performed from git). (cherry picked from commit a48b5de) --- src/octoprint/_version.py | 11 +++++++++-- src/octoprint/server/__init__.py | 9 +++++++-- src/octoprint/templates/index.jinja2 | 2 +- versioneer.py | 25 +++++++++++++++++++++---- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/octoprint/_version.py b/src/octoprint/_version.py index 52aefd49..3808a4f8 100644 --- a/src/octoprint/_version.py +++ b/src/octoprint/_version.py @@ -166,7 +166,7 @@ def versions_from_lookup(lookup, root, verbose=False): if dirty: version += "-dirty" full += "-dirty" - return {"version": version, "full": full} + return {"version": version, "full": full, "branch": current_branch} return {} @@ -195,7 +195,14 @@ def versions_from_vcs(tag_prefix, root, verbose=False): full = stdout.strip() if tag.endswith("-dirty"): full += "-dirty" - return {"version": tag, "full": full} + + stdout = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], + cwd=root) + if stdout is None: + branch = None + else: + branch = stdout.strip() + return {"version": tag, "full": full, "branch": branch} def versions_from_parentdir(parentdir_prefix, root, verbose=False): diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 692bc5f0..3f38b899 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -41,8 +41,12 @@ import octoprint.timelapse import octoprint._version -VERSION = octoprint._version.get_versions()['version'] +versions = octoprint._version.get_versions() +VERSION = versions['version'] +BRANCH = versions['branch'] if 'branch' in versions else None +DISPLAY_VERSION = "%s (%s branch)" % (VERSION, BRANCH) if BRANCH else VERSION +del versions @app.route("/") def index(): @@ -58,6 +62,7 @@ def index(): firstRun=settings().getBoolean(["server", "firstRun"]) and (userManager is None or not userManager.hasBeenCustomized()), debug=debug, version=VERSION, + display_version=DISPLAY_VERSION, stylesheet=settings().get(["devel", "stylesheet"]), gcodeMobileThreshold=settings().get(["gcodeViewer", "mobileSizeThreshold"]), gcodeThreshold=settings().get(["gcodeViewer", "sizeThreshold"]) @@ -126,7 +131,7 @@ class Server(): self._initLogging(self._debug) logger = logging.getLogger(__name__) - logger.info("Starting OctoPrint (%s)" % VERSION) + logger.info("Starting OctoPrint %s" % DISPLAY_VERSION) eventManager = events.eventManager() gcodeManager = gcodefiles.GcodeManager() diff --git a/src/octoprint/templates/index.jinja2 b/src/octoprint/templates/index.jinja2 index 70c30fd6..28d5b4f3 100644 --- a/src/octoprint/templates/index.jinja2 +++ b/src/octoprint/templates/index.jinja2 @@ -597,7 +597,7 @@