diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index c97a8c71524..e786a52bf16 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/**' @@ -69,12 +71,15 @@ 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, these docs + # won't be published + if: github.event_name == 'pull_request' run: task docs:build - - name: Deploy - # publish docs only when PR is merged on master + - name: Publish docs + # determine docs version for the commit pushed and publish accordingly using Mike 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 + env: + REMOTE: https://x-access-token:${{secrets.GITHUB_TOKEN}}@github.com/${{github.repository}}.git + + run: python docs/build.py 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/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" diff --git a/docs/build.py b/docs/build.py new file mode 100644 index 00000000000..77530c859c9 --- /dev/null +++ b/docs/build.py @@ -0,0 +1,126 @@ +# 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 + + args = [ + "task", + "docs:publish", + f"DOCS_REMOTE={remote}", + f"DOCS_VERSION={docs_version}", + f"DOCS_ALIAS={alias}", + ] + if remote: + subprocess.run(args, shell=True, check=True, cwd=repo_dir) + else: + print(" ".join(args)) + + 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/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..af169843596 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -6,14 +6,10 @@ 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/)' - -# Configuration -site_dir: public -edit_uri: "" +copyright: Copyright 2020 ARDUINO SA (http://www.arduino.cc/) # Theme theme: @@ -57,7 +53,7 @@ markdown_extensions: # Navigation nav: - - Documentation Home: 'index.md' + - Documentation Home: index.md - installation.md - getting-started.md - CONTRIBUTING.md @@ -108,3 +104,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 diff --git a/requirements_docs.txt b/requirements_docs.txt index 21f876b4370..a3cdb15d5be 100644 --- a/requirements_docs.txt +++ b/requirements_docs.txt @@ -1,2 +1,4 @@ -mkdocs -mkdocs-material \ No newline at end of file +mkdocs<1.2 +mkdocs-material<5 +mike +gitpython \ No newline at end of file