Skip to content

Python Build

Python Build #2386

Workflow file for this run

# The following workflow provides an opinionated template you can customize for your own needs.
#
# If you are not an Octopus user, the "Push to Octopus", "Generate Octopus Deploy build information",
# and "Create Octopus Release" steps can be safely deleted.
#
# To configure Octopus, set the OCTOPUS_API_TOKEN secret to the Octopus API key, and
# set the OCTOPUS_SERVER_URL secret to the Octopus URL.
#
# Double check the "project" and "deploy_to" properties in the "Create Octopus Release" step
# match your Octopus projects and environments.
#
# Get a trial Octopus instance from https://octopus.com/start
name: Python Build
'on':
workflow_dispatch: { }
push:
paths-ignore:
- '.octopus/**'
schedule:
- cron: '0 0 * * *'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Set up Python
uses: actions/setup-python@v5
with:
# Need 3.11 because of https://stackoverflow.com/questions/77364550/attributeerror-module-pkgutil-has-no-attribute-impimporter-did-you-mean
python-version: 3.11
- uses: actions/checkout@v4
- name: Install tools
run: |
pip install pytest coverage setuptools radon xenon
sudo apt-get install -y pycodestyle mypy
- name: Linting
shell: bash
run: find . -path ./domain/sanitizers/stringlifier -prune -o -path ./venv -prune -o -name "*.py" -print0 | xargs -0 -n1 pycodestyle --ignore=E501,W503,E126
- name: Complexity Enforcement
shell: bash
run: find . -path ./domain/sanitizers/stringlifier -prune -o -path ./tests -prune -o -path ./venv -prune -o -path ./.venv -prune -o -name "*.py" -print0 | xargs -0 -n1 xenon --max-absolute C
test:
name: Test
runs-on: ubuntu-latest
needs: validate
strategy:
matrix:
group: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ]
steps:
- name: Install terraform
uses: hashicorp/setup-terraform@v3
- name: Set up Python
uses: actions/setup-python@v5
with:
# Need 3.11 because of https://stackoverflow.com/questions/77364550/attributeerror-module-pkgutil-has-no-attribute-impimporter-did-you-mean
python-version: 3.11
- uses: actions/checkout@v4
- name: Start Azurite
run: docker run -d -p 10000:10000 -p 10001:10001 -p 10002:10002 mcr.microsoft.com/azure-storage/azurite
shell: bash
- uses: robinraju/[email protected]
with:
repository: "OctopusSolutionsEngineering/OctopusTerraformExport"
latest: true
fileName: "octoterra_linux_amd64_azure.zip"
- uses: robinraju/[email protected]
with:
repository: "OctopusSolutionsEngineering/OctopusRecommendationEngine"
latest: true
fileName: "octolint_linux_amd64_azure.zip"
- name: Start Octoterra
run: |
# Start the octoterra microservice
mkdir octoterra
mv octoterra_linux_amd64_azure.zip octoterra
cd octoterra
unzip octoterra_linux_amd64_azure.zip
chmod +x octoterra_linux_amd64_azure
./octoterra_linux_amd64_azure &
env:
# The port used when running the octoterra microservice
FUNCTIONS_CUSTOMHANDLER_PORT: 8081
shell: bash
- name: Start Octolint
run: |
# Start the octolint microservice
mkdir octolint
mv octolint_linux_amd64_azure.zip octolint
cd octolint
unzip octolint_linux_amd64_azure.zip
chmod +x octolint_linux_amd64_azure
./octolint_linux_amd64_azure &
env:
# The port used when running the octolint microservice
FUNCTIONS_CUSTOMHANDLER_PORT: 8082
shell: bash
- name: Install Dependencies
run: pip install -r requirements.txt
shell: bash
# To run tests locally, use these instructions:
# 1. source <venv>/bin/activate
# 2. pip install coverage pytest
# 3. pip install -r requirements.txt
# 4. coverage run --omit="./domain/sanitizers/stringlifier/*" -m pytest --junitxml=results.xml
- name: Test
run: |
pip install pytest pytest-split coverage setuptools
PYTHONPATH=$(pwd) coverage run --omit="./tests/*" --omit="./domain/sanitizers/stringlifier/*" -m pytest --junitxml=results.xml --splits 15 --group ${{ matrix.group }}
# See https://github.com/pytest-dev/pytest/issues/2393
ret=$?
if [ "$ret" = 5 ]; then
echo "No tests collected. Exiting with 0 (instead of 5)."
exit 0
fi
ls -la
ls -la tests
exit "$ret"
env:
# These are the details of Slack
SLACK_CLIENT_ID: ${{ secrets.SLACK_CLIENT_ID }}
SLACK_CLIENT_REDIRECT: ${{ secrets.SLACK_CLIENT_REDIRECT }}
# These are the details for zendesk
ZENDESK_EMAIL: ${{ secrets.ZENDESK_EMAIL }}
ZENDESK_TOKEN: ${{ secrets.ZENDESK_TOKEN }}
# These are the connection details to Azure OpenAI
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_ENDPOINT: ${{ secrets.OPENAI_ENDPOINT }}
# This is the deployment used for function calling
OPENAI_API_DEPLOYMENT_FUNCTIONS: gpt-4o-mini
# This is the deployment used for answering prompts
OPENAI_API_DEPLOYMENT_QUERY: gpt4o
# This is a base 64 encoded version of the Octopus license
LICENSE: ${{ secrets.OCTOPUS_LICENSE }}
# This is the slack webhook used to send messages
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
# This is the connection string to Azurite, the test Aure Storage emulator
AzureWebJobsStorage: DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;
# This is the list of admin users
APPLICATION_USERS_ADMIN: "[160104]"
# This is the test user that is used to simulate a login
TEST_GH_USER: "160104"
# This is the GitHub token passed to the chat tests
GH_TEST_TOKEN: ${{ secrets.GH_TEST_TOKEN }}
# This is the encryption salt used when saving encrypted values to Azure Storage
ENCRYPTION_PASSWORD: password
# This is the encryption salt used when saving encrypted values to Azure Storage
ENCRYPTION_SALT: salt
# The location of the octoterra microservice
APPLICATION_OCTOTERRA_URL: http://localhost:8081
# The location of the octolint microservice
APPLICATION_OCTOLINT_URL: http://localhost:8082
GITHUB_CLIENT_REDIRECT: https://aiagent.octopus.com/api/oauth_callback
GITHUB_CLIENT_ID: 4ae1ef506301902a41a5
shell: /usr/bin/bash --noprofile --norc {0}
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage${{ matrix.group }}
include-hidden-files: true
path: .coverage
- name: Upload test artifacts
uses: actions/upload-artifact@v4
with:
name: junit-test-summary-${{ matrix.group }}
path: results.xml
retention-days: 1
test-results:
needs: test
runs-on: ubuntu-latest
steps:
- name: Set up Python
uses: actions/setup-python@v5
with:
# Need 3.11 because of https://stackoverflow.com/questions/77364550/attributeerror-module-pkgutil-has-no-attribute-impimporter-did-you-mean
python-version: 3.11
- uses: actions/checkout@v4
- name: Download artifacts
uses: actions/download-artifact@v4
- name: Run coverage
run: |
pip install pytest coverage setuptools
coverage combine coverage*/.coverage*
coverage xml --omit="./domain/sanitizers/stringlifier/*"
coverage html --omit="./domain/sanitizers/stringlifier/*" --fail-under=80
- name: Upload combined coverage
uses: actions/upload-artifact@v4
with:
name: coverage
include-hidden-files: true
path: .coverage
- name: Upload coverage xml
uses: actions/upload-artifact@v4
with:
name: coverage.xml
include-hidden-files: true
path: coverage.xml
- name: Coverage Badge
uses: tj-actions/coverage-badge-py@v2
- name: Verify Changed files
uses: tj-actions/verify-changed-files@v17
id: verify-changed-files
with:
files: coverage.svg
- name: Commit files
if: steps.verify-changed-files.outputs.files_changed == 'true'
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add coverage.svg
git commit -m "Updated coverage.svg"
- name: Push changes
if: steps.verify-changed-files.outputs.files_changed == 'true'
uses: ad-m/github-push-action@master
continue-on-error: true
with:
github_token: ${{ secrets.github_token }}
branch: ${{ github.ref }}
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Install junit-report-merger
run: npm install -g junit-report-merger
- name: Merge reports
run: >
jrm ./junit-test-summary.xml
"junit-test-summary-1/*.xml"
"junit-test-summary-2/*.xml"
"junit-test-summary-3/*.xml"
"junit-test-summary-4/*.xml"
"junit-test-summary-5/*.xml"
"junit-test-summary-6/*.xml"
"junit-test-summary-7/*.xml"
"junit-test-summary-8/*.xml"
"junit-test-summary-9/*.xml"
"junit-test-summary-10/*.xml"
"junit-test-summary-11/*.xml"
"junit-test-summary-12/*.xml"
"junit-test-summary-13/*.xml"
"junit-test-summary-14/*.xml"
"junit-test-summary-15/*.xml"
- if: always()
name: Report
uses: dorny/test-reporter@v1
with:
name: Python Tests
path: junit-test-summary.xml
reporter: java-junit
fail-on-error: 'false'
build:
name: Build
needs: test-results
runs-on: ubuntu-latest
# group: ubuntu-large-runner
# labels: 4core-ubuntu-runner
steps:
- uses: actions/checkout@v4
with:
fetch-depth: '0'
- name: Set up Python
uses: actions/setup-python@v5
with:
# Need 3.11 because of https://stackoverflow.com/questions/77364550/attributeerror-module-pkgutil-has-no-attribute-impimporter-did-you-mean
python-version: 3.11
- name: Install GitVersion
uses: gittools/actions/gitversion/[email protected]
with:
versionSpec: 5.x
- id: determine_version
name: Determine Version
uses: gittools/actions/gitversion/[email protected]
with:
overrideConfig: mode=Mainline
- name: List Dependencies
run: pip install pipdeptree; pipdeptree > dependencies.txt
shell: bash
- name: Collect Dependencies
uses: actions/upload-artifact@v4
with:
name: Dependencies
path: dependencies.txt
- name: List Dependency Updates
run: python3 -m pip list --outdated --format=json | jq -r '.[] | .name+"="+.latest_version' > dependencyUpdates.txt || true
shell: bash
- name: Collect Dependency Updates
uses: actions/upload-artifact@v4
with:
name: Dependencies Updates
path: dependencyUpdates.txt
- name: Generate SBOM
if: ${{ github.event_name != 'schedule' }}
run: |
python -m pip install cyclonedx-bom
cyclonedx-py environment > bom.json
shell: bash
- name: Package
if: ${{ github.event_name != 'schedule' }}
run: |-
zip -R OctopusCopilot.${{ steps.determine_version.outputs.semVer }}.zip \
'*.py' \
'*.pyc' \
'*.html' \
'*.htm' \
'*.css' \
'*.js' \
'*.min' \
'*.map' \
'*.sql' \
'*.png' \
'*.jpg' \
'*.jpeg' \
'*.gif' \
'*.json' \
'*.env' \
'*.txt' \
'*.Procfile' \
'*.bestType' \
'*.conf' \
'*.encodings' \
'*.graphql' \
-x "octoterra/*" \
-x "**/__pycache__/*" \
-x ".vscode/*" \
-x "tests/*" \
shell: bash
- name: Tag Release
if: ${{ github.ref == 'refs/heads/main' && github.event_name != 'schedule' }}
uses: mathieudutour/[email protected]
with:
custom_tag: ${{ steps.determine_version.outputs.semVer }}
github_token: ${{ secrets.GITHUB_TOKEN }}
- id: create_release
name: Create Release
if: ${{ github.ref == 'refs/heads/main' && github.event_name != 'schedule' }}
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.determine_version.outputs.semVer }}+run${{ github.run_number }}-attempt${{ github.run_attempt }}
release_name: Release ${{ steps.determine_version.outputs.semVer }} Run ${{ github.run_number }} Attempt ${{ github.run_attempt }}
draft: ${{ github.ref == 'refs/heads/main' && 'false' || 'true' }}
name: Release ${{ steps.determine_version.outputs.semVer }} Run ${{ github.run_number }} Attempt ${{ github.run_attempt }}
- name: Upload Release Asset
if: ${{ github.ref == 'refs/heads/main' && github.event_name != 'schedule' }}
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.determine_version.outputs.semVer }}+run${{ github.run_number }}-attempt${{ github.run_attempt }}
files: OctopusCopilot.${{ steps.determine_version.outputs.semVer }}.zip
- name: Push packages to Octopus Deploy
if: ${{ github.event_name != 'schedule' }}
uses: OctopusDeploy/push-package-action@v3
env:
OCTOPUS_API_KEY: ${{ secrets.COPILOT_OCTOPUS_API }}
OCTOPUS_URL: ${{ secrets.COPILOT_OCTOPUS_URL }}
OCTOPUS_SPACE: ${{ secrets.COPILOT_OCTOPUS_SPACE }}
with:
packages: OctopusCopilot.${{ steps.determine_version.outputs.semVer }}.zip
overwrite_mode: OverwriteExisting
- name: Push build information to Octopus Deploy
if: ${{ github.event_name != 'schedule' }}
uses: OctopusDeploy/push-build-information-action@v3
env:
OCTOPUS_API_KEY: ${{ secrets.COPILOT_OCTOPUS_API }}
OCTOPUS_URL: ${{ secrets.COPILOT_OCTOPUS_URL }}
OCTOPUS_SPACE: ${{ secrets.COPILOT_OCTOPUS_SPACE }}
with:
packages: OctopusCopilot
version: ${{ steps.determine_version.outputs.semVer }}
overwrite_mode: OverwriteExisting
- name: Create Octopus Release
if: ${{ github.ref == 'refs/heads/main' && github.event_name != 'schedule' }}
uses: OctopusDeploy/create-release-action@v3
env:
OCTOPUS_API_KEY: ${{ secrets.COPILOT_OCTOPUS_API }}
OCTOPUS_URL: ${{ secrets.COPILOT_OCTOPUS_URL }}
OCTOPUS_SPACE: ${{ secrets.COPILOT_OCTOPUS_SPACE }}
with:
project: Octopus Copilot Function
packages: OctopusCopilot:${{ steps.determine_version.outputs.semVer }}
release_number: ${{ steps.determine_version.outputs.semVer }}+${{ steps.determine_version.outputs.ShortSha }}.${{ github.run_number }}.${{ github.run_attempt }}
git_ref: main
release_notes: |
* GitHub Owner: OctopusSolutionsEngineering
* GitHub Repo: OctopusCopilot
* GitHub Workflow: build.yaml
* GitHub Sha: ${{ github.sha }}
* GitHub Run: ${{ github.run_number }}
* GitHub Attempt: ${{ github.run_attempt }}
* GitHub Run ID: ${{ github.run_id }}
Here are the notes for the packages
#{each package in Octopus.Release.Package}
- #{package.PackageId} #{package.Version}
#{each commit in package.Commits}
- [#{commit.CommitId}](#{commit.LinkUrl}) - #{commit.Comment}
#{/each}
#{each workItem in package.WorkItems}
- [#{workItem.Id}](#{workItem.LinkUrl}) - #{workItem.Description}
#{/each}
#{/each}
permissions:
id-token: write
checks: write
contents: write