From e0edcca7285bf6a9847cd0663302e6cb59bbb22a Mon Sep 17 00:00:00 2001
From: Massimiliano Pippi <m.pippi@arduino.cc>
Date: Mon, 18 May 2020 10:06:57 +0200
Subject: [PATCH 1/8] introduce mike

---
 docs/css/version-select.css |  5 ++++
 docs/js/version-select.js   | 50 +++++++++++++++++++++++++++++++++++++
 mkdocs.yml                  | 13 +++++++---
 3 files changed, 64 insertions(+), 4 deletions(-)
 create mode 100644 docs/css/version-select.css
 create mode 100644 docs/js/version-select.js

diff --git a/docs/css/version-select.css b/docs/css/version-select.css
new file mode 100644
index 00000000000..49079bf4686
--- /dev/null
+++ b/docs/css/version-select.css
@@ -0,0 +1,5 @@
+@media only screen and (max-width:76.1875em) {
+  #version-selector {
+    padding: .6rem .8rem;
+  }
+}
diff --git a/docs/js/version-select.js b/docs/js/version-select.js
new file mode 100644
index 00000000000..06a35bf9fee
--- /dev/null
+++ b/docs/js/version-select.js
@@ -0,0 +1,50 @@
+window.addEventListener("DOMContentLoaded", function() {
+  // This is a bit hacky. Figure out the base URL from a known CSS file the
+  // template refers to...
+  var ex = new RegExp("/?assets/fonts/material-icons.css$");
+  var sheet = document.querySelector('link[href$="material-icons.css"]');
+
+  var REL_BASE_URL = sheet.getAttribute("href").replace(ex, "");
+  var ABS_BASE_URL = sheet.href.replace(ex, "");
+  var CURRENT_VERSION = ABS_BASE_URL.split("/").pop();
+
+  function makeSelect(options, selected) {
+    var select = document.createElement("select");
+    select.classList.add("form-control");
+
+    options.forEach(function(i) {
+      var option = new Option(i.text, i.value, undefined,
+                              i.value === selected);
+      select.add(option);
+    });
+
+    return select;
+  }
+
+  var xhr = new XMLHttpRequest();
+  xhr.open("GET", REL_BASE_URL + "/../versions.json");
+  xhr.onload = function() {
+    var versions = JSON.parse(this.responseText);
+
+    var realVersion = versions.find(function(i) {
+      return i.version === CURRENT_VERSION ||
+             i.aliases.includes(CURRENT_VERSION);
+    }).version;
+
+    var select = makeSelect(versions.map(function(i) {
+      return {text: i.title, value: i.version};
+    }), realVersion);
+    select.addEventListener("change", function(event) {
+      window.location.href = REL_BASE_URL + "/../" + this.value;
+    });
+
+    var container = document.createElement("div");
+    container.id = "version-selector";
+    container.className = "md-nav__item";
+    container.appendChild(select);
+
+    var sidebar = document.querySelector(".md-nav--primary > .md-nav__list");
+    sidebar.parentNode.insertBefore(container, sidebar);
+  };
+  xhr.send();
+});
diff --git a/mkdocs.yml b/mkdocs.yml
index 134d6f10ba0..f42d4bdc524 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -6,14 +6,13 @@ site_url: https://arduino.github.io/arduino-cli/
 # Repository
 repo_name: arduino/arduino-cli
 repo_url: https://github.com/arduino/arduino-cli
-edit_uri: ""
+edit_uri: ''
 
 # Copyright
-copyright: 'Copyright 2020 ARDUINO SA (http://www.arduino.cc/)'
+copyright: Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
 
 # Configuration
 site_dir: public
-edit_uri: ""
 
 # Theme
 theme:
@@ -57,7 +56,7 @@ markdown_extensions:
 
 # Navigation
 nav:
-  - Documentation Home: 'index.md'
+  - Documentation Home: index.md
   - installation.md
   - getting-started.md
   - CONTRIBUTING.md
@@ -108,3 +107,9 @@ nav:
   - library-specification.md
   - platform-specification.md
   - package_index.json specification: package_index_json-specification.md
+
+extra_css:
+  - css/version-select.css
+
+extra_javascript:
+  - js/version-select.js

From 9bb405312f9a49473762f64d6277becf558c1a69 Mon Sep 17 00:00:00 2001
From: Massimiliano Pippi <m.pippi@arduino.cc>
Date: Mon, 18 May 2020 15:34:36 +0200
Subject: [PATCH 2/8] use default 'site' dir for output

---
 .gitignore | 3 ++-
 mkdocs.yml | 3 ---
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/.gitignore b/.gitignore
index 76546d22e23..5de33223d9c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,7 +22,8 @@ venv
 .DS_Store
 
 # Mkdocs
