Skip to content

Commit

Permalink
ephemeral: hard-shutdown Docker CI Job
Browse files Browse the repository at this point in the history
  • Loading branch information
boromir674 committed Oct 30, 2024
1 parent 5475287 commit 2be7560
Show file tree
Hide file tree
Showing 2 changed files with 303 additions and 1 deletion.
302 changes: 302 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
#################################
## PYTHON STATIC CODE ANALYSIS ##
## Reusable Workflow ##
#################################

# Static Code Analysis (SCA) is a set of techniques for examining source code
# without executing it.

### TOOLS ###
# - Ruff
# - Isort
# - Black
# - Pyflakes, Pyroma, McCabe, DodgyRun, Profile Validator
# - Pylint (legacy)

## AUTONOMOUS JOB ##

# 0. Never run
# 1. Always run
# 2. Run SQA on conditions
# - Triggered on a Long-living branch (ie main)
# - Triggered on a v* tag (ie v1.0.0)
# - Source Code changed, compared to previous commit
# 3. Run SQA, if Source Code changed, compared to previous commit

on:
workflow_call:
inputs:
# Defaults to Policy 2 (CI/CD)
run_policy:
required: false
type: string
default: '2'
dedicated_branches:
required: false
type: string
default: 'master, main, dev'
source_code_targets:
required: false
type: string
default: 'src'
## Parametrizing Runtime Environment (ie py version)
python_version:
required: false
type: string
default: '3.10'
## Parametrizing Code Analysis Acceptance Criteria
pylint_threshold:
required: false
type: string
default: '8.0' # out of 10
## Parametrize Bandit Acceptance Criteria
bandit:
required: false
type: string
description: 'Bandit Acceptance Criteria'
# ie { "l": 10 } -> allowes at most 10 low, 0 m, and 0 h
# ie { "l": 10, "m": 3 } -> allowes at most 10 low, 3 m, and 0 h
# Default is 6 allowed low, 0 m, and 0 h
default: '{"l": 8, "m": 0, "h": 0}'

jobs:
# Decide whether to run Lint Job, given incoming Signal
lint_policy:
name: "Run Lint Job?"
runs-on: ubuntu-latest
if: always() && inputs.run_policy != 0
steps:
- if: ${{ !contains('1, 2, 3', inputs.run_policy) }}
run: 'echo "Invalid run_policy: ${{ inputs.run_policy }}. Must be >0 and <4" && exit 1'

- if: inputs.run_policy == 1
name: 'POLICY: 1 -> Trigger'
run: echo "SHOULD_RUN_SQA=true" >> $GITHUB_ENV

- if: inputs.run_policy == 2 && contains(inputs.dedicated_branches, github.ref_name)
name: 'POLICY: 2 & Branch: ${{ github.ref_name }} -> Trigger'
run: echo "SHOULD_RUN_SQA=true" >> $GITHUB_ENV

- if: inputs.run_policy == 2 && startsWith(github.ref, 'refs/tags/v')
name: 'POLICY: 2 & Tag: ${{ github.ref_name }} -> Trigger'
run: echo "SHOULD_RUN_SQA=true" >> $GITHUB_ENV

- if: ${{ env.SHOULD_RUN_SQA != 'true' && contains('2, 3', inputs.run_policy) }}
name: 'POLICY: 2, 3 -> Derive from DIFF'
run: echo "SHOULD_DERIVE_FROM_DIFF=true" >> $GITHUB_ENV

- if: ${{ env.SHOULD_DERIVE_FROM_DIFF }}
name: 'POLICY: 2, 3 -> Checkout Code to compute DIFF'
uses: actions/checkout@v4
with:
fetch-depth: 2

