Project

General

Profile

Download (13.4 KB) Statistics
| Branch: | Tag: | Revision:
1
# This reusable CI script is licensed under the MIT license.
2
# See the complete license text at the end.
3

    
4
### Configuration section
5

    
6
# The versions of the compiler to test the project against.
7
.build-matrix:
8
  # parallel:
9
    # https://docs.gitlab.com/ee/ci/yaml/README.html#parallel-matrix-jobs
10
    # matrix:
11
    # - OCAML_COMPILER: "4.00.1"
12
    # - OCAML_COMPILER: "4.01.0"
13
    # - OCAML_COMPILER: "4.02.1"
14
    # - OCAML_COMPILER: "4.02.3"
15
    # - OCAML_COMPILER: "4.03.0"
16
    # - OCAML_COMPILER: "4.04.2"
17
    # - OCAML_COMPILER: "4.05.0"
18
    # - OCAML_COMPILER: "4.06.1"
19
    # - OCAML_COMPILER: "4.07.1"
20
    # - OCAML_COMPILER: "4.08.1"
21
    # - OCAML_COMPILER: "4.09.1"
22
    # - OCAML_COMPILER: "4.10.0"
23
    variables:
24
      OCAML_COMPILER: "4.11.1"
25

    
26
variables:
27
  CLEAN_OPAM_CACHE: "false"
28
  CLEAN_DUNE_CACHE: "false"
29
  # If CLEAN_OPAM_CACHE is set to "true", the opam switch from previous CI jobs
30
  # will not be reused.
31
  # If CLEAN_DUNE_CACHE is set to "true", the dune _build directory
32
  # from previous CI jobs will not be reused.
33

    
34
  # In particular, you can run a manual pipeline
35
  # with either variable set to "true" to purge a faulty cache.
36

    
37
  # To run a manual gitlab pipeline, on your project go to
38
  # "CI/CD" > pipelines, click "Run pipeline", the interface
39
  # then offers to override variables, so you can change the
40
  # value of any variable defined here.
41

    
42
  DUNE_BUILD_TARGETS: "@all"
43
  DUNE_TEST_TARGETS: "@runtest"
44
  DUNE_DOC_TARGETS: "@doc"
45
  # If you make one of these variables empty (: ""),
46
  # the corresponding build step will be skipped.
47
  # Setting them to other values can refine the build
48
  # process, for example "@foo/runtest @bar/runtest" would only
49
  # run the tests inside directories 'foo/' and 'bar/'
50
  # (see Dune documentation on target aliases).
51

    
52

    
53
# This CI script is written to be portable across projects.
54
# None of the code below hardcodes project filenames,
55
# OCaml versions or other project-specific details.
56
#
57
# If you want to add new behavior for your project, we recommend
58
# trying to factorize it with project-agnostic code below,
59
# and project-specific or manually-overridable choices in the
60
# 'variables' section above.
61

    
62

    
63
### Hacking advice
64
#
65
# Reference documentation for Gitlab CI configuration files:
66
#  https://docs.gitlab.com/ee/ci/yaml/
67
#
68
# If you edit this file and make syntax errors, the default
69
# error messages will not help you, but Gitlab offers a "CI Lint"
70
# service that provides very nice error messages:
71
#   https://docs.gitlab.com/ee/ci/lint.html
72
#
73
# To use this linter, go to "CI/CD > Pipelines", click the "CI Lint"
74
# button in the top right, copy-paste your .gitlab-ci.yml file in
75
# the form and click "Validate".
76
#
77
# We recommend systematically using the CI Lint whenever you change
78
# this file, it can save you from wasted time and frustration.
79

    
80

    
81
### Stages
82

    
83
stages:
84
  - build        # build the project and run its tests
85
  - deploy       # deploys a website to Pages from the 'pages' branch
86

    
87

    
88
### Build stage
89
#
90
# build the project and run its tests
91

    
92
build:
93
  stage: build
94

    
95
  extends: .build-matrix # defines OCAML_COMPILER
