From: Vladimír Vondruš Date: Tue, 6 Apr 2021 16:37:19 +0000 (+0200) Subject: package/ci: migrate to CircleCI. X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~cjwatson/git?a=commitdiff_plain;h=613fe93db8dd227395a133d9462385682a3bd231;p=blog.git package/ci: migrate to CircleCI. Thanks, Travis, for being silent for three months straight. Fuck that "service". --- diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 120000 index 00000000..2aac66ea --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1 @@ +../package/ci/circleci.yml \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 120000 index bcbff366..00000000 --- a/.travis.yml +++ /dev/null @@ -1 +0,0 @@ -package/ci/travis.yml \ No newline at end of file diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 985667c0..2403c6ac 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -121,10 +121,11 @@ Test organization: files named ``test_something.py`` take their input from case of Doxygen, comment-out the line that removes the ``html`` directory in ``__init__.py`` to see all test output files. -The project is built on Travis CI on Linux with Python 3.5, 3.6 and 3.7; -documentation themes are tested only on 3.6+ and math rendering is disabled as -it's impossible to get it working on Travis (https://github.com/mosra/m.css/pull/75). -Build and coverage status is presented at https://mcss.mosra.cz/build-status/. +The project is built on CircleCI on Linux with Python 3.5, 3.6 and 3.7; +documentation themes are tested only on 3.6+ and math rendering is currently +disabled as getting it to work was historically impossible +(https://github.com/mosra/m.css/pull/75). Build and coverage status is +presented at https://mcss.mosra.cz/build-status/. Contact ======= diff --git a/README.rst b/README.rst index 677ee332..af14565a 100644 --- a/README.rst +++ b/README.rst @@ -7,8 +7,8 @@ content-oriented websites.* .. image:: https://badges.gitter.im/mosra/m.css.svg :alt: Join the chat at https://gitter.im/mosra/m.css :target: https://gitter.im/mosra/m.css?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge -.. image:: https://travis-ci.com/mosra/m.css.svg?branch=master - :target: https://travis-ci.com/mosra/m.css +.. image:: https://circleci.com/gh/mosra/m.css.svg?style=shield + :target: https://circleci.com/gh/mosra/m.css .. image:: https://codecov.io/gh/mosra/m.css/branch/master/graph/badge.svg :target: https://codecov.io/gh/mosra/m.css .. image:: https://img.shields.io/badge/License-MIT-green.svg diff --git a/doc/build-status.js b/doc/build-status.js index 1ecc83e1..3fc5e587 100644 --- a/doc/build-status.js +++ b/doc/build-status.js @@ -1,7 +1,4 @@ var projects = [['mosra/m.css', 'master']]; -var latestTravisJobs = []; -var travisDone = 0; -var travisJobIdRe = /JOBID=([a-zA-Z0-9-]+)/ /* Ability to override the projects via query string */ if(location.search) { @@ -25,12 +22,12 @@ function timeDiff(before, now) { return "now"; } -function fetchTravisJobStatus(latestJobs) { +/* https://circleci.com/docs/api/#recent-builds-for-a-single-project */ +function fetchLatestCircleCiJobs(project, branch) { var req = window.XDomainRequest ? new XDomainRequest() : new XMLHttpRequest(); if(!req) return; - req.open("GET", 'https://api.travis-ci.com/jobs?ids[]=' + latestJobs.join('&ids[]='), true); - req.setRequestHeader("Accept", "application/vnd.travis-ci.2.1+json"); + req.open('GET', 'https://circleci.com/api/v1.1/project/github/' + project + '/tree/' + branch + '?limit=10&offset=0&shallow=true'); req.responseType = 'json'; req.onreadystatechange = function() { if(req.readyState != 4) return; @@ -38,87 +35,81 @@ function fetchTravisJobStatus(latestJobs) { //console.log(req.response); var now = new Date(Date.now()); - var jobs = req.response['jobs']; - for(var i = 0; i != jobs.length; ++i) { - var match = jobs[i]['config']['env'].match(travisJobIdRe); - if(!match) continue; - - /* ID is combined repository name (w/o author) and the job ID from - environment */ - var repo = jobs[i]['repository_slug']; - var id = repo.substr(repo.indexOf('/') + 1).replace("m.css", "mcss") + "-" + match[1]; + + /* It's not possible to query just the latest build, so instead we have + to query N latest jobs and then go as long as they have the same + commit. Which is kinda silly, but better than going one-by-one like + with Travis, right? */ + var commit = ''; + for(var i = 0; i != req.response.length; ++i) { + var job = req.response[i]; + + /* Some other commit, we have everything. Otherwise remember the + commit for the next iteration. */ + if(commit && job['vcs_revision'] != commit) + break; + commit = job['vcs_revision']; + + /* If the YML fails to parse, job_name is Build Error. Skip it + completely to avoid errors down the line. */ + if(job['workflows']['job_name'] == 'Build Error') + continue; + + var id = job['reponame'].replace("m.css", "mcss") + '-' + job['workflows']['job_name']; var elem = document.getElementById(id); if(!elem) { - console.log('Unknown Travis job ID', id); + console.log('Unknown CircleCI job ID', id); continue; } var type; var status; var ageField; - if(jobs[i]['state'] == 'passed') { + if(job['status'] == 'success') { type = 'm-success'; status = '✔'; - ageField = 'finished_at'; - } else if(jobs[i]['state'] == 'started') { - type = 'm-warning'; - status = '↺'; - ageField = 'started_at'; - } else if(jobs[i]['state'] == 'canceled') { - type = 'm-dim'; - status = '∅'; - ageField = 'finished_at'; - } else if(jobs[i]['state'] == 'received' || - jobs[i]['state'] == 'created' || - jobs[i]['state'] == 'queued') { + ageField = 'stop_time'; + } else if(job['status'] == 'queued' || job['status'] == 'scheduled' || job['status'] == 'not_running') { type = 'm-info'; status = '…'; - ageField = ''; - } else if(jobs[i]['state'] == 'errored' || - jobs[i]['state'] == 'failed') { + ageField = 'queued_at'; + } else if(job['status'] == 'not_running') { + type = 'm-info'; + status = '…'; + ageField = 'usage_queued_at'; + } else if(job['status'] == 'running') { + type = 'm-warning'; + status = '↺'; + ageField = 'start_time'; + } else if(job['status'] == 'failed' || job['status'] == 'infrastructure_fail' || job['status'] == 'timedout') { type = 'm-danger'; status = '✘'; - ageField = 'finished_at'; + ageField = 'stop_time'; + } else if(job['status'] == 'canceled') { + type = 'm-dim'; + status = '∅'; + ageField = 'stop_time'; } else { + /* retried, not_run, not_running, no_test, fixed -- not sure + what exactly these mean */ type = 'm-default'; - status = jobs[i]['state']; - ageField = 'started_at'; + status = job['status']; + ageField = 'usage_queued_at'; } - var age; - var title; - if(ageField) { - age = timeDiff(new Date(Date.parse(jobs[i][ageField])), now); - title = jobs[i]['state'] + ' @ ' + jobs[i][ageField]; - } else { - age = ''; - title = jobs[i]['state']; - } + var age = timeDiff(new Date(Date.parse(job[ageField])), now); - elem.innerHTML = '' + status + '
' + age + '
'; - elem.className = type; + /* Update the field only if it's not already filled -- in that case + it means this job got re-run. */ + if(!elem.className) { + elem.innerHTML = '' + status + '
' + age + '
'; + elem.className = type; + } } }; req.send(); } -function fetchLatestTravisJobs(project, branch) { - var req = window.XDomainRequest ? new XDomainRequest() : new XMLHttpRequest(); - if(!req) return; - - req.open("GET", 'https://api.travis-ci.com/repos/' + project + '/branches/' + branch, true); - req.setRequestHeader("Accept", "application/vnd.travis-ci.2.1+json"); - req.responseType = 'json'; - req.onreadystatechange = function() { - if(req.readyState != 4) return; - - latestTravisJobs = latestTravisJobs.concat(req.response['branch']['job_ids']); - if(++travisDone == projects.length) - fetchTravisJobStatus(latestTravisJobs); - }; - req.send(); -} - function fetchLatestCodecovJobs(project, branch) { var req = window.XDomainRequest ? new XDomainRequest() : new XMLHttpRequest(); if(!req) return; @@ -152,6 +143,6 @@ function fetchLatestCodecovJobs(project, branch) { } for(var i = 0; i != projects.length; ++i) { - fetchLatestTravisJobs(projects[i][0], projects[i][1]); + fetchLatestCircleCiJobs(projects[i][0], projects[i][1]); fetchLatestCodecovJobs(projects[i][0], projects[i][1]); } diff --git a/package/ci/circleci.yml b/package/ci/circleci.yml new file mode 100644 index 00000000..837612aa --- /dev/null +++ b/package/ci/circleci.yml @@ -0,0 +1,221 @@ +version: 2.1 + +orbs: + codecov: codecov/codecov@1.1.1 + +notify: + webhooks: + # Unfortunately Gitter messages are too verbose (one five-line message per + # each job in the build matrix) and thus not wanted + #- url: https://webhooks.gitter.im/e/cfbadbd34d28708a57c6 + +executors: + python-3_5: + docker: + - image: python:3.5.10 + python-3_6: + docker: + - image: python:3.6.12 + python-3_7: + docker: + - image: python:3.7.9 + node-10: + docker: + - image: node:10.24.0-buster-slim + +commands: + install-base: + parameters: + extra: + type: string + default: "" + steps: + - run: + name: Update apt and install base packages + # Git needed always for verifying we have up-to-date generated files + command: | + apt update + apt install -y git << parameters.extra >> + + install-python-deps: + steps: + - run: + name: Install Python dependencies + # Everything broken with Pelican 4.5, stay on older version until + # that's resolved: https://github.com/mosra/m.css/issues/178 + # Pyphen 0.10 has significantly different hyphenation results, staying + # on an older version until I can investigate + command: | + pip install jinja2 pelican==4.2.0 Pyphen==0.9.5 Pillow coverage codecov matplotlib qrcode + - run: + name: Fix unheard-of cursed issues + # otherwise i get Error: unsupported locale setting + # https://stackoverflow.com/a/59637279 + command: | + apt install -y locales + sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen + dpkg-reconfigure --frontend=noninteractive locales + echo 'export LC_ALL=en_US.UTF-8' >> $BASH_ENV + echo 'export LC_CTYPE=en_US.UTF-8' >> $BASH_ENV + + test-theme: + steps: + - run: + name: Test the theme + command: | + cd pelican-theme + python -m unittest + - run: + name: Verify compiled CSS is up-to-date + command: | + cd css + ./postprocess.sh + git diff --color=always . | cat + git diff-index --exit-code HEAD -- . + + test-plugins: + steps: + - run: + name: Test plugins + # TODO: add tests for the math plugin as well + command: | + cd plugins + coverage run -m unittest + cp .coverage ../.coverage.plugins + + test-documentation-themes: + parameters: + pybind-version: + type: string + python-version: + type: string + steps: + - run: + name: Install Doxygen + command: | + mkdir -p $HOME/bin + export PATH=$HOME/bin:$PATH + echo 'export PATH=$HOME/bin:$PATH' >> $BASH_ENV + wget https://sourceforge.net/projects/doxygen/files/rel-1.8.17/doxygen-1.8.17.linux.bin.tar.gz + tar -xzf doxygen-1.8.17.linux.bin.tar.gz + cp doxygen-1.8.17/bin/doxygen $HOME/bin + doxygen -v + - run: + name: Install pybind11 + command: | + apt update + apt install -y cmake ninja-build + wget --no-clobber https://github.com/pybind/pybind11/archive/v<< parameters.pybind-version >>.tar.gz + tar -xzf v<< parameters.pybind-version >>.tar.gz + + cd pybind11-<< parameters.pybind-version >> + mkdir -p build && cd build + cmake .. \ + -DCMAKE_INSTALL_PREFIX=$HOME/pybind<< parameters.pybind-version >> \ + -DPYBIND11_PYTHON_VERSION=<< parameters.python-version >> \ + -DPYBIND11_TEST=OFF \ + -G Ninja + ninja install + - run: + name: Build & test Python bindings code + command: | + cd documentation/test_python + mkdir -p build && cd build + cmake .. \ + -DCMAKE_PREFIX_PATH=$HOME/pybind<< parameters.pybind-version >> \ + -DPYBIND11_PYTHON_VERSION=<< parameters.python-version >> \ + -G Ninja + ninja + - run: + name: Test documentation themes + command: | + cd documentation + coverage run -m unittest + cp .coverage ../.coverage.documentation + + test-search: + steps: + - run: + name: Install Node.js dependencies + command: | + npm install istanbul codecov + - run: + name: Test JavaScript search + command: | + cd documentation + node ../node_modules/istanbul/lib/cli.js cover test/test-search.js + - run: + name: Verify JavaScript search data are up-to-date + command: | + cd documentation/test + ./populate-js-test-data.py + git diff --color=always . | cat + git diff-index --exit-code HEAD -- . + + coverage: + steps: + - run: + name: Collect and upload code coverage + command: | + coverage combine + codecov + +jobs: + py35: + executor: python-3_5 + steps: + - install-base: + extra: graphviz + - install-python-deps + - checkout + - test-theme + - test-plugins + - coverage + + py36: + executor: python-3_6 + steps: + - install-base: + extra: graphviz cmake ninja-build wget + - install-python-deps + - checkout + - test-theme + - test-plugins + - test-documentation-themes: + python-version: "3.6" + pybind-version: "2.2.4" + - coverage + + py37: + executor: python-3_7 + steps: + - install-base: + extra: graphviz cmake ninja-build wget + - install-python-deps + - checkout + - test-theme + - test-plugins + - test-documentation-themes: + python-version: "3.7" + pybind-version: "2.3.0" + - coverage + + js: + executor: node-10 + steps: + - install-base: + extra: python3 curl + - checkout + - test-search + # This FUCKING DAMN THING doesn't propagate a failure if curl doesn't exist + # WHAT THE FUCK + - codecov/upload + +workflows: + version: 2 + build: + jobs: + - py35 + - py36 + - py37 + - js diff --git a/package/ci/setup-pybind11.sh b/package/ci/setup-pybind11.sh deleted file mode 100755 index d952b6fc..00000000 --- a/package/ci/setup-pybind11.sh +++ /dev/null @@ -1,12 +0,0 @@ -set -e - -wget --no-clobber https://github.com/pybind/pybind11/archive/v$PYBIND_VERSION.tar.gz && tar -xzf v$PYBIND_VERSION.tar.gz - -cd pybind11-$PYBIND_VERSION -mkdir -p build && cd build -cmake .. \ - -DCMAKE_INSTALL_PREFIX=$HOME/pybind$PYBIND_VERSION \ - -DPYBIND11_PYTHON_VERSION=$TRAVIS_PYTHON_VERSION \ - -DPYBIND11_TEST=OFF \ - -G Ninja -ninja install diff --git a/package/ci/travis.yml b/package/ci/travis.yml deleted file mode 100644 index 784c6a39..00000000 --- a/package/ci/travis.yml +++ /dev/null @@ -1,115 +0,0 @@ -dist: xenial - -matrix: - include: - - language: python - python: 3.5 - addons: - apt: - packages: - - graphviz - env: - - WITH_THEME=ON - - WITH_DOCUMENTATION=OFF - - WITH_NODE=OFF - - JOBID=py35 - - language: python - python: 3.6 - addons: - apt: - packages: - - graphviz - - cmake - - ninja-build - env: - - WITH_THEME=ON - - WITH_DOCUMENTATION=ON - - WITH_NODE=OFF - - TRAVIS_BROKEN_MATPLOTLIB_SEED=YES - - PYBIND_VERSION=2.2.4 - - JOBID=py36 - - language: python - python: 3.7 - addons: - apt: - packages: - - graphviz - - cmake - - ninja-build - env: - - WITH_THEME=ON - - WITH_DOCUMENTATION=ON - - WITH_NODE=OFF - - TRAVIS_BROKEN_MATPLOTLIB_SEED=YES - - PYBIND_VERSION=2.3.0 - - JOBID=py37 - - language: node_js - node_js: 8 - env: - - WITH_THEME=OFF - - WITH_DOCUMENTATION=OFF - - WITH_NODE=ON - - JOBID=js - -install: - # Everything broken with Pelican 4.5, stay on older version until that's - # resolved: https://github.com/mosra/m.css/issues/178 - # Pyphen 0.10 has significantly different hyphenation results, staying on an - # older version until I can investigate - - if [ "$WITH_THEME" == "ON" ]; then pip install jinja2 pelican==4.2.0 Pyphen==0.9.5 Pillow coverage codecov matplotlib qrcode; fi - - if [ "$WITH_NODE" == "ON" ]; then npm install istanbul codecov; fi - - # Needed for doxygen binaries - - if [ "$WITH_DOCUMENTATION" == "ON" ]; then mkdir -p $HOME/bin && export PATH=$HOME/bin:$PATH; fi - - # Old releases are randomly deleted from doxygen.nl, use sourceforge instead - - if [ "$WITH_DOCUMENTATION" == "ON" ] && [ ! -f $HOME/bin/doxygen ]; then wget "https://sourceforge.net/projects/doxygen/files/rel-1.8.17/doxygen-1.8.17.linux.bin.tar.gz" && tar -xzf doxygen-1.8.17.linux.bin.tar.gz && cp doxygen-1.8.17/bin/doxygen $HOME/bin && doxygen -v; fi - -script: - # Test the theme. No code coverage there. - - if [ "$WITH_THEME" == "ON" ]; then cd $TRAVIS_BUILD_DIR/pelican-theme && python -m unittest; fi - - # Test plugins. Math plugin is not tested as dvisvgm is unusable even on - # 16.04. - - if [ "$WITH_THEME" == "ON" ]; then cd $TRAVIS_BUILD_DIR/plugins && coverage run -m unittest && cp .coverage ../.coverage.plugins; fi - - # Test documentation themes. Needs also to compile a bunch of things for the - # pybind11 tests. Math rendering is not tested as dvisvgm is unusable even on - # 16.04. - - if [ "$WITH_DOCUMENTATION" == "ON" ] && [ ! -f $HOME/pybind11/include ]; then $TRAVIS_BUILD_DIR/package/ci/setup-pybind11.sh; fi - - if [ "$WITH_DOCUMENTATION" == "ON" ]; then cd $TRAVIS_BUILD_DIR/documentation/test_python && mkdir -p build && cd build && cmake -DCMAKE_PREFIX_PATH=$HOME/pybind$PYBIND_VERSION -DPYBIND11_PYTHON_VERSION=$TRAVIS_PYTHON_VERSION -G Ninja .. && ninja; fi - - if [ "$WITH_DOCUMENTATION" == "ON" ]; then cd $TRAVIS_BUILD_DIR/documentation && coverage run -m unittest && cp .coverage ../.coverage.doxygen; fi - - # Test client doxygen JS - - if [ "$WITH_NODE" == "ON" ]; then cd $TRAVIS_BUILD_DIR/documentation && node ../node_modules/istanbul/lib/cli.js cover test/test-search.js; fi - - # Test that compiled CSS is up-to-date. First display the diff, then check - # with diff-index which should print what's wrong and return with non-zero - # exit code. - - if [ "$WITH_THEME" == "ON" ]; then cd $TRAVIS_BUILD_DIR/css && ./postprocess.sh && git diff --color=always . | cat; fi - - if [ "$WITH_THEME" == "ON" ]; then cd $TRAVIS_BUILD_DIR/css && git diff-index --exit-code HEAD -- .; fi - - # Test that JS search test data are up-to-date as well. Would be best to do - # it on the Node.js job but that one has just Python 3.5 which doesn't know - # enum.Flag. - - if [ "$WITH_DOCUMENTATION" == "ON" ]; then cd $TRAVIS_BUILD_DIR/documentation/test&& ./populate-js-test-data.py && git diff --color=always . | cat; fi - - if [ "$WITH_DOCUMENTATION" == "ON" ]; then cd $TRAVIS_BUILD_DIR/documentation/test/js-test-data && git diff-index --exit-code HEAD -- .; fi - -# Cache the downloaded doxygen and pybind11 -cache: - directories: - - $HOME/bin - - $HOME/pybind2.2.4 - - $HOME/pybind2.3.0 - -notifications: - webhooks: - urls: - - https://webhooks.gitter.im/e/73c7db47c27b45b9ceaf - on_success: change - on_failure: always - on_start: never - -after_success: - - if [ "$WITH_THEME" == "ON" ] || [ "$WITH_DOCUMENTATION" == "ON" ]; then cd $TRAVIS_BUILD_DIR && coverage combine && codecov; fi - - if [ "$WITH_NODE" == "ON" ]; then cd $TRAVIS_BUILD_DIR && codecov; fi