Commit 2f162fc1 authored by Pierre Smeyers's avatar Pierre Smeyers
Browse files

Merge branch 'initial' into 'main'

feat: New GitLab-CI Build Component for Zola

Closes #1

See merge request to-be-continuous/zola!1
parents bd8ff859 5b68b269
Loading
Loading
Loading
Loading
+36 −54
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ include:
variables:
  # 2: set/override template variables
  # ⚠ this is only an example
  ZOLA_BUILD_ARGS: "build --with-my-args"
  ZOLA_BUILD_ARGS: "--minify"
```

## Global configuration
@@ -44,7 +44,9 @@ The Zola template uses some global configuration used throughout all jobs.

| Input / Variable      | Description                            | Default value     |
| --------------------- | -------------------------------------- | ----------------- |
| `image` / `ZOLA_IMAGE` | The Docker image used to run `zola` | `docker.io/zola:latest`<br/>[![Trivy Badge](https://to-be-continuous.gitlab.io/doc/secu/trivy-badge-ZOLA_IMAGE.svg)](https://to-be-continuous.gitlab.io/doc/secu/trivy-ZOLA_IMAGE) |
| `image` / `ZOLA_IMAGE` | The Docker image used to run `zola` | `docker.io/jauderho/zola:latest`<br/>[![Trivy Badge](https://to-be-continuous.gitlab.io/doc/secu/trivy-badge-ZOLA_IMAGE.svg)](https://to-be-continuous.gitlab.io/doc/secu/trivy-ZOLA_IMAGE) |
| `site-dir` / `ZOLA_SITE_DIR` | The directory the rendered content will be placed | `public` |
| `workspace-dir` / `ZOLA_WORKSPACE_DIR` | The directory containing the zola site in the repository | `.` |

## Jobs

@@ -56,70 +58,28 @@ It uses the following variable:

| Input / Variable      | Description                              | Default value     |
| --------------------- | ---------------------------------------- | ----------------- |
| `build-args` / `ZOLA_BUILD_ARGS`      | Arguments used by the build job          | `build --with-default-args` |

### SonarQube analysis

If you're using the SonarQube template to analyse your ZOLA code, here are 2 sample `sonar-project.properties` files.

```properties
# see: https://docs.sonarsource.com/sonarqube-server/analyzing-source-code/test-coverage/zola-test-coverage/
# set your source directories here (relative to the sonar-project.properties file)
sonar.sources=.
# exclude unwanted directories and files from being analysed
sonar.exclusions=output/**,**/*_test.zola