96

    
97
  variables:
98
    ARTIFACTS: "artifacts/$OCAML_COMPILER"
99
    # a local shortcut for the per-compiler artifact repository
100

    
101
    FF_USE_FASTZIP: "true"
102
    # A workaround against a bug in gitlab-runner's default
103
    # unzipping implementation, which partially breaks caching for the dune _build cache.
104
    # See https://gitlab.com/gitlab-org/gitlab-runner/-/issues/27496 for more details.
105

    
106
  artifacts:
107
    paths:
108
      - artifacts/$OCAML_COMPILER
109

    
110
  # run this job only if a 'dune-project' file exists;
111
  # (In particular, this will not run in your "pages" branch
112
  # if it does not itself use Dune.)
113
  rules:
114
    # https://docs.gitlab.com/ee/ci/yaml/#rules
115
    - exists:
116
        - dune-project
117

    
118
  # This CI script uses a local switch, so we don't need
119
  # a docker image with a pre-installed OCaml version, just opam.
120
  # See https://hub.docker.com/r/ocaml/opam/ for other images.
121
  image: ocaml/opam:debian-testing-opam
122

    
123
  # We use a local opam switch in `./_opam` that is cached
124
  # by Gitlab, and reused across all branches and pull requests.
125
  cache:
126
    # https://docs.gitlab.com/ee/ci/yaml/#cache
127
    key: $OCAML_COMPILER
128
    # keep a distinct cache for each compiler version
129
    paths:
130
      - _opam
131
      # Reusing the same opam environment over a long time might result into
132
      # unnatural choice of dependencies: repeatedly installing and updating
133
      # dependencies may result in a different solver choices than doing
134
      # a fresh setup.
135
      #
136
      # You can manually clean the _opam cache by running a manual pipeline
137
      # with the variable CLEAN_OPAM_CACHE set to "true".
138

    
139
      - _build
140
      # You can manually clean the dune _build cache by running a manual pipeline
141
      # with the variable CLEAN_DUNE_CACHE set to "true".
142

    
143
  script:
144
    - if [ "$CLEAN_OPAM_CACHE" == "true" ]; then echo "we clean the _opam cache as explicitly requested"; rm -fR _opam; fi
145
    - if [ "$CLEAN_DUNE_CACHE" == "true" ]; then echo "we clean the dune _build cache as explicitly requested"; rm -fR _build; fi
146
    # Note: Gitlab supports multi-line scripts with "- |" or "- >", but the
147
    # display in the logs does not show the whole script being run, reducing
148
    # understandability. We only use multi-line scripts for "echo" line,
149
    # and otherwise keep long one-liners.
150
    #   https://docs.gitlab.com/ee/ci/yaml/script.html#split-long-commands
151

    
152
    # see https://docs.gitlab.com/ee/ci/jobs/index.html#custom-collapsible-sections
153
    # to understand the "section_{start,end}:`date +%s`:<name>\r\e[0K human-readable text" pattern
154
    - echo -e "section_start:`date +%s`:setup_switch\r\e[0K setup opam switch"
155
    - if [ -d _opam ]; then echo "we reuse the local opam switch from the CI cache"; fi
156

    
157
    - if [ ! -d _opam ]; then echo "no local switch in the CI cache, we setup a new switch"; opam switch create --yes --no-install . $OCAML_COMPILER; fi
158
    # --no-install prevents installing the package or its dependencies.
159
    # we want to setup the external dependencies (depext) first, see below.
160
    - echo -e "section_end:`date +%s`:setup_switch\r\e[0K"
161

    
162
    - echo -e "section_start:`date +%s`:setup_deps\r\e[0K setup the package dependencies"
163
    # (Note: when opam 2.1 will be available with native depext integration,
164
    # the complex dance below will be replaceable by just
165
    #   opam install . --deps-only --locked --with-test --with-doc --yes
166
    # which should start by installing depexts
167
    # (possibly we will need to set some explicit environment variable first).
