Repository: adafruit/circup (language: python)

Issue:
Add Cookiecutter standards to Circup repo
Circup should be updated to use pre-commit, and docs, etc. so it has the same dev process to the libraries themselves.


Discussion / hints:

Fix patch:
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 6344b2f..9b00064 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,3 +1,6 @@
+<!-- SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+
+SPDX-License-Identifier: MIT -->
 Thank you for opening an issue on an Adafruit Python library repository.  To
 improve the speed of resolution please review the following guidelines and
 common troubleshooting steps below before creating the issue:
@@ -19,7 +22,7 @@ common troubleshooting steps below before creating the issue:
     library the code depends on is not installed.  Check the tutorial/guide or
     README to ensure you have installed the necessary libraries.  Usually the
     missing library can be installed with the `pip` tool, but check the tutorial/guide
-    for the exact command.  
+    for the exact command.
 
   - **Be sure you are supplying adequate power to the board.**  Check the specs of
     your board and power in an external power supply.  In many cases just
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 7b641eb..af509f3 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,3 +1,6 @@
+<!-- SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+
+SPDX-License-Identifier: MIT -->
 Thank you for creating a pull request to contribute to Adafruit's GitHub code!
 Before you open the request please review the following guidelines and tips to
 help it be more easily integrated:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 671345a..d378cd0 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,3 +1,7 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+
+# SPDX-License-Identifier: MIT
+
 name: Build CI
 
 on: [pull_request, push]
@@ -17,13 +21,22 @@ jobs:
       uses: actions/setup-python@v1
       with:
         python-version: 3.6
+    - name: Pip install Sphinx & pre-commit
+      run: |
+        pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit
     - name: Versions
       run: |
         python3 --version
+        pre-commit --version
     - name: Checkout Current Repo
       uses: actions/checkout@v1
       with:
         submodules: true
+    - name: Library version
+      run: git describe --dirty --always --tags
+    - name: Pre-commit hooks
+      run: |
+        pre-commit run --all-files
     - name: Checkout tools repo
       uses: actions/checkout@v2
       with:
@@ -33,20 +46,10 @@ jobs:
       # (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.)
       run: |
         source actions-ci/install.sh
-    - name: Pip install pylint, black, & Sphinx
-      run: |
-        pip install --force-reinstall pylint black Sphinx sphinx-rtd-theme
-    - name: Library version
-      run: git describe --dirty --always --tags
-    - name: Check formatting
-      run: |
-        black --check --target-version=py35 .
-    - name: PyLint
-      run: |
-        pylint $( find . -path './circup.py' )
     - name: Run Test Suite
       run: |
-        make test
+        pytest --random-order --cov-config .coveragerc --cov-report term-missing --cov=circup
     - name: Build docs
+      working-directory: docs
       run: |
-        make docs
+        sphinx-build -E -W -b html . _build/html
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 220e398..ffeb537 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,3 +1,7 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+
+# SPDX-License-Identifier: MIT
+
 name: Release Actions
 
 on:
diff --git a/.gitignore b/.gitignore
index 85d1245..4c1aa22 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,7 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+
 # Byte-compiled / optimized / DLL files
 __pycache__/
 *.py[cod]
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..c5c63c2
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,32 @@
+# SPDX-FileCopyrightText: 2020 Diego Elio Pettenò
+#
+# SPDX-License-Identifier: Unlicense
+
+repos:
+-   repo: https://github.com/pycqa/pylint
+    rev: pylint-2.6.0
+    hooks:
+    -   id: pylint
+        name: lint (examples)
+        types: [python]
+        files: ^examples/
+        args:
+        - --disable=missing-docstring,invalid-name,bad-whitespace
+    -   id: pylint
+        name: lint (code)
+        types: [python]
+        exclude: "^(docs/|examples/|setup.py$)"
+-   repo: https://github.com/python/black
+    rev: 20.8b1
+    hooks:
+    - id: black
+-   repo: https://github.com/fsfe/reuse-tool
+    rev: v0.12.1
+    hooks:
+    - id: reuse
+-   repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v2.3.0
+    hooks:
+    -   id: check-yaml
+    -   id: end-of-file-fixer
+    -   id: trailing-whitespace
diff --git a/.pylintrc b/.pylintrc
index df34d04..4e4d74b 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -1,3 +1,6 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
 [MASTER]
 
 # A comma-separated list of package or module names from where C extensions may
diff --git a/CHANGES.rst b/CHANGES.rst
deleted file mode 100644
index 9a0cc3f..0000000
--- a/CHANGES.rst
+++ /dev/null
@@ -1,68 +0,0 @@
-Release History
-===============
-
-0.0.8
------
-
-* Added requirements.txt support to both freeze and install commands. Many thanks to Steven Abadie for this really useful feature.
-
-0.0.7
------
-
-Two new features contributed from the community:
-
-* Run circup via ``python -m circup``. Thank you to Joe DeVivo for this contribution.
-* Add an uninstall command. Thank you to Steven Abadie for this new feature.
-
-0.0.6
------
-
-This release includes a security fix
-
-
-0.0.5
------
-
-Fixed error message when Bundle Unavailable
-
-* Error message when bundle unavailable is better
-* Fixed a couple types
-
-
-0.0.4
------
-
-Added install and show commands
-
-* Circup now has an install command to install a CircuitPython library onto your device.
-* It also has a show command to show you what is available.
-
-0.0.3
------
-
-Automated Release Deployment Bug Fix
-
-* Fix missing PyPI egg dependency
-
-0.0.2
------
-
-Initial PyPI Release Automation w/ TravisCI
-
-* Add Continuous Integration with TravisCI
-* Deploy ``circup`` releases to PyPI automatically with TravisCI
-
-0.0.1
------
-
-Initial release.
-
-* Core project scaffolding.
-* ``circup freeze`` - lists version details for all modules found on the
-  connected CIRCUITPYTHON device.
-* ``circup list`` - lists all modules requiring an update found on the the
-  connected CIRCUITPYTHON device.
-* ``circup update`` - interactively update out-of-date modules found on the
-  connected CIRCUITPYTHON device.
-* 100% test coverage.
-* Documentation.
diff --git a/CODE_OF_CONDUCT.rst.license b/CODE_OF_CONDUCT.rst.license
new file mode 100644
index 0000000..168c840
--- /dev/null
+++ b/CODE_OF_CONDUCT.rst.license
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 4a0d6cd..ee3ae3c 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -18,3 +18,123 @@ If you have an employment contract with your employer please make sure that
 they don't automatically own your work product. Make sure to get any necessary
 approvals before contributing. Another term for this contribution off-hours is
 moonlighting.
