Development

To set up a development environment for the API and app, make sure you have a relatively recent version of Docker and Make on your development machine (and that you’ve added your daily user to the docker group). For the agent, make sure you have pyenv with all the current versions of Python installed.

API

Clone the official Git repository at https://git.sr.ht/~arx10/procustodibus-api. All the development scripts assume that you will run the database and API via the dev docker-compose.yml file in a custom Docker network named custos (using the 10.242.4.0/24 address block).

Init

In your local procustodibus-api/ directory, run the make init command to start up the database and build the dev Docker image:

$ make init
TODO

The dev Docker container builds the necessary version of Erlang and Elixir from scratch (it will take 10+ minutes to complete). All the other Make tasks run in this dev Docker container.

Once this command has completed, run the following command to generate a random application-level encryption key:

$ openssl rand -base64 32
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

Copy the random key, open up the .env file (generated by the make init command), and paste it in as the value for the DB_ALEK_1 environment variable:

# .env
DB_ALEK_1="value:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="

Test

To run the full linting and test suite, run the make check command (which will take a minute or two to complete):

$ make check
docker-compose run api mix format --check-formatted
Creating api_api_run ... done
docker-compose run api mix compile --all-warnings --warnings-as-errors
Creating api_api_run ... done
docker-compose run api mix credo  --strict
Creating api_api_run ... done
Checking 365 source files (this might take a while) ...

Please report incorrect results: https://github.com/rrrene/credo/issues

Analysis took 17.1 seconds (2.5s to load, 14.6s running 67 checks on 365 files)
3756 mods/funs, found no issues.

Use `mix credo explain` to explain issues, `mix credo --help` for options.
docker-compose run api mix sobelow --config
Creating api_api_run ... done
Checking Sobelow version...

A new version of Sobelow is available:
mix archive.install hex sobelow

##############################################
#                                            #
#          Running Sobelow - v0.12.2         #
#  Created by Griffin Byatt - @griffinbyatt  #
#     NCC Group - https://nccgroup.trust     #
#                                            #
##############################################

Config.HTTPS: HTTPS Not Enabled - High Confidence


HTTPS configuration details could not be found in `prod.exs`.

-----------------------------------------------

... SCAN COMPLETE ...

docker-compose run api mix test
Creating api_api_run ... done
Excluding tags: [:aws]

..........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Finished in 43.9 seconds (23.3s async, 20.6s sync)
58 doctests, 3570 tests, 0 failures, 2 excluded

Randomized with seed 450375

Watch

To run a specific unit test file every time you make a change to the source code, run the make watch command, specifying the unit test file to run with the TEST option:

$ make watch TEST=test/api_web/controllers/page_controller_test.exs
docker-compose run shell mix test.watch test/api_web/controllers/page_controller_test.exs
Creating api_shell_run ... done

Running tests...
Compiling 16 files (.ex)
Generated api app
Excluding tags: [:aws]

  1) test index GET / (ApiWeb.PageControllerTest)
     test/api_web/controllers/page_controller_test.exs:5
     Assertion with == failed
     code:  assert conn |> get(~p"/") |> json_response(200) == %{}
     left:  %{"foo" => "bar"}
     right: %{}
     stacktrace:
       test/api_web/controllers/page_controller_test.exs:6: (test)

.
Finished in 0.09 seconds (0.00s async, 0.09s sync)
2 tests, 1 failure

Randomized with seed 498117

Server

The make dev command will run the API server, with an interactive IEx shell running in your current terminal:

