auto-generate API clients, package them, publish them and publish docs to GitLab pages
Part of #23 (closed). Lays groundwork for uis/devops/continuous-delivery/ci-templates#93 (closed) and #22 (closed).
This MR extends the existing schema generation template to support auto-generation of OpenAPI clients and their documentation. This MR:
- Generates TypeScript + Axios and Python + urllib3 API clients
- Packages the clients
- (On commit to
main
) publishes the clients to GitLab's package registry- You can see some examples at https://gitlab.developers.cam.ac.uk/uis/devops/experiments/rjw57/misc/-/packages
- Generates documentation for the clients and uses
redoc
to document the API itself - Generates documentation for the backend Python code based on the pattern in the Regent House ballots app which is destined for the webapp boilerplate.
- This MR provides additional testing for that pattern and, once merged, probably shows it is ready for adding to the boilerplate.
- Includes the generated API client documentation in the project documentation
- See the generated docs at https://uis.uniofcam.dev/-/devops/iam/activate-account/api/-/jobs/1916064/artifacts/public/index.html
- (On commit to
main
) publishes the documentation via GitLab pages.
How to review
- Read the "Small fixes" section below to see incidental fixes made by this MR.
- Read
.gitlab/openapi-generator.md
which is intended to form the kernel of the Guidebook documentation. - Read
.gitlab/openapi-generator-local.gitlab-ci.yml
and.gitlab/docs-local.gitlab-ci.yml
as they give you an idea of the "special" config for this repo. - Read
mkdocs.yaml
in the root to give you an idea of the special docs config for this repo. Most ofdocs/
itself is intended to be moved to the boilerplate and has already been through one round of review in the Regent House Ballots application. It is a straightforward copy-paste and ideally will survive unchanged into the copier boilerplate. - Read
.gitlab/openapi-generator.gitlab-ci.yml
to see how the sausage is made.
Individual commits provide wider context but summaries are provided below.
Work intended for boilerplates and/or CI template repo
The config intending to stay in this repository is that in mkdocs.yaml
in the root and the .gitlab/*-local.gitlab-ci.yml
files. The OpenAPI generator CI config is intended to form part of the Common CI pipeline (and as such does nothing unless enabled by the presence of a file or explicitly via CI variables). We do some light customisation of the mkdocs template to deal with incorporating the generated client documentation.
The majority of docs/
is intended to make its way into the boilerplate. Most of it is relatively uninteresting but docs/scripts/gen-reference-nav.py
is intended to be generic and is currently present in both this MR and the Regent House Ballots webapp. Should docs/
survive review I intend to move a fair chunk of it into the copier
template. This should have little to no effect on this project once merged but will enable other projects to have automatically generated documentation in future.
Small fixes
Some small fixes were needed before starting work on this MR. They are present as separate commits but, in brief:
- add a maximum line length to .editorconfig for markdown files to appease my linter.
- bump the flake8 version to allow it to parse the more modern f-string idiom we use.
- adding missing
__init__.py
files for management command module which broke documentation generation - add a missing LICENSE file referenced from the README
- fix incorrect indenting in some docstrings
- adding type annotations consistent with the docstrings
All of the above docstring warts cause documentation generation to fail because consistency between docstrings and type annotations is now enforced.
Versioning
The behaviour of the generic OpenAPI generator template is to base the API client version on the schema version. This causes an issue when building and publishing clients since both npm and PyPI don't allow you to "re-publish" clients with the same version. As such we add an additional requirement to the basic schema job rule that the schema must actually have changed in order to regenerate clients. This still has the small wart that you'll only see failure to publish because you forgot to bump the version spec on runs to main
but for this particular project, that's not an issue since we override schema generation to make use of the release version and that will always be different when commits hit main
.
Client generation
In order to have some confidence that our approach is language-neutral, we generate two clients: one TypeScript and one Python. The generation jobs are based on generic npm and python templates and individual clients can be disabled via ..._DISABLED
variables if desired by downstream users.
We choose sensible defaults for package names, etc but these can be overridden globally or per-generator. We demonstrate this feature in this MR since the default package name is the GitLab project name and for this project the name, api
, is not terribly descriptive. Additionally Python module names can't contain -
and so we additionally override the Python package name.
Generated clients are written to $OPENAPI_GENERATOR_SOURCE_ARTIFACT_DIR
and are available to later CI jobs should they be needed.
Packaging
Once generated, clients are packaged. Again we create generic npm and Python packaging jobs and specialise them for our particular clients. The build artefacts are written to $OPENAPI_GENERATOR_PACKAGE_ARTIFACT_DIR
. We would add those artefacts to the release-it
configuration but, currently, release-it
is unable to add binary assets to releases.
Once release-it
is fixed, adding the assets is a case of adding a single line to .release-it.json
to specify the package artefact locations.
TypeScript clients are packaged as a tarball which is suitable for passing to npm install
and/or npm publish
and Python clients generate source distribution tarballs and wheels.
Publishing
On commits to main
, the packaged client artefacts are published to the GitLab package registry. You can see an example of this at https://gitlab.developers.cam.ac.uk/uis/devops/experiments/rjw57/misc/-/packages.
We use semver-compatible "build metadata" in the version number to additionally encode both the CI job id which generated the package and the git commit SHA. This is good practice in general for being able to tie package artefacts back to the processes which created them.
Client Documentation
We use language-appropriate documentation generators to document each generated client and use redoc
to document the API schema itself. These are all created via documentation jobs and stored as job artefacts. The default template doesn't do anything with them but this MR shows a potential publication pathway.
Project Documentation
Our common CI pipeline supports mkdocs
-based documentation natively and so we make use of that to generate project documentation which includes the API client documentation. This is a "MVP" effort at the moment. It's likely to need some grooming as time goes on. The mkdocs
config is based on one intended to end up in the webapp boilerplate and this MR provides additional baking to that end.
On commits to main
the generated documentation is published via GitLab pages.
The documentation generated from this MR may be previewed at https://uis.uniofcam.dev/-/devops/iam/activate-account/api/-/jobs/1916064/artifacts/public/index.html
For example, here is some of the generated API docs:
Following the "API Clients" link leads you to a list of API client documentation. For example, for TypeScript:
and for Python:
The "REST API Reference" link leads you to the API documentation generated by redoc. For example:
Further work
The generated project documentation is intentionally sparse since that's not the main focus of this MR. I've added just enough to actually make the documentation a) exist and b) link to the API client documentation but more work is needed. In particular there is no information on how to install any of the generated clients.