168
    - opam install depext --yes
169
    - opam install . --dry-run --deps-only --locked --with-test --with-doc --yes | awk '/-> installed/{print $3}' | xargs opam depext -iy
170
    # the magical command above comes from https://github.com/ocaml/opam/issues/3790
171
    # it installs both external dependencies and opam dependencies
172
    - echo "(we used --locked to honor any lockfiles if present)"
173
    - echo -e "section_end:`date +%s`:setup_deps\r\e[0K"
174

    
175
    - echo -e "section_start:`date +%s`:project_build\r\e[0K build the project"
176
    - eval $(opam env)
177
    - if [ "$DUNE_BUILD_TARGETS" != "" ]; then dune build $DUNE_BUILD_TARGETS --display short; else echo "skipped (DUNE_BUILD_TARGETS is empty)"; fi
178
    - echo -e "section_end:`date +%s`:project_build\r\e[0K"
179

    
180
    - echo -e "section_start:`date +%s`:project_tests\r\e[0K run the tests"
181
    - if [ "$DUNE_TEST_TARGETS" != "" ]; then dune build $DUNE_TEST_TARGETS --display short; else echo "skipped (DUNE_TEST_TARGETS is empty)"; fi
182
    - echo -e "section_end:`date +%s`:project_tests\r\e[0K"
183

    
184
    - echo -e "section_start:`date +%s`:project_doc\r\e[0K build the documentation"
185
    - if [ "$DUNE_DOC_TARGETS" != "" ]; then dune build $DUNE_DOC_TARGETS --display short; else echo "skipped (DUNE_DOC_TARGETS is empty)"; fi
186
    - echo -e "section_end:`date +%s`:project_doc\r\e[0K"
187

    
188
    - echo -e "section_start:`date +%s`:artifacts\r\e[0K populating the artifacts"
189
    - mkdir -p $ARTIFACTS
190
    - >
191
      echo "Build artifacts will be available at
192
              $CI_JOB_URL/artifacts/browse/$ARTIFACTS
193
            Note: by default Gitlab only keeps them for a few weeks."
194
    - >
195
      if [ "Dune_DOC_TARGETS" != "" ]; then
196
        cp -r _build/default/_doc/_html $ARTIFACTS/doc;
197
        echo "Documentation:
198
              $CI_JOB_URL/artifacts/browse/$ARTIFACTS/doc/index.html";
199
      fi
200
    - >
201
      if [ -f _build/log ];
202
      then
203
        mkdir -p $ARTIFACTS/_build;
204
        cp _build/log $ARTIFACTS/_build/log
205
        echo "Dune build log:
206
              $CI_JOB_URL/artifacts/browse/$ARTIFACTS/_build/log";
207
      fi
208
    - echo -e "section_end:`date +%s`:artifacts\r\e[0K"
