chiark / gitweb /
package/ci: migrate to CircleCI.
authorVladimír Vondruš <mosra@centrum.cz>
Tue, 6 Apr 2021 16:37:19 +0000 (18:37 +0200)
committerVladimír Vondruš <mosra@centrum.cz>
Tue, 6 Apr 2021 18:27:04 +0000 (20:27 +0200)
Thanks, Travis, for being silent for three months straight. Fuck that
"service".

.circleci/config.yml [new symlink]
.travis.yml [deleted symlink]
CONTRIBUTING.rst
README.rst
doc/build-status.js
package/ci/circleci.yml [new file with mode: 0644]
package/ci/setup-pybind11.sh [deleted file]
package/ci/travis.yml [deleted file]

diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 120000 (symlink)
index 0000000..2aac66e
--- /dev/null
@@ -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 (symlink)
index bcbff36..0000000
+++ /dev/null
@@ -1 +0,0 @@
-package/ci/travis.yml
\ No newline at end of file
index 985667c07d5c2075a265dc18fdc7fd4d7e8a2b23..2403c6ac52105ad27e0b8a8ae890e58d901fdfa1 100644 (file)
@@ -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
 =======
index 677ee33257eebc934bd21044bda5c8e223d8e8d4..af14565a362522e6a1ac058a725613bc6d079168 100644 (file)
@@ -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
index 1ecc83e1e456a8888637a21eb7c8e41983a49a04..3fc5e587ae752a840bc90d8b79c0e302fec6b03e 100644 (file)
@@ -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 = '<a href="https://travis-ci.com/' + repo + '/jobs/' + jobs[i]['id'] + '" title="' + title + '">' + status + '<br /><span class="m-text m-small">' + age + '</span></a>';
-            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 = '<a href="' + job['build_url'] + '" title="' + job['status'] + ' @ ' + job[ageField] + '">' + status + '<br /><span class="m-text m-small">' + age + '</span></a>';
+                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 (file)
index 0000000..837612a
--- /dev/null
@@ -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 (executable)
index d952b6f..0000000
+++ /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 (file)
index 784c6a3..0000000
+++ /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