$ make dev
docker-compose run --use-aliases api iex -S mix phx.server
Creating api_api_run ... done
00:07:41.582 application=swoosh mfa=Swoosh.Application.runtime_children/2 [info] Running Swoosh mailbox preview server with Cowboy using http on port 4001
00:07:41.999 application=phoenix mfa=Phoenix.Endpoint.Cowboy2Adapter.start_link/3 [info] Running ApiWeb.Endpoint with cowboy 2.10.0 at 0.0.0.0:4000 (http)
00:07:42.010 application=phoenix mfa=Phoenix.Endpoint.Supervisor.log_access_url/2 [info] Access ApiWeb.Endpoint at http://10.242.4.242:4000
00:07:42.061 application=ecto_sql mfa=Ecto.Adapters.SQL.log/4 [debug] QUERY OK db=2.7ms decode=2.6ms queue=1.1ms idle=57.2ms
INSERT INTO "system_recurring_tasks" ("name","node") VALUES ($1,$2) ON CONFLICT DO NOTHING RETURNING "id" ["alert_endpoint_stats", ""]
00:07:42.072 application=ecto_sql mfa=Ecto.Adapters.SQL.log/4 [debug] QUERY OK db=1.2ms queue=1.3ms idle=78.0ms
INSERT INTO "system_recurring_tasks" ("name","node") VALUES ($1,$2) ON CONFLICT DO NOTHING RETURNING "id" ["alert_log_authn", ""]
00:07:42.076 application=ecto_sql mfa=Ecto.Adapters.SQL.log/4 [debug] QUERY OK db=1.5ms queue=1.2ms idle=80.8ms
INSERT INTO "system_recurring_tasks" ("name","node") VALUES ($1,$2) ON CONFLICT DO NOTHING RETURNING "id" ["alert_log_authz", ""]
00:07:42.080 application=ecto_sql mfa=Ecto.Adapters.SQL.log/4 [debug] QUERY OK db=2.0ms queue=2.0ms idle=84.3ms
INSERT INTO "system_recurring_tasks" ("name","node") VALUES ($1,$2) ON CONFLICT DO NOTHING RETURNING "id" ["alert_log_changes", ""]
00:07:42.086 application=ecto_sql mfa=Ecto.Adapters.SQL.log/4 [debug] QUERY OK db=2.1ms queue=2.5ms idle=89.1ms
INSERT INTO "system_recurring_tasks" ("name","node") VALUES ($1,$2) ON CONFLICT DO NOTHING RETURNING "id" ["cache_geoip_uses", ""]
00:07:42.089 application=ecto_sql mfa=Ecto.Adapters.SQL.log/4 [debug] QUERY OK db=1.0ms queue=1.2ms idle=94.2ms
INSERT INTO "system_recurring_tasks" ("name","node") VALUES ($1,$2) ON CONFLICT DO NOTHING RETURNING "id" ["post_alerts_hook", ""]
00:07:42.091 application=ecto_sql mfa=Ecto.Adapters.SQL.log/4 [debug] QUERY OK db=1.1ms queue=1.1ms idle=96.7ms
INSERT INTO "system_recurring_tasks" ("name","node") VALUES ($1,$2) ON CONFLICT DO NOTHING RETURNING "id" ["post_log_authn_hook", ""]
00:07:42.094 application=ecto_sql mfa=Ecto.Adapters.SQL.log/4 [debug] QUERY OK db=1.5ms queue=1.1ms idle=99.3ms
INSERT INTO "system_recurring_tasks" ("name","node") VALUES ($1,$2) ON CONFLICT DO NOTHING RETURNING "id" ["post_log_changes_hook", ""]
00:07:42.098 application=ecto_sql mfa=Ecto.Adapters.SQL.log/4 [debug] QUERY OK db=1.7ms queue=1.8ms idle=102.3ms
INSERT INTO "system_recurring_tasks" ("name","node") VALUES ($1,$2) ON CONFLICT DO NOTHING RETURNING "id" ["post_endpoint_stats_hook", ""]
00:07:42.101 application=ecto_sql mfa=Ecto.Adapters.SQL.log/4 [debug] QUERY OK db=1.1ms queue=1.6ms idle=104.6ms
INSERT INTO "system_recurring_tasks" ("name","node") VALUES ($1,$2) ON CONFLICT DO NOTHING RETURNING "id" ["check_hung_ldap_polls", ""]
00:07:42.102 application=ecto_sql mfa=Ecto.Adapters.SQL.log/4 [debug] QUERY OK db=0.4ms idle=48.6ms
INSERT INTO "system_recurring_tasks" ("name","node") VALUES ($1,$2) ON CONFLICT DO NOTHING RETURNING "id" ["rotate_preshared_keys", ""]
00:07:42.103 application=ecto_sql mfa=Ecto.Adapters.SQL.log/4 [debug] QUERY OK db=0.3ms idle=29.6ms
INSERT INTO "system_recurring_tasks" ("name","node") VALUES ($1,$2) ON CONFLICT DO NOTHING RETURNING "id" ["update_sessions_last_used", ""]
Erlang/OTP 26 [erts-14.0.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit:ns]