209

    
210

    
211
### CI performance
212

    
213
# Performance analysis of one 'build' job on a small, simple library project
214
# (the project build/test/doc time is basically neglectible) on gitlab.com:
215
#
216
# (To get those numbers for your project, just look at a passing build and fold all
217
#  log sections.)
218
#
219
# 2m total
220
#   56s preparing the docker image and CI environment
221
#   14s restoring the _opam cache
222
#   10s installing the project dependencies
223
#       (external dependencies are not cached, opam dependencies are cached)
224
#    3s project build, tests, doc
225
#   31s saving the _opam cache
226
#    6s uploading artifacts
227
#
228
# When running with CLEAN_OPAM_CACHE=true, the costs are similar, except for
229
#  7m30s building the local opam switch
230
#  2m07s building the project dependencies (external and opam)
231
# This shows that caching the local _opam switch is very important, as
232
# its setup time dominates the build (for a small project).
233
#
234
# Another thing that is clear from these numbers is that splitting
235
# your CI in several jobs could be fairly expensive: for each job you
236
# pay 56s seconds of docker setup, plus 14s to restore the _opam
237
# cache. (The 31s to save the cache afterwards can be avoided by using
238
# a "policy: pull" setting if the job uses the _opam in a read-only
239
# way.)
240
#
241
# This is why this CI script does everything in a single 'build' step.
242

    
243

    
244
### Deploy stage
245
#
246
# pushing some project artifacts to the web.
247

    
248
# The "pages" rule uploads a custom website whenever someones
249
# pushes a commit to the "pages" branch of the repository.
250
#
251
# The content of the website is the sub-filesystem
252
# located in the "docs/" subdirectory of the branch index.
253
#
254
# This behavior emulates the way per-repository Github pages work.
255
#
256
# If the repository is at
257
#   https://gitlab.com/<group>/<project>,
258
# then the published website will be at
259
#   https://<group>.gitlab.io/<project>
260
# In general see
261
#   https://docs.gitlab.com/ee/user/project/pages/getting_started_part_one.html#gitlab-pages-default-domain-names
262
#
263
# We previously used a setup where the documentation was pushed to
264
# Gitlab Pages automatically when pushing a tag (a new release). This
265
# was much less flexible, with no easy way for users to publish web
266
# content *other* than the documentation, or to control when the
267
# content should be refreshed.
268
#
269
# It is fairly simple to publish the documentation
270
# from any branch with the current interface.
271
# Here is an example script:
272
#
273
#    # compute the current commit hash and branch name
274
#    git rev-parse --short HEAD > /tmp/commit
275
#    git rev-parse --abbrev-ref HEAD > /tmp/branch
276
#    cat .gitlab-ci.yml /tmp
277
#
278
#    # move to the 'pages' branch and commit the documentation there
279
#    git checkout pages
280
#    mkdir -p docs
281
#    git rm --ignore-unmatch -r docs > /dev/null
282
#    cp -r ${DOC_PATH} docs
283
#    git add docs
284
#    # we ensure that the CI configuration is present on the 'pages' branch,
285
#    # otherwise pushing from there may not deploy as expected.
286
#    cp /tmp/.gitlab-ci.yml .
287
#    git add .gitlab-ci.yml
288
#    git commit -m "documentation for $(cat /tmp/branch) ($(cat /tmp/commit))" \
289
#    && git checkout $(cat /tmp/branch) \
290
#    || git checkout $(cat /tmp/branch)
291
#
292
#    echo
293
#    echo "The documentation was committed to the 'pages' branch."
294
#    echo "You can now push it with"
295
#    echo "    git push origin pages"
296
#
297
pages:
298
  # Hardcoded values in the job:
299
  # (for Gitlab to understand that this job builds the Pages)
300
  # - the job name must be 'pages'
301
  # - the stage must be 'deploy'
302
  # - the artifacts must be a 'public' directory
303

    
304
  stage: deploy
305

    
306
  # no need for an OCaml environment
307
  image: alpine:latest
308

    
309
  # no cache
310

    
311
  # run this job only in the 'pages' branch
312
  rules:
313
    # https://docs.gitlab.com/ee/ci/yaml/#rules
314
    - if: '$CI_COMMIT_REF_NAME == "pages"'
315
      when: on_success
316
  artifacts:
317
    paths:
318
      - public
319
  script:
320
    - rm -fR public/
321
    - mkdir -p docs
322
    - cp -r docs public
323

    
324

    
325

    
326
## MIT License
327
#
328
# Copyright 2021 Gabriel Scherer
329
#
330
# Permission is hereby granted, free of charge, to any person obtaining
331
# a copy of this software and associated documentation files
332
# (the "Software"), to deal in the Software without restriction,
333
# including without limitation the rights to use, copy, modify, merge,
334
# publish, distribute, sublicense, and/or sell copies of the Software,
335
# and to permit persons to whom the Software is furnished to do so,
336
# subject to the following conditions:
337
#
338
# The above copyright notice and this permission notice shall be
339
# included in all copies or substantial portions of the Software.
340
#
341
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
342
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
343
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
344
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
345
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
346
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
347
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
(2-2/18)