# .github/workflows/artifacts.ymlname:BuildandReleaseArtifactson:push:tags: ['v*']
workflow_dispatch:jobs:build-artifacts:name:Build(${{matrix.os}})runs-on:${{matrix.os}}strategy:matrix:include:-os:ubuntu-latestartifact_name:app-linux-x64asset_name:app-linux-x64.tar.gz-os:macos-latestartifact_name:app-macos-x64asset_name:app-macos-x64.tar.gz-os:windows-latestartifact_name:app-windows-x64asset_name:app-windows-x64.zipsteps:-uses:actions/checkout@v4-name:Buildapplicationrun:|
echo "Building for ${{ matrix.os }}"
mkdir -p dist
# Build commands here
-name:Package(Unix)if:runner.os!='Windows'run:|
tar -czvf ${{ matrix.asset_name }} -C dist .
-name:Package(Windows)if:runner.os=='Windows'run:|
Compress-Archive -Path dist/* -DestinationPath ${{ matrix.asset_name }}
shell:pwsh-name:Uploadartifactuses:actions/upload-artifact@v4with:name:${{matrix.artifact_name}}path:${{matrix.asset_name}}retention-days:30compression-level:9create-release:name:CreateReleaseneeds:build-artifactsruns-on:ubuntu-latestpermissions:contents:writesteps:-uses:actions/checkout@v4-name:Downloadallartifactsuses:actions/download-artifact@v4with:path:artifacts/merge-multiple:false-name:Listartifactsrun:findartifacts/-typef-name:Generatechangelogid:changelogrun:|
# Extract changelog for this version
VERSION=${GITHUB_REF#refs/tags/}
echo "version=$VERSION" >> $GITHUB_OUTPUT
# Generate release notescat<<EOF>release_notes.md## What's ChangedSee [CHANGELOG.md](CHANGELOG.md)fordetails.## Assets-\`app-linux-x64.tar.gz\`-Linux(x64)-\`app-macos-x64.tar.gz\`-macOS(x64)-\`app-windows-x64.zip\`-Windows(x64)EOF-name:CreateReleaseuses:softprops/action-gh-release@v2with:name:Release${{steps.changelog.outputs.version}}body_path:release_notes.mddraft:falseprerelease:${{contains(github.ref,'alpha')||contains(github.ref,'beta')}}files:|
artifacts/**/*
generate_release_notes:true
5. Reusable Workflows
# .github/workflows/reusable-python-ci.ymlname:ReusablePythonCIon:workflow_call:inputs:python-version:description:'Python version to use'required:falsetype:stringdefault:'3.11'test-command:description:'Test command to run'required:falsetype:stringdefault:'pytest tests/ -v'coverage:description:'Enable coverage reporting'required:falsetype:booleandefault:trueinstall-extras:description:'Package extras to install'required:falsetype:stringdefault:'dev'secrets:CODECOV_TOKEN:description:'Codecov upload token'required:falseoutputs:coverage-percent:description:'Test coverage percentage'value:${{jobs.test.outputs.coverage}}jobs:lint:name:Lintruns-on:ubuntu-lateststeps:-uses:actions/checkout@v4-name:SetupPythonuses:actions/setup-python@v5with:python-version:${{inputs.python-version}}cache:'pip'-name:Installlintersrun:pipinstallruff-name:Runruffrun:ruffcheck.test:name:Testneeds:lintruns-on:ubuntu-latestoutputs:coverage:${{steps.coverage.outputs.percent}}steps:-uses:actions/checkout@v4-name:SetupPythonuses:actions/setup-python@v5with:python-version:${{inputs.python-version}}cache:'pip'-name:Installdependenciesrun:pipinstall-e".[${{ inputs.install-extras }}]"-name:Runtestsrun:${{inputs.test-command}}-name:Runtestswithcoverageif:inputs.coveragerun:|
pip install pytest-cov
pytest tests/ -v --cov=src --cov-report=xml --cov-report=term-missing
-name:Extractcoverageif:inputs.coverageid:coveragerun:|
COVERAGE=$(python -c "import xml.etree.ElementTree as ET; print(f\"{float(ET.parse('coverage.xml').getroot().get('line-rate')) * 100:.1f}\")")
echo "percent=$COVERAGE" >> $GITHUB_OUTPUT
-name:Uploadcoverageif:inputs.coverage&&secrets.CODECOV_TOKENuses:codecov/codecov-action@v4with:token:${{secrets.CODECOV_TOKEN}}files:./coverage.xml
# .github/workflows/ci.yml - Using the reusable workflowname:CIon:push:branches: [main]
pull_request:jobs:python-ci:uses:./.github/workflows/reusable-python-ci.ymlwith:python-version:'3.11'test-command:'pytest tests/ -v --tb=short'coverage:trueinstall-extras:'dev,test'secrets:CODECOV_TOKEN:${{secrets.CODECOV_TOKEN}}
6. Composite Actions
# .github/actions/setup-project/action.ymlname:'Setup Project'description:'Set up Python environment with dependencies and caching'inputs:python-version:description:'Python version'required:falsedefault:'3.11'install-dev:description:'Install dev dependencies'required:falsedefault:'true'working-directory:description:'Working directory'required:falsedefault:'.'outputs:python-path:description:'Path to Python executable'value:${{steps.setup-python.outputs.python-path}}cache-hit:description:'Whether cache was hit'value:${{steps.pip-cache.outputs.cache-hit}}runs:using:'composite'steps:-name:SetupPythonid:setup-pythonuses:actions/setup-python@v5with:python-version:${{inputs.python-version}}-name:Getpipcachedirid:pip-cache-dirshell:bashrun:echo"dir=$(pip cache dir)">>$GITHUB_OUTPUT-name:Cachepipid:pip-cacheuses:actions/cache@v4with:path:${{steps.pip-cache-dir.outputs.dir}}key:pip-${{runner.os}}-${{inputs.python-version}}-${{hashFiles('**/pyproject.toml','**/requirements*.txt')}}restore-keys:|
pip-${{ runner.os }}-${{ inputs.python-version }}-
pip-${{ runner.os }}-
-name:Installbasedependenciesshell:bashworking-directory:${{inputs.working-directory}}run:|
python -m pip install --upgrade pip wheel setuptools
-name:Installprojectshell:bashworking-directory:${{inputs.working-directory}}run:|
if [ "${{ inputs.install-dev }}" == "true" ]; then
pip install -e ".[dev]"
else
pip install -e .
fi
# Using the composite actionjobs:build:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v4-name:SetupProjectuses:./.github/actions/setup-projectwith:python-version:'3.11'install-dev:'true'-name:Runtestsrun:pytesttests/-v
# .github/workflows/maintenance.ymlname:RepositoryMaintenanceon:schedule:# Run every Monday at 6 AM UTC-cron:'0 6 * * 1'workflow_dispatch:permissions:contents:writeissues:writepull-requests:writejobs:dependency-update:name:UpdateDependenciesruns-on:ubuntu-lateststeps:-uses:actions/checkout@v4-name:SetupPythonuses:actions/setup-python@v5with:python-version:'3.11'-name:Updatedependenciesrun:|
pip install pip-tools
pip-compile --upgrade requirements.in -o requirements.txt
pip-compile --upgrade requirements-dev.in -o requirements-dev.txt
-name:CreatePRifchangesuses:peter-evans/create-pull-request@v6with:token:${{secrets.GITHUB_TOKEN}}commit-message:'chore(deps): update dependencies'title:'chore(deps): Weekly dependency update'body:|
Automated weekly dependency update.
PleasereviewthechangesandmergeifCIpasses.branch:deps/weekly-updatedelete-branch:truelabels:dependencies,automatedstale-issues:name:CloseStaleIssuesruns-on:ubuntu-lateststeps:-uses:actions/stale@v9with:repo-token:${{secrets.GITHUB_TOKEN}}stale-issue-message:|
This issue has been automatically marked as stale because it has not had recent activity.
It will be closed in 7 days if no further activity occurs.
stale-pr-message:|
This PR has been automatically marked as stale because it has not had recent activity.
It will be closed in 7 days if no further activity occurs.
stale-issue-label:'stale'stale-pr-label:'stale'days-before-stale:60days-before-close:7exempt-issue-labels:'pinned,security,enhancement'exempt-pr-labels:'pinned,security'security-scan:name:SecurityAuditruns-on:ubuntu-lateststeps:-uses:actions/checkout@v4-name:RunTrivyvulnerabilityscanneruses:aquasecurity/trivy-action@masterwith:scan-type:'fs'ignore-unfixed:trueformat:'sarif'output:'trivy-results.sarif'severity:'CRITICAL,HIGH'-name:UploadTrivyscanresultsuses:github/codeql-action/upload-sarif@v3with:sarif_file:'trivy-results.sarif'-name:Pythonsafetycheckrun:|
pip install safety
safety check --full-report || true
10. PR Automation and Checks
# .github/workflows/pr-checks.ymlname:PRCheckson:pull_request:types: [opened, synchronize, reopened, edited]
permissions:contents:readpull-requests:writejobs:validate-pr:name:ValidatePRruns-on:ubuntu-lateststeps:-uses:actions/checkout@v4-name:CheckPRtitleuses:amannn/action-semantic-pull-request@v5env:GITHUB_TOKEN:${{secrets.GITHUB_TOKEN}}with:types:|
feat
fix
docs
style
refactor
perf
test
build
ci
chore
revert
requireScope:falsesubjectPattern:^.{1,50}$subjectPatternError:|
PR title must be 50 characters or less
-name:Checkforbreakingchangesif:contains(github.event.pull_request.title,'!')run:|
echo "::warning::This PR contains breaking changes"
-name:Addsizelabelsuses:codelytv/pr-size-labeler@v1with:GITHUB_TOKEN:${{secrets.GITHUB_TOKEN}}xs_label:'size/xs'xs_max_size:10s_label:'size/s's_max_size:100m_label:'size/m'm_max_size:500l_label:'size/l'l_max_size:1000xl_label:'size/xl'fail_if_xl:falsemessage_if_xl:|
This PR is very large. Please consider breaking it into smaller PRs.
auto-assign:name:AutoAssignruns-on:ubuntu-lateststeps:-name:Auto-assignauthoruses:kentaro-m/auto-assign-action@v1with:configuration-path:'.github/auto-assign.yml'label-pr:name:LabelPRruns-on:ubuntu-lateststeps:-uses:actions/labeler@v5with:repo-token:${{secrets.GITHUB_TOKEN}}configuration-path:.github/labeler.yml
# Always pin action versions with SHA-uses:actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11# v4.1.1# Use minimum required permissionspermissions:contents:readpackages:write# Never hardcode secretsenv:API_KEY:${{secrets.API_KEY}}# Good# API_KEY: "sk-1234567890" # Never do this# Use OIDC for cloud provider authentication-uses:aws-actions/configure-aws-credentials@v4with:role-to-assume:${{secrets.AWS_ROLE_ARN}}
2. Performance Optimization
# Use caching aggressively-uses:actions/cache@v4with:path:~/.cache/pipkey:pip-${{hashFiles('requirements.txt')}}# Use matrix fail-fast wiselystrategy:fail-fast:false# Continue other jobs on failure# Limit concurrent runsconcurrency:group:${{github.workflow}}-${{github.ref}}cancel-in-progress:true
3. Maintainability
# Use reusable workflowsjobs:test:uses:./.github/workflows/reusable-test.yml# Extract common steps into composite actions-uses:./.github/actions/setup-project# Use environment variables for configurationenv:PYTHON_VERSION:'3.11'NODE_VERSION:'20'
4. Error Handling
# Use continue-on-error for non-critical steps-name:Optionalstepcontinue-on-error:truerun:optional-command# Add timeout to prevent stuck jobsjobs:build:timeout-minutes:30# Always clean up resources-name:Cleanupif:always()run:cleanup-command
Troubleshooting
Common Issues
Issue: Workflow not triggering
# Check trigger pathson:push:paths:-'src/**'# Only triggers for src/ changes# Verify branch names matchon:push:branches:-main-'release/*'# Use quotes for patterns