- if: ${{ env.SHOULD_DERIVE_FROM_DIFF }}
name: 'POLICY: 2, 3 -> Check Source Code DIFF'
run: |
echo "============ List Modified Files ============"
git diff --name-only HEAD^ HEAD
CHANGED_FILES=$(git diff --name-only HEAD^ HEAD)
# Read Folders we 'Watch' for changes
TARGETS=$(echo "${{ inputs.source_code_targets }}" | tr ',' '\n')
# Loop through the Watched Folders
for TARGET in $TARGETS; do
# if rel path of changed file matches glob pattern
if [[ $CHANGED_FILES == *"$TARGET"* ]]; then
echo "SHOULD_RUN_SQA=true" >> $GITHUB_ENV
echo "QA Target: $TARGET found in CHANGED_FILES!"
break
fi
done
### OUTPUT of JOB ###
- name: "Set 'Run Static Code Analysis' Signal to ${{ env.SHOULD_RUN_SQA }}"
id: set_sqa_signal
run: echo "RUN_SQA=${{ env.SHOULD_RUN_SQA }}" >> $GITHUB_OUTPUT
outputs:
RUN_SQA: ${{ steps.set_sqa_signal.outputs.RUN_SQA }}

lint:
name: "Static Code Analysis"
runs-on: ubuntu-latest
needs: lint_policy
if: always() && needs.lint_policy.outputs.RUN_SQA == 'true'
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ inputs.python_version }}
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python_version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox==3.28
## Isort ##
- name: 'Isort: Require Semantic and Alphabetic order of the Python Imports'
if: ${{ matrix.platform != 'windows-latest' }}
run: tox -e isort -vv -s false

## Black ##
- name: 'Black: Require Project Style "opinion" to be followed by the Python Code'
if: ${{ matrix.platform != 'windows-latest' }}
run: tox -e black -vv -s false

## Ruff ##
- name: 'Ruff: Require Project to pass Ruff Checks'
run: tox -e ruff -vv -s false

## Pyflakes, Pyroma, McCabe, DodgyRun, Profile Validator ##
- name: Run tox -e prospector
if: ${{ matrix.platform != 'windows-latest' }}
run: tox -e prospector -vv -s false

bandit:
name: "Bandit: Security Analysis"
runs-on: ubuntu-latest
needs: lint_policy
if: always() && needs.lint_policy.outputs.RUN_SQA == 'true'
env:
_SARIF_FILE: results-sarif.json
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ inputs.python_version }}
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python_version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox==3.28
## Run BANDIT to identify CWEs ##
- name: 'Bandit: Security Analysis'
run: tox -e bandit -vv -s false -- -q -f sarif -o "${{ env._SARIF_FILE }}" || 'true'

- name: Upload artifact
uses: actions/upload-artifact@main
with:
name: ${{ env._SARIF_FILE }}
path: ${{ env._SARIF_FILE }}
if-no-files-found: error

