diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8ca95240ce19c07953efecd144cdb0c7831a00f9..dfdd84bc829654fbbd7370cf38fb36a7bd02bc22 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,2 +1,6 @@ include: - local: "/.gitlab/webapp.gitlab-ci.yml" + # OpenAPI client generation templates. Destined for the common ci-templates repo. + - local: "/.gitlab/openapi-generator.gitlab-ci.yml" + # Local customisations for OpenAPI generation. + - local: "/.gitlab/openapi-generator-local.gitlab-ci.yml" diff --git a/.gitlab/openapi-generator-local.gitlab-ci.yml b/.gitlab/openapi-generator-local.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..aaef0b154f4ed4b512e6f76ba62faf2725e8dda8 --- /dev/null +++ b/.gitlab/openapi-generator-local.gitlab-ci.yml @@ -0,0 +1,17 @@ +# *REPOSITORY LOCAL* specialisation of the OpenAPI generator templates. + +variables: + # We need to explicitly enable the OpenAPI generation since we don't have an openapi.yaml file in + # the root of our repository which would otherwise signal that. + OPENAPI_GENERATOR_ENABLED: "1" + +# We do not ship the OpenAPI schema in the repository and so the openapi:schema job needs to be +# extended to create the schema. +openapi:schema: + services: + - docker:dind + image: + name: docker:latest + script: + - cp secrets.env.in secrets.env + - docker compose run --rm manage spectacular --fail-on-warn --file "$OPENAPI_GENERATOR_SCHEMA_ARTIFACT_PATH" diff --git a/.gitlab/openapi-generator.gitlab-ci.yml b/.gitlab/openapi-generator.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..5dc90f7f3fd02f58190c15cbd27d974fc9848d66 --- /dev/null +++ b/.gitlab/openapi-generator.gitlab-ci.yml @@ -0,0 +1,100 @@ +# Reusable template for generating API clients from an OpenAPI schema. +# +# See the openapi-generator.md file in this directory for detailed usage information. +# +# This template is intended to be included in the common pipeline and so follows the common pipeline +# rules: +# +# - No jobs are added unless triggered explicitly or via the presence of a special file. +# - All jobs can be disabled if incorrectly triggered. +# - All "public" CI variables are namespaced to avoid collisions. +# - All build artefacts are added to a configurable directory to allow customisation if that +# directory conflicts with one in the repository. +# +# This template requires the Auto DevOps stages. + +variables: + # Location of the OpenAPI schema file within the repository. + OPENAPI_GENERATOR_SCHEMA_PATH: "openapi.yaml" + + # Location of various job artefacts. These will always be relative to $CI_PROJECT_DIR. + OPENAPI_GENERATOR_ARTIFACT_DIR: "openapi" + OPENAPI_GENERATOR_SCHEMA_ARTIFACT_PATH: $OPENAPI_GENERATOR_ARTIFACT_DIR/schema.yml + OPENAPI_GENERATOR_SCHEMA_VERSION_ARTIFACT: $OPENAPI_GENERATOR_ARTIFACT_DIR/schema-version + + # Specify Docker images used by jobs. + OPENAPI_GENERATOR_YQ_IMAGE: mikefarah/yq:4 + OPENAPI_GENERATOR_OPENAPI_GENERATOR_IMAGE: openapitools/openapi-generator-cli:v7.10.0 + +# Ensure that the schema is present as the $OPENAPI_GENERATOR_SCHEMA_ARTIFACT_PATH artefact. The +# default behaviour is to copy the file specified by $OPENAPI_GENERATOR_SCHEMA_PATH but the job can +# be extended for more complex behaviour or dynamically generated schema. +openapi:schema: + stage: build + needs: [] + before_script: + # Ensure that the directory which is to contain the schema exists. + - mkdir -p $(dirname "$OPENAPI_GENERATOR_SCHEMA_ARTIFACT_PATH") + script: + # The default behaviour is to copy the schema file to the artifact directory. + - cp "$OPENAPI_GENERATOR_SCHEMA_PATH" "$OPENAPI_GENERATOR_SCHEMA_ARTIFACT_PATH" + artifacts: + paths: + - $OPENAPI_GENERATOR_SCHEMA_ARTIFACT_PATH + # When extending this job, rules which *disable* jobs should be included *above* these rules. + # Rules which *enable* the job should be included *below* them. + rules: + # Never run any OpenAPI generate jobs if OPENAPI_GENERATOR_DISABLED is set. + - if: $OPENAPI_GENERATOR_DISABLED + when: never + # If no API specification is provided, never run. + - if: ($OPENAPI_GENERATOR_SCHEMA_PATH == null) || ($OPENAPI_GENERATOR_SCHEMA_PATH == "") + when: never + # If an OpenAPI spec is present in the repository, use it. + - if: $OPENAPI_GENERATOR_SCHEMA_PATH + exists: + paths: + - $OPENAPI_GENERATOR_SCHEMA_PATH + # Finally, allow OpenAPI generation to be explicitly enabled if desired. + - if: $OPENAPI_GENERATOR_ENABLED + +# Extract the version from the OpenAPI schema. This is required by some generator jobs as some +# generator templates do not respect the version set in the schema. The version is written to +# $OPENAPI_GENERATOR_SCHEMA_VERSION_ARTIFACT. Note that even generators which support reading the +# version from the schema should respect the artefact generated by this job since it provides a +# customisation point to override the package version if needed. +openapi:schema:version: + stage: build + needs: ["openapi:schema"] + image: + name: $OPENAPI_GENERATOR_YQ_IMAGE + entrypoint: [""] + script: + - yq -r ".info.version" "$OPENAPI_GENERATOR_SCHEMA_ARTIFACT_PATH" >"$OPENAPI_GENERATOR_SCHEMA_VERSION_ARTIFACT" + artifacts: + paths: + - $OPENAPI_GENERATOR_SCHEMA_VERSION_ARTIFACT + rules: + - !reference ["openapi:schema", rules] + +# Template CI job for running openapi-generator. +.openapi:generator-cli: + image: + name: $OPENAPI_GENERATOR_OPENAPI_GENERATOR_IMAGE + entrypoint: [""] + variables: + # Customise how openapi-generator-cli is run. + OPENAPI_GENERATOR_CMD: "/usr/local/bin/docker-entrypoint.sh" + rules: + - !reference ["openapi:schema", rules] + +# Validate the OpenAPI schema. +openapi:schema:validate: + extends: ".openapi:generator-cli" + stage: test + needs: ["openapi:schema"] + script: + - |- + $OPENAPI_GENERATOR_CMD validate \ + --input-spec "$OPENAPI_GENERATOR_SCHEMA_ARTIFACT_PATH" \ + --recommend diff --git a/.gitlab/openapi-generator.md b/.gitlab/openapi-generator.md new file mode 100644 index 0000000000000000000000000000000000000000..25a6ae55265439f85cd0f1f8e171cf48d1f03193 --- /dev/null +++ b/.gitlab/openapi-generator.md @@ -0,0 +1,54 @@ +<!-- This file is being drafted here ahead of being merged into the guidebook. --> + +# OpenAPI client generation + +This document describes the use of the OpenAPI generator template. The OpenAPI generator template is +intended to be included in the "common" pipeline and so will not add any jobs to the pipeline unless +specifically triggered. + +## Basic use + +Add a file named `openapi.yaml` to the root of your repository with the OpenAPI schema in it. This +will trigger a run of the OpenAPI client generator. No additional CI configuration is needed. + +If you schema is stored in a different file, the `OPENAPI_GENERATOR_SCHEMA_PATH` CI variable may be +set to override the schema location. The variable should be set to a path relative to the repository +root which points to the schema file. + +## Generating the schema in CI + +If you generate your OpenAPI schema dynamically, you need to explicitly enable OpenAPI client +generation by setting the `OPENAPI_GENERATOR_ENABLED` variable. Additionally you'll need to override +the `openapi:schema` job to generate your schema and write it to the path stored in the +`OPENAPI_GENERATOR_SCHEMA_ARTIFACT_PATH` variable. + +For example: + +```yaml +# .gitlab-ci.yml + +variables: + # We need to explicitly enable the OpenAPI generation since we don't have an openapi.yaml file in + # the root of our repository which would otherwise signal that. + OPENAPI_GENERATOR_ENABLED: "1" + +# We do not ship the OpenAPI schema in the repository and so the openapi:schema job needs to be +# overridden to use our schema generation command. +openapi:schema: + script: + - my-schema-generator --output=$OPENAPI_GENERATOR_SCHEMA_ARTIFACT_PATH +``` + +Not that the existing `openapi:schema` job's `before_script` ensures that the directory containing +that path exists and so you do not need a `mkdir` or equivalent. + +## CI variables + +The following variables can be set to change the behaviour of the CI template. + +|Variable|Default|Description| +|-|-|-| +|`OPENAPI_GENERATOR_ENABLED`|*unset*|Set to non-empty value to *enable* OpenAPI client generation if not otherwise automatically triggered.| +|`OPENAPI_GENERATOR_DISABLED`|*unset*|Set to non-empty value to *disable* OpenAPI client generation if automatically triggered when it shouldn't be.| +|`OPENAPI_GENERATOR_SCHEMA_PATH`|`openapi.yaml`|Location of static OpenAPI schema file within the repository.| +|`OPENAPI_GENERATOR_ARTIFACT_DIR`|`openapi`|Directory in which generated artifacts are placed.|