Interactive Elixir (1.15.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Users.get_user!(1) |> Map.get(:name)
"System User"
iex(2)>

In a different terminal, run the following command to try sending a request to the API:

$ curl -w'\n' http://10.242.4.242:4000/health
[
  {
    "error": null,
    "healthy": true,
    "name": "DB",
    "time": 4581
  }
]

If you make changes to the API source code, it will for the most part reload automatically the next time you make a web request. For example, open up the lib/api_web/controllers/page_controller.ex file, and change its index/2 function to return some dummy JSON value:

# lib/api_web/controllers/page_controller.ex
defmodule ApiWeb.PageController do
  use ApiWeb, :controller

  def index(conn, _params) do
    conn
    |> cache_control("public, max-age=3600")
    # change this line to return some dummy value like %{foo: "bar"}:
    |> json(%{foo: "bar"})
  end

  # ...
end

cURL the controller action you just changed to view the results of your change:

$ curl -w'\n' http://10.242.4.242:4000/
{"foo":"bar"}

Shell

To run arbitrary Mix tasks (such as mix format), start a shell for the dev Docker image with the make shell command:

$ make shell
docker-compose run shell /bin/bash
Creating api_shell_run ... done
jose@dd383b95cd32:/srv/api$ mix format
jose@dd383b95cd32:/srv/api$

Database

To run arbitrary SQL queries in the database, start a shell for the database with the make shell.db command:

$ make shell.db
docker-compose up -d db
Creating network "custos" with the default driver
Creating api_db_1 ... done
docker exec -u postgres -it api_db_1 psql
psql (13.11)
Type "help" for help.

postgres=#

Run the \c custos command to switch to the dev database; then you can run SQL queries on the dev data:

postgres=# \c custos
You are now connected to database "custos" as user "postgres".
custos=# select * from users;
 id |       pub_id        | active |    name     | org_id |      inserted_at       |       updated_at       | type  | locked
----+---------------------+--------+-------------+--------+------------------------+------------------------+-------+--------
  1 | 6620344162125037829 | t      | System User |      1 | 2020-01-01 00:00:00+00 | 2020-01-01 00:00:00+00 | admin | f
(1 row)

Directories

Here’s a quick tour of the directories in the API project:

  • assets: static files served by the API (such as favicon.ico)

  • bin: miscellaneous shell scripts

  • _build: output of build scripts

  • build: build scripts

  • config: standard Elixir config directory

  • ee: Enterprise Edition code and tests

  • lib: standard Elixir code directory

  • me: your private scratch directory

  • ops: scripts run on deployed systems

  • priv: standard Elixir resources directory

  • test: standard Elixir tests directory

App

Clone the official Git repository at https://git.sr.ht/~arx10/procustodibus-app. All the development scripts assume that you will run the app via the dev docker-compose.yml file in a custom Docker network named custos_app (using the 10.242.5.0/28 address block).

Init

In your local procustodibus-app/ directory, run the make init command to build the dev Docker image:

$ make init
TODO

The dev Docker container includes the necessary version of Node.js and NPM. The make init command will also install the app UI’s dev dependencies (in the usual node_modules/ directory).

Test

To run the full linting and test suite, run the make check command (which will take a minute or two to complete):

$ make check
docker-compose run app npm run lint
Creating app_app_run ... done

> custos@1.0.0 lint
> npm run lint-js && npm run lint-css && npm run format-check


> custos@1.0.0 lint-js
> eslint --ext .vue,.js,.jsx,.cjs,.mjs --report-unused-disable-directives --ignore-path .gitignore --cache --cache-location out/ .


> custos@1.0.0 lint-css
> stylelint "src/**/*.{css,less,sass,scss,sss,styl,vue}" --report-needless-disables --report-invalid-scope-disables --ignore-path .gitignore --cache --cache-location out/


> custos@1.0.0 format-check
> prettier --check src/ tests/

Checking formatting...
All matched files use Prettier code style!
docker-compose run app npm run test
Creating app_app_run ... done

> custos@1.0.0 test
> vitest run --coverage


 RUN  v0.34.1 /srv/app
      Coverage enabled with v8

stderr | tests/unit/utils/text.spec.js > decodeBase64() > returns blank if not base64 encoded
cannot decode as base64 utf-8: 12345

stderr | tests/unit/utils/text.spec.js > decodeBase64Url() > returns blank if not base64 encoded
cannot decode as base64 utf-8: 12345

 ✓ tests/unit/utils/background.spec.js (28) 6773ms
 ✓ tests/unit/utils/login.spec.js (34)
 ✓ tests/unit/utils/rdap.spec.js (10) 320ms
 ✓ tests/unit/utils/date.spec.js (73)
 ✓ tests/unit/utils/axios.spec.js (11)
 ✓ tests/unit/utils/text.spec.js (74)
 ✓ tests/unit/utils/model.spec.js (22)
 ✓ tests/unit/utils/env.spec.js (15)
 ✓ tests/unit/utils/session.spec.js (13)
 ✓ tests/unit/utils/timeline.spec.js (40)
 ✓ tests/unit/stores/user.spec.js (10)
 ✓ tests/unit/utils/download.spec.js (17)
 ✓ tests/unit/stores/mfa.spec.js (10)
 ✓ tests/unit/utils/misc.spec.js (26)
 ✓ tests/unit/utils/error.spec.js (5)
 ✓ tests/unit/utils/form.spec.js (8)
 ✓ tests/unit/utils/message.spec.js (6)
 ✓ tests/unit/utils/pageantry.spec.js (16)
 ✓ tests/unit/utils/storage.spec.js (4)

 Test Files  19 passed (19)
      Tests  422 passed (422)
   Start at  01:35:22
   Duration  11.85s (transform 618ms, setup 4ms, collect 3.48s, tests 7.74s, environment 13.61s, prepare 2.65s)

 % Coverage report from v8
-----------------------|---------|----------|---------|---------|-----------------------------------------------------------------
File                   | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------------------|---------|----------|---------|---------|-----------------------------------------------------------------
All files              |   95.37 |    91.33 |   95.23 |   95.37 |
 components/app        |     100 |      100 |     100 |     100 |
  app-confirm-form.vue |     100 |      100 |     100 |     100 |
  app-message.vue      |     100 |      100 |     100 |     100 |
  app-modal-form.vue   |     100 |      100 |     100 |     100 |
 stores                |   99.14 |    80.21 |     100 |   99.14 |
  features.js          |     100 |       75 |     100 |     100 | 28
  mfa.js               |     100 |      100 |     100 |     100 |
  prefs.js             |   83.33 |    66.66 |     100 |   83.33 | 8-10
  session.js           |     100 |    86.53 |     100 |     100 | 135-141,193-197
  user.js              |     100 |    52.63 |     100 |     100 | 34-36,39-40,45,49,57-58
 utils                 |   94.68 |    92.74 |   94.47 |   94.68 |
  axios.js             |   89.44 |    86.36 |   83.33 |   89.44 | 45-57,94-97,131-133,199-201
  background.js        |   95.45 |    96.96 |     100 |   95.45 | 14-21
  date.js              |     100 |    99.11 |     100 |     100 | 75
  download.js          |   97.19 |       94 |   92.85 |   97.19 | 16-21,157,159
  env.js               |   98.29 |      100 |    92.3 |   98.29 | 77-78
  error.js             |   80.21 |    56.09 |   88.88 |   80.21 | 102-123,133,137,139,141,143,145,149,163,165,167,169,172,183-185
  form.js              |   82.53 |      100 |   66.66 |   82.53 | 29-39
  login.js             |   98.84 |     91.8 |     100 |   98.84 | 232,234-235
  message.js           |     100 |      100 |     100 |     100 |
  misc.js              |     100 |      100 |     100 |     100 |
  model.js             |   80.45 |    83.33 |   91.66 |   80.45 | 40-73,83,85,87,89,91,93,95,97,99
  pageantry.js         |     100 |      100 |     100 |     100 |
  rdap.js              |     100 |    95.45 |     100 |     100 | 15
  storage.js           |     100 |    83.33 |     100 |     100 | 19
  text.js              |   94.87 |      100 |   92.59 |   94.87 | 303-315,323-327
  timeline.js          |     100 |    91.25 |     100 |     100 | 78,159,217,233,236,272-273
-----------------------|---------|----------|---------|---------|-----------------------------------------------------------------

Watch

To run all the tests every time you make a change to the source code, run the make watch command:

$ make watch
...
 Test Files  19 passed (19)
      Tests  422 passed (422)
   Start at  01:38:43
   Duration  13.81s (transform 573ms, setup 3ms, collect 2.57s, tests 7.43s, environment 10.04s, prepare 1.95s)


 PASS  Waiting for file changes...
       press h to show help, press q to quit

For the most part, only affected tests will re-run when you make a change.

Press h while this command is waiting to see several options for filtering the tests that are run.

Server

The make dev command will run the dev Vite server, serving a dev build of the app UI:

$ make dev
  VITE v4.4.8  ready in 816 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: http://10.242.5.12:5173/
  ➜  press h to show help

In a different terminal, run the following command to verify the dev server is up and running:

$ curl -sS http://10.242.5.12:5173/ | head
<!DOCTYPE html>
<html lang="en">
  <head>
    <script type="module" src="/@vite/client"></script>

    <meta charset="UTF-8">
    <link rel="icon" href="/favicon.ico">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Pro Custodibus</title>
    <link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-180x180.png" sizes="180x180">

If you make changes to the app source code, it will for the most part reload automatically live in your web browser. For example, open up the src/components/nav/nav-header.vue file, and change the name of the logo.svg image to some dummy value:

<!-- src/components/nav/nav-header.vue -->
<template>
  <header class="header">
    <nav-navbar class="container" transparent>
      <template #brand>
        <nav-navbar-item tag="a" :href="`${env.wwwUrl}/`">
          <!-- change this line to use some dummy image name like foo.svg: -->
          <img src="@/assets/foo.svg" :alt="env.title" />
        </nav-navbar-item>
      </template>
      <!-- ... -->

If you have the app opened up in a web browser, you’ll see the logo image in the page header has disappeared, and replaced with its alt text (“Local Pro Custodibus”).

Shell

To run arbitrary NPM commands (such as npm outdated), start a shell with the dev Docker image with the following command:

$ make shell
docker-compose run -u $(id -u) shell /bin/bash
Creating app_shell_run ... done
node@d6b679ca310c:/srv/app$ npm outdated
Package           Current  Wanted  Latest  Location                       Depended by
axios-extensions    3.1.7   3.1.7   3.1.6  node_modules/axios-extensions  app

Directories

Here’s a quick tour of the directories in the app project:

  • bin: miscellaneous shell scripts

  • build: build scripts

  • coverage: coverage output

  • ee: Enterprise Edition code and tests

  • node_modules: standard Node.js dependencies directory

  • ops: scripts run on deployed systems

  • out: build output

  • public: static files served by the app (such as favicon.ico)

  • src: source code

    • src/assets: static files directly included by the source code

    • src/components: Vue.js components

    • src/mixins: Vue.js mixins and composables

    • src/pages: Vue.js pages

    • src/plugins: Vue.js plugins

    • src/stores: Pinia stores

    • src/styles: SCSS stylesheets

    • src/utils: generic JavaScript utility functions

  • tests: unit tests

Agent

Clone the official Git repository at https://git.sr.ht/~arx10/procustodibus-agent. All the development scripts assume that you will use tox for development in an Python virtualenv.

Init

In your local procustodibus-agent/ directory, create a virtualenv with pyenv:

$ pyenv virtualenv 3.10.12 procustodibus-agent

Then activate the virtualenv:

$ pyenv local procustodibus-agent 3.7.17 3.8.17 3.9.17 3.10.12 3.11.4

And install tox into the virtualenv:

(procustodibus-agent) $ pip install tox
Collecting tox
  Downloading tox-4.9.0-py3-none-any.whl (152 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 152.8/152.8 kB 2.9 MB/s eta 0:00:00
Collecting packaging>=23.1
  Using cached packaging-23.1-py3-none-any.whl (48 kB)
Collecting tomli>=2.0.1
  Using cached tomli-2.0.1-py3-none-any.whl (12 kB)
Collecting cachetools>=5.3.1
  Downloading cachetools-5.3.1-py3-none-any.whl (9.3 kB)
Collecting colorama>=0.4.6
  Using cached colorama-0.4.6-py2.py3-none-any.whl (25 kB)
Collecting pyproject-api>=1.5.3
  Downloading pyproject_api-1.5.4-py3-none-any.whl (12 kB)
Collecting pluggy>=1.2
  Using cached pluggy-1.2.0-py3-none-any.whl (17 kB)
Collecting platformdirs>=3.10
  Downloading platformdirs-3.10.0-py3-none-any.whl (17 kB)
Collecting filelock>=3.12.2
  Using cached filelock-3.12.2-py3-none-any.whl (10 kB)
Collecting virtualenv>=20.24.3
  Downloading virtualenv-20.24.3-py3-none-any.whl (3.0 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.0/3.0 MB 44.3 MB/s eta 0:00:00
Collecting chardet>=5.2
  Downloading chardet-5.2.0-py3-none-any.whl (199 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 199.4/199.4 kB 20.2 MB/s eta 0:00:00
Collecting distlib<1,>=0.3.7
  Downloading distlib-0.3.7-py2.py3-none-any.whl (468 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 468.9/468.9 kB 32.4 MB/s eta 0:00:00
Installing collected packages: distlib, tomli, pluggy, platformdirs, packaging, filelock, colorama, chardet, cachetools, virtualenv, pyproject-api, tox
Successfully installed cachetools-5.3.1 chardet-5.2.0 colorama-0.4.6 distlib-0.3.7 filelock-3.12.2 packaging-23.1 platformdirs-3.10.0 pluggy-1.2.0 pyproject-api-1.5.4 tomli-2.0.1 tox-4.9.0 virtualenv-20.24.3

Finally, install the pre-commit and pre-push hooks:

(procustodibus-agent) $ tox -e pre-commit -- install
pre-commit: install_deps> python -I -m pip install pre-commit
pre-commit: commands[0]> pre-commit install
pre-commit installed at .git/hooks/pre-commit
  pre-commit: OK (3.89=setup[3.67]+cmd[0.22] seconds)
  congratulations :) (7.41 seconds)
(procustodibus-agent) $ tox -e pre-commit -- install -t pre-push
pre-commit: commands[0]> pre-commit install -t pre-push
pre-commit installed at .git/hooks/pre-push
  pre-commit: OK (0.18=setup[0.01]+cmd[0.18] seconds)
  congratulations :) (1.48 seconds)

Test

Simply run the tox command to run the full linting and test suite (which will take several minutes to complete):

(procustodibus-agent) $ tox
.pkg-cpython37: install_requires> python -I -m pip install 'setuptools>=40.8.0' wheel
.pkg-cpython37: _optional_hooks> python /home/me/source/pyenv/versions/3.10.9/envs/procustodibus-agent/lib/python3.10/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg-cpython37: get_requires_for_build_editable> python /home/me/source/pyenv/versions/3.10.9/envs/procustodibus-agent/lib/python3.10/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg-cpython37: install_requires_for_build_editable> python -I -m pip install wheel
.pkg-cpython37: build_editable> python /home/me/source/pyenv/versions/3.10.9/envs/procustodibus-agent/lib/python3.10/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
py37: install_package_deps> python -I -m pip install 'docopt-ng~=0.7' 'inflection~=0.5' 'pynacl~=1.5' pytest pytest-cov pytest-datadir pytest-mock pytest-timeout pytest-watch 'requests~=2.27' 'tabulate~=0.8'
py37: install_package> python -I -m pip install --force-reinstall --no-deps /home/me/procustodibus-agent/.tox/.tmp/package/1/procustodibus_agent-1.3.3-0.editable-py3-none-any.whl
py37: commands[0]> pytest
============================================================================================================= test session starts =============================================================================================================
platform linux -- Python 3.7.16, pytest-7.4.0, pluggy-1.2.0
cachedir: .tox/py37/.pytest_cache
rootdir: /home/me/procustodibus-agent
plugins: mock-3.11.1, datadir-1.4.1, cov-4.1.0, timeout-2.1.0
collected 252 items

test/test_api.py ..................................                                                                                                                                                                                     [ 13%]

...

test/mfa/test_init.py .....................................                                                                                                                                                                             [100%]

---------- coverage: platform linux, python 3.10.9-final-0 -----------
Coverage HTML written to dir htmlcov


============================================================================================================= 252 passed in 1.82s =============================================================================================================
coverage: commands[1]> coverage report
Name                                      Stmts   Miss  Cover
-------------------------------------------------------------
procustodibus_agent/__init__.py               5      0   100%
procustodibus_agent/agent.py                 47     31    34%
procustodibus_agent/api.py                  155      4    97%
procustodibus_agent/cli.py                   24     24     0%
procustodibus_agent/cnf.py                  215      7    97%
procustodibus_agent/connectivity.py          77      2    97%
procustodibus_agent/credentials.py           19     19     0%
procustodibus_agent/executor.py             416     24    94%
procustodibus_agent/ip_route.py              36      4    89%
procustodibus_agent/mfa/__init__.py          83     12    86%
procustodibus_agent/mfa/api.py               33      1    97%
procustodibus_agent/mfa/cli.py               29     29     0%
procustodibus_agent/resolve_hostname.py      56      0   100%
procustodibus_agent/wg.py                    44      2    95%
procustodibus_agent/wg_cnf.py               174      2    99%
-------------------------------------------------------------
TOTAL                                      1413    161    89%
coverage: OK ✔ in 9.32 seconds
flake8: install_package_deps> python -I -m pip install cohesion darglint dlint 'docopt-ng~=0.7' flake8 flake8-alfred flake8-bandit flake8-broken-line flake8-bugbear flake8-builtins flake8-class-attributes-order flake8-coding flake8-cognitive-complexity flake8-comprehensions flake8-debugger flake8-docstrings flake8-eradicate flake8-executable flake8-expression-complexity flake8-fixme flake8-functions flake8-isort flake8-logging-format flake8-pep3101 flake8-print flake8-pytest flake8-pytest-style flake8-requirements flake8-rst flake8-spellcheck flake8-string-format 'inflection~=0.5' pep8-naming 'pynacl~=1.5' 'requests~=2.27' 'tabulate~=0.8'
flake8: install_package> python -I -m pip install --force-reinstall --no-deps /home/me/procustodibus-agent/.tox/.tmp/package/6/procustodibus_agent-1.3.3-0.editable-py3-none-any.whl
flake8: commands[0]> flake8
.pkg: _exit> python /home/me/procustodibus-agent/versions/3.10.9/envs/testpyenv/lib/python3.10/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg-cpython37: _exit> python /home/me/procustodibus-agent/versions/3.10.9/envs/testpyenv/lib/python3.10/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg-cpython38: _exit> python /home/me/procustodibus-agent/versions/3.10.9/envs/testpyenv/lib/python3.10/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg-cpython39: _exit> python /home/me/procustodibus-agent/versions/3.10.9/envs/testpyenv/lib/python3.10/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
flake8: OK ✔ in 1 minute 26.16 seconds
build: install_deps> python -I -m pip install setuptools wheel
build: commands[0]> python setup.py -q sdist bdist_wheel
/home/me/procustodibus-agent/.tox/build/lib/python3.10/site-packages/setuptools/_distutils/cmd.py:66: SetuptoolsDeprecationWarning: setup.py install is deprecated.
!!

        ********************************************************************************
        Please avoid running ``setup.py`` directly.
        Instead, use pypa/build, pypa/installer or other
        standards-based tools.

        See https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html for details.
        ********************************************************************************

!!
  self.initialize_options()
  py37: OK (11.96=setup[9.99]+cmd[1.96] seconds)
  py38: OK (11.16=setup[9.34]+cmd[1.83] seconds)
  py39: OK (11.37=setup[9.48]+cmd[1.89] seconds)
  py310: OK (11.09=setup[9.35]+cmd[1.73] seconds)
  py311: OK (11.05=setup[9.32]+cmd[1.72] seconds)
  coverage: OK (9.32=setup[6.79]+cmd[2.30,0.23] seconds)
  flake8: OK (86.16=setup[14.63]+cmd[71.53] seconds)
  build: OK (1.94=setup[1.46]+cmd[0.48] seconds)
  congratulations :) (155.31 seconds)

Watch

To run all the tests every time you make a change to the source code, run the tox -e watch command:

(procustodibus-agent) $ tox -e watch
cli develop-inst-noop: /home/me/procustodibus-agent
cli installed: certifi==2023.7.22,cffi==1.15.1,charset-normalizer==3.2.0,colorama==0.4.6,coverage==7.3.0,docopt==0.6.2,docopt-ng==0.9.0,exceptiongroup==1.1.2,idna==3.4,inflection==0.5.1,iniconfig==2.0.0,packaging==23.1,pluggy==1.2.0,# Editable Git install (procustodibus-agent==1.3.3) with either a deleted local remote or invalid URI:,# 'git@git.sr.ht:~arx10/procustodibus-agent',-e /home/me/procustodibus-agent,pycparser==2.21,PyNaCl==1.5.0,pytest==7.4.0,pytest-cov==4.1.0,pytest-datadir==1.4.1,pytest-mock==3.11.1,pytest-timeout==2.1.0,pytest-watch==4.2.0,requests==2.31.0,tabulate==0.9.0,tomli==2.0.1,urllib3==2.0.4,watchdog==3.0.0
watch run-test-pre: PYTHONHASHSEED='526703176'
watch run-test: commands[0] | pytest-watch

[Wed Aug 02 18:49:23 2023] Running: py.test
================================================= test session starts =================================================
platform linux -- Python 3.10.6, pytest-7.4.0, pluggy-1.2.0
cachedir: .tox/watch/.pytest_cache
rootdir: /home/me/procustodibus-agent
plugins: mock-3.11.1, datadir-1.4.1, cov-4.1.0, timeout-2.1.0
collected 252 items

test/test_api.py ..................................                                                             [ 13%]
test/test_cnf.py .............................................                                                  [ 31%]
test/test_connectivity.py ..............                                                                        [ 36%]
test/test_executor.py ...........................................                                               [ 53%]
test/test_ip_route.py .....                                                                                     [ 55%]
test/test_resolve_hostname.py .........................                                                         [ 65%]
test/test_wg.py .........                                                                                       [ 69%]
test/test_wg_cnf.py .................................                                                           [ 82%]
test/mfa/test_api.py .......                                                                                    [ 85%]
test/mfa/test_init.py .....................................                                                     [100%]

================================================= 252 passed in 1.07s =================================================

CLI

To run the agent CLI (Command-Line Interface), run the tox -e cli command, followed by -- and the arguments to the agent (eg --help):

(procustodibus-agent) $ tox -e cli -- --help
cli develop-inst-nodeps: /home/me/procustodibus-agent
cli installed: certifi==2023.7.22,cffi==1.15.1,charset-normalizer==3.2.0,colorama==0.4.6,coverage==7.3.0,docopt==0.6.2,docopt-ng==0.9.0,exceptiongroup==1.1.2,idna==3.4,inflection==0.5.1,iniconfig==2.0.0,packaging==23.1,pluggy==1.2.0,# Editable Git install (procustodibus-agent==1.3.3) with either a deleted local remote or invalid URI:,# 'git@git.sr.ht:~axr10/procustodibus-agent',-e /home/me/procustodibus-agent,pycparser==2.21,PyNaCl==1.5.0,pytest==7.4.0,pytest-cov==4.1.0,pytest-datadir==1.4.1,pytest-mock==3.11.1,pytest-timeout==2.1.0,pytest-watch==4.2.0,requests==2.31.0,tabulate==0.9.0,tomli==2.0.1,urllib3==2.0.4,watchdog==3.0.0
cli run-test-pre: PYTHONHASHSEED='337286959'
cli run-test: commands[0] | procustodibus-agent --help
Pro Custodibus Agent.

Synchronizes your WireGuard settings with Pro Custodibus.

Usage:
  procustodibus-agent [--config=CONFIG] [--loop=SECONDS]
                      [-v | -vv | --verbosity=LEVEL]
  procustodibus-agent --help
  procustodibus-agent --test [--config=CONFIG] [-v | -vv | --verbosity=LEVEL]
  procustodibus-agent --version

Options:
  -h --help             Show this help
  --test                Run connectivity check
  --version             Show agent version
  -c --config=CONFIG    Config file
  -l --loop=SECONDS     Loop indefinitely, sending ping every SECONDS
  --verbosity=LEVEL     Log level (ERROR, WARNING, INFO, DEBUG)
  -v                    INFO verbosity
  -vv                   DEBUG verbosity
___________________________________________________________________________________________________________________ summary ___________________________________________________________________________________________________________________
  cli: commands succeeded
  congratulations :)

If you make changes to the agent source code, it will automatically recompile the next time you execute it. For example, open up the procustodibus_agent/cli.py file, and change the main() function to print out some dummy value when the --version option is used:

# procustodibus_agent/cli.py
# ...
def main():
    """CLI Entry point."""
    args = docopt(__doc__)
    if args["--version"]:
        # change this line to print some dummy value like "FOOBAR":
        print("FOOBAR")
    elif args["--test"]:
        check(args["--config"], args["--verbosity"] or args["-v"])
    else:
        run(
            args["--config"],
            args["--verbosity"] or args["-v"],
            args["--loop"],
        )
# ...

Run the CLI with the --version option to view the results of your change:

(procustodibus-agent) $ tox -e cli -- --version
cli develop-inst-noop: /home/me/procustodibus-agent
cli installed: certifi==2023.7.22,cffi==1.15.1,charset-normalizer==3.2.0,colorama==0.4.6,coverage==7.3.0,docopt==0.6.2,docopt-ng==0.9.0,exceptiongroup==1.1.2,idna==3.4,inflection==0.5.1,iniconfig==2.0.0,packaging==23.1,pluggy==1.2.0,# Editable Git install (procustodibus-agent==1.3.3) with either a deleted local remote or invalid URI:,# 'git@git.sr.ht:~arx10/procustodibus-agent',-e /home/me/procustodibus-agent,pycparser==2.21,PyNaCl==1.5.0,pytest==7.4.0,pytest-cov==4.1.0,pytest-datadir==1.4.1,pytest-mock==3.11.1,pytest-timeout==2.1.0,pytest-watch==4.2.0,requests==2.31.0,tabulate==0.9.0,tomli==2.0.1,urllib3==2.0.4,watchdog==3.0.0
cli run-test-pre: PYTHONHASHSEED='127587549'
cli run-test: commands[0] | procustodibus-agent --version
FOOBAR
_______________________________________________________ summary _______________________________________________________
  cli: commands succeeded
  congratulations :)

Install Tests

To run the install tests, first build the Docker container for each:

(procustodibus-agent) $ docker-compose -f test_install/docker-compose.yml build --pull
TODO

Then run the install tests:

(procustodibus-agent) $ tox -e py310 test_install
TODO

The install tests will take around 15 minutes to complete.

Directories

Here’s a quick tour of the directories in the agent project:

  • build: build working directory

  • .builds: SourceHut build scripts

  • dist: output of build scripts

  • docker: docker build scripts

  • etc: service configuration files

  • htmlcov: coverage output

  • procustodibus_agent: source code

  • procustodibus_agent.egg-info: local install directory

  • requirements: requirements files

  • test: unit tests

  • test_install: install tests (run via Docker)

  • .tox: tox working directory