summaryrefslogtreecommitdiff
path: root/Taskfile.yml
blob: b2febc57057b3feee475868ae09d3e6c1e1e6781 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
# https://taskfile.dev/usage/
# https://pkg.go.dev/text/template
# https://go-task.github.io/slim-sprig/
version: "3"

vars:
  TASKFILE_DIR:
    sh: pwd
  POETRY: poetry
  # The prefix to use when running dev commands
  RUN_PREFIX: "{{.POETRY}} run"
  # The python to use for running in the venv
  VENV_PYTHON: "{{.RUN_PREFIX}} python"
    # The python interpreter to use.
  PYTHON: python
  # Truthish values ("true", "1", etc.) results in java being installed as a
  # system dependency.
  INSTALL_SYSTEM_DEPS_JAVA: "false"
  # Truthish values ("true", "1", etc.) results in extras being installed with
  # pip.
  INSTALL_EXTRAS: "false"
  # Truthish values ("true", "1", etc.) results in extensive tests being ran and
  # dependencies for extensive tests being installed.
  EXTENSIVE: "false"
  # The python version for which tox should run, empty string does not restrict
  # python versions.
  TOX_PYTHON_VERSION: ""
  TEST_HARNESS: '{{if (and (mustFromJson .EXTENSIVE) (not (eq OS "windows")))}}./with-fuseki.sh{{end}} '
  # Truthish values ("true", "1", etc.) results in github specific things being
  # done.
  WITH_GITHUB_ACTIONS: "false"
  # Truthish values ("true", "1", etc.) results in coverage being generated for
  # relevant commands.
  WITH_COVERAGE: "false"
  PIP_COMPILE: pip-compile
  DOCKER: docker
  OCI_REFERENCE: ghcr.io/rdflib/rdflib