- uses: github/codeql-action/init@v3
with:
# config-file: ./.github/codeql/codeql-config.yml # or ${{ vars.VAR_CONTENT }} !
config: |
paths-ignore:
- 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/'
- 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/**'
- 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/**/*'
- 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/*'
- 'src/cookiecutter_python/{{ cookiecutter.project_slug }}/tests/test_cli.py'
# disable-default-queries: true
# queries:
# - uses: security-extended
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ${{ env._SARIF_FILE }}

- name: 'Parse Bandit Results, and prepare comparing LOW, MEDIUM, and HIGH'
run: |
# Parse Totals Object
TOTALS=$(jq '.runs[0].properties.metrics._totals' "${{ env._SARIF_FILE }}")
# Find CWE's of HIGH Severity
HIGH=$(echo $TOTALS | jq '.["SEVERITY.HIGH"]')
# Find CWE's of MEDIUM Severity
MEDIUM=$(echo $TOTALS | jq '.["SEVERITY.MEDIUM"]')
# Find CWE's of LOW Severity
LOW=$(echo $TOTALS | jq '.["SEVERITY.LOW"]')
# Report and Env
echo HIGH=$HIGH >> $GITHUB_ENV
echo MEDIUM=$MEDIUM >> $GITHUB_ENV
echo LOW=$LOW >> $GITHUB_ENV
- name: Write to Job Step Summary
run: |
echo "## Bandit - Results" >> $GITHUB_STEP_SUMMARY
echo "HIGH: $HIGH" >> $GITHUB_STEP_SUMMARY
echo "MEDIUM: $MEDIUM" >> $GITHUB_STEP_SUMMARY
echo "LOW: $LOW" >> $GITHUB_STEP_SUMMARY
- name: Destructure Input Acceptance Thresholds
run: |
MAX_HIGH=$(echo '${{ inputs.bandit }}' | jq -r '.h')
MAX_MEDIUM=$(echo '${{ inputs.bandit }}' | jq -r '.m')
MAX_LOW=$(echo '${{ inputs.bandit }}' | jq -r '.l')
echo "## User Acceptable - Thresholds" >> $GITHUB_STEP_SUMMARY
echo "HIGH: $MAX_HIGH" >> $GITHUB_STEP_SUMMARY
echo "MEDIUM: $MAX_MEDIUM" >> $GITHUB_STEP_SUMMARY
echo "LOW: $MAX_LOW" >> $GITHUB_STEP_SUMMARY
echo MAX_HIGH=$MAX_HIGH >> $GITHUB_ENV
echo MAX_MEDIUM=$MAX_MEDIUM >> $GITHUB_ENV
echo MAX_LOW=$MAX_LOW >> $GITHUB_ENV
#### JOB STATUS ####
- name: "If we found more than ${{ env.MAX_HIGH }} HIGH Severity CWE's, Fail!"
if: ${{ env.MAX_HIGH < env.HIGH }}
run: |
echo "Bandit Failed! | Found more HIGH Severity CWE's in code than allowed/acceptable amount."
echo "[ERROR] **HIGH**: $HIGH > $MAX_HIGH" >> $GITHUB_STEP_SUMMARY
exit 1
- name: "If we found more than ${{ env.MAX_MEDIUM }} MEDIUM Severity CWE's, Fail!"
if: ${{ env.MAX_MEDIUM < env.MEDIUM }}
run: |
echo "Bandit Failed! | Found more MEDIUM Severity CWE's in code than allowed/acceptable amount."
echo "[ERROR] **MEDIUM**: $MEDIUM > $MAX_MEDIUM" >> $GITHUB_STEP_SUMMARY
exit 1
- name: "If we found more than ${{ env.MAX_LOW }} LOW Severity CWE's, Fail!"
if: ${{ env.MAX_LOW < env.LOW }}
run: |
echo "Bandit Failed! | Found more LOW Severity CWE's in code than allowed/acceptable amount."
echo "[ERROR] **LOW**: $LOW > $MAX_LOW" >> $GITHUB_STEP_SUMMARY
exit 1
- run: echo "**Bandit Passed!**" >> $GITHUB_STEP_SUMMARY

legacy_pylint:
runs-on: ubuntu-latest
needs: lint_policy
if: always() && needs.lint_policy.outputs.RUN_SQA == 'true'
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ inputs.python_version }}
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python_version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox==3.28
## Pylint (legacy) ##
- name: Run Pylint tool on Python Code Base
run: tox -e pylint -vv -s false | tee pylint-result.txt

- run: cat pylint-result.txt

- name: 'Check Pylint Score > ${{ inputs.pylint_threshold }}/10'
if: ${{ matrix.platform != 'windows-latest' }}
run: |
SCORE=`sed -n 's/^Your code has been rated at \([-0-9.]*\)\/.*/\1/p' pylint-result.txt`
echo "SCORE -> $SCORE"
# threshold check
if awk "BEGIN {exit !($SCORE >= ${{ inputs.pylint_threshold }})}"; then
echo "PyLint Passed! | Score: ${SCORE} out of 10 | Threshold: ${{ inputs.pylint_threshold }}"
else
echo "PyLint Failed! | Score: ${SCORE} out of 10 | Threshold: ${{ inputs.pylint_threshold }}"
exit 1
fi
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ env:
PUBLISH_ON_PYPI: "true"
DRAW_DEPENDENCIES: "true"
PREVENT_CODECOV_TEST_COVERAGE: "false"
DOCKER_JOB_ON: "true"
DOCKER_JOB_ON: "false"
###############################

#### DOCKER Job Policy #####
Expand Down

0 comments on commit 2be7560

Please sign in to comment.