From 5fe6b7d471921981d9a2a4fc1c5f75fc9f3aef41 Mon Sep 17 00:00:00 2001
From: Marshall McDonnell <mcdonnellmt@ornl.gov>
Date: Wed, 15 May 2019 13:01:19 -0400
Subject: [PATCH] Conda recipe with deployment on tags via Travis (#235)

* Adding conda recipe and CD to travis

* Removed master-only from travis yaml

* Removed un-used USER variable

* Fixed summary in meta.yaml

* Added mantidTS channel, libGL install, and updates to upload

* Fix conda upload function call

* added mantid channel to travis yaml

* Cleanup in conda recipe

* Add the build section to conda recipe + refactor run

* Fixing conda build statements

* Fix conda upload function call

* Rerun travis build with new api token setup for conda deploy

* Adding conda recipe and CD to travis

* Removed master-only from travis yaml

* Removed un-used USER variable

* Fixed summary in meta.yaml

* Added mantidTS channel, libGL install, and updates to upload

* Fix conda upload function call

* added mantid channel to travis yaml

* Cleanup in conda recipe

* Add the build section to conda recipe + refactor run

* Fixing conda build statements

* Fix conda upload function call

* Moved main script and updated setup.py for console_scripts

* Updated addiedevel.sh with change to main script

* Added flake8 to Pipfile

* Flake8 fixes to addie/main.py

* Added package pyX string, setuptools to build, and test for import of addie to recipe

* Refactor of conda setup + deploy in travis CI

* Adding python to recipe to debug failure

* Added pytest to setup.py

* Added conda install lines to travis yaml

* Pinned version of mantid workbench in recipe

* Added pinned workbench version to travis yaml

* Remove pinned versions and dependencies included in mantid

* Refactor setup.py for requirements files

* Updated recipe and requirement files

* Removed pinned workbench after changing upstream labels

* Remove setup requirements to fix conda recipe issues

* Fix flake8 for setup.py

* Switch travis conda deploy to production
---
 .travis.yml                     | 58 +++++++++++++++++++++------------
 Pipfile                         |  1 +
 scripts/addie => addie/main.py  | 56 +++++++++++++++----------------
 addiedevel.sh                   |  2 +-
 conda.recipe/anaconda_upload.sh | 16 +++++++++
 conda.recipe/meta.yaml          | 45 +++++++++++++++++++++++++
 install-requirements.txt        |  4 +++
 requirements.txt                |  9 -----
 setup.py                        | 39 ++++++++++++++++++----
 test-requirements.txt           |  2 ++
 10 files changed, 166 insertions(+), 66 deletions(-)
 rename scripts/addie => addie/main.py (96%)
 create mode 100644 conda.recipe/anaconda_upload.sh
 create mode 100644 conda.recipe/meta.yaml
 create mode 100644 install-requirements.txt
 delete mode 100644 requirements.txt
 create mode 100644 test-requirements.txt

diff --git a/.travis.yml b/.travis.yml
index 6bd3540e..4b297789 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,10 +1,6 @@
 language: python
 dist: trusty
 
-branches:
-  only:
-  - master
-
 services:
   - xvfb
 
@@ -21,34 +17,44 @@ matrix:
 
 before_install:
 - |
+  # Install conda
   "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16"
   if [ "$TRAVIS_OS_NAME" = "linux" ]; then export CXX=g++-4.8 CC=gcc-4.8; fi
   if [ "$TRAVIS_OS_NAME" = "linux" ]; then OS=Linux-x86_64; else OS=MacOSX-x86_64; fi
   wget -O miniconda.sh https://repo.continuum.io/miniconda/Miniconda${CONDA:0:1}-latest-$OS.sh
   bash miniconda.sh -b -p $HOME/miniconda
   export PATH="$HOME/miniconda/bin:$PATH"
-  conda config --set always_yes yes --set changeps1 no
-  conda config --add channels conda-forge
-  conda update  -q conda
-  conda create  -n test-environment -q python=$CONDA
-  conda update  -n test-environment -q conda
-  conda install -n test-environment -q conda-build
-  conda install -n test-environment -c mantid mantid-workbench nexus poco
-  conda install -n test-environment flake8
 
 install:
 - |
-  source activate test-environment
-  conda list
+  # Setup channels
+  conda config --set always_yes yes --set changeps1 no --set anaconda_upload no
+  conda config --add channels conda-forge --add channels marshallmcdonnell --add channels mantid --add channels mantid/label/nightly
+
+  conda update  -q conda
+  conda info -a
+
+  # Activate environment
+  conda create -q -n addie_env python=$CONDA flake8 conda-build conda-verify anaconda-client
+  source activate addie_env
+
+  # Build recipe and install addie
+  conda build conda.recipe
+  export PKG_FILE=$(conda build conda.recipe --output)
+  conda install ${PKG_FILE}
 
 before_script:
-  # create a properties file that turns off network access
-  - mkdir ~/.mantid
-  - echo "CheckMantidVersion.OnStartup=0" > ~/.mantid/Mantid.user.properties
-  - echo "UpdateInstrumentDefinitions.OnStartup=0" >> ~/.mantid/Mantid.user.properties
-  - echo "usagereports.enabled=0" >> ~/.mantid/Mantid.user.properties
-  - export DISPLAY=:99.0
-  - sleep 3
+  - |
+    # create a properties file that turns off network access
+    mkdir ~/.mantid
+    echo "CheckMantidVersion.OnStartup=0" > ~/.mantid/Mantid.user.properties
+    echo "UpdateInstrumentDefinitions.OnStartup=0" >> ~/.mantid/Mantid.user.properties
+    echo "usagereports.enabled=0" >> ~/.mantid/Mantid.user.properties
+    export DISPLAY=:99.0
+    sleep 3
+  - |
+    conda install -c mantid/label/nightly mantid-workbench
+    conda install -c marshallmcdonnell mantid-total-scattering-python-wrapper
 
 script:
   # lint the code and generate an error if a warning is introduced
@@ -70,3 +76,13 @@ script:
   - python setup.py install
   - which addie
   - xvfb-run --server-args="-screen 0 640x480x24" --auto-servernum addie --version
+
+deploy:
+ # Deploy conda package to Anaconda.org https://anaconda.org/addie-diffraction/addie
+ - provider: script
+   script: cd conda.recipe && chmod +x anaconda_upload.sh && ./anaconda_upload.sh ${PKG_FILE}
+   skip_cleanup: true
+   skip_existing: true
+   on:
+     branch: master
+     tags: true
diff --git a/Pipfile b/Pipfile
index 00548471..2294dafb 100644
--- a/Pipfile
+++ b/Pipfile
@@ -17,3 +17,4 @@ psutil = "*"
 pyoncat = {file = "https://oncat.ornl.gov/packages/pyoncat-1.3.2-py2.py3-none-any.whl"}
 six = "==1.11.0"
 QtPy = "==1.6.0"
+flake8 = "*"
diff --git a/scripts/addie b/addie/main.py
similarity index 96%
rename from scripts/addie
rename to addie/main.py
index c174b6bb..17a8551d 100755
--- a/scripts/addie
+++ b/addie/main.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 from __future__ import (absolute_import, division, print_function)
 
 import copy
@@ -43,8 +42,7 @@
 
 # PyONcat
 try:
-    from addie.processing.mantid.master_table.import_from_database.import_from_database_handler import \
-        ImportFromDatabaseHandler
+    from addie.processing.mantid.master_table.import_from_database.import_from_database_handler import ImportFromDatabaseHandler # noqa
     ONCAT_ENABLED = True
 except ImportError:
     print('pyoncat module not found. Functionality disabled')
@@ -55,7 +53,8 @@
 import addie.addiedriver as driver
 import addie.calculate_gr.edit_sq_dialog
 
-from addie.icons import icons_rc # necessary to see all the icons
+# necessary to see all the icons
+from addie.icons import icons_rc # noqa
 
 from addie.calculate_gr.pdf_lines_manager import PDFPlotManager
 
@@ -425,7 +424,7 @@ def help_button_clicked_autonom(self):
 
     def main_tab_widget_changed(self, tab_selected):
         if tab_selected == 0:
-            o_gui = Step1GuiHandler(main_window=self)
+            Step1GuiHandler(main_window=self)
             autonom_event_handler.check_step1_gui(self)
         if tab_selected == 1:
             _o_gui = Step2GuiHandler(main_window=self)
@@ -471,7 +470,7 @@ def update_logbook(self, text):
         if self.job_monitor_interface is None:
             self.logbook_thread.stop()
         else:
-            _logbook_handler = LogbookHandler(parent=self)
+            LogbookHandler(parent=self)
 
     # autoNOM
 
@@ -921,32 +920,33 @@ def apply_clicked(self):
         self.close()
 
 
-def main(mode):
+def main(config=None):
+
+    if config is None:
+        import argparse  # noqa
+        parser = argparse.ArgumentParser(description='ADvanced DIffraction Environment')
+        parser.add_argument('--version', action='version', version='%(prog)s version {}'.format(__version__))
+        parser.add_argument('--mode', type=str, default='mantid',
+                            help='Set processing mode (default=%(default)s)', choices=['mantid', 'idl'])
+
+        try:  # set up bash completion as a soft dependency
+            import argcomplete  # noqa
+            argcomplete.autocomplete(parser)
+        except ImportError:
+            pass  # silently skip this
+
+        # parse the command line options
+        config = parser.parse_args()
+
     app = QApplication(sys.argv)
-    app.setOrganizationName("Qtrac Ltd.")
-    app.setOrganizationDomain("qtrac.eu")
-    app.setApplicationName("Image Changer")
+    app.setOrganizationName("ORNL / SNS")
+    app.setOrganizationDomain("https://neutrons.ornl.gov/")
+    app.setApplicationName("ADDIE: ADvanced DIffraction Environment")
     app.setWindowIcon(QIcon(":/icon.png"))
-    form = MainWindow(processing_mode=mode)
+    form = MainWindow(processing_mode=config.mode)
     form.show()
     app.exec_()
 
 
 if __name__ == '__main__':
-    import argparse  # noqa
-    parser = argparse.ArgumentParser(description='ADvanced DIffraction Environment')
-    parser.add_argument('--version', action='version', version='%(prog)s version {}'.format(__version__))
-    parser.add_argument('--mode', type=str, default='mantid',
-                        help='Set processing mode (default=%(default)s)', choices=['mantid', 'idl'])
-
-    try:  # set up bash completion as a soft dependency
-        import argcomplete  # noqa
-        argcomplete.autocomplete(parser)
-    except ImportError:
-        pass  # silently skip this
-
-    # parse the command line options
-    options = parser.parse_args()
-
-    # start the main program
-    main(options.mode)
+    main()
diff --git a/addiedevel.sh b/addiedevel.sh
index ad4fd471..e222d4b4 100755
--- a/addiedevel.sh
+++ b/addiedevel.sh
@@ -30,4 +30,4 @@ echo using $RAW_PYTHON version $PYTHON_VERSION
 $CMD setup.py build
 
 # launch addie
-QT_API=$LOCAL_QT_API PYTHONPATH=build/lib:$PYTHONPATH $CMD --classic build/scripts-${PYTHON_VERSION}/addie
+QT_API=$LOCAL_QT_API PYTHONPATH=build/lib:$PYTHONPATH $CMD --classic build/lib/addie/main.py
diff --git a/conda.recipe/anaconda_upload.sh b/conda.recipe/anaconda_upload.sh
new file mode 100644
index 00000000..08c9d63a
--- /dev/null
+++ b/conda.recipe/anaconda_upload.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+  
+# Input
+if [[ $# -ne 1 ]]
+then
+  echo "Usage: anaconda_upload.sh <full package path>"
+  exit 1
+fi
+
+PKG_PATH=$1
+PKG_FILE=$(basename ${PKG_PATH})
+
+echo "Uploading ${PKG_PATH} artifact..."
+anaconda -v -t ${CONDA_UPLOAD_TOKEN} upload ${PKG_PATH} --force
+
+echo "Successfully deployed ${PKG_FILE} to Anaconda.org."
diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml
new file mode 100644
index 00000000..f0d617d8
--- /dev/null
+++ b/conda.recipe/meta.yaml
@@ -0,0 +1,45 @@
+{% set data = load_setup_py_data() %}
+
+package:
+  name: "addie"
+  version: "{{ data['version'] }}"
+
+source:
+  path: ..
+
+build:
+  string: py{{py}}
+  script: python setup.py install --single-version-externally-managed --record=record.txt
+
+requirements:
+  build:
+    - mantid-total-scattering-python-wrapper
+    - mantid-workbench
+    - periodictable
+    - psutil
+    - python
+    - setuptools
+
+  run:
+    - mantid-total-scattering-python-wrapper
+    - mantid-workbench
+    - periodictable
+    - psutil
+    - python
+
+test:
+  imports:
+    - addie
+
+about:
+  home: https://github.com/neutrons/addie
+  license: GPL (version 3)
+  license_family: GPL3
+  license_file: 
+  summary: ADvanced DIffraction Environment
+
+extra:
+  recipe-maintainers:
+    - JeanBilheux
+    - marshallmcdonnell
+    - peterfpeterson
diff --git a/install-requirements.txt b/install-requirements.txt
new file mode 100644
index 00000000..abae8b44
--- /dev/null
+++ b/install-requirements.txt
@@ -0,0 +1,4 @@
+mantid-total-scattering
+periodictable
+psutil
+QtPy
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index f5cd9126..00000000
--- a/requirements.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-mantid-total-scattering
-matplotlib==2.2.3
-mock==2.0.0
-numpy==1.15.4
-pytest==4.2.1
-periodictable==1.5.0
-psutil
-QtPy==1.6.0
-six==1.11.0
diff --git a/setup.py b/setup.py
index 6585d91a..8f6d89f4 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,31 @@
+import os
 from setuptools import setup, find_packages
 import versioneer  # https://github.com/warner/python-versioneer
 
+# ==============================================================================
+# Constants
+# ==============================================================================
+THIS_DIR = os.path.dirname(__file__)
+
+# ==============================================================================
+# Package requirements helper
+# ==============================================================================
+
+
+def read_requirements_from_file(filepath):
+    '''Read a list of requirements from the given file and split into a
+    list of strings. It is assumed that the file is a flat
+    list with one requirement per line.
+    :param filepath: Path to the file to read
+    :return: A list of strings containing the requirements
+    '''
+    with open(filepath, 'rU') as req_file:
+        return req_file.readlines()
+
+
+setup_args = dict(install_requires=read_requirements_from_file(os.path.join(THIS_DIR, 'install-requirements.txt')),
+                  tests_require=read_requirements_from_file(os.path.join(THIS_DIR, 'test-requirements.txt')))
+
 setup(name="addie",
       version=versioneer.get_version(),
       cmdclass=versioneer.get_cmdclass(),
@@ -10,16 +35,16 @@
       url="http://github.com/neutrons/addie",
       long_description="""Should have a longer description""",
       license="The MIT License (MIT)",
-      scripts=["scripts/addie"],
+      entry_points = {
+        'console_scripts': [
+            "addie = addie.main:main"
+        ]
+      },
       packages=find_packages(),
       package_data={'': ['*.ui', '*.png', '*.qrc', '*.json']},
       include_package_data=True,
-      install_requires=[
-        'matplotlib <= 2.2.3',
-        'numpy == 1.15.4',
-        'periodictable == 1.5.0',
-        'psutil==5.4.2',
-        'QtPy==1.6.0' ],
       setup_requires=[],
+      install_requires=setup_args["install_requires"],
+      tests_require=setup_args["install_requires"] + setup_args["tests_require"],
       test_suite='tests'
       )
diff --git a/test-requirements.txt b/test-requirements.txt
new file mode 100644
index 00000000..93253de9
--- /dev/null
+++ b/test-requirements.txt
@@ -0,0 +1,2 @@
+mock
+pytest