tasks:
  install:system-deps:
    desc: Install system dependencies
    cmds:
      - echo "OS = {{OS}}"
      - echo "ARCH = {{ARCH}}"
      - |
        {{if (and (mustFromJson .EXTENSIVE) (eq OS "linux"))}}
          if type apt-get >/dev/null 2>&1
          then
            sudo apt-get install -y libdb-dev
          elif type dnf >/dev/null 2>&1
          then
            sudo dnf install -y libdb-devel
          fi
        {{else if (and (mustFromJson .EXTENSIVE) (eq OS "darwin"))}}
          brew install berkeley-db@4
        {{end}}

  install:tox:
    desc: Install tox
    cmds:
      - "{{.PYTHON}} -m pip install tox {{if (mustFromJson .WITH_GITHUB_ACTIONS)}}tox-gh-actions{{end}}"

  poetry:configure:
    desc: Configure the environment for development with poetry
    cmds:
      - |
        {{if .POETRY_PYTHON}} {{.POETRY}} env use {{.POETRY_PYTHON}} {{end}}
        {{.POETRY}} install {{if (or (mustFromJson .INSTALL_EXTRAS) (mustFromJson .EXTENSIVE))}} --all-extras{{end}} {{.CLI_ARGS}}

  configure:
    desc: Configure the environment for development
    cmds:
      - task: venv:install

  venv:install:
    desc: Create and install a venv
    cmds:
      - task: poetry:configure

  venv:clean:
    desc: Clean the virtual environment
    cmds:
      - "{{.POETRY}} env remove --all {{.CLI_ARGS}}"

  venv:run:
    desc: Run a command inside the venv
    cmds:
      - cmd: |
          {{.RUN_PREFIX}} {{.CLI_ARGS}}

  run:
    desc: Run a command. If WITH_VENV is truthish, the command will be run inside the virtual environment.
    cmds:
      - task: venv:run

  tox:
    desc: Run tox
    cmds:
      - echo "TOXENV=${TOXENV}"
      - |
        {{if .TOX_PYTEST_ARGS}}TOX_PYTEST_ARGS={{shellQuote .TOX_PYTEST_ARGS}}{{end}} \
        {{if .TOX_JUNIT_XML_PREFIX}}TOX_JUNIT_XML_PREFIX={{shellQuote .TOX_JUNIT_XML_PREFIX}}{{end}} \
        {{if .COVERAGE_FILE}}COVERAGE_FILE={{shellQuote .COVERAGE_FILE}}{{end}} \
        {{.TEST_HARNESS}} \
        {{.PYTHON | shellQuote}} \
          -m tox \
          {{.CLI_ARGS}}
    env:
      TOXENV: '{{if .TOX_PYTHON_VERSION}}py{{mustRegexReplaceAll "^([0-9]+)[.]([0-9]+).*" .TOX_PYTHON_VERSION "${1}${2}"}}{{if (mustFromJson .EXTENSIVE)}}-extensive{{end}}{{.TOXENV_SUFFIX | default ""}}{{end}}'
  test:
    desc: Run tests
    cmds:
      - '{{.TEST_HARNESS}}{{.RUN_PREFIX}} pytest {{if (mustFromJson .WITH_COVERAGE)}}--cov --cov-report={{end}} {{.CLI_ARGS}}'
  flake8:
    desc: Run flake8
    cmds:
      - |
        if {{.VENV_PYTHON}} -c 'import importlib.util; exit(0 if importlib.util.find_spec("flakeheaven") is not None else 1)'
        then
          1>&2 echo "running flakeheaven"
          {{.VENV_PYTHON}} -m flakeheaven lint {{.CLI_ARGS}}
        else
          1>&2 echo "skipping flakeheaven as it is not installed, likely because python version is older than 3.8"
        fi
  black:
    desc: Run black
    cmds:
      - '{{.VENV_PYTHON}} -m black {{if (mustFromJson (.CHECK | default "false"))}}--check --diff {{end}}{{.CLI_ARGS | default "."}}'
  isort:
    desc: Run isort
    cmds:
      - '{{.VENV_PYTHON}} -m isort {{if (mustFromJson (.CHECK | default "false"))}}--check --diff {{end}}{{.CLI_ARGS | default "."}}'
  mypy:
    desc: Run mypy
    cmds:
      - "{{.VENV_PYTHON}} -m mypy --show-error-context --show-error-codes {{.CLI_ARGS}}"

  lint:fix:
    desc: Fix auto-fixable linting errors
    cmds:
      - task: isort
      - task: black

  lint:
    desc: Perform linting
    cmds:
      - task: isort
        vars: { CHECK: true }
      - task: black
        vars: { CHECK: true }
      - task: flake8

  validate:static:
    desc: Perform static validation
    cmds:
      - task: lint
      - task: mypy

  validate:fix:
    desc: Fix auto-fixable validation errors.
    cmds:
      - task: lint:fix

  validate:
    desc: Perform all validation
    cmds:
      - task: validate:static
      - task: test

  docs:clean:
    desc: Clean generated documentation
    cmds:
      - task: _rimraf
        vars: { RIMRAF_TARGET: "docs/_build/" }

  docs:
    desc: Build documentation
    cmds:
      - echo "PYTHONPATH=${PYTHONPATH}"
      - "{{.VENV_PYTHON}} -m sphinx.cmd.build -T -W -b html -d docs/_build/doctree docs docs/_build/html {{.CLI_ARGS}}"

  docs:live-server:
    desc: Run a live server on generated docs
    cmds:
      - 'echo "NOTE: Docs must be built for this to work"'
      - npx -p live-server live-server docs/_build/html/ {{.CLI_ARGS}}

  default:
    desc: Run validate
    cmds:
      - task: validate

  clean:mypy:
    desc: Clean mypy cache
    cmds:
      - task: _rimraf
        vars: { RIMRAF_TARGET: ".mypy_cache" }
      - task: clean:tox:mypy

  clean:tox:
    desc: Clean tox environments
    cmds:
      - task: _rimraf
        vars: { RIMRAF_TARGET: ".tox" }

  clean:tox:mypy:
    desc: Clean mypy cache inside tox environments
    cmds:
      - task: _rimraf
        vars: { RIMRAF_TARGET: ".tox/*/.mypy_cache/" }

  clean:
    desc: Clean everything
    cmds:
      - task: docs:clean
      - task: clean:tox
      - task: clean:mypy
      - task: venv:clean
      - task: _rimraf
        vars: { RIMRAF_TARGET: ".var/devcontainer" }
      - task: _rimraf
        vars: { RIMRAF_TARGET: "var/test-sdist" }

  test:data:fetch:
    desc: Fetch test data.
    cmds:
      - "{{.VENV_PYTHON}} test/data/fetcher.py {{.CLI_ARGS}}"

  pre-commit:install:
    desc: Install pre-commit hooks
    cmds:
      - pre-commit install {{.CLI_ARGS}}

  pre-commit:run:
    desc: Run pre-commit
    cmds:
      - pre-commit run {{.CLI_ARGS}}

  pre-commit:run:all-files:
    desc: Run pre-commit on all files
    cmds:
      - pre-commit run --all-files  {{.CLI_ARGS}}

  gha:validate:
    desc: GitHub Actions Validation Workflow
    env:
      COVERALLS_PARALLEL: true
      COVERALLS_FLAG_NAME: "{{.OS}}-{{.TOX_PYTHON_VERSION}}{{.MATRIX_SUFFIX}}"
      COVERALLS_SERVICE_NAME: '{{.COVERALLS_SERVICE_NAME | default (env "COVERALLS_SERVICE_NAME") | default "github"}}'
    cmds:
      - task: install:system-deps
      - task: install:tox
        vars:
          WITH_GITHUB_ACTIONS: true
      - cmd: "{{.PYTHON}} -m pip install coveralls"
      - task: tox
        vars:
          COVERAGE_FILE: ".coverage"
      - cmd: coveralls

  gha:flake8:
    desc: GitHub Actions flake8 workflow
    cmds:
      - task: poetry:configure
        vars:
          CLI_ARGS: --no-root --only=flake8
      - task: flake8

  cmd:rdfpipe:
    desc: Run rdfpipe
    cmds:
      - cmd: "{{.VENV_PYTHON}} -m rdflib.tools.rdfpipe {{.CLI_ARGS}}"

  pip-compile:
    cmds:
      - cmd: "{{.PIP_COMPILE}} --quiet --annotate --emit-options --resolver=backtracking {{.CLI_ARGS}}"

  docker:prepare:
    cmds:
      - task: pip-compile
        vars: { CLI_ARGS: "docker/latest/requirements.in" }
  docker:unstable:
    desc: ...
    cmds:
      - cmd: |
          # fetch for caching ...
          {{.DOCKER}} image pull {{.OCI_REFERENCE}}:unstable || :

          set -eo pipefail
          mkdir -vp var
          {{.POETRY}} export -o var/requirements.txt
          pipx run --spec=build build --wheel
          {{.DOCKER}} buildx build \
            --cache-to=type=inline \
            --cache-from=type=registry,ref={{.OCI_REFERENCE}}:unstable \
            --tag {{.OCI_REFERENCE}}:unstable \
            --progress plain \
            -f docker/unstable/Dockerfile \
            {{.DOCKER_BUILD_ARGS}} \
            .

          if {{.DOCKER_PUSH | default "false"}}
          then
            {{.DOCKER}} image push {{.OCI_REFERENCE}}:unstable
          fi
  docker:latest:
    desc: ...
    cmds:
      - cmd: |
          # fetch for caching ...
          {{.DOCKER}} image pull {{.OCI_REFERENCE}}:latest || :

          set -eo pipefail

          {{.DOCKER}} buildx build \
            --cache-to=type=inline \
            --cache-from=type=registry,ref={{.OCI_REFERENCE}}:latest \
            --tag {{.OCI_REFERENCE}}:latest \
            --progress plain \
            -f docker/latest/Dockerfile \
            {{.DOCKER_BUILD_ARGS}} \
            .

          _latest_rdflib_version=$({{.DOCKER}} run --entrypoint= {{.OCI_REFERENCE}}:latest bash -c 'pip show rdflib | sed -n "s/^Version: //gp"')
          echo "_latest_rdflib_version=${_latest_rdflib_version}"

          {{.DOCKER}} image tag {{.OCI_REFERENCE}}:latest {{.OCI_REFERENCE}}:${_latest_rdflib_version}

          if {{.DOCKER_PUSH | default "false"}}
          then
            {{.DOCKER}} image push {{.OCI_REFERENCE}}:latest
            {{.DOCKER}} image push {{.OCI_REFERENCE}}:${_latest_rdflib_version}
          fi

  docs:build-diagrams:
    desc: Build documentation diagrams
    cmds:
      - cmd: |
          shopt -s globstar;
          for plantuml_file in ./**/*.plantuml
          do
            cat "${plantuml_file}" \
              | docker run --rm -i plantuml/plantuml -tsvg -pipe \
              > "${plantuml_file%.*}.svg"
          done

  test:sdist:
    desc: Run tests on the sdist artifact
    cmds:
      - task: _rimraf
        vars: { RIMRAF_TARGET: "dist" }
      - task: _rimraf
        vars: { RIMRAF_TARGET: "var/test-sdist" }
      - poetry build
      - python -c 'import tarfile, glob; tarfile.open(glob.glob("dist/*.tar.gz")[0]).extractall("var/test-sdist")'
      - |
        cd var/test-sdist/rdflib-*
        poetry install
        poetry run mypy --show-error-context --show-error-codes -p rdflib
        poetry run sphinx-build -T -W -b html -d docs/_build/doctree docs docs/_build/html
        poetry run pytest

  test:no_internet:
    desc: Run tests without internet access
    cmds:
      - |
        {{.TEST_HARNESS}}{{.RUN_PREFIX}} firejail --net=none -- pytest -m "not webtest" {{.CLI_ARGS}}
  _rimraf:
    # This task is a utility task for recursively removing directories, it is
    # similar to rm -rf but not identical and it should work wherever there is
    # a python interpreter. The name is inspired by
    # <https://www.npmjs.com/package/rimraf>.
    - cmd: |
        {{.PYTHON}} -c '
        from pathlib import Path;
        import sys, shutil;
        for path in sys.argv[1:]:
          if Path(path).exists():
            sys.stderr.write(f"removing {path}\n")
            shutil.rmtree(path, ignore_errors=True)
        ' {{.RIMRAF_TARGET}}