diff --git a/README.md b/README.md index 9bc4d5e3cd79078c15788c7946dc1c696dcff8b8..7d06b4ca272ae560705a54f2ecfcc6b1fe337df8 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,12 @@ and/or `setup.py` and/or `requirements.txt`), but the build system might also be ## Jobs +### `py-package` job + +This job allows building your Python project [distribution packages](https://packaging.python.org/en/latest/glossary/#term-Distribution-Package). + +It is bound to the `build` stage, it is **disabled by default** and can be enabled by setting `$PYTHON_PACKAGE_ENABLED` to `true`. + ### Lint jobs #### `py-pylint` job @@ -206,7 +212,7 @@ It is bound to the `test` stage, and uses the following variables: | Name | description | default value | | ---------------- | ---------------------------------------------------------------------- | ----------------- | -| `BANDIT_ENABLED` | Set to `true` to enable Bandit analysis | _none_ (disabled) | +| `BANDIT_ENABLED` | Set to `true` to enable Bandit analysis | _none_ (disabled) | | `BANDIT_ARGS` | Additional [Bandit CLI options](https://github.com/PyCQA/bandit#usage) | `--recursive .` | This job outputs a **textual report** in the console, and in case of failure also exports a JSON report in the `reports/` @@ -220,7 +226,7 @@ It is bound to the `test` stage, and uses the following variables: | Name | description | default value | | ---------------- | ----------------------------------------------------------------------- | ----------------- | -| `SAFETY_ENABLED` | Set to `true` to enable Safety job | _none_ (disabled) | +| `SAFETY_ENABLED` | Set to `true` to enable Safety job | _none_ (disabled) | | `SAFETY_ARGS` | Additional [Safety CLI options](https://github.com/pyupio/safety#usage) | `--full-report` | This job outputs a **textual report** in the console, and in case of failure also exports a JSON report in the `reports/` @@ -234,62 +240,92 @@ It is bound to the `test` stage, and uses the following variables: | Name | description | default value | | ---------------- | ----------------------------------------------------------------------- | ----------------- | -| `PYTHON_TRIVY_ENABLED` | Set to `true` to enable Trivy job | _none_ (disabled) | +| `PYTHON_TRIVY_ENABLED` | Set to `true` to enable Trivy job | _none_ (disabled) | | `PYTHON_TRIVY_ARGS` | Additional [Trivy CLI options](https://aquasecurity.github.io/trivy/v0.21.1/getting-started/cli/fs/) | `--vuln-type library` | This job outputs a **textual report** in the console, and in case of failure also exports a JSON report in the `reports/` directory _(relative to project root dir)_. -### Package jobs +### `py-release` job + +This job is **disabled by default** and allows to perform a complete release of your Python code: -#### `py-package` job +1. increase the Python project version, +2. Git commit changes and create a Git tag with the new version number, +3. build the [Python packages](https://packaging.python.org/), +4. publish the built packages to a PyPI compatible repository ([GitLab packages](https://docs.gitlab.com/ee/user/packages/pypi_repository/) by default). -This job is **disabled by default** and performs a packaging of your Python code. +The Python template supports two packaging systems: -It is bound to the `package-build` stage, applies only on git tags and uses the following variables: +* [Poetry](https://python-poetry.org/): uses Poetry-specific [version](https://python-poetry.org/docs/cli/#version), [build](https://python-poetry.org/docs/cli/#build) and [publish](https://python-poetry.org/docs/cli/#publish) commands. +* [Setuptools](https://setuptools.pypa.io/): uses [Bumpversion](https://github.com/peritus/bumpversion) as version management, [build](https://pypa-build.readthedocs.io/) as package builder and [Twine](https://twine.readthedocs.io/) to publish. -| Name | description | default value | -| --------------- | ---------------------------------------------------- | ------------- | -| `PYTHON_FORCE_PACKAGE` | Set to `true` to force the packaging even if not on tag related event | _none_ (disabled) | +The release job is bound to the `publish` stage, appears only on production and integration branches and uses the following variables: -### Publish jobs +| Name | description | default value | +| ----------------------- | ----------------------------------------------------------------------- | ----------------- | +| `PYTHON_RELEASE_ENABLED`| Set to `true` to enable the release job | _none_ (disabled) | +| `PYTHON_RELEASE_NEXT` | The part of the version to increase (one of: `major`, `minor`, `patch`) | `minor` | +| `PYTHON_SEMREL_RELEASE_DISABLED`| Set to `true` to disable [semantic-release integration](#semantic-release-integration) | _none_ (disabled) | +| `GIT_USERNAME` | Git username for Git push operations (see below) | _none_ | +| :lock: `GIT_PASSWORD` | Git password for Git push operations (see below) | _none_ | +| :lock: `GIT_PRIVATE_KEY`| SSH key for Git push operations (see below) | _none_ | +| `PYTHON_REPOSITORY_URL`| Target PyPI repository to publish packages | _[GitLab project's PyPI packages repository](https://docs.gitlab.com/ee/user/packages/pypi_repository/)_ | +| `PYTHON_REPOSITORY_USERNAME`| Target PyPI repository username credential | `gitlab-ci-token` | +| :lock: `PYTHON_REPOSITORY_PASSWORD`| Target PyPI repository password credential | `$CI_JOB_TOKEN` | -#### `py-release` job +#### Setuptools tip -This job is **disabled by default** and performs an automatic tagging of your Python code. +If you're using a `setup.cfg` declarative file for your project Setuptools configuration, then you will have to write a +`.bumpversion.cfg` file to workaround a bug that prevents Bumpversion from updating the project version in your `setup.cfg` file. -* [Bumpversion](https://github.com/peritus/bumpversion) Python library is used for version management. -* Looks for an existing `.bumpversion.cfg` at the project root. If found, it will be the configuration used by bumpversion. If not, the `$RELEASE_VERSION_PART` variable and `setup.py` will be used instead. -* Creating a Git tag involves an authenticated and authorized Git user. +Example of `.bumpversion.cfg` file: -**Don't use your personal password !!! -Use an [access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html) with write_repository rights. -If you have a generic account, add it to the project and generate access token from this account.** +```ini +[bumpversion] +# same version as in your setup.cfg +current_version = 0.5.0 -It is bound to the `publish` stage, applies only on master branch and uses the following variables: +[bumpversion:file:setup.cfg] +# any additional config here +# see: https://github.com/peritus/bumpversion#file-specific-configuration +``` -| Name | description | default value | -| ---------------------- | ----------------------------------------------------------------------- | ----------------- | -| `RELEASE_VERSION_PART` | The part of the version to increase (one of: `major`, `minor`, `patch`) | `minor` | -| `RELEASE_USERNAME` | Username credential for git push | _none_ (disabled) | -| `RELEASE_ACCESS_TOKEN` | Password credential for git push | _none_ | +#### `semantic-release` integration -#### `py-publish` job +If you activate the [`semantic-release-info` job from the `semantic-release` template](https://gitlab.com/to-be-continuous/semantic-release/#semantic-release-info-job), the `py-release` job will rely on the generated next version info. +Thus, a release will be performed only if a next semantic release is present. -This job is **disabled by default** and performs a publication of your Python code. +You should disable the `semantic-release` job (as it's the `py-release` job that will perform the release and so we only need the `semantic-release-info` job) by setting `SEMREL_RELEASE_DISABLED` to `true`. -It is bound to the `publish` stage, applies only on git tags and uses the following variables: +Finally, the semantic-release integration can be disabled with the `PYTHON_SEMREL_RELEASE_DISABLED` variable. -| Name | description | default value | -| ---------------------- | -------------------------------------------------------- | ----------------- | -| `PYTHON_PUBLISH_ENABLED`| Set to `true` to enable the publish job | _none_ (disabled) | -| `TWINE_REPOSITORY_URL` | Where to publish your Python project | GitLab Project's Pypi Packages registry | -| `TWINE_USERNAME` | Username credential to publish to \$TWINE_REPOSITORY_URL | `gitlab-ci-token` | -| `TWINE_PASSWORD` | Password credential to publish to \$TWINE_REPOSITORY_URL | `$CI_JOB_TOKEN` | +#### -More info: +#### Git authentication + +A Python release involves some Git push operations. + +You can either use a SSH key or user/password credentials. + +##### Using a SSH key + +We recommend you to use a [project deploy key](https://docs.gitlab.com/ee/user/project/deploy_keys/#project-deploy-keys) with write access to your project. + +The key should not have a passphrase (see [how to generate a new SSH key pair](https://docs.gitlab.com/ce/ssh/README.html#generating-a-new-ssh-key-pair)). + +Specify :lock: `$GIT_PRIVATE_KEY` as secret project variable with the private part of the deploy key. + +```PEM +-----BEGIN OPENSSH PRIVATE KEY----- +blablabla +-----END OPENSSH PRIVATE KEY----- +``` + +The template handles both classic variable and file variable. + +##### Using user/password credentials -* [Python Packaging User Guide](https://packaging.python.org/) -* [PyPI packages in the Package Registry](https://docs.gitlab.com/ee/user/packages/pypi_repository/) +Simply specify :lock: `$GIT_USERNAME` and :lock: `$GIT_PASSWORD` as secret project variables. -If you want to automatically create tag and publish your Python package, please have a look [here](#release-python) +Note that the password should be an access token (preferably a [project](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html) or [group](https://docs.gitlab.com/ee/user/group/settings/group_access_tokens.html) access token) with `read_repository` and `write_repository` scopes. diff --git a/kicker.json b/kicker.json index 9c5a0583efc2821978e64ad618c3d0e85588e985..da0fc1ee4a5257f65937e8a5868b6cadae33d5fe 100644 --- a/kicker.json +++ b/kicker.json @@ -150,51 +150,14 @@ } ] }, - { - "id": "package", - "name": "package", - "description": "Packaging of your Python code", - "variables": [ - { - "name": "PYTHON_FORCE_PACKAGE", - "description": "Force the packaging even if not on tag related event", - "type": "boolean" - } - ] - }, - { - "id": "publish", - "name": "Publish", - "description": "Publish your code to a [Twine](https://pypi.org/project/twine/) repository", - "enable_with": "PYTHON_PUBLISH_ENABLED", - "variables": [ - { - "name": "TWINE_REPOSITORY_URL", - "type": "url", - "description": "Twine repository url to publish you python project", - "default": "${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/packages/pypi" - }, - { - "name": "TWINE_USERNAME", - "description": "Twine repository username credential", - "secret": true, - "default": "gitlab-ci-token" - }, - { - "name": "TWINE_PASSWORD", - "description": "Twine repository password credential", - "secret": true, - "default": "$CI_JOB_TOKEN" - } - ] - }, { "id": "release", "name": "Release", "description": "Manually trigger a release of your code (uses [bumpversion](https://pypi.org/project/bumpversion/))", + "enable_with": "PYTHON_RELEASE_ENABLED", "variables": [ { - "name": "RELEASE_VERSION_PART", + "name": "PYTHON_RELEASE_NEXT", "type": "enum", "values": [ "", @@ -207,16 +170,43 @@ "advanced": true }, { - "name": "RELEASE_USERNAME", - "description": "Username credential for Git push", + "name": "PYTHON_SEMREL_RELEASE_DISABLED", + "description": "Disable semantic-release integration", + "type": "boolean", + "advanced": true + }, + { + "name": "GIT_USERNAME", + "description": "Git username for Git push operations", + "secret": true + }, + { + "name": "GIT_PASSWORD", + "description": "Git password for Git push operations", + "secret": true + }, + { + "name": "GIT_PRIVATE_KEY", + "description": "SSH key for Git push operations", + "secret": true + }, + { + "name": "PYTHON_REPOSITORY_URL", + "type": "url", + "description": "Target PyPI repository to publish packages.\n\n_defaults to [GitLab project's packages repository](https://docs.gitlab.com/ee/user/packages/pypi_repository/)_", + "default": "${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/packages/pypi" + }, + { + "name": "PYTHON_REPOSITORY_USERNAME", + "description": "Target PyPI repository username credential", "secret": true, - "mandatory": true + "default": "gitlab-ci-token" }, { - "name": "RELEASE_ACCESS_TOKEN", - "description": "Password credential for Git push", + "name": "PYTHON_REPOSITORY_PASSWORD", + "description": "Target PyPI repository password credential", "secret": true, - "mandatory": true + "default": "$CI_JOB_TOKEN" } ] } diff --git a/templates/gitlab-ci-python.yml b/templates/gitlab-ci-python.yml index 75c9a905f10716ab19b4eaef7a6ea0cb1bd22427..a8188ebe1b0e05b09b9bd40f88994b95cb9d865d 100644 --- a/templates/gitlab-ci-python.yml +++ b/templates/gitlab-ci-python.yml @@ -46,13 +46,13 @@ variables: PYTHON_TRIVY_IMAGE: aquasec/trivy:latest PYTHON_TRIVY_ARGS: "--vuln-type library" - RELEASE_VERSION_PART: "minor" + PYTHON_RELEASE_NEXT: "minor" # By default, publish on the Packages registry of the project # https://docs.gitlab.com/ee/user/packages/pypi_repository/#authenticate-with-a-ci-job-token - TWINE_REPOSITORY_URL: ${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/packages/pypi - TWINE_USERNAME: 'gitlab-ci-token' - TWINE_PASSWORD: $CI_JOB_TOKEN + PYTHON_REPOSITORY_URL: ${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/packages/pypi + PYTHON_REPOSITORY_USERNAME: 'gitlab-ci-token' + PYTHON_REPOSITORY_PASSWORD: $CI_JOB_TOKEN .python-scripts: &python-scripts | @@ -360,65 +360,165 @@ variables: if ! command -v poetry > /dev/null; then pip install ${PIP_OPTS} poetry; fi poetry build ;; - setuptools) + *) # shellcheck disable=SC2086 - pip install ${PIP_OPTS} setuptools build + pip install ${PIP_OPTS} build python -m build ;; - *) - log_error "--- packaging is unsupported with $PYTHON_BUILD_SYSTEM build system: read template doc" - exit 1 - ;; esac } - function _publish() { - case "$PYTHON_BUILD_SYSTEM" in - poetry) - # shellcheck disable=SC2086 - if ! command -v poetry > /dev/null; then pip install ${PIP_OPTS} poetry; fi - poetry config repositories.user_defined "$TWINE_REPOSITORY_URL" - poetry publish --username "$TWINE_USERNAME" --password "$TWINE_PASSWORD" --repository user_defined - ;; - setuptools) - # shellcheck disable=SC2086 - pip install ${PIP_OPTS} twine - twine upload --verbose dist/*.tar.gz - twine upload --verbose dist/*.whl - ;; - *) - log_error "--- publish is unsupported with $PYTHON_BUILD_SYSTEM build system: read template doc" + function configure_scm_auth() { + git_base_url=$(echo "$CI_REPOSITORY_URL" | cut -d\@ -f2) + if [[ -n "${GIT_USERNAME}" ]] && [[ -n "${GIT_PASSWORD}" ]]; then + log_info "--- using https protocol with SCM credentials from env (\$GIT_USERNAME and \$GIT_PASSWORD)..." + export git_auth_url="https://${GIT_USERNAME}:${GIT_PASSWORD}@${git_base_url}" + elif [[ -n "${GIT_PRIVATE_KEY}" ]]; then + log_info "--- using ssh protocol with SSH key from env (\$GIT_PRIVATE_KEY)..." + mkdir -m 700 "${HOME}/.ssh" + ssh-keyscan -H "${CI_SERVER_HOST}" >> ~/.ssh/known_hosts + eval "$(ssh-agent -s)" + # Handle file variable + if [[ -f "${GIT_PRIVATE_KEY}" ]]; then + tr -d '\r' < "${GIT_PRIVATE_KEY}" | ssh-add - + else + echo "${GIT_PRIVATE_KEY}" | tr -d '\r' | ssh-add - + fi + export git_auth_url="git@${git_base_url/\//:}" + else + log_error "--- Please specify either \$GIT_USERNAME and \$GIT_PASSWORD or \$GIT_PRIVATE_KEY variables to enable release (see doc)." exit 1 - ;; - esac + fi } function _release() { - if [[ "${PYTHON_BUILD_SYSTEM}" == "poetry" ]] + # 0: guess packaging system + if [[ -f "pyproject.toml" ]] + then + # that might be PEP 517 if a build-backend is specified + # otherwise it might be only used as configuration file for development tools... + build_backend=$(sed -rn 's/^build-backend *= *"([^"]*)".*/\1/p' pyproject.toml) + if [[ "$build_backend" ]] + then + case "$build_backend" in + poetry.core.masonry.api) + log_info "--- Packaging system auto-detected: Poetry" + pkg_system="poetry" + ;; + setuptools.build_meta) + log_info "--- Packaging system auto-detected: Setuptools (PEP 517)" + pkg_system="setuptools" + ;; + *) + log_error "--- Unsupported PEP 517 backend \\e[33;1m${build_backend}\\e[0m: abort" + exit 1 + ;; + esac + fi + fi + + if [[ -z "$pkg_system" ]] + then + if [[ -f "setup.py" ]] + then + log_info "--- Packaging system auto-detected: Setuptools (legacy)" + pkg_system="setuptools" + else + log_error "--- Couldn't find any supported packaging system: abort" + exit 1 + fi + fi + + # 1: retrieve next release info from semantic-release + if [ "$SEMREL_INFO_ON" ] && [ "$PYTHON_SEMREL_RELEASE_DISABLED" != "true" ] + then + if [ -z "$SEMREL_INFO_NEXT_VERSION" ] + then + log_info "[semantic-release] no new version to release: skip" + exit 0 + else + py_cur_version="$SEMREL_INFO_LAST_VERSION" + py_next_version="$SEMREL_INFO_NEXT_VERSION" + py_release_part="$SEMREL_INFO_NEXT_VERSION_TYPE" + log_info "[semantic-release] new ($py_release_part) release required \\e[1;94m${py_cur_version}\\e[0m → \\e[1;94m${py_next_version}\\e[0m" + fi + fi + + # 2: bumpversion (+ Git commit & tag) + if [[ "$pkg_system" == "poetry" ]] then # shellcheck disable=SC2086 if ! command -v poetry > /dev/null; then pip install ${PIP_OPTS} poetry; fi - poetry version "${RELEASE_VERSION_PART}" + if [[ -z "$py_next_version" ]] + then + py_cur_version=$(poetry version --short) + py_next_version="$PYTHON_RELEASE_NEXT" + fi + log_info "[Poetry] change version \\e[1;94m${py_cur_version}\\e[0m → \\e[1;94m${py_next_version}\\e[0m" + poetry version ${TRACE+--verbose} "$py_next_version" + # eval exact next version + py_next_version=$(poetry version --short) + git add pyproject.toml + git commit -m "chore(python-release): $py_cur_version → $py_next_version [ci skip]" + git tag "$py_next_version" else + # Setuptools / bumpversion # shellcheck disable=SC2086 pip install ${PIP_OPTS} bumpversion - - if [[ -f ".bumpversion.cfg" ]]; then - log_info "--- .bumpversion.cfg file found " - export bumpversion_args="${RELEASE_VERSION_PART} --verbose" + py_commit_message="chore(python-release): {current_version} → {new_version} [ci skip]" + if [[ "$py_next_version" ]] + then + # explicit release version (semantic-release) + log_info "[Setuptools] bumpversion \\e[1;94m${py_cur_version}\\e[0m → \\e[1;94m${py_next_version}\\e[0m" + # create cfg in case it doesn't exist - will be updated by bumpversion + touch .bumpversion.cfg + bumpversion ${TRACE+--verbose} --current-version "$py_cur_version" --commit --message "$py_commit_message" --tag --tag-name "{new_version}" "$py_release_part" + elif [[ -f "setup.py" ]] + then + # retrieve current version from setup.py + py_cur_version=$(python setup.py --version) + py_release_part="$PYTHON_RELEASE_NEXT" + log_info "[Setuptools] bumpversion ($py_release_part) from \\e[1;94m${py_cur_version}\\e[0m" + bumpversion ${TRACE+--verbose} --current-version "$py_cur_version" --commit --message "$py_commit_message" --tag --tag-name "{new_version}" "$py_release_part" + elif [[ -f ".bumpversion.cfg" ]] + then + # current version shall be set in .bumpversion.cfg + py_release_part="$PYTHON_RELEASE_NEXT" + log_info "[bumpversion] increase \\e[1;94m${py_release_part}\\e[0m" + bumpversion ${TRACE+--verbose} --commit --message "$py_commit_message" --tag --tag-name "{new_version}" "$py_release_part" else - log_info "--- No .bumpversion.cfg file found " - if [[ -f "setup.py" ]]; then - log_info "--- Getting current version of setup.py file " - current_version=$(python setup.py --version) - export bumpversion_args=" --verbose --current-version ${current_version} --tag --tag-name {new_version} --commit ${RELEASE_VERSION_PART} setup.py" - else - log_warn "--- No setup.py file found. Cannot perform release." - fi + log_error "--- setup.py or .bumpversion.cfg file required to retrieve current version: cannot perform release" + exit 1 fi - log_info "--- Release args: ${bumpversion_args}" + fi + + # 3: Git commit, tag and push + log_info "--- git push commit and tag..." + git push "$git_auth_url" "$CI_BUILD_REF_NAME" + git push "$git_auth_url" --tags + + # 4: build new version distribution + log_info "--- build distribution packages..." + if [[ "$pkg_system" == "poetry" ]] + then + poetry build ${TRACE+--verbose} + else + # shellcheck disable=SC2086 + pip install ${PIP_OPTS} build + rm -rf dist + python -m build + fi - bumpversion "${bumpversion_args}" + # 5: publish packages + log_info "--- publish distribution packages..." + if [[ "$pkg_system" == "poetry" ]] + then + poetry config repositories.user_defined "$PYTHON_REPOSITORY_URL" + poetry publish ${TRACE+--verbose} --username "$PYTHON_REPOSITORY_USERNAME" --password "$PYTHON_REPOSITORY_PASSWORD" --repository user_defined + else + # shellcheck disable=SC2086 + pip install ${PIP_OPTS} twine + twine upload ${TRACE+--verbose} --username "$PYTHON_REPOSITORY_USERNAME" --password "$PYTHON_REPOSITORY_PASSWORD" --repository-url "$PYTHON_REPOSITORY_URL" dist/* fi } @@ -473,12 +573,26 @@ variables: stages: - build - test - - package-build - publish ############################################################################################### # build stage # ############################################################################################### +# build Python packages as artifacts +py-package: + extends: .python-base + stage: build + script: + - _package + artifacts: + paths: + - $PYTHON_PROJECT_DIR/dist/* + rules: + # exclude merge requests + - if: $CI_MERGE_REQUEST_ID + when: never + - if: '$PYTHON_PACKAGE_ENABLED == "true"' + py-lint: extends: .python-base stage: build @@ -713,7 +827,6 @@ py-trivy: fi trivy fs ${PYTHON_TRIVY_ARGS} --format table --exit-code 0 $PYTHON_PROJECT_DIR trivy fs ${PYTHON_TRIVY_ARGS} --format json --output reports/trivy-python.json --exit-code 1 $PYTHON_PROJECT_DIR - artifacts: name: "$CI_JOB_NAME artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG" expire_in: 1 day @@ -735,62 +848,30 @@ py-trivy: when: manual allow_failure: true -############################################################################################### -# package stage # -############################################################################################### - -# (on tag creation): create packages as artifacts -py-package: - extends: .python-base - stage: package-build - script: - - _package - artifacts: - paths: - - $PYTHON_PROJECT_DIR/dist/*.tar.gz - - $PYTHON_PROJECT_DIR/dist/*.whl - rules: - # on tags - - if: '$CI_COMMIT_TAG' - - if: '$PYTHON_FORCE_PACKAGE == "true"' - -############################################################################################### -# publish stage # -############################################################################################### - -# (on tag creation): performs a release -py-publish: - extends: .python-base - stage: publish - script: - - assert_defined "$TWINE_USERNAME" 'Missing required env $TWINE_USERNAME' - - assert_defined "$TWINE_PASSWORD" 'Missing required env $TWINE_PASSWORD' - - _publish - rules: - # on tags with $PYTHON_PUBLISH_ENABLED set - - if: '$PYTHON_PUBLISH_ENABLED == "true" && $CI_COMMIT_TAG' - # (manual from master branch): triggers a release (tag creation) py-release: extends: .python-base stage: publish script: - - git config --global user.email '$GITLAB_USER_EMAIL' - - git config --global user.name '$GITLAB_USER_LOGIN' + - git config --global user.email "$GITLAB_USER_EMAIL" + - git config --global user.name "$GITLAB_USER_LOGIN" - git checkout -B $CI_BUILD_REF_NAME + - configure_scm_auth - _release - - git_url_base=`echo ${CI_REPOSITORY_URL} | cut -d\@ -f2` - - git push https://${RELEASE_USERNAME}:${RELEASE_ACCESS_TOKEN}@${git_url_base} --tags - - git push https://${RELEASE_USERNAME}:${RELEASE_ACCESS_TOKEN}@${git_url_base} $CI_BUILD_REF_NAME + artifacts: + paths: + - $PYTHON_PROJECT_DIR/dist/* rules: # exclude merge requests - if: $CI_MERGE_REQUEST_ID when: never - # on production branch(es): manual & non-blocking if $RELEASE_USERNAME is set - - if: '$RELEASE_USERNAME && $CI_COMMIT_REF_NAME =~ $PROD_REF' - when: manual - allow_failure: true - # on integration branch(es): manual & non-blocking if $RELEASE_USERNAME is set - - if: '$RELEASE_USERNAME && $CI_COMMIT_REF_NAME =~ $INTEG_REF' + # exclude if $PYTHON_RELEASE_ENABLED not set + - if: '$PYTHON_RELEASE_ENABLED != "true"' + when: never + # exclude on non-prod, non-integ branches + - if: '$CI_COMMIT_REF_NAME !~ $PROD_REF && $CI_COMMIT_REF_NAME !~ $INTEG_REF' + when: never + # else: manual + - if: '$PYTHON_RELEASE_ENABLED == "true"' # useless but prevents GitLab warning when: manual allow_failure: true