Best Practices#

When using these GitLab CI/CD templates, follow these recommended practices to ensure secure and reliable release workflows.

Branch Protection#

Use branch protection to ensure releases happen from controlled branches. This prevents unauthorized or accidental releases from feature branches.

release-to-pypi:
  extends: .release_pypi
  only:
    - main
    - master

Credential Management#

Store tokens and credentials as masked GitLab CI variables:

  1. Navigate to your project’s Settings > CI/CD > Variables

  2. Add your credentials as variables

  3. Enable the Masked option to hide values in job logs

  4. Enable the Protected option to restrict access to protected branches

Prefer API Tokens#

Prefer API tokens over passwords whenever supported:

  • PyPI: Use API tokens instead of account passwords

  • Read the Docs: Use project-specific API tokens

  • Other services: Follow their token-based authentication when available

API tokens can be:

  • Scoped to specific permissions

  • Easily revoked without changing your account password

  • Generated separately for different projects or purposes

Separate Workflows#

Keep documentation builds separate from package publication unless atomic release workflows are required.

This allows you to:

  • Update documentation independently of releases

  • Test documentation builds without triggering a release

  • Maintain more granular control over your CI/CD pipeline

stages:
  - test
  - build-docs
  - release

release-docs:
  extends: .release_read_the_docs
  stage: build-docs

release-to-pypi:
  extends: .release_pypi
  stage: release
  needs: ["release-docs"]

Version Tagging#

Use Git tags to trigger releases instead of branch names:

release-to-pypi:
  extends: .release_pypi
  only:
    - tags

This ensures that:

  • Releases are explicitly versioned

  • You maintain a clear history of releases

  • Rollbacks are easier to manage

Testing Before Release#

Always run your test suite before release jobs:

stages:
  - test
  - release

test:
  stage: test
  script:
    - pip install -e .[dev]
    - pytest

release-to-pypi:
  extends: .release_pypi
  stage: release
  needs: ["test"]