+/site/
 /public/
 /docsgen/arduino-cli
 /docs/rpc/*.md
-/docs/commands/*.md
\ No newline at end of file
+/docs/commands/*.md
diff --git a/mkdocs.yml b/mkdocs.yml
index f42d4bdc524..af169843596 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -11,9 +11,6 @@ edit_uri: ''
 # Copyright
 copyright: Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
 
-# Configuration
-site_dir: public
-
 # Theme
 theme:
   name: material

From a80540282e18c1be5f40ebfa02f80ea899965ecf Mon Sep 17 00:00:00 2001
From: Massimiliano Pippi <m.pippi@arduino.cc>
Date: Mon, 18 May 2020 18:05:47 +0200
Subject: [PATCH 3/8] install mike and pin material theme

---
 requirements_docs.txt | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/requirements_docs.txt b/requirements_docs.txt
index 21f876b4370..1ed1baf6134 100644
--- a/requirements_docs.txt
+++ b/requirements_docs.txt
@@ -1,2 +1,3 @@
-mkdocs
-mkdocs-material
\ No newline at end of file
+mkdocs<1.2
+mkdocs-material<5
+mike
\ No newline at end of file

From a94c7a5b2522bc5619be6d68e02b4ab901886171 Mon Sep 17 00:00:00 2001
From: Massimiliano Pippi <m.pippi@arduino.cc>
Date: Tue, 19 May 2020 15:53:58 +0200
Subject: [PATCH 4/8] add task to build&publish docs using Mike

---
 Taskfile.yml | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/Taskfile.yml b/Taskfile.yml
index d98fe1bae76..774fac6abdd 100755
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -34,6 +34,14 @@ tasks:
     cmds:
       - mkdocs build -s
 
+  docs:publish:
+    desc: Use Mike to build and push versioned docs
+    deps:
+      - docs:gen:commands
+      - docs:gen:protobuf
+    cmds:
+      - mike deploy -r {{.DOCS_REMOTE}} {{.DOCS_VERSION}} {{.DOCS_ALIAS}}
+
   docs:serve:
     desc: Run documentation website locally
     deps:
@@ -126,3 +134,7 @@ vars:
   GOLINTBIN:
     sh: go list -f {{"{{"}}".Target{{"}}"}}" golang.org/x/lint/golint
   GOLINTFLAGS: "-min_confidence 0.8 -set_exit_status"
+  # docs versioning
+  DOCS_VERSION: dev
+  DOCS_ALIAS: ""
+  DOCS_REMOTE: "origin"

From a11f45b70b7d31d55688d3b1306196e04b47ab41 Mon Sep 17 00:00:00 2001
From: Massimiliano Pippi <m.pippi@arduino.cc>
Date: Tue, 19 May 2020 15:54:16 +0200
Subject: [PATCH 5/8] publish dev and versioned docs

---
 .github/workflows/docs.yaml | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
index c97a8c71524..e1c7684b665 100644
--- a/.github/workflows/docs.yaml
+++ b/.github/workflows/docs.yaml
@@ -14,6 +14,8 @@ on:
   push:
     branches:
       - master
+      # release branches have names like 0.8.x, 0.9.x, ...
+      - '[0-9]+.[0-9]+.x'
     # At this day, GitHub doesn't support YAML anchors, d'oh!
     paths:
       - 'docs/**'
@@ -24,6 +26,8 @@ on:
 jobs:
   build:
     runs-on: ubuntu-latest
+    env:
+      REMOTE: https://x-access-token:${{secrets.GITHUB_TOKEN}}@github.com/${{github.repository}}.git
 
     steps:
       - name: Checkout
@@ -69,12 +73,17 @@ jobs:
           python3 -m pip install -r ./requirements_docs.txt
 
       - name: Build docs website
+        # this runs on every PR to ensure the docs build is sane
+        if: github.event_name == 'pull_request'
         run: task docs:build
 
-      - name: Deploy
-        # publish docs only when PR is merged on master
-        if: github.event_name == 'push'
-        uses: peaceiris/actions-gh-pages@v3
-        with:
-          github_token: ${{ secrets.GITHUB_TOKEN }}
-          publish_dir: ./public
\ No newline at end of file
+      - name: Publish dev docs
+        # dev docs are published every time a commit is pushed to master
+        if: github.event_name == 'push' && github.ref == 'master'
+        run: task docs:publish DOCS_REMOTE=$REMOTE DOCS_VERSION=dev
+
+      - name: Publish versioned docs
+        # versioned docs are published every time a commit is pushed to
+        # a release branch
+        if: github.event_name == 'push' && github.ref != 'master'
+        run: task docs:publish DOCS_REMOTE=$REMOTE DOCS_VERSION=${{github.ref}}

From 2d256a2deceaa2b45ae13deb7463eb738512c8ad Mon Sep 17 00:00:00 2001
From: Massimiliano Pippi <m.pippi@arduino.cc>
Date: Wed, 20 May 2020 14:56:28 +0200
Subject: [PATCH 6/8] narrow down env definition

---
 .github/workflows/docs.yaml | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
index e1c7684b665..f2ca53eea4d 100644
--- a/.github/workflows/docs.yaml
+++ b/.github/workflows/docs.yaml
@@ -26,8 +26,6 @@ on:
 jobs:
   build:
     runs-on: ubuntu-latest
-    env:
-      REMOTE: https://x-access-token:${{secrets.GITHUB_TOKEN}}@github.com/${{github.repository}}.git
 
     steps:
       - name: Checkout
@@ -73,13 +71,17 @@ jobs:
           python3 -m pip install -r ./requirements_docs.txt
 
       - name: Build docs website
-        # this runs on every PR to ensure the docs build is sane
+        # this runs on every PR to ensure the docs build is sane, these docs
+        # won't be published
         if: github.event_name == 'pull_request'
         run: task docs:build
 
-      - name: Publish dev docs
+      - name: Publish docs
         # dev docs are published every time a commit is pushed to master
         if: github.event_name == 'push' && github.ref == 'master'
+        env:
+          REMOTE: https://x-access-token:${{secrets.GITHUB_TOKEN}}@github.com/${{github.repository}}.git
+          BRANCH_NAME: ${{github.ref}}
         run: task docs:publish DOCS_REMOTE=$REMOTE DOCS_VERSION=dev
 
       - name: Publish versioned docs

From 5077dc91d09675f7091ca065fb0e409587d0133b Mon Sep 17 00:00:00 2001
From: Massimiliano Pippi <m.pippi@arduino.cc>
Date: Wed, 20 May 2020 15:03:36 +0200
Subject: [PATCH 7/8] add build script to handle versioning

---
 .github/workflows/docs.yaml |  12 +---
 docs/build.py               | 120 ++++++++++++++++++++++++++++++++++++
 requirements_docs.txt       |   3 +-
 3 files changed, 125 insertions(+), 10 deletions(-)
 create mode 100644 docs/build.py

diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
index f2ca53eea4d..e786a52bf16 100644
--- a/.github/workflows/docs.yaml
+++ b/.github/workflows/docs.yaml
@@ -77,15 +77,9 @@ jobs:
         run: task docs:build
 
       - name: Publish docs
-        # dev docs are published every time a commit is pushed to master
-        if: github.event_name == 'push' && github.ref == 'master'
+        # determine docs version for the commit pushed and publish accordingly using Mike
+        if: github.event_name == 'push'
         env:
           REMOTE: https://x-access-token:${{secrets.GITHUB_TOKEN}}@github.com/${{github.repository}}.git
-          BRANCH_NAME: ${{github.ref}}
-        run: task docs:publish DOCS_REMOTE=$REMOTE DOCS_VERSION=dev
 
-      - name: Publish versioned docs
-        # versioned docs are published every time a commit is pushed to
-        # a release branch
-        if: github.event_name == 'push' && github.ref != 'master'
-        run: task docs:publish DOCS_REMOTE=$REMOTE DOCS_VERSION=${{github.ref}}
+        run: python docs/build.py
diff --git a/docs/build.py b/docs/build.py
new file mode 100644
index 00000000000..498d58d360f
--- /dev/null
+++ b/docs/build.py
@@ -0,0 +1,120 @@
+# This file is part of arduino-cli.
+
+# Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
+
+# This software is released under the GNU General Public License version 3,
+# which covers the main part of arduino-cli.
+# The terms of this license can be found at:
+# https://www.gnu.org/licenses/gpl-3.0.en.html
+
+# You can be released from the requirements of the above licenses by purchasing
+# a commercial license. Buying such a license is mandatory if you want to
+# modify or otherwise use the software for commercial activities involving the
+# Arduino software without disclosing the source code of your own applications.
+# To purchase a commercial license, send an email to license@arduino.cc.
+import os
+import sys
+import re
+import unittest
+import subprocess
+
+import semver
+from git import Repo
+
+
+class TestScript(unittest.TestCase):
+    def test_get_docs_version(self):
+        ver, alias = get_docs_version("master", [])
+        self.assertEqual(ver, "dev")
+        self.assertEqual(alias, "")
+
+        release_names = ["1.4.x", "0.13.x"]
+        ver, alias = get_docs_version("0.13.x", release_names)
+        self.assertEqual(ver, "0.13")
+        self.assertEqual(alias, "")
+        ver, alias = get_docs_version("1.4.x", release_names)
+        self.assertEqual(ver, "1.4")
+        self.assertEqual(alias, "latest")
+
+        ver, alias = get_docs_version("0.1.x", [])
+        self.assertIsNone(ver)
+        self.assertIsNone(alias)
+
+
+def get_docs_version(ref_name, release_branches):
+    if ref_name == "master":
+        return "dev", ""
+
+    if ref_name in release_branches:
+        # if version is latest, add an alias
+        alias = "latest" if ref_name == release_branches[0] else ""
+        # strip `.x` suffix from the branch name to get the version: 0.3.x -> 0.3
+        return ref_name[:-2], alias
+
+    return None, None
+
+
+def get_rel_branch_names(blist):
+    """Get the names of the release branches, sorted from newest to older.
+
+    Only process remote refs so we're sure to get all of them and clean up the
+    name so that we have a list of strings like 0.6.x, 0.7.x, ...
+    """
+    pattern = re.compile(r"origin/(\d+\.\d+\.x)")
+    names = []
+    for b in blist:
+        res = pattern.search(b.name)
+        if res is not None:
+            names.append(res.group(1))
+
+    # Since sorting is stable, first sort by major...
+    names = sorted(names, key=lambda x: int(x.split(".")[0]), reverse=True)
+    # ...then by minor
+    return sorted(names, key=lambda x: int(x.split(".")[1]), reverse=True)
+
+
+def main(repo_dir):
+    # Git remote must be set to publish docs
+    remote = os.environ.get("REMOTE")
+    if not remote:
+        print("REMOTE env var must be set to publish, running dry mode")
+
+    # Get current repo
+    repo = Repo(repo_dir)
+
+    # Get the list of release branch names
+    rel_br_names = get_rel_branch_names(repo.refs)
+
+    # Deduce docs version from current branch. Use the 'latest' alias if
+    # version is the most recent
+    docs_version, alias = get_docs_version(repo.active_branch.name, rel_br_names)
+    if docs_version is None:
+        print(
+            f"Can't get version from current branch '{repo.active_branch}', skip docs generation"
+        )
+        return 0
+
+    if remote:
+        args = ["mike", "deploy", "-p", "-r", remote, docs_version, alias]
+        subprocess.run(args, shell=True, check=True)
+    else:
+        print("mike", "deploy", docs_version, alias)
+
+    return 0
+
+
+# Usage:
+#
+#     To run the tests:
+#         $python build.py test
+#
+#     To run the script (must be run from within the repo tree):
+#         $python build.py
+#
+if __name__ == "__main__":
+    if len(sys.argv) > 1 and sys.argv[1] == "test":
+        unittest.main(argv=[""], exit=False)
+        sys.exit(0)
+
+    here = os.path.dirname(os.path.realpath(__file__))
+    sys.exit(main(os.path.join(here, "..")))
diff --git a/requirements_docs.txt b/requirements_docs.txt
index 1ed1baf6134..a3cdb15d5be 100644
--- a/requirements_docs.txt
+++ b/requirements_docs.txt
@@ -1,3 +1,4 @@
 mkdocs<1.2
 mkdocs-material<5
-mike
\ No newline at end of file
+mike
+gitpython
\ No newline at end of file

From 66479301afec52539568d886dc718508bbbda461 Mon Sep 17 00:00:00 2001
From: Massimiliano Pippi <m.pippi@arduino.cc>
Date: Wed, 20 May 2020 15:18:43 +0200
Subject: [PATCH 8/8] invoke task, not mike directly

---
 docs/build.py | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/docs/build.py b/docs/build.py
index 498d58d360f..77530c859c9 100644
--- a/docs/build.py
+++ b/docs/build.py
@@ -94,11 +94,17 @@ def main(repo_dir):
         )
         return 0
 
+    args = [
+        "task",
+        "docs:publish",
+        f"DOCS_REMOTE={remote}",
+        f"DOCS_VERSION={docs_version}",
+        f"DOCS_ALIAS={alias}",
+    ]
     if remote:
-        args = ["mike", "deploy", "-p", "-r", remote, docs_version, alias]
-        subprocess.run(args, shell=True, check=True)
+        subprocess.run(args, shell=True, check=True, cwd=repo_dir)
     else:
-        print("mike", "deploy", docs_version, alias)
+        print(" ".join(args))
 
     return 0