revert "chore(rc): mark source code Sem Ver as '-rc', Release Candidate" #1245
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: CI/CD Pipeline | |
# Continuous Integration / Continuous Delivery | |
# Triggers on all branches and tags starting with v | |
### Full Job Matrix for Stress Testing on: ### | |
# - tags v* | |
# - the 'stress-test' branch (GITHUB_REF_NAME == 'stress-test') | |
### PyPI publish on: ### | |
# - v* tags on 'master' branch only | |
## Test PyPI publish on: ## | |
# - v*-rc 'pre-release' tags on 'release' branch | |
### Dockerhub publish on ### | |
# - all branches and tags | |
on: | |
push: | |
branches: | |
- "*" | |
tags: | |
- v* | |
env: | |
### STRESS TEST Job MATRIX ### | |
FULL_MATRIX_STRATEGY: "{\"platform\": [\"ubuntu-latest\", \"macos-latest\", \"windows-latest\"], \"python-version\": [\"3.7\", \"3.8\", \"3.9\", \"3.10\", \"3.11\"]}" | |
# Python 3.7 has reached End of Life (EOL) on June 27th, 2023 | |
# Python 3.12 is in bugfix mode, same as 3.11 -> can start supporting 3.12 it | |
UBUNTU_PY310_STRATEGY: "{\"platform\":[\"ubuntu-latest\"], \"python-version\":[\"3.10\"]}" | |
TEST_STRATEGY: "{\"platform\":[\"ubuntu-latest\", \"macos-latest\", \"windows-latest\"], \"python-version\":[\"3.9\"]}" | |
##### JOB ON/OFF SWITCHES ##### | |
RUN_UNIT_TESTS: "true" | |
RUN_LINT_CHECKS: "true" | |
DOCKER_JOB_ON: "true" | |
PUBLISH_ON_PYPI: "true" | |
DRAW_DEPENDENCIES: "true" | |
############################### | |
### DOCKER Job Policy #### | |
# Override Docker Policy-dependent decision-making and | |
# Accept any ALL (branch/build) to Publish to Dockerhub | |
# if true, it will push image and ignore DOCKER_JOB_POLICY | |
ALWAYS_BUILD_N_PUBLSIH_DOCKER: "false" | |
DOCKER_JOB_POLICY: "CDeployment" | |
# - CDeployment : Builds and Publishes only if Tests ran and passed | |
# - CDelivery : Builds and Publishes if Tests Passed or if Tests were Skipped | |
############################ | |
#### STATIC CODE ANALYSIS Job #### | |
ALWAYS_LINT: "false" | |
LINT_JOB_POLICY: '2' # {2, 3} | |
## Python Runtime version to set the Job runner with ## | |
STATIC_ANALYSIS_PY: "3.10" | |
## Pylint Minimum Acceptance Rating/Score ## | |
PYLINT_SCORE_THRESHOLD: "8.2" | |
#### CODE VISUALIZATION Job #### | |
ALWAYS_CODE_VIZ: "false" | |
CODE_VIZ_POLICY: '2' # {2, 3} | |
########################## | |
jobs: | |
# we use the below to read the workflow env vars and be able to use in "- if:" Job conditionals | |
# now we can do -> if: ${{ needs.set_github_outputs.outputs.TESTS_ENABLED == 'true' }} | |
# github does not have a way to simply do "- if: ${{ env.RUN_UNIT_TESTS == 'true' }} " !! | |
set_github_outputs: | |
name: Read Workflow Env Section Vars and set Github Outputs | |
runs-on: ubuntu-latest | |
steps: | |
- name: Pass 'env' section variables to GITHUB_OUTPUT | |
id: pass-env-to-output | |
env: | |
BOARDING_EVENT: 'do Boarding CI Tests' | |
run: | | |
# set the matrix strategy to Full Matrix Stress Test if on master/main or stress-test branch or any tag | |
BRANCH_NAME=${GITHUB_REF_NAME} | |
if [[ $BRANCH_NAME == "stress-test" || $GITHUB_REF == refs/tags/* ]]; then | |
echo "matrix=$FULL_MATRIX_STRATEGY" >> $GITHUB_OUTPUT | |
# github.event.head_commit.message has 'do Boarding CI Tests' string | |
elif [[ "${{ contains(github.event.head_commit.message, env.BOARDING_EVENT) }}" == 'true' ]]; then | |
echo "matrix=$TEST_STRATEGY" >> $GITHUB_OUTPUT | |
else | |
echo "matrix=$UBUNTU_PY310_STRATEGY" >> $GITHUB_OUTPUT | |
fi | |
echo "TESTS_ENABLED=$RUN_UNIT_TESTS" >> $GITHUB_OUTPUT | |
echo "PUBLISH_ON_PYPI=$PUBLISH_ON_PYPI" >> $GITHUB_OUTPUT | |
## Docker - Pipeline Settings ## | |
- id: derive_docker_policy | |
run: echo "POL=${{ (env.DOCKER_JOB_ON != 'true' && '0') || (env.ALWAYS_BUILD_N_PUBLSIH_DOCKER == 'true' && '1') || (env.DOCKER_JOB_POLICY == 'CDeployment' && '2') || (env.DOCKER_JOB_POLICY == 'CDelivery' && '3') }}" >> $GITHUB_OUTPUT | |
## Static Code Analysis - Pipeline Settings ## | |
- id: derive_sqa_policy | |
run: echo "POL=${{ (env.RUN_LINT_CHECKS != 'true' && '0') || (env.ALWAYS_LINT == 'true' && '1') || env.LINT_JOB_POLICY }}" >> $GITHUB_OUTPUT | |
- id: read_sqa_py | |
run: echo SQA_PY=${{ env.STATIC_ANALYSIS_PY }} >> $GITHUB_OUTPUT | |
- id: read_pylint_score_threshold | |
run: echo PYLINT_SCORE_THRESHOLD=${{ env.PYLINT_SCORE_THRESHOLD }} >> $GITHUB_OUTPUT | |
## Code Visualization - Pipeline Settings ## | |
- id: derive_code_viz_policy | |
run: echo "POL=${{ (env.DRAW_DEPENDENCIES != 'true' && '0') || (env.ALWAYS_CODE_VIZ == 'true' && '1') || env.CODE_VIZ_POLICY }}" >> $GITHUB_OUTPUT | |
outputs: | |
matrix: ${{ steps.pass-env-to-output.outputs.matrix }} | |
TESTS_ENABLED: ${{ steps.pass-env-to-output.outputs.TESTS_ENABLED }} | |
PUBLISH_ON_PYPI: ${{ steps.pass-env-to-output.outputs.PUBLISH_ON_PYPI }} | |
## Docker - Pipeline Settings ## | |
PIPE_DOCKER_POLICY: ${{ steps.derive_docker_policy.outputs.POL }} | |
## Static Code Analysis - Pipeline Settings ## | |
PIPE_SQA_POLICY: ${{ steps.derive_sqa_policy.outputs.POL }} | |
PIPE_SQA_PY: ${{ steps.read_sqa_py.outputs.SQA_PY }} | |
PIPE_SQA_PYLINT_PASS_SCORE: ${{ steps.read_pylint_score_threshold.outputs.PYLINT_SCORE_THRESHOLD }} | |
## Code Visualization - Pipeline Settings ## | |
PIPE_CODE_VIZ_POLICY: ${{ steps.derive_code_viz_policy.outputs.POL }} | |
# RUN TEST SUITE ON ALL PLATFORMS | |
test_suite: | |
runs-on: ${{ matrix.platform }} | |
needs: set_github_outputs | |
if: ${{ needs.set_github_outputs.outputs.TESTS_ENABLED == 'true' }} | |
strategy: | |
matrix: ${{fromJSON(needs.set_github_outputs.outputs.matrix)}} | |
outputs: | |
SEMVER_PIP_FORMAT: ${{ steps.parse_version.outputs.SEMVER_PIP_FORMAT }} | |
steps: | |
- run: echo "Platform -> ${{ matrix.platform }} , Python -> ${{ matrix.python-version }}" | |
- uses: actions/checkout@v3 | |
- name: Set up Python ${{ matrix.python-version }} | |
uses: actions/setup-python@v4 | |
with: | |
python-version: ${{ matrix.python-version }} | |
- run: python -m pip install --upgrade pip && python -m pip install tox==3.28 tox-gh-actions | |
- name: Do Type Checking | |
run: tox -e type -vv -s false | |
- name: Parse package version from __init__.py to assist building | |
shell: bash | |
id: parse_version | |
run: | | |
PARSER="src/cookiecutter_python/{{ cookiecutter.project_slug }}/scripts/parse_version.py" | |
PARSED_VERSION=$(python "${PARSER}") | |
# transform version (ie 1.7.4-rc.1) to match the name of the wheel produced (ie 1.7.4rc1) | |
WHEEL_VERSION=$(echo $PARSED_VERSION | sed -E 's/([^.]*)\.([^.]*)\.([^-]*)-(rc)\.?(.*)/\1.\2.\3\4\5/') | |
# manually append the 0 to index the release candidate | |
# we account for wheel building that automatically does the above | |
last_two=${WHEEL_VERSION: -2} | |
if [[ $last_two == "rc" ]]; then | |
WHEEL_VERSION="${WHEEL_VERSION}0" | |
fi | |
echo "==== $PARSED_VERSION --> $WHEEL_VERSION" | |
echo "PKG_VERSION=$WHEEL_VERSION" >> $GITHUB_ENV # to be used in the next step | |
echo "SEMVER_PIP_FORMAT=$WHEEL_VERSION" >> $GITHUB_OUTPUT # to be used in other jobs | |
###### TEST SUITE RUN ###### | |
- name: Run Unit Tests | |
run: tox -vv -s false | |
env: | |
PLATFORM: ${{ matrix.platform }} | |
## Code Coverage ## | |
- name: "Combine Coverage (dev, sdist, wheel) & make Reports" | |
run: tox -e coverage --sitepackages -vv -s false | |
- name: Rename Coverage Files | |
shell: bash | |
run: | | |
mv ./.tox/coverage.xml ./coverage-${{ matrix.platform }}-${{ matrix.python-version }}.xml | |
- name: "Upload Test Coverage as Artifacts" | |
uses: actions/upload-artifact@v3 | |
with: | |
name: all_coverage_raw | |
path: coverage-${{ matrix.platform }}-${{ matrix.python-version }}.xml | |
if-no-files-found: error | |
- name: Check for compliance with Python Best Practices | |
shell: bash | |
run: | | |
DIST_DIR=dist | |
echo "DIST_DIR=dist" >> $GITHUB_ENV | |
mkdir ${DIST_DIR} | |
mv ".tox/${DIST_DIR}/cookiecutter_python-${PKG_VERSION}.tar.gz" "${DIST_DIR}" | |
mv ".tox/${DIST_DIR}/cookiecutter_python-${PKG_VERSION}-py3-none-any.whl" "${DIST_DIR}" | |
tox -e check -vv -s false | |
- name: Upload Source & Wheel distributions as Artefacts | |
uses: actions/upload-artifact@v3 | |
with: | |
name: dist-${{ matrix.platform }}-${{ matrix.python-version }} | |
path: ${{ env.DIST_DIR }} | |
if-no-files-found: error | |
codecov_coverage_host: | |
runs-on: ubuntu-latest | |
needs: test_suite | |
steps: | |
- uses: actions/checkout@v3 | |
- name: Get Codecov binary | |
run: | | |
curl -Os https://uploader.codecov.io/latest/linux/codecov | |
chmod +x codecov | |
- name: Download Raw Coverage Data Artefacts | |
uses: actions/download-artifact@v3 | |
with: | |
name: all_coverage_raw | |
- name: Upload Coverage Reports to Codecov | |
run: | | |
for file in coverage*.xml; do | |
OS_NAME=$(echo $file | sed -E "s/coverage-(\w\+)-/\1/") | |
PY_VERSION=$(echo $file | sed -E "s/coverage-\w\+-(\d\.)\+/\1/") | |
./codecov -f $file -e "OS=$OS_NAME,PYTHON=$PY_VERSION" --flags unittests --verbose | |
echo "Sent to Codecov: $file !" | |
done | |
## DOCKER BUILD and PUBLISH ON DOCKERHUB ## | |
docker_build: | |
needs: [set_github_outputs, test_suite] | |
uses: boromir674/automated-workflows/.github/workflows/[email protected] | |
if: always() | |
with: | |
acceptance_policy: ${{ needs.set_github_outputs.outputs.PIPE_DOCKER_POLICY }} | |
image_slug: "generate-python" | |
# target_stage: "some_stage_alias" # no stage, means no `--target` flag, on build | |
tests_pass: ${{ needs.test_suite.result == 'success' }} | |
tests_run: ${{ !contains(fromJSON('["skipped", "cancelled"]'), needs.test_suite.result) }} | |
DOCKER_USER: ${{ vars.DOCKER_USER }} | |
secrets: | |
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} | |
## JOB: Signal for Automated PyPI Upload ## | |
check_which_git_branch_we_are_on: | |
runs-on: ubuntu-latest | |
needs: set_github_outputs | |
if: ${{ startsWith(github.event.ref, 'refs/tags/v') && needs.set_github_outputs.outputs.PUBLISH_ON_PYPI == 'true' }} | |
# Signals only if all below are True: | |
# - PyPI Job Switch is ON | |
# - Workflow Event is a 'v*' Tag (e.g. v1.0.0, v-test) | |
# - Tag is tagging a commit on either the 'master' or 'release' Branch | |
env: | |
RELEASE_BR: 'release' | |
MAIN_BR: 'master' | |
steps: | |
# Fetch 'master' and 'release' branches | |
- uses: actions/checkout@v3 | |
with: | |
fetch-depth: 0 | |
- run: git branch --track "${{ env.RELEASE_BR }}" "origin/${{ env.RELEASE_BR }}" | |
- name: "Check if '${{ github.ref }}' tag is on '${{ env.MAIN_BR }}' branch" | |
uses: rickstaa/action-contains-tag@v1 | |
id: main_contains_tag | |
with: | |
reference: ${{ env.MAIN_BR }} | |
tag: "${{ github.ref }}" | |
- name: "Check if '${{ github.ref }}' tag is on '${{ env.RELEASE_BR }}' branch" | |
uses: rickstaa/action-contains-tag@v1 | |
id: release_contains_tag | |
with: | |
reference: ${{ env.RELEASE_BR }} | |
tag: "${{ github.ref }}" | |
- name: Pick Production or Test Environment, if tag on master or release branch respectively | |
id: set_environment_name | |
run: | | |
DEPLOY=true | |
if [[ "${{ steps.main_contains_tag.outputs.retval }}" == "true" ]]; then | |
echo "ENVIRONMENT_NAME=PROD_DEPLOYMENT" >> $GITHUB_OUTPUT | |
elif [[ "${{ steps.release_contains_tag.outputs.retval }}" == "true" ]]; then | |
echo "ENVIRONMENT_NAME=TEST_DEPLOYMENT" >> $GITHUB_OUTPUT | |
else | |
echo "A tag was pushed but not on master or release branch. No deployment will be done." | |
DEPLOY=false | |
fi | |
echo "AUTOMATED_DEPLOY=$DEPLOY" >> $GITHUB_OUTPUT | |
outputs: | |
ENVIRONMENT_NAME: ${{ steps.set_environment_name.outputs.ENVIRONMENT_NAME }} | |
AUTOMATED_DEPLOY: ${{ steps.set_environment_name.outputs.AUTOMATED_DEPLOY }} | |
TAG_ON_MASTER: ${{ steps.main_contains_tag.outputs.retval }} | |
## JOB: PYPI UPLOAD ## | |
pypi_publish: | |
needs: [test_suite, check_which_git_branch_we_are_on] | |
if: always() | |
uses: boromir674/automated-workflows/.github/workflows/pypi_env.yml@8e97f596067fcbbaa0a6927ec1ee47dce4ab5f1a | |
with: | |
should_trigger: ${{ needs.check_which_git_branch_we_are_on.outputs.AUTOMATED_DEPLOY == 'true' }} | |
distro_name: cookiecutter_python | |
distro_version: ${{ needs.test_suite.outputs.SEMVER_PIP_FORMAT }} | |
pypi_env: '${{ needs.check_which_git_branch_we_are_on.outputs.ENVIRONMENT_NAME }}' | |
artifacts_path: downloaded-artifacts | |
require_wheel: true | |
allow_existing: true | |
secrets: | |
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} | |
### STATIC CODE ANALYSIS & LINTING ### | |
lint: | |
name: Static Code Analysis | |
needs: set_github_outputs | |
uses: ./.github/workflows/policy_lint.yml | |
with: | |
run_policy: ${{ needs.set_github_outputs.outputs.PIPE_SQA_POLICY }} | |
dedicated_branches: 'master, main, dev' | |
source_code_targets: 'src,tests,scripts' | |
python_version: ${{ needs.set_github_outputs.outputs.PIPE_SQA_PY }} | |
pylint_threshold: ${{ needs.set_github_outputs.outputs.PIPE_SQA_PYLINT_PASS_SCORE }} | |
### DRAW PYTHON DEPENDENCY GRAPHS ### | |
code_visualization: | |
needs: set_github_outputs | |
name: Code Visualization of Python Imports as Graphs, in .svg | |
uses: boromir674/automated-workflows/.github/workflows/python_imports.yml@test | |
with: | |
run_policy: '${{ needs.set_github_outputs.outputs.PIPE_CODE_VIZ_POLICY }}' | |
branches: 'main, master, dev' | |
source_code_targets: 'src' | |
python_version: '3.10' | |
artifacts_dir: 'dependency-graphs' |