From e0edcca7285bf6a9847cd0663302e6cb59bbb22a Mon Sep 17 00:00:00 2001 From: Massimiliano Pippi 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 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 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 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 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 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 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 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