# set your tests directories here (relative to the sonar-project.properties file)
sonar.tests=.
sonar.test.inclusions=**/*_test.zola

# tests report (TODO)
sonar.zola.testExecutionReportPaths=reports/sonar_test_report.xml
# coverage report (TODO)
sonar.zola.coverage.reportPaths=reports/coverage.cov
```

More info:

* [Zola language support](https://docs.sonarsource.com/sonarqube-server/analyzing-source-code/test-coverage/zola-test-coverage/)
* [test coverage](https://docs.sonarsource.com/sonarqube-server/analyzing-source-code/test-coverage/test-coverage-parameters/) & [test execution](https://docs.sonarsource.com/sonarqube-server/analyzing-source-code/test-coverage/test-execution-parameters/) parameters
* [external analyzer reports](https://docs.sonarsource.com/sonarqube-server/analyzing-source-code/importing-external-issues/external-analyzer-reports/)
| `build-args` / `ZOLA_BUILD_ARGS`      | Arguments used by the [build job](https://www.getzola.org/documentation/getting-started/cli-usage/#build) | `--minify` |

### `zola-lint` job

This job performs a [lint](link-to-the-tool) analysis of your code, mapped to the `build` stage.
This job performs a [zola check](https://www.getzola.org/documentation/getting-started/cli-usage/#check) analysis of your code, mapped to the `build` stage.

It uses the following variables:

| Input / Variable      | Description                                | Default value     |
| --------------------- | ------------------------------------------ | ----------------- |
| `lint-image` / `ZOLA_LINT_IMAGE`      | The Docker image used to run the lint tool | `zola-lint:latest`<br/>[![Trivy Badge](https://to-be-continuous.gitlab.io/doc/secu/trivy-badge-ZOLA_LINT_IMAGE.svg)](https://to-be-continuous.gitlab.io/doc/secu/trivy-ZOLA_LINT_IMAGE) |
| `lint-disabled` / `ZOLA_LINT_DISABLED`   | Set to `true` to disable the `lint` analysis| _none_ (enabled) |
| `lint-args` / `ZOLA_LINT_ARGS`       | Lint [options and arguments](link-to-the-cli-options) | `--serevity=medium` |

### `zola-depcheck` job

This job enables a manual [dependency check](link-to-the-tool) analysis of your code, mapped to the `test` stage.

It uses the following variables:

| Input / Variable      | Description                                | Default value     |
| --------------------- | ------------------------------------------ | ----------------- |
| `depcheck-image` / `ZOLA_DEPCHECK_IMAGE`  | The Docker image used to run the dependency check tool | `zola-depcheck:latest`<br/>[![Trivy Badge](https://to-be-continuous.gitlab.io/doc/secu/trivy-badge-ZOLA_DEPCHECK_IMAGE.svg)](https://to-be-continuous.gitlab.io/doc/secu/trivy-ZOLA_DEPCHECK_IMAGE) |
| `depcheck-args` / `ZOLA_DEPCHECK_ARGS`   | Dependency check [options and arguments](link-to-the-cli-options) | _none_ |
| `lint-args` / `ZOLA_LINT_ARGS`       | Lint [options and arguments](https://www.getzola.org/documentation/getting-started/cli-usage/#check) | `--drafts` |

### `zola-publish` job
### `zola-lychee` job

This job is **disabled by default** and performs a publish of your built binaries.

It uses the following variables:
This job checks links with [lychee](https://github.com/lycheeverse/lychee). It uses the following variable:

| Input / Variable | Description                                       | Default value     |
| --------------------- | -------------------------------------- | ----------------- |
| `publish-enabled` / `ZOLA_PUBLISH_ENABLED` | Variable to enable the publish job     | _none_ (disabled) |
| `publish-args` / `ZOLA_PUBLISH_ARGS`    | Arguments used by the publish job      | `publish --with-default-args` |
| :lock: `ZOLA_PUBLISH_LOGIN` | Login to use to publish           | **must be defined** |
| :lock: `ZOLA_PUBLISH_PASSWORD` | Password to use to publish     | **must be defined** |
| ----------------------- | ------------------------------------------------- | ----------------- |
| `lychee-enabled` / `ZOLA_LYCHEE_ENABLED` | Set to `true` to enable this job                  | _none_ (disabled) |
| `lychee-image` / `ZOLA_LYCHEE_IMAGE` | The Docker image used to run [lychee](https://github.com/lycheeverse/lychee) | `docker.io/lycheeverse/lychee:latest` <br/>[![Trivy Badge](https://to-be-continuous.gitlab.io/doc/secu/trivy-badge-MKD_LYCHEE_IMAGE.svg)](https://to-be-continuous.gitlab.io/doc/secu/trivy-MKD_LYCHEE_IMAGE) |
| `lychee-args` / `ZOLA_LYCHEE_ARGS` | [lychee arguments](https://github.com/lycheeverse/lychee#commandline-parameters) to execute | `content` |

### Secrets management

@@ -134,3 +94,25 @@ Here are some advices about your **secrets** (variables marked with a :lock:):
  simply define its value as the [Base64](https://en.wikipedia.org/wiki/Base64) encoded value prefixed with `@b64@`:
  it will then be possible to mask it and the template will automatically decode it prior to using it.
3. Don't forget to escape special characters (e.g.: `$` -> `$$`).

## Publishing

:warning: this template is not a deployment template and it only builds a MkDocs project.

You might deploy the generated site using a [GitLab pages](https://docs.gitlab.com/user/project/pages/) job (there is [a variant for that](#gitlab-pages-variant)) or any other method you see fit.

## Variants

### GitLab Pages variant

Basically it copies the content of the zola generated site folder (`public` by default) to the `public` folder which is published by [GitLab pages](https://docs.gitlab.com/user/project/pages/#how-it-works).

If you wish to use it, Add the following to your `.gitlab-ci.yml`:

```yaml
include:
  # main template
  - component: $CI_SERVER_FQDN/to-be-continuous/zola/gitlab-ci-zola@1.0.0
  # GitLab pages variant
  - component: $CI_SERVER_FQDN/to-be-continuous/zola/gitlab-ci-zola-pages@1.0.0
```
+38 −46
Original line number Diff line number Diff line

{
  "name": "Zola",
  "description": "Build, test and verify your [Zola](https://www.getzola.org/) projects",
@@ -11,75 +10,68 @@
    {
      "name": "ZOLA_IMAGE",
      "description": "The Docker image used to run `zola`",
      "default": "docker.io/zola:1.2.3"
      "default": "docker.io/jauderho/zola:latest"
    },
    {
      "name": "ZOLA_BUILD_ARGS",
      "description": "Arguments used by the build job",
      "default": "build --with-default-args",
      "description": "Arguments used by the [build job](https://www.getzola.org/documentation/getting-started/cli-usage/#build)",
      "default": "--minify",
      "advanced": true
    },
    {
      "name": "ZOLA_SITE_DIR",
      "description": "The directory the rendered content will be placed",
      "default": "public",
      "advanced": true
    },
    {
      "name": "ZOLA_WORKSPACE_DIR",
      "description": "The directory containing the zola site in the repository",
      "default": ".",
      "advanced": true
    }
  ],
  "features": [
    {
      "id": "lint",
      "name": "ZOLA lint",
      "description": "[ZOLA lint](link-to-the-tool) analysis",
      "name": "Zola lint",
      "description": "[Zola check](https://www.getzola.org/documentation/getting-started/cli-usage/#check) analysis",
      "disable_with": "ZOLA_LINT_DISABLED",
      "variables": [
        {
          "name": "ZOLA_LINT_IMAGE",
          "description": "The Docker image used to run the lint tool",
          "default": "docker.io/zola-lint:latest"
        },
        {
          "name": "ZOLA_LINT_ARGS",
          "description": "Lint [options and arguments](link-to-the-cli-options)",
          "default": "--serevity=medium",
          "advanced": true
        }
      ]
    },
    {
      "id": "depcheck",
      "name": "ZOLA dependency check",
      "description": "[ZOLA dependency check](link-to-the-tool) analysis",
      "variables": [
        {
          "name": "ZOLA_DEPCHECK_IMAGE",
          "description": "The Docker image used to run the dependency check tool",
          "default": "docker.io/zola-depcheck:latest"
        },
        {
          "name": "ZOLA_DEPCHECK_ARGS",
          "description": "Dependency check [options and arguments](link-to-the-cli-options)",
          "description": "Lint [options and arguments](https://www.getzola.org/documentation/getting-started/cli-usage/#check)",
          "default": "--drafts",
          "advanced": true
        }
      ]
    },
    {
      "id": "publish",
      "name": "Publish",
      "description": "Publish your package to a repository",
      "enable_with": "ZOLA_PUBLISH_ENABLED",
      "id": "lychee",
      "name": "lychee",
      "description": "Checks broken links and emails with [lychee](https://github.com/lycheeverse/lychee)",
      "enable_with": "ZOLA_LYCHEE_ENABLED",
      "variables": [
        {
          "name": "ZOLA_PUBLISH_ARGS",
          "description": "Arguments used by the publish job",
          "default": "publish --with-default-args",
          "name": "ZOLA_LYCHEE_IMAGE",
          "description": "The Docker image used to run [lychee](https://github.com/lycheeverse/lychee)",
          "default": "docker.io/lycheeverse/lychee:latest",
          "advanced": true
        },
        {
          "name": "ZOLA_PUBLISH_LOGIN",
          "description": "Login to use to publish",
          "secret": true
        },
        {
          "name": "ZOLA_PUBLISH_PASSWORD",
          "description": "Password to use to publish",
          "secret": true
          "name": "ZOLA_LYCHEE_ARGS",
          "description": "[lychee arguments](https://github.com/lycheeverse/lychee#commandline-parameters) to execute",
          "default": "content"
        }
      ]
    }
  ],
  "variants": [
    {
      "id": "pages",
      "name": "GitLab Pages",
      "description": "Adds a job to publish the generated documentation to GitLab pages",
      "template_path": "templates/gitlab-ci-zola-pages.yml"
    }
  ]
}
 No newline at end of file
+84 −0
Original line number Diff line number Diff line
# ==================================================
# Variables definition
# ==================================================
spec:
  inputs: {}
---
variables:
  # default production ref name (pattern)
  PROD_REF: /^(master|main)$/

# ==================================================
# Stages definition
# ==================================================
stages:
  - build
  - test
  - package-build
  - package-test
  - infra
  - deploy
  - acceptance
  - publish
  - infra-prod
  - production

.zola-pages-scripts: &zola-pages-scripts |
  # BEGSCRIPT
  set -e

  function log_info() {
      echo -e "[\\e[1;94mINFO\\e[0m] $*"
  }

  function log_warn() {
      echo -e "[\\e[1;93mWARN\\e[0m] $*"
  }

  function log_error() {
      echo -e "[\\e[1;91mERROR\\e[0m] $*"
  }

  # ENDSCRIPT


pages:
  stage: production
  before_script:
    - !reference [.zola-pages-scripts]
  script:
    - zola_dist=${ZOLA_WORKSPACE_DIR:-.}/${ZOLA_SITE_DIR:-public}
    - target="public"
    - |
      if [ "${zola_dist}" -ef "${target}" ]
      then
        log_info "configured zola site dir already is pages expected folder"
      else
        log_info "copying zola generated site from \\e[33;1m${zola_dist}\\e[0m to pages expected folder \\e[33;1m${target}\\e[0m"
        mkdir -p public
        cp -r ${zola_dist}/* ${target}
      fi
    # compress text resources
    # see: https://docs.gitlab.com/user/project/pages/introduction/#serving-compressed-assets
    - |
      if command -v gzip > /dev/null; then
        log_info "compressing text resources (gzip)..."
        find public -type f -regex '.*\.\(htm\|html\|txt\|text\|js\|css\|json\)$' -exec gzip -f -k {} \;
      fi
    - |
      if command -v brotli > /dev/null; then
        log_info "compressing text resources (br)..."
        find public -type f -regex '.*\.\(htm\|html\|txt\|text\|js\|css\|json\)$' -exec brotli -f -k {} \;
      fi
  artifacts:
    paths:
      - public
    expire_in: 1 days
  environment:
    name: pages
    url: ${CI_PAGES_URL}
  rules:
    # on production branch(es)
    - if: '$CI_COMMIT_REF_NAME =~ $PROD_REF'

+43 −94
Original line number Diff line number Diff line
@@ -15,36 +15,35 @@
# =========================================================================================
spec:
  inputs:

    image:
      description: The Docker image used to run `zola`
      default: docker.io/zola:1.2.3
      default: docker.io/jauderho/zola:latest
    build-args:
      description: Arguments used by the build job
      default: build --with-default-args
      description: Arguments used by the [build job](https://www.getzola.org/documentation/getting-started/cli-usage/#build)
      default: "--minify"
    lint-disabled:
      description: Disable ZOLA lint
      description: Disable Zola lint
      type: boolean
      default: false
    lint-image:
      description: The Docker image used to run the lint tool
      default: docker.io/zola-lint:latest
    lint-args:
      description: Lint [options and arguments](link-to-the-cli-options)
      default: --serevity=medium
    depcheck-image:
      description: The Docker image used to run the dependency check tool
      default: docker.io/zola-depcheck:latest
    depcheck-args:
      description: Dependency check [options and arguments](link-to-the-cli-options)
      default: ''
    publish-enabled:
      description: Enable Publish
      description: Lint [options and arguments](https://www.getzola.org/documentation/getting-started/cli-usage/#check)
      default: --drafts
    lychee-enabled:
      description: Enable lychee
      type: boolean
      default: false
    publish-args:
      description: Arguments used by the publish job
      default: publish --with-default-args
    lychee-image:
      description: The Docker image used to run [lychee](https://github.com/lycheeverse/lychee)
      default: docker.io/lycheeverse/lychee:latest
    lychee-args:
      description: '[lychee arguments](https://github.com/lycheeverse/lychee#commandline-parameters) to execute'
      default: content
    site-dir:
      description: The directory the rendered content will be placed
      default: public
    workspace-dir:
      description: The directory containing the zola site in the repository
      default: .
---
# default workflow rules: Merge Request pipelines
.tbc-workflow-rules:
@@ -111,18 +110,17 @@ workflow:
variables:
  # Default Docker image (use a public image - can be overridden)
  ZOLA_IMAGE: $[[ inputs.image ]]

  ZOLA_BUILD_ARGS: $[[ inputs.build-args ]]

  ZOLA_PUBLISH_ENABLED: $[[ inputs.publish-enabled ]]
  ZOLA_PUBLISH_ARGS: $[[ inputs.publish-args ]]

  ZOLA_LINT_DISABLED: $[[ inputs.lint-disabled ]]
  ZOLA_LINT_IMAGE: $[[ inputs.lint-image ]]
  ZOLA_LINT_ARGS: $[[ inputs.lint-args ]]

  ZOLA_DEPCHECK_IMAGE: $[[ inputs.depcheck-image ]]
  ZOLA_DEPCHECK_ARGS: $[[ inputs.depcheck-args ]]
  ZOLA_LYCHEE_ENABLED: $[[ inputs.lychee-enabled ]]
  ZOLA_LYCHEE_IMAGE: $[[ inputs.lychee-image ]]
  ZOLA_LYCHEE_ARGS: $[[ inputs.lychee-args ]]

  ZOLA_WORKSPACE_DIR: $[[ inputs.workspace-dir ]]
  ZOLA_SITE_DIR: $[[ inputs.site-dir ]]

  # default production ref name (pattern)
  PROD_REF: '/^(master|main)$/'
@@ -388,69 +386,46 @@ stages:
# job prototype
# defines default Docker image, tracking probe, cache policy and tags
.zola-base:
  image: $ZOLA_IMAGE
  image:
    name: $ZOLA_IMAGE
    entrypoint: [""]
  services:
    - name: "$TBC_TRACKING_IMAGE"
      command: ["--service", "zola", "1.0.0"]
  before_script:
    - !reference [.zola-scripts]
    - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}"
  variables:
    # TODO (if necessary): set cache dir variables
    XDG_CACHE_HOME: "$CI_PROJECT_DIR/.cache"
  cache:
    # cache shall be per branch per template
    key: "${CI_COMMIT_REF_SLUG}-zola"
    fallback_keys:
      - "${CI_DEFAULT_BRANCH}-zola"
    when: always
    # cache shall be per branch per template
    key: "$CI_COMMIT_REF_SLUG-zola"
    when: always
    paths:
      - .cache/

# (example) build & test job
# build & test job
zola-build:
  extends: .zola-base
  stage: build
  script:
    - mkdir -p -m 777 reports
    # TODO (if possible): $TRACE set enables debug logs on the tool
    # TODO (if possible): force test tool to produce JUnit report(s)
    # TODO (if possible): force test tool to compute code coverage with report
    - zola ${TRACE+--verbose} --coverage --junit --output=reports/zola-test.xunit.xml $ZOLA_BUILD_ARGS
    - output_coverage
  # TODO: code coverage support and GitLab integration (see: https://docs.gitlab.com/ci/yaml/#coverage)
  coverage: '/^(\d+.\d+\%) covered$/'
  # keep build artifacts and test reports (see: https://docs.gitlab.com/ci/yaml/#artifactsreportsjunit)
    - cd ${ZOLA_WORKSPACE_DIR}
    - zola build ${ZOLA_BUILD_ARGS} --output-dir ${ZOLA_SITE_DIR}
  artifacts:
    name: "$CI_JOB_NAME artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
    expire_in: 1 day
    reports:
      # TODO: Unit tests use JUnit format and GitLab integration (see: https://docs.gitlab.com/ci/yaml/#artifactsreports)
      junit:
        - reports/zola-test.xunit.xml
    paths:
        - build/
        - reports/
        - ${ZOLA_WORKSPACE_DIR}/${ZOLA_SITE_DIR}

# (example) linter job
# linter job
zola-lint:
  extends: .zola-base
  stage: build
  image: $ZOLA_LINT_IMAGE
  # force no dependency
  dependencies: []
  script:
    - mkdir -p -m 777 reports
    - zola_lint ${TRACE+--verbose} --output=reports/zola-lint.native.json $ZOLA_LINT_ARGS
  artifacts:
    name: "$CI_JOB_NAME artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
    expire_in: 1 day
    when: always
    paths:
      - reports/zola-lint.*
    - cd ${ZOLA_WORKSPACE_DIR}
    - zola check ${ZOLA_LINT_ARGS}
  rules:
    # exclude if $ZOLA_LINT_DISABLED
    - if: '$ZOLA_LINT_DISABLED == "true"'
@@ -458,41 +433,15 @@ zola-lint:
    # .test-policy rules
    - !reference [.test-policy, rules]

# (example) dependency check job
zola-depcheck:
zola-lychee:
  extends: .zola-base
  stage: test
  image: $ZOLA_DEPCHECK_IMAGE
  # force no dependency
  dependencies: []
  script:
    - zola_depcheck ${TRACE+--verbose} $ZOLA_DEPCHECK_ARGS
  rules:
    # on schedule: auto
    - if: '$CI_PIPELINE_SOURCE == "schedule"'
      allow_failure: true
      when: always
    # all other cases: manual & non-blocking
    - when: manual
      allow_failure: true

# (example) publish job activated on env ($ZOLA_PUBLISH_ENABLED), with required $ZOLA_PUBLISH_LOGIN and $ZOLA_PUBLISH_PASSWORD env verification
zola-publish:
  extends: .zola-base
  stage: publish
  before_script:
    - !reference [.zola-scripts]
    # verify $ZOLA_PUBLISH_LOGIN and $ZOLA_PUBLISH_PASSWORD are set
    - assert_defined "$ZOLA_PUBLISH_LOGIN" 'Missing required env $ZOLA_PUBLISH_LOGIN'
    - assert_defined "$ZOLA_PUBLISH_PASSWORD" 'Missing required env $ZOLA_PUBLISH_PASSWORD'
    - zola login --login=$ZOLA_PUBLISH_LOGIN --password=$ZOLA_PUBLISH_PASSWORD
  image:
    name: "$ZOLA_LYCHEE_IMAGE"
    entrypoint: [""]
  script:
    - zola $ZOLA_PUBLISH_ARGS
    - lychee ${ZOLA_LYCHEE_ARGS}
  rules:
    # exclude if $ZOLA_PUBLISH_ENABLED unset
    - if: '$ZOLA_PUBLISH_ENABLED != "true"'
    - if: '$ZOLA_LYCHEE_ENABLED != "true"'
      when: never
    # on integration or production branch(es): manual & non-blocking
    - if: '$CI_COMMIT_REF_NAME =~ $INTEG_REF || $CI_COMMIT_REF_NAME =~ $PROD_REF'
      when: manual
      allow_failure: true
    - !reference [.test-policy, rules]