+
+
+Developer Setup
+---------------
+
+.. note::
+
+    Please try to use Python 3.6+ while developing CircUp. This is so we can
+    use the
+    `Black code formatter <https://black.readthedocs.io/en/stable/index.html>`_
+    (which only works with Python 3.6+).
+
+Clone the repository and from the root of the project,
+install the requirements::
+
+    pip install -e ".[dev]"
+
+Run the test suite::
+
+    pytest --random-order --cov-config .coveragerc --cov-report term-missing --cov=circup
+
+.. warning::
+
+    Whenever you run ``make check``, to ensure the test suite starts from a
+    known clean state, all auto-generated assets are deleted. This includes
+    assets generated by running ``pip install -e ".[dev]"``, including the
+    ``circup`` command itself. Simply re-run ``pip`` to re-generate the
+    assets.
+
+How Does Circup Work?
+#####################
+
+The ``circup`` tool checks for a connected CircuitPython device by
+interrogating the local filesystem to find a path to a directory which ends
+with ``"CIRCUITPYTHON"`` (the name under which a CircuitPython device is
+mounted by the host operating system). This is handled in the ``find_device``
+function.
+
+A Python module on a connected device is represented by an instance of the
+``Module`` class. This class provides useful methods for discerning if the
+module is out of date, returning useful representations of it in order to
+display information to the user, or updating the module on the connected
+device with whatever the version is in the latest Adafruit CircuitPython
+Bundle.
+
+All of the libraries included in the Adafruit CircuitPython Bundle contain,
+somewhere within their code, two metadata objects called ``__version__`` and
+``__repo__``.
+
+The ``__repo__`` object is a string containing the GitHub repository URL, as
+used to clone the project.
+
+The ``__version__`` object is interesting because *within the source code in
+Git* the value is **always** the string ``"0.0.0-auto.0"``. When a new release
+is made of the bundle, this value is automatically replaced by the build
+scripts to the correct version information, which will always conform to the
+`semver standard <https://semver.org/>`_.
+
+Given this context, the ``circup`` tool will check a configuration file
+to discern what *it* thinks is the latest version of the bundle. If there is
+no configuration file (for example, on first run), then the bundle version is
+assumed to be ``"0"``.
+
+Next, it checks GitHub for the tag value (denoting the version) of the very
+latest bundle release. Bundle versions are based upon the date of release, for
+instance ``"20190904"``. If the latest version on GitHub is later than the
+version ``circup`` currently has, then the latest version of the bundle
+is automatically downloaded and cached away somewhere.
+
+In this way, the ``circup`` tool is able to have available to it both a path
+to a connected CIRCUITPYTHON devce and a copy of the latest version, including
+the all important version information, of the Adafruit CircuitPython Bundle.
+
+Exactly the same function (``get_modules``) is used to extract the metadata
+from the modules on both the connected device and in the bundle cache. This
+metadata is used to instantiate instances of the ``Module`` class which is
+subsequently used to facilitate the various commands the tool makes available.
+
+These commands are defined at the very end of the ``circup.py`` code.
+
+Unit tests can be found in the ``tests`` directory. CircUp uses
+`pytest <http://www.pytest.org/en/latest/>`_ style testing conventions. Test
+functions should include a comment to describe its *intention*. We currently
+have 100% unit test coverage for all the core functionality (excluding
+functions used to define the CLI commands).
+
+To run the full test suite, type::
+
+    pytest --random-order --cov-config .coveragerc --cov-report term-missing --cov=circup
+
+All code is formatted using the stylistic conventions enforced by
+`black <https://black.readthedocs.io/en/stable/>`_. Python coding standard are
+enforced by Pylint and verification of licensing is handled by REUSE. All of these
+are run using pre-commit, which you can run by using::
+
+    pip install pre-commit
+    pre-commit run --all-files
+
+Please see the output from ``pre-commit`` for more information about the various
+available options to help you work with the code base.
+
+Before submitting a PR, please remember to ``pre-commit run --all-files``.
+But if  you forget the CI process in Github will run it for you. ;-)
+
+CircUp uses the `Click <https://click.palletsprojects.com/en/7.x/>`_ module to
+run command-line interaction. The
+`AppDirs <https://pypi.org/project/appdirs/>`_ module is used to determine
+where to store user-specific assets created by the tool in such a way that
+meets the host operating system's usual conventions. The
+`python-semver <https://github.com/k-bx/python-semver>`_ package is used to
+validate and compare the semver values associated with modules. The ubiquitous
+`requests <http://python-requests.org/>`_ module is used for HTTP activity.
+
+Documentation, generated by `Sphinx <http://www.sphinx-doc.org/en/master/>`_,
+is based on this README and assembled by assets in the ``doc`` subdirectory.
+The latest version of the docs will be found on
+`Read the Docs <https://circup.readthedocs.io/>`_.
+
+Discussion of this tool happens on the Adafruit CircuitPython
+`Discord channel <https://discord.gg/rqrKDjU>`_.
diff --git a/CONTRIBUTING.rst.license b/CONTRIBUTING.rst.license
new file mode 100644
index 0000000..168c840
--- /dev/null
+++ b/CONTRIBUTING.rst.license
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
diff --git a/LICENSES/CC-BY-4.0.txt  b/LICENSES/CC-BY-4.0.txt 
new file mode 100644
index 0000000..3f92dfc
--- /dev/null
+++ b/LICENSES/CC-BY-4.0.txt 
@@ -0,0 +1,324 @@
+Creative Commons Attribution 4.0 International Creative Commons Corporation
+("Creative Commons") is not a law firm and does not provide legal services
+or legal advice. Distribution of Creative Commons public licenses does not
+create a lawyer-client or other relationship. Creative Commons makes its licenses
+and related information available on an "as-is" basis. Creative Commons gives
+no warranties regarding its licenses, any material licensed under their terms
+and conditions, or any related information. Creative Commons disclaims all
+liability for damages resulting from their use to the fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and conditions
+that creators and other rights holders may use to share original works of
+authorship and other material subject to copyright and certain other rights
+specified in the public license below. The following considerations are for
+informational purposes only, are not exhaustive, and do not form part of our
+licenses.
+
+Considerations for licensors: Our public licenses are intended for use by
+those authorized to give the public permission to use material in ways otherwise
+restricted by copyright and certain other rights. Our licenses are irrevocable.
+Licensors should read and understand the terms and conditions of the license
+they choose before applying it. Licensors should also secure all rights necessary
+before applying our licenses so that the public can reuse the material as
+expected. Licensors should clearly mark any material not subject to the license.
+This includes other CC-licensed material, or material used under an exception
+or limitation to copyright. More considerations for licensors : wiki.creativecommons.org/Considerations_for_licensors
+
+Considerations for the public: By using one of our public licenses, a licensor
+grants the public permission to use the licensed material under specified
+terms and conditions. If the licensor's permission is not necessary for any
+reason–for example, because of any applicable exception or limitation to copyright–then
+that use is not regulated by the license. Our licenses grant only permissions
+under copyright and certain other rights that a licensor has authority to
+grant. Use of the licensed material may still be restricted for other reasons,
+including because others have copyright or other rights in the material. A
+licensor may make special requests, such as asking that all changes be marked
+or described. Although not required by our licenses, you are encouraged to
+respect those requests where reasonable. More considerations for the public
+: wiki.creativecommons.org/Considerations_for_licensees Creative Commons Attribution
+4.0 International Public License
+
+By exercising the Licensed Rights (defined below), You accept and agree to
+be bound by the terms and conditions of this Creative Commons Attribution
+4.0 International Public License ("Public License"). To the extent this Public
+License may be interpreted as a contract, You are granted the Licensed Rights
+in consideration of Your acceptance of these terms and conditions, and the
+Licensor grants You such rights in consideration of benefits the Licensor
+receives from making the Licensed Material available under these terms and
+conditions.
+
+Section 1 – Definitions.
+
+a. Adapted Material means material subject to Copyright and Similar Rights
+that is derived from or based upon the Licensed Material and in which the
+Licensed Material is translated, altered, arranged, transformed, or otherwise
+modified in a manner requiring permission under the Copyright and Similar
+Rights held by the Licensor. For purposes of this Public License, where the
+Licensed Material is a musical work, performance, or sound recording, Adapted
+Material is always produced where the Licensed Material is synched in timed
+relation with a moving image.
+
+b. Adapter's License means the license You apply to Your Copyright and Similar
+Rights in Your contributions to Adapted Material in accordance with the terms
+and conditions of this Public License.
+
+c. Copyright and Similar Rights means copyright and/or similar rights closely
+related to copyright including, without limitation, performance, broadcast,
+sound recording, and Sui Generis Database Rights, without regard to how the
+rights are labeled or categorized. For purposes of this Public License, the
+rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
+
+d. Effective Technological Measures means those measures that, in the absence
+of proper authority, may not be circumvented under laws fulfilling obligations
+under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996,
+and/or similar international agreements.
+
+e. Exceptions and Limitations means fair use, fair dealing, and/or any other
+exception or limitation to Copyright and Similar Rights that applies to Your
+use of the Licensed Material.
+
+f. Licensed Material means the artistic or literary work, database, or other
+material to which the Licensor applied this Public License.
+
+g. Licensed Rights means the rights granted to You subject to the terms and
+conditions of this Public License, which are limited to all Copyright and
+Similar Rights that apply to Your use of the Licensed Material and that the
+Licensor has authority to license.
+
+h. Licensor means the individual(s) or entity(ies) granting rights under this
+Public License.
+
+i. Share means to provide material to the public by any means or process that
+requires permission under the Licensed Rights, such as reproduction, public
+display, public performance, distribution, dissemination, communication, or
+importation, and to make material available to the public including in ways
+that members of the public may access the material from a place and at a time
+individually chosen by them.
+
+j. Sui Generis Database Rights means rights other than copyright resulting
+from Directive 96/9/EC of the European Parliament and of the Council of 11
+March 1996 on the legal protection of databases, as amended and/or succeeded,
+as well as other essentially equivalent rights anywhere in the world.
+
+k. You means the individual or entity exercising the Licensed Rights under
+this Public License. Your has a corresponding meaning.
+
+Section 2 – Scope.
+
+   a. License grant.
+
+1. Subject to the terms and conditions of this Public License, the Licensor
+hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive,
+irrevocable license to exercise the Licensed Rights in the Licensed Material
+to:
+
+         A. reproduce and Share the Licensed Material, in whole or in part; and
+
+         B. produce, reproduce, and Share Adapted Material.
+
+2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions
+and Limitations apply to Your use, this Public License does not apply, and
+You do not need to comply with its terms and conditions.
+
+      3. Term. The term of this Public License is specified in Section 6(a).
+
+4. Media and formats; technical modifications allowed. The Licensor authorizes
+You to exercise the Licensed Rights in all media and formats whether now known
+or hereafter created, and to make technical modifications necessary to do
+so. The Licensor waives and/or agrees not to assert any right or authority
+to forbid You from making technical modifications necessary to exercise the
+Licensed Rights, including technical modifications necessary to circumvent
+Effective Technological Measures. For purposes of this Public License, simply
+making modifications authorized by this Section 2(a)(4) never produces Adapted
+Material.
+
+      5. Downstream recipients.
+
+A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed
+Material automatically receives an offer from the Licensor to exercise the
+Licensed Rights under the terms and conditions of this Public License.
+
+B. No downstream restrictions. You may not offer or impose any additional
+or different terms or conditions on, or apply any Effective Technological
+Measures to, the Licensed Material if doing so restricts exercise of the Licensed
+Rights by any recipient of the Licensed Material.
+
+6. No endorsement. Nothing in this Public License constitutes or may be construed
+as permission to assert or imply that You are, or that Your use of the Licensed
+Material is, connected with, or sponsored, endorsed, or granted official status
+by, the Licensor or others designated to receive attribution as provided in
+Section 3(a)(1)(A)(i).
+
+   b. Other rights.
+
+1. Moral rights, such as the right of integrity, are not licensed under this
+Public License, nor are publicity, privacy, and/or other similar personality
+rights; however, to the extent possible, the Licensor waives and/or agrees
+not to assert any such rights held by the Licensor to the limited extent necessary
+to allow You to exercise the Licensed Rights, but not otherwise.
+
+2. Patent and trademark rights are not licensed under this Public License.
+
+3. To the extent possible, the Licensor waives any right to collect royalties
+from You for the exercise of the Licensed Rights, whether directly or through
+a collecting society under any voluntary or waivable statutory or compulsory
+licensing scheme. In all other cases the Licensor expressly reserves any right
+to collect such royalties.
+
+Section 3 – License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the following
+conditions.
+
+   a. Attribution.
+
+1. If You Share the Licensed Material (including in modified form), You must:
+
+A. retain the following if it is supplied by the Licensor with the Licensed
+Material:
+
+i. identification of the creator(s) of the Licensed Material and any others
+designated to receive attribution, in any reasonable manner requested by the
+Licensor (including by pseudonym if designated);
+
+            ii. a copyright notice;
+
+            iii. a notice that refers to this Public License;
+
+            iv. a notice that refers to the disclaimer of warranties;
+
+v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
+
+B. indicate if You modified the Licensed Material and retain an indication
+of any previous modifications; and
+
+C. indicate the Licensed Material is licensed under this Public License, and
+include the text of, or the URI or hyperlink to, this Public License.
+
+2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner
+based on the medium, means, and context in which You Share the Licensed Material.
+For example, it may be reasonable to satisfy the conditions by providing a
+URI or hyperlink to a resource that includes the required information.
+
+3. If requested by the Licensor, You must remove any of the information required
+by Section 3(a)(1)(A) to the extent reasonably practicable.
+
+4. If You Share Adapted Material You produce, the Adapter's License You apply
+must not prevent recipients of the Adapted Material from complying with this
+Public License.
+
+Section 4 – Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that apply to
+Your use of the Licensed Material:
+
+a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract,
+reuse, reproduce, and Share all or a substantial portion of the contents of
+the database;
+
+b. if You include all or a substantial portion of the database contents in
+a database in which You have Sui Generis Database Rights, then the database
+in which You have Sui Generis Database Rights (but not its individual contents)
+is Adapted Material; and
+
+c. You must comply with the conditions in Section 3(a) if You Share all or
+a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not replace
+Your obligations under this Public License where the Licensed Rights include
+other Copyright and Similar Rights.
+
+Section 5 – Disclaimer of Warranties and Limitation of Liability.
+
+a. Unless otherwise separately undertaken by the Licensor, to the extent possible,
+the Licensor offers the Licensed Material as-is and as-available, and makes
+no representations or warranties of any kind concerning the Licensed Material,
+whether express, implied, statutory, or other. This includes, without limitation,
+warranties of title, merchantability, fitness for a particular purpose, non-infringement,
+absence of latent or other defects, accuracy, or the presence or absence of
+errors, whether or not known or discoverable. Where disclaimers of warranties
+are not allowed in full or in part, this disclaimer may not apply to You.
+
+b. To the extent possible, in no event will the Licensor be liable to You
+on any legal theory (including, without limitation, negligence) or otherwise
+for any direct, special, indirect, incidental, consequential, punitive, exemplary,
+or other losses, costs, expenses, or damages arising out of this Public License
+or use of the Licensed Material, even if the Licensor has been advised of
+the possibility of such losses, costs, expenses, or damages. Where a limitation
+of liability is not allowed in full or in part, this limitation may not apply
+to You.
+
+c. The disclaimer of warranties and limitation of liability provided above
+shall be interpreted in a manner that, to the extent possible, most closely
+approximates an absolute disclaimer and waiver of all liability.
+
+Section 6 – Term and Termination.
+
+a. This Public License applies for the term of the Copyright and Similar Rights
+licensed here. However, if You fail to comply with this Public License, then
+Your rights under this Public License terminate automatically.
+
+b. Where Your right to use the Licensed Material has terminated under Section
+6(a), it reinstates:
+
+1. automatically as of the date the violation is cured, provided it is cured
+within 30 days of Your discovery of the violation; or
+
+      2. upon express reinstatement by the Licensor.
+
+c. For the avoidance of doubt, this Section 6(b) does not affect any right
+the Licensor may have to seek remedies for Your violations of this Public
+License.
+
+d. For the avoidance of doubt, the Licensor may also offer the Licensed Material
+under separate terms or conditions or stop distributing the Licensed Material
+at any time; however, doing so will not terminate this Public License.
+
+   e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
+
+Section 7 – Other Terms and Conditions.
+
+a. The Licensor shall not be bound by any additional or different terms or
+conditions communicated by You unless expressly agreed.
+
+b. Any arrangements, understandings, or agreements regarding the Licensed
+Material not stated herein are separate from and independent of the terms
+and conditions of this Public License.
+
+Section 8 – Interpretation.
+
+a. For the avoidance of doubt, this Public License does not, and shall not
+be interpreted to, reduce, limit, restrict, or impose conditions on any use
+of the Licensed Material that could lawfully be made without permission under
+this Public License.
+
+b. To the extent possible, if any provision of this Public License is deemed
+unenforceable, it shall be automatically reformed to the minimum extent necessary
+to make it enforceable. If the provision cannot be reformed, it shall be severed
+from this Public License without affecting the enforceability of the remaining
+terms and conditions.
+
+c. No term or condition of this Public License will be waived and no failure
+to comply consented to unless expressly agreed to by the Licensor.
+
+d. Nothing in this Public License constitutes or may be interpreted as a limitation
+upon, or waiver of, any privileges and immunities that apply to the Licensor
+or You, including from the legal processes of any jurisdiction or authority.
+
+Creative Commons is not a party to its public licenses. Notwithstanding, Creative
+Commons may elect to apply one of its public licenses to material it publishes
+and in those instances will be considered the "Licensor." The text of the
+Creative Commons public licenses is dedicated to the public domain under the
+CC0 Public Domain Dedication. Except for the limited purpose of indicating
+that material is shared under a Creative Commons public license or as otherwise
+permitted by the Creative Commons policies published at creativecommons.org/policies,
+Creative Commons does not authorize the use of the trademark "Creative Commons"
+or any other trademark or logo of Creative Commons without its prior written
+consent including, without limitation, in connection with any unauthorized
+modifications to any of its public licenses or any other arrangements, understandings,
+or agreements concerning use of licensed material. For the avoidance of doubt,
+this paragraph does not form part of the public licenses.
+
+Creative Commons may be contacted at creativecommons.org.
diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt
new file mode 100644
index 0000000..204b93d
--- /dev/null
+++ b/LICENSES/MIT.txt
@@ -0,0 +1,19 @@
+MIT License Copyright (c) <year> <copyright holders>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/LICENSES/Unlicense.txt b/LICENSES/Unlicense.txt
new file mode 100644
index 0000000..24a8f90
--- /dev/null
+++ b/LICENSES/Unlicense.txt
@@ -0,0 +1,20 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute
+this software, either in source code form or as a compiled binary, for any
+purpose, commercial or non-commercial, and by any means.
+
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and
+to the detriment of our heirs and successors. We intend this dedication to
+be an overt act of relinquishment in perpetuity of all present and future
+rights to this software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information,
+please refer to <https://unlicense.org/>
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index 7539083..0000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,1 +0,0 @@
-include CHANGES.rst
diff --git a/Makefile b/Makefile
deleted file mode 100644
index f6c5c7f..0000000
--- a/Makefile
+++ /dev/null
@@ -1,66 +0,0 @@
-XARGS := xargs -0 $(shell test $$(uname) = Linux && echo -r)
-GREP_T_FLAG := $(shell test $$(uname) = Linux && echo -T)
-
-all:
-	@echo "\nThere is no default Makefile target right now. Try:\n"
-	@echo "make clean - reset the project and remove auto-generated assets."
-	@ecxho "make black - runs Black Python code formatter."
-	@echo "make pylint - runs Python Linter."
-	@echo "make test - run the test suite."
-	@echo "make coverage - view a report on test coverage."
-	@echo "make tidy - tidy code with the 'black' formatter."
-	@echo "make check - run all the checkers and tests."
-	@echo "make dist - make a dist/wheel for the project."
-	@echo "make publish-test - publish the project to PyPI test instance."
-	@echo "make publish-live - publish the project to PyPI production."
-	@echo "make docs - run sphinx to create project documentation.\n"
-
-clean:
-	rm -rf build
-	rm -rf dist
-	rm -rf .coverage
-	rm -rf .eggs
-	rm -rf .pytest_cache
-	rm -rf .tox
-	rm -rf docs/_build
-	find . \( -name '*.py[co]' -o -name dropin.cache \) -delete
-	find . \( -name '*.bak' -o -name dropin.cache \) -delete
-	find . \( -name '*.tgz' -o -name dropin.cache \) -delete
-	find . | grep -E "(__pycache__)" | xargs rm -rf
-
-
-black:
-	black --check --target-version=py35 .
-
-pylint:
-	pylint circup.py
-
-test: clean
-	pytest --random-order
-
-coverage: clean
-	pytest --random-order --cov-config .coveragerc --cov-report term-missing --cov=circup tests/
-
-tidy: clean
-	@echo "\nTidying code with black..."
-	black --target-version=py35 .
-
-check: clean tidy black pylint coverage
-
-dist: check
-	@echo "\nChecks pass, good to package..."
-	python setup.py sdist bdist_wheel
-
-publish-test: dist
-	@echo "\nPackaging complete... Uploading to PyPi..."
-	twine upload -r test --sign dist/*
-
-publish-live: dist
-	@echo "\nPackaging complete... Uploading to PyPi..."
-	twine upload --sign dist/*
-
-docs: clean
-	$(MAKE) -C docs html
-	@echo "\nDocumentation can be found here:"
-	@echo file://`pwd`/docs/_build/html/index.html
-	@echo "\n"
diff --git a/README.rst b/README.rst
index a134b63..946b667 100644
--- a/README.rst
+++ b/README.rst
@@ -28,11 +28,11 @@ If you have no idea what a virtualenv is, try the following command,
     * On Windows, type the same command, but append ``Scripts`` to the
       resulting path.
 
-What?
------
+What does Circup Do?
+--------------------
 
-Each CircuitPython library on the device (``.py``, *NOT* ``.mpy`` at this time)
-usually has a version number as metadata within the module.
+Each CircuitPython library on the device usually has a version number as
+metadata within the module.
 
 This utility looks at all the libraries on the device and checks if they are
 the most recent (compared to the versions found in the most recent version of
@@ -51,7 +51,7 @@ https://circuitpython.org/libraries
 Usage
 -----
 
-If you need more detailed help using Circup see the Learn Guide article 
+If you need more detailed help using Circup see the Learn Guide article
 `"Use CircUp to easily keep your CircuitPython libraries up to date" <https://learn.adafruit.com/keep-your-circuitpython-libraries-on-devices-up-to-date-with-circup/>`_.
 
 First, plug in a device running CircuiPython. This should appear as a mounted
@@ -110,9 +110,9 @@ To list all the modules that require an update::
     $ circup list
     The following modules are out of date or probably need an update.
 
-    Module             Version  Latest   
-    ------------------ -------- -------- 
-    adafruit_binascii  v1.0     1.0.1    
+    Module             Version  Latest
+    ------------------ -------- --------
+    adafruit_binascii  v1.0     1.0.1
     adafruit_ble       1.0.2    4.0
 
 To interactively update the out-of-date modules::
@@ -187,145 +187,6 @@ That's it!
 
     https://github.com/adafruit/circup
 
-Developer Setup
----------------
-
-.. note::
-
-    Please try to use Python 3.6+ while developing CircUp. This is so we can
-    use the
-    `Black code formatter <https://black.readthedocs.io/en/stable/index.html>`_
-    (which only works with Python 3.6+).
-
-Clone the repository then make a virtualenv. From the root of the project,
-install the requirements::
-
-    pip install -e ".[dev]"
-
-Run the test suite::
-
-    make check
-
-.. warning::
-
-    Whenever you run ``make check``, to ensure the test suite starts from a
-    known clean state, all auto-generated assets are deleted. This includes
-    assets generated by running ``pip install -e ".[dev]"``, including the
-    ``circup`` command itself. Simply re-run ``pip`` to re-generate the
-    assets.
-
-There is a Makefile that helps with most of the common workflows associated
-with development. Typing "make" on its own will list the options thus::
-
-    $ make
-
-    There is no default Makefile target right now. Try:
-
-    make clean - reset the project and remove auto-generated assets.
-    make pyflakes - run the PyFlakes code checker.
-    make pycodestyle - run the PEP8 style checker.
-    make test - run the test suite.
-    make coverage - view a report on test coverage.
-    make tidy - tidy code with the 'black' formatter.
-    make check - run all the checkers and tests.
-    make dist - make a dist/wheel for the project.
-    make publish-test - publish the project to PyPI test instance.
-    make publish-live - publish the project to PyPI production.
-    make docs - run sphinx to create project documentation.
-
-.. note::
-
-    On Windows there is a ``make.cmd`` file that calls ``make.py``: a script
-    that works in a similar way to the ``make`` command on Unix-like operating
-    systems. Typing ``make`` will display help for the various commands it
-    provides that are equivalent of those in the Unix Makefile.
-
-How?
-####
-
-The ``circup`` tool checks for a connected CircuitPython device by
-interrogating the local filesystem to find a path to a directory which ends
-with ``"CIRCUITPYTHON"`` (the name under which a CircuitPython device is
-mounted by the host operating system). This is handled in the ``find_device``
-function.
-
-A Python module on a connected device is represented by an instance of the
-``Module`` class. This class provides useful methods for discerning if the
-module is out of date, returning useful representations of it in order to
-display information to the user, or updating the module on the connected
-device with whatever the version is in the latest Adafruit CircuitPython
-Bundle.
-
-All of the libraries included in the Adafruit CircuitPython Bundle contain,
-somewhere within their code, two metadata objects called ``__version__`` and
-``__repo__``.
-
-The ``__repo__`` object is a string containing the GitHub repository URL, as
-used to clone the project.
-
-The ``__version__`` object is interesting because *within the source code in
-Git* the value is **always** the string ``"0.0.0-auto.0"``. When a new release
-is made of the bundle, this value is automatically replaced by the build
-scripts to the correct version information, which will always conform to the
-`semver standard <https://semver.org/>`_.
-
-Given this context, the ``circup`` tool will check a configuration file
-to discern what *it* thinks is the latest version of the bundle. If there is
-no configuration file (for example, on first run), then the bundle version is
-assumed to be ``"0"``.
-
-Next, it checks GitHub for the tag value (denoting the version) of the very
-latest bundle release. Bundle versions are based upon the date of release, for
-instance ``"20190904"``. If the latest version on GitHub is later than the
-version ``circup`` currently has, then the latest version of the bundle
-is automatically downloaded and cached away somewhere.
-
-In this way, the ``circup`` tool is able to have available to it both a path
-to a connected CIRCUITPYTHON devce and a copy of the latest version, including
-the all important version information, of the Adafruit CircuitPython Bundle.
-
-Exactly the same function (``get_modules``) is used to extract the metadata
-from the modules on both the connected device and in the bundle cache. This
-metadata is used to instantiate instances of the ``Module`` class which is
-subsequently used to facilitate the various commands the tool makes available.
-
-These commands are defined at the very end of the ``circup.py`` code.
-
-Unit tests can be found in the ``tests`` directory. CircUp uses
-`pytest <http://www.pytest.org/en/latest/>`_ style testing conventions. Test
-functions should include a comment to describe its *intention*. We currently
-have 100% unit test coverage for all the core functionality (excluding
-functions used to define the CLI commands).
-
-To run the full test suite, type::
-
-    make check
-
-All code is formatted using the stylistic conventions enforced by
-`black <https://black.readthedocs.io/en/stable/>`_. The tidying of code
-formatting is part of the ``make check`` process, but you can also just use::
-
-    make tidy
-
-Please see the output from ``make`` for more information about the various
-available options to help you work with the code base. TL;DR ``make check``
-runs everything.
-
-Before submitting a PR, please remember to ``make check``. ;-)
-
-CircUp uses the `Click <https://click.palletsprojects.com/en/7.x/>`_ module to
-run command-line interaction. The
-`AppDirs <https://pypi.org/project/appdirs/>`_ module is used to determine
-where to store user-specific assets created by the tool in such a way that
-meets the host operating system's usual conventions. The
-`python-semver <https://github.com/k-bx/python-semver>`_ package is used to
-validate and compare the semver values associated with modules. The ubiquitous
-`requests <http://python-requests.org/>`_ module is used for HTTP activity.
-
-Documentation, generated by `Sphinx <http://www.sphinx-doc.org/en/master/>`_,
-is based on this README and assembled by assets in the ``doc`` subdirectory.
-The latest version of the docs will be found on
-`Read the Docs <https://circup.readthedocs.io/>`_.
 
 Discussion of this tool happens on the Adafruit CircuitPython
 `Discord channel <https://discord.gg/rqrKDjU>`_.
diff --git a/README.rst.license b/README.rst.license
new file mode 100644
index 0000000..168c840
--- /dev/null
+++ b/README.rst.license
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
diff --git a/circup.py b/circup.py
index aec2215..1ba2669 100644
--- a/circup.py
+++ b/circup.py
@@ -1,25 +1,8 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
 """
 CircUp -- a utility to manage and update libraries on a CircuitPython device.
-
-Copyright (c) 2019 Adafruit Industries
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
 """
 
 
@@ -58,7 +41,7 @@
 LOG_DIR = appdirs.user_log_dir(appname="circup", appauthor="adafruit")
 #: The location of the log file for the utility.
 LOGFILE = os.path.join(LOG_DIR, "circup.log")
-# Blank lines and libraries don't go on devices
+#:  The libraries (and blank lines) which don't go on devices
 NOT_MCU_LIBRARIES = [
     "",
     "adafruit-blinka",
@@ -106,8 +89,8 @@ def __init__(self, path, repo, device_version, bundle_version, mpy):
         resulting self.file value will be None, and the name will be the
         basename of the directory path.
 
-        :param str path: The path to the module on the connected CIRCUITPYTHON
-        device.
+        :param str path: The path to the module on the connected
+            CIRCUITPYTHON device.
         :param str repo: The URL of the Git repository for this module.
         :param str device_version: The semver value for the version on device.
         :param str bundle_version: The semver value for the version in bundle.
@@ -233,12 +216,16 @@ def __repr__(self):
 def clean_library_name(assumed_library_name):
     """
     Most CP repos and library names are look like this:
+
         repo: Adafruit_CircuitPython_LC709203F
         library: adafruit_lc709203f
-        But some do not and this handles cleaning that up.
+
+    But some do not and this handles cleaning that up.
     Also cleans up if the pypi or reponame is passed in instead of the
     CP library name.
-    :param str an assumed name of a library from user or requirements.txt entry
+
+    :param str assumed_library_name: An assumed name of a library from user
+        or requirements.txt entry
     :return: str proper library name
     """
     not_standard_names = {
@@ -302,7 +289,7 @@ def ensure_latest_bundle():
 def extract_metadata(path):
     """
     Given an file path, return a dictionary containing metadata extracted from
-    dunder attributes found therein. Works with both *.py and *.mpy files.
+    dunder attributes found therein. Works with both .py and .mpy files.
 
     For Python source files, such metadata assignments should be simple and
     single-line. For example::
@@ -310,7 +297,7 @@ def extract_metadata(path):
         __version__ = "1.1.4"
         __repo__ = "https://github.com/adafruit/SomeLibrary.git"
 
-    For byte compiled *.mpy files, a brute force / backtrack approach is used
+    For byte compiled .mpy files, a brute force / backtrack approach is used
     to find the __version__ number in the file -- see comments in the
     code for the implementation details.
 
@@ -538,8 +525,9 @@ def get_circuitpython_version(device_path):
 def get_dependencies(*requested_libraries, mod_names, to_install=()):
     """
     Return a list of other CircuitPython libraries
-    :param tuple requested_libraries we loop through and find dependencies
-    :param object mod_names all the modules metadata from bundle
+
+    :param tuple requested_libraries: The libraries to search for dependencies
+    :param object mod_names:  All the modules metadata from bundle
     :return: tuple of module names to install which we build
     """
     # Internal variables
@@ -662,7 +650,8 @@ def get_requirements(library_name):
     Return a string of the requirements.txt for a GitHub Repo
     NOTE: This is only looks at the py bundle. No known differences in the mpy
     bundle for requirements.txt
-    :param str repo_name GitHub repo name
+
+    :param str library_name: CircuitPython library name
     :return: str the content of requirements.txt or None if not found
     """
     tag = get_latest_tag()
@@ -682,15 +671,14 @@ def install_module(device_path, name, py, mod_names):  # pragma: no cover
     Finds a connected device and installs a given module name if it
     is available in the current module bundle and is not already
     installed on the device.
+    TODO: There is currently no check for the version.
 
     :param str device_path: The path to the connected board.
     :param str name: Name of module to install
     :param bool py: Boolean to specify if the module should be installed from
                     source or from a pre-compiled module
-    :params mod_names: Dictionary of metadata from modules that can be generated
+    :param mod_names: Dictionary of metadata from modules that can be generated
                        with get_bundle_versions()
-
-    TODO: There is currently no check for the version.
     """
     if not name:
         click.echo("No module name(s) provided.")
@@ -753,7 +741,9 @@ def install_module(device_path, name, py, mod_names):  # pragma: no cover
 
 def libraries_from_requirements(requirements):
     """
-    :param str requirements is a string version of a requirements.txt
+    Clean up supplied requirements.txt and turn into tuple of CP libraries
+
+    :param str requirements: A string version of a requirements.txt
     :return: tuple of library names
     """
     libraries = ()
diff --git a/docs/Makefile b/docs/Makefile
deleted file mode 100644
index d4bb2cb..0000000
--- a/docs/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-# Minimal makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line, and also
-# from the environment for the first two.
-SPHINXOPTS    ?=
-SPHINXBUILD   ?= sphinx-build
-SOURCEDIR     = .
-BUILDDIR      = _build
-
-# Put it first so that "make" without argument is like "make help".
-help:
-	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
-
-.PHONY: help Makefile
-
-# Catch-all target: route all unknown targets to Sphinx using the new
-# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
-%: Makefile
-	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico
new file mode 100644
index 0000000..5aca983
Binary files /dev/null and b/docs/_static/favicon.ico differ
diff --git a/docs/_static/favicon.ico.license b/docs/_static/favicon.ico.license
new file mode 100644
index 0000000..86a3fbf
--- /dev/null
+++ b/docs/_static/favicon.ico.license
@@ -0,0 +1,3 @@
+SPDX-FileCopyrightText: 2018 Phillip Torrone for Adafruit Industries
+
+SPDX-License-Identifier: CC-BY-4.0
diff --git a/docs/conf.py b/docs/conf.py
index 93678d6..a71107e 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,81 +1,186 @@
-# Configuration file for the Sphinx documentation builder.
-#
-# This file only contains a selection of the most common options. For a full
-# list see the documentation:
-# https://www.sphinx-doc.org/en/master/usage/configuration.html
-
-# -- Path setup --------------------------------------------------------------
+# -*- coding: utf-8 -*-
 
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
+# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
 #
+# SPDX-License-Identifier: MIT
+
 import os
 import sys
 
 sys.path.insert(0, os.path.abspath(".."))
 
-
-# -- Project information -----------------------------------------------------
-
-project = "CircUp"
-copyright = "2019, Adafruit Industries"
-author = "Adafruit Industries"
-
-# The full version, including alpha/beta/rc tags
-import circup
-
-release = circup.__version__
-
-
-# -- General configuration ---------------------------------------------------
+# -- General configuration ------------------------------------------------
 
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
 extensions = [
     "sphinx.ext.autodoc",
+    "sphinx.ext.intersphinx",
+    "sphinx.ext.napoleon",
+    "sphinx.ext.todo",
     "sphinx.ext.viewcode",
 ]
 
+# TODO: Please Read!
+# Uncomment the below if you use native CircuitPython modules such as
+# digitalio, micropython and busio. List the modules you use. Without it, the
+# autodoc module docs will fail to generate with a warning.
+# autodoc_mock_imports = ["digitalio", "busio"]
+
+
+intersphinx_mapping = {
+    "python": ("https://docs.python.org/3.4", None),
+    "CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None),
+}
+
+# Show the docstring from both the class and its __init__() method.
+autoclass_content = "both"
+
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ["_templates"]
 
+source_suffix = ".rst"
+
+# The master toctree document.
+master_doc = "index"
+
+# General information about the project.
+project = "Circup"
+copyright = "2019, Adafruit Industries"
+author = "Adafruit Industries"
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = "1.0"
+# The full version, including alpha/beta/rc tags.
+release = "1.0"
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
-# This pattern also affects html_static_path and html_extra_path.
-exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = [
+    "_build",
+    "Thumbs.db",
+    ".DS_Store",
+    ".env",
+    "CODE_OF_CONDUCT.md",
+]
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#
+default_role = "any"
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#
+add_function_parentheses = True
 
 # The name of the Pygments (syntax highlighting) style to use.
 pygments_style = "sphinx"
 
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+# If this is True, todo emits a warning for each TODO entries. The default is False.
+todo_emit_warnings = True
+
+napoleon_numpy_docstring = False
 
-# -- Options for HTML output -------------------------------------------------
+# -- Options for HTML output ----------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
 #
-html_theme = "alabaster"
+on_rtd = os.environ.get("READTHEDOCS", None) == "True"
+
+if not on_rtd:  # only import and set the theme if we're building docs locally
+    try:
+        import sphinx_rtd_theme
+
+        html_theme = "sphinx_rtd_theme"
+        html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."]
+    except:
+        html_theme = "default"
+        html_theme_path = ["."]
+else:
+    html_theme_path = ["."]
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
 html_static_path = ["_static"]
 
-html_logo = "logo.png"
-
-html_theme_options = {
-    "description": "The CircuitPython Library Updater",
-    "logo_name": True,
-    "logo_text_align": "center",
-    "github_user": "adafruit",
-    "github_repo": "circup",
-    "page_width": "1200px",
+# The name of an image file (relative to this directory) to use as a favicon of
+# the docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#
+html_favicon = "_static/favicon.ico"
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = "Adafruit Circup doc"
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+    # The paper size ('letterpaper' or 'a4paper').
+    # 'papersize': 'letterpaper',
+    # The font size ('10pt', '11pt' or '12pt').
+    # 'pointsize': '10pt',
+    # Additional stuff for the LaTeX preamble.
+    # 'preamble': '',
+    # Latex figure (float) alignment
+    # 'figure_align': 'htbp',
 }
 
-html_sidebars = {
-    "**": [
-        "about.html",
-        "searchbox.html",
-    ]
-}
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    (
+        master_doc,
+        "Adafruit_Circup.tex",
+        "Adafruit Circup Documentation",
+        author,
+        "manual",
+    ),
+]
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    (
+        master_doc,
+        "Circup",
+        "Adafruit Circup Library Documentation",
+        [author],
+        1,
+    ),
+]
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+    (
+        master_doc,
+        "Adafruit Circup",
+        "Adafruit Circup Documentation",
+        author,
+        "Adafruit Circup",
+    ),
+]
diff --git a/docs/index.rst b/docs/index.rst
index efecbe0..d06ef8f 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -13,7 +13,6 @@ API
 .. automodule:: circup
     :members:
 
-.. include:: ../CHANGES.rst
 
 License
 =======
diff --git a/docs/index.rst.license b/docs/index.rst.license
new file mode 100644
index 0000000..168c840
--- /dev/null
+++ b/docs/index.rst.license
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
diff --git a/docs/logo.png.license b/docs/logo.png.license
new file mode 100644
index 0000000..168c840
--- /dev/null
+++ b/docs/logo.png.license
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
diff --git a/docs/make.bat b/docs/make.bat
deleted file mode 100644
index 2119f51..0000000
--- a/docs/make.bat
+++ /dev/null
@@ -1,35 +0,0 @@
-@ECHO OFF
-
-pushd %~dp0
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
-	set SPHINXBUILD=sphinx-build
-)
-set SOURCEDIR=.
-set BUILDDIR=_build
-
-if "%1" == "" goto help
-
-%SPHINXBUILD% >NUL 2>NUL
-if errorlevel 9009 (
-	echo.
-	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
-	echo.installed, then set the SPHINXBUILD environment variable to point
-	echo.to the full path of the 'sphinx-build' executable. Alternatively you
-	echo.may add the Sphinx directory to PATH.
-	echo.
-	echo.If you don't have Sphinx installed, grab it from
-	echo.http://sphinx-doc.org/
-	exit /b 1
-)
-
-%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
-goto end
-
-:help
-%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
-
-:end
-popd
diff --git a/make.cmd b/make.cmd
deleted file mode 100644
index 6305497..0000000
--- a/make.cmd
+++ /dev/null
@@ -1,1 +0,0 @@
-python make\make.py %*
diff --git a/make/make.py b/make/make.py
deleted file mode 100644
index 5dab996..0000000
--- a/make/make.py
+++ /dev/null
@@ -1,272 +0,0 @@
-#!python3
-"""
-A "pretend" make command written in Python for Windows users. :-)
-"""
-import os
-import sys
-import fnmatch
-import shutil
-import subprocess
-
-PYTEST = "pytest"
-BLACK = "black"
-PYLINT = "pylint"
-
-INCLUDE_PATTERNS = {"*.py"}
-EXCLUDE_PATTERNS = {"build/*", "docs/*"}
-_exported = {}
-
-
-def _walk(start_from=".", include_patterns=None, exclude_patterns=None, recurse=True):
-    if include_patterns:
-        _include_patterns = set(os.path.normpath(p) for p in include_patterns)
-    else:
-        _include_patterns = set()
-    if exclude_patterns:
-        _exclude_patterns = set(os.path.normpath(p) for p in exclude_patterns)
-    else:
-        _exclude_patterns = set()
-
-    for dirpath, dirnames, filenames in os.walk(start_from):
-        for filename in filenames:
-            filepath = os.path.normpath(os.path.join(dirpath, filename))
-
-            if not any(
-                fnmatch.fnmatch(filepath, pattern) for pattern in _include_patterns
-            ):
-                continue
-
-            if any(fnmatch.fnmatch(filepath, pattern) for pattern in _exclude_patterns):
-                continue
-
-            yield filepath
-
-        if not recurse:
-            break
-
-
-def _process_code(executable, use_python, *args):
-    """
-    Perform some action (check, translate etc.) across the .py files
-    in the codebase, skipping docs and build artefacts.
-    """
-    if use_python:
-        execution = ["python", executable]
-    else:
-        execution = [executable]
-    returncodes = set()
-    for filepath in _walk(".", INCLUDE_PATTERNS, EXCLUDE_PATTERNS, False):
-        p = subprocess.run(execution + [filepath] + list(args))
-        returncodes.add(p.returncode)
-    for filepath in _walk("tests", INCLUDE_PATTERNS, EXCLUDE_PATTERNS):
-        p = subprocess.run(execution + [filepath] + list(args))
-        returncodes.add(p.returncode)
-    return max(returncodes)
-
-
-def _rmtree(dirpath, cascade_errors=False):
-    """
-    Remove a directory and its contents, including subdirectories.
-    """
-    try:
-        shutil.rmtree(dirpath)
-    except OSError:
-        if cascade_errors:
-            raise
-
-
-def _rmfiles(start_from, pattern):
-    """
-    Remove files from a directory and its descendants.
-
-    Starting from `start_from` directory and working downwards,
-    remove all files which match `pattern`, eg *.pyc.
-    """
-    for filepath in _walk(start_from, {pattern}):
-        os.remove(filepath)
-
-
-def export(function):
-    """
-    Decorator to tag certain functions as exported, meaning
-    that they show up as a command, with arguments, when this
-    file is run.
-    """
-    _exported[function.__name__] = function
-    return function
-
-
-@export
-def test(*pytest_args):
-    """
-    Run the test suite.
-
-    Call py.test to run the test suite with additional args.
-    The subprocess runner will raise an exception if py.test exits
-    with a failure value. This forces things to stop if tests fail.
-    """
-    print("\ntest")
-    return subprocess.run([PYTEST] + list(pytest_args)).returncode
-
-
-@export
-def coverage():
-    """
-    View a report on test coverage.
-
-    Call py.test with coverage turned on.
-    """
-    print("\ncoverage")
-    return subprocess.run(
-        [
-            PYTEST,
-            "--cov-config",
-            ".coveragerc",
-            "--cov-report",
-            "term-missing",
-            "--cov=circup",
-            "tests/",
-        ]
-    ).returncode
-
-
-@export
-def black(*black_args):
-    """
-    Run Black in check mode
-    """
-    args = (BLACK, "--check", "--target-version", "py35", ".") + black_args
-    result = subprocess.run(args).returncode
-    if result > 0:
-        return result
-
-
-@export
-def pylint():
-    """
-    Run python Linter
-    """
-    # args = ("circup.py",)
-    # return _process_code(PYLINT, False, *args)
-    args = (PYLINT, "circup.py")
-    result = subprocess.run(args).returncode
-    if result > 0:
-        return result
-
-
-@export
-def tidy(*tidy_args):
-    """
-    Run black against the code and tests.
-    """
-    print("\nTidy code")
-    args = (BLACK, "--target-version", "py35", ".")
-    result = subprocess.run(args).returncode
-    if result > 0:
-        return result
-
-
-@export
-def check():
-    """
-    Run all the checkers and tests.
-    """
-    print("\nCheck")
-    funcs = [clean, tidy, black, pylint, coverage]
-    for func in funcs:
-        return_code = func()
-        if return_code != 0:
-            return return_code
-    return 0
-
-
-@export
-def clean():
-    """
-    Reset the project and remove auto-generated assets.
-    """
-    print("\nClean")
-    _rmtree("build")
-    _rmtree("dist")
-    _rmtree("circup.egg-info")
-    _rmtree("coverage")
-    _rmtree("docs/build")
-    _rmfiles(".", "*.pyc")
-    return 0
-
-
-@export
-def dist():
-    """
-    Generate a source distribution and a binary wheel.
-    """
-    check()
-    print("Checks pass; good to package")
-    return subprocess.run(["python", "setup.py", "sdist", "bdist_wheel"]).returncode
-
-
-@export
-def publish_test():
-    """
-    Upload to a test PyPI.
-    """
-    dist()
-    print("Packaging complete; upload to PyPI")
-    return subprocess.run(
-        ["twine", "upload", "-r", "test", "--sign", "dist/*"]
-    ).returncode
-
-
-@export
-def publish_live():
-    """
-    Upload to PyPI.
-    """
-    dist()
-    print("Packaging complete; upload to PyPI")
-    return subprocess.run(["twine", "upload", "--sign", "dist/*"]).returncode
-
-
-@export
-def docs():
-    """
-    Build the docs.
-    """
-    cwd = os.getcwd()
-    os.chdir("docs")
-    try:
-        return subprocess.run(["cmd", "/c", "make.bat", "html"]).returncode
-    except Exception:
-        return 1
-    finally:
-        os.chdir(cwd)
-
-
-@export
-def help():
-    """
-    Display all commands with their description in alphabetical order.
-    """
-    module_doc = sys.modules["__main__"].__doc__ or "check"
-    print(module_doc + "\n" + "=" * len(module_doc) + "\n")
-
-    for command, function in sorted(_exported.items()):
-        doc = function.__doc__
-        print("make {}{}".format(command, doc))
-
-
-def main(command="help", *args):
-    """
-    Dispatch on command name, passing all remaining parameters to the
-    module-level function.
-    """
-    try:
-        function = _exported[command]
-    except KeyError:
-        raise RuntimeError("No such command: %s" % command)
-    else:
-        return function(*args)
-
-
-if __name__ == "__main__":
-    sys.exit(main(*sys.argv[1:]))
diff --git a/requirements.txt b/requirements.txt
index 0a35715..47e4996 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,6 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
 alabaster==0.7.12
 appdirs==1.4.3
 atomicwrites==1.3.0
diff --git a/setup.py b/setup.py
index f3d4db6..daeb895 100644
--- a/setup.py
+++ b/setup.py
@@ -1,3 +1,8 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+
+
 """A setuptools based setup module.
 See:
 https://packaging.python.org/guides/distributing-packages-using-setuptools/


Test patch:
diff --git a/tests/bad_module/my_module.py b/tests/bad_module/my_module.py
index bbba9de..006bde2 100644
--- a/tests/bad_module/my_module.py
+++ b/tests/bad_module/my_module.py
@@ -1,3 +1,10 @@
-# A simple directory based Python module that's missing metadata.
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+
+"""A simple directory based Python module that's missing metadata."""
+
+
 def hello():
+    """A hello function"""
     return "Hello, World!"
diff --git a/tests/bundle.json.license b/tests/bundle.json.license
new file mode 100644
index 0000000..168c840
--- /dev/null
+++ b/tests/bundle.json.license
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
diff --git a/tests/device.json.license b/tests/device.json.license
new file mode 100644
index 0000000..168c840
--- /dev/null
+++ b/tests/device.json.license
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
diff --git a/tests/dir_module/my_module.py b/tests/dir_module/my_module.py
index c5f48b5..0910a02 100644
--- a/tests/dir_module/my_module.py
+++ b/tests/dir_module/my_module.py
@@ -1,8 +1,13 @@
-# A simple directory based Python module containing expected "local" metadata.
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+
+"""A simple directory based Python module containing expected "local" metadata."""
 
 __version__ = "3.2.1"
 __repo__ = "https://github.com/adafruit/SomeModule.git"
 
 
 def hello():
+    """A hello function"""
     return "Hello, World!"
diff --git a/tests/local_module.py b/tests/local_module.py
index 770470a..62ea5ac 100644
--- a/tests/local_module.py
+++ b/tests/local_module.py
@@ -1,8 +1,13 @@
-# A simple Python file containing expected "local" metadata.
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+
+"""A simple Python file containing expected "local" metadata."""
 
 __version__ = "1.2.3"
 __repo__ = "https://github.com/adafruit/SomeLibrary.git"
 
 
 def hello():
+    """A hello function"""
     return "Hello, World!"
diff --git a/tests/mount_exists.txt.license b/tests/mount_exists.txt.license
new file mode 100644
index 0000000..168c840
--- /dev/null
+++ b/tests/mount_exists.txt.license
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
diff --git a/tests/mount_missing.txt.license b/tests/mount_missing.txt.license
new file mode 100644
index 0000000..168c840
--- /dev/null
+++ b/tests/mount_missing.txt.license
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
diff --git a/tests/remote_module.py b/tests/remote_module.py
index ba7ea5b..8f707e3 100644
--- a/tests/remote_module.py
+++ b/tests/remote_module.py
@@ -1,8 +1,13 @@
-# A simple Python file containing expected "remote" metadata.
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+
+"""A simple Python file containing expected "remote" metadata."""
 
 __version__ = "2.3.4"
 __repo__ = "https://github.com/adafruit/SomeLibrary.git"
 
 
 def hello():
+    """A hello function"""
     return "Hello, World!"
diff --git a/tests/test_circup.py b/tests/test_circup.py
index 5441a51..52d51db 100644
--- a/tests/test_circup.py
+++ b/tests/test_circup.py
@@ -1,3 +1,6 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
 """
 Unit tests for the circup module.
 
@@ -22,13 +25,16 @@
 SOFTWARE.
 """
 import os
-import circup
 import ctypes
-import pytest
 import json
-import requests
 from unittest import mock
+
+
 from click.testing import CliRunner
+import pytest
+import requests
+
+import circup
 
 
 def test_Module_init_file_module():
@@ -316,7 +322,7 @@ def test_get_latest_tag():
         "/Adafruit_CircuitPython_Bundle/releases/tag/20190903"
     )
     expected_url = (
-        "https://github.com/adafruit/Adafruit_CircuitPython_Bundle" "/releases/latest"
+        "https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/latest"
     )
     with mock.patch("circup.requests.get", return_value=response) as mock_get:
         result = circup.get_latest_tag()
@@ -558,6 +564,10 @@ def test_ensure_latest_bundle_to_update():
 
 
 def test_ensure_latest_bundle_to_update_http_error():
+    # pylint: disable=pointless-statement
+    # 2nd to last line is deemed pointless.
+    # Don't understand but for now this is an accpetable workaround
+    # Tracking issue will be opened.
     """
     If an HTTP error happens during a bundle update, print a friendly
     error message and exit 1.
@@ -663,6 +673,9 @@ def test_get_bundle_network_error():
 
 
 def test_show_command():
+    """
+    test_show_command
+    """
     runner = CliRunner()
     TEST_BUNDLE_MODULES = ["one.py", "two.py", "three.py"]
     with mock.patch("circup.get_bundle_versions", return_value=TEST_BUNDLE_MODULES):
@@ -672,6 +685,9 @@ def test_show_command():
 
 
 def test_show_match_command():
+    """
+    test_show_match_command
+    """
     runner = CliRunner()
     TEST_BUNDLE_MODULES = ["one.py", "two.py", "three.py"]
     with mock.patch("circup.get_bundle_versions", return_value=TEST_BUNDLE_MODULES):
@@ -681,7 +697,9 @@ def test_show_match_command():
 
 
 def test_show_match_py_command():
-    # Check that py does not match the .py extention in the module names
+    """
+    Check that py does not match the .py extention in the module names
+    """
     runner = CliRunner()
     TEST_BUNDLE_MODULES = ["one.py", "two.py", "three.py"]
     with mock.patch("circup.get_bundle_versions", return_value=TEST_BUNDLE_MODULES):
diff --git a/tests/test_module.mpy.license b/tests/test_module.mpy.license
new file mode 100644
index 0000000..168c840
--- /dev/null
+++ b/tests/test_module.mpy.license
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: 2019 Nicholas Tollervey, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT