Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
303 changes: 302 additions & 1 deletion .github/copilot-instructions.md

Large diffs are not rendered by default.

18 changes: 12 additions & 6 deletions .github/python.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@ applyTo: '**/*.py'

# Copilot Instructions (Python)

## Critical: Load Pylint Configuration First
## Critical: Load Ruff Configuration First

**BEFORE making any changes to Python files**, always load the pylint configuration into context:
**BEFORE making any changes to Python files**, always load the ruff configuration into context:

1. Use `read_file` to load `.pylintrc`
2. Review the disabled rules and enabled checks
1. Use `read_file` to load `pyproject.toml` and review the `[tool.ruff]` and `[tool.ruff.lint]` sections
2. Review the ignored rules and per-file exceptions
3. Apply these rules when writing or modifying Python code

This ensures all code changes comply with the project's linting standards from the start.

## Ruff Expectations

- Use explicit imports (avoid `from module import *`), especially in notebooks, to prevent `F403/F405`.
- Keep lines within the configured length limit (see `pyproject.toml`), and wrap long strings or calls.
- Avoid f-strings without placeholders (e.g., `F541`).

## Goals

- Make changes that are easy to review, test, and maintain.
Expand Down Expand Up @@ -62,8 +68,8 @@ This ensures all code changes comply with the project's linting standards from t

Before completing any Python code changes, verify:

- All pylint warnings and errors are resolved (`pylint --rcfile=.pylintrc <file>`)
- Pylint rules cover these, but we don't see .pylintrc being added to the context. Therefore, please pay special attention to these common occurrences:
- All ruff warnings and errors are resolved (`ruff check <file>`)
- Ruff rules cover these, but we don't see `pyproject.toml` being added to context. Therefore, please pay special attention to these common occurrences:
- No trailing whitespace
- No assertion of empty strings in tests (use `assert not`)
- Code follows PEP 8 and the style guidelines in this file
Expand Down
38 changes: 17 additions & 21 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,18 @@ jobs:
uv run python -c "import coverage.html; print(coverage.html.__file__)"

# Lint the Python files & upload the result statistics
- name: Run pylint analysis
id: pylint
- name: Run ruff analysis
id: ruff
run: |
mkdir -p tests/python/pylint/reports
# Use python -m pylint and tee to ensure output is captured and visible in logs
uv run python -m pylint --rcfile .pylintrc infrastructure samples setup shared 2>&1 | tee tests/python/pylint/reports/latest.txt
mkdir -p tests/python/ruff/reports
uv run ruff check infrastructure samples setup shared 2>&1 | tee tests/python/ruff/reports/latest.txt
uv run ruff check --output-format json infrastructure samples setup shared > tests/python/ruff/reports/latest.json 2>/dev/null || true

- name: Upload pylint reports
- name: Upload ruff reports
uses: actions/upload-artifact@v4
with:
name: pylint-reports-${{ matrix.python-version }}
path: tests/python/pylint/reports/
name: ruff-reports-${{ matrix.python-version }}
path: tests/python/ruff/reports/

# Static code analysis through simple compilation to ensure code is syntactically sound
- name: Verify bytecode compilation
Expand Down Expand Up @@ -82,17 +82,13 @@ jobs:
- name: Extract and Summarize Metrics
id: metrics
run: |
# Pylint Score
TEXT_REPORT="tests/python/pylint/reports/latest.txt"
if [ -s "$TEXT_REPORT" ]; then
PYLINT_SCORE=$(grep -Eo 'Your code has been rated at [0-9.]+/10' "$TEXT_REPORT" | grep -Eo '[0-9.]+/10' | head -n 1)
if [ -n "$PYLINT_SCORE" ]; then
echo "pylint_score=$PYLINT_SCORE" >> "$GITHUB_OUTPUT"
else
echo "pylint_score=N/A" >> "$GITHUB_OUTPUT"
fi
# Ruff Issue Count
JSON_REPORT="tests/python/ruff/reports/latest.json"
if [ -f "$JSON_REPORT" ] && command -v jq &> /dev/null; then
RUFF_ISSUES=$(jq 'length' "$JSON_REPORT" 2>/dev/null || echo "N/A")
echo "ruff_issues=$RUFF_ISSUES" >> "$GITHUB_OUTPUT"
else
echo "pylint_score=N/A" >> "$GITHUB_OUTPUT"
echo "ruff_issues=N/A" >> "$GITHUB_OUTPUT"
fi

# Coverage Percentage
Expand All @@ -114,23 +110,23 @@ jobs:

| Metric | Status | Value |
| :--- | :---: | :--- |
| **Pylint Score** | ${{ steps.pylint.outcome == 'success' && '✅' || '⚠️' }} | `${{ steps.metrics.outputs.pylint_score }}` |
| **Ruff** | ${{ steps.ruff.outcome == 'success' && '✅' || '⚠️' }} | `${{ steps.metrics.outputs.ruff_issues }} issue(s)` |
| **Unit Tests** | ${{ steps.pytest.outcome == 'success' && '✅' || '❌' }} | `${{ steps.pytest.outcome }}` |
| **Code Coverage** | 📊 | `${{ steps.metrics.outputs.coverage }}` |

[Full Workflow Logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})

- name: Generate Job Summary
run: |
PYLINT_SCORE="${{ steps.metrics.outputs.pylint_score }}"
RUFF_ISSUES="${{ steps.metrics.outputs.ruff_issues }}"
PYTEST_OUTCOME="${{ steps.pytest.outcome }}"
COVERAGE="${{ steps.metrics.outputs.coverage }}"

echo "## 🐍 Python ${{ matrix.python-version }} Execution Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Category | Status | Detail |" >> $GITHUB_STEP_SUMMARY
echo "| :--- | :---: | :--- |" >> $GITHUB_STEP_SUMMARY
echo "| **Pylint** | ${{ steps.pylint.outcome == 'success' && '✅' || '⚠️' }} | Score: \`${PYLINT_SCORE:-N/A}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Ruff** | ${{ steps.ruff.outcome == 'success' && '✅' || '⚠️' }} | Issues: \`${RUFF_ISSUES:-N/A}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Pytest** | ${{ steps.pytest.outcome == 'success' && '✅' || '❌' }} | Outcome: \`${PYTEST_OUTCOME:-N/A}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Coverage** | 📊 | Total: \`${COVERAGE:-N/A}\` |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ htmlcov/
tests/python/htmlcov/

# Pylint reports
tests/python/pylint/reports/
tests/python/ruff/reports/
tests/python/$JsonReport
tests/python/$TextReport

Expand Down
41 changes: 0 additions & 41 deletions .pylintrc

This file was deleted.

4 changes: 4 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
{
"unwantedRecommendations": [
"ms-python.pylint"
],
"recommendations": [
"ms-python.python",
"ms-python.debugpy",
"ms-toolsai.jupyter",
"ms-azuretools.vscode-bicep",
"ms-vscode.azurecli",
"charliermarsh.ruff",
"GitHub.copilot",
"GitHub.copilot-chat",
"donjayamanne.vscode-default-python-kernel"
Expand Down
6 changes: 4 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
},
"[python]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.unusedImports": "explicit"
"source.fixAll.ruff": "explicit",
"source.organizeImports.ruff": "explicit"
},
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true
},
"editor.renderWhitespace": "trailing",
Expand All @@ -17,6 +18,7 @@
"jupyter.kernels.trusted": [
"./.venv/Scripts/python.exe"
],
"pylint.enabled": false,
"python.analysis.exclude": [
"**/node_modules",
"**/__pycache__",
Expand Down
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ It's quick and easy to get started!

| Infrastructure Name | Description |
|:-------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Simple API Management][infra-simple-apim] | **Just the basics with a publicly accessible API Management instance** fronting your APIs. This is the innermost way to experience and experiment with the APIM policies. |
| [API Management & Container Apps][infra-apim-aca] | APIs are often implemented in containers running in **Azure Container Apps**. This architecture accesses the container apps publicly. It's beneficial to test both APIM and container app URLs to contrast and compare experiences of API calls through and bypassing APIM. It is not intended to be a security baseline. |
| [Front Door & API Management & Container Apps][infra-afd-apim-pe] | **A secure implementation of Azure Front Door connecting to APIM via the new private link integration!** This traffic, once it traverses through Front Door, rides entirely on Microsoft-owned and operated networks. The connection from APIM to Container Apps is secured but through a VNet configuration (it is also entirely possible to do this via private link). **APIM Standard V2** is used here to accept a private link from Front Door. |
| [Application Gateway (Private Endpoint) & API Management & Container Apps][infra-appgw-apim-pe] | **A secure implementation of Azure Application Gateway connecting to APIM via the new private link integration!** This traffic, once it traverses through App Gateway, uses a private endpoint set up in the VNet's private endpoint subnet. The connection from APIM to Container Apps is secured but through a VNet configuration (it is also entirely possible to do this via private link). APIM Standard V2 is used here to accept a private link from App Gateway. |
| [Application Gateway (VNet) & API Management & Container Apps][infra-appgw-apim] | Full VNet injection of APIM and ACA! APIM is shielded from any type of traffic unless it comes through App Gateway. This offers maximum isolation for instances in which customers seek VNet injection. |
| [Front Door & API Management & Container Apps][infra-afd-apim-pe] | **A secure implementation of Azure Front Door connecting to APIM via the new private link integration!** This traffic, once it traverses through Front Door, rides entirely on Microsoft-owned and operated networks. The connection from APIM to Container Apps is secured but through a VNet configuration (it is also entirely possible to do this via private link). **APIM Standard V2** is used here to accept a private link from Front Door. |
| [Simple API Management][infra-simple-apim] | **Just the basics with a publicly accessible API Management instance** fronting your APIs. This is the innermost way to experience and experiment with the APIM policies. |
</details>

## 📁 List of Samples
Expand All @@ -65,11 +65,12 @@ It's quick and easy to get started!
|:------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------|:------------------------------|
| [AuthX][sample-authx] | Authentication and role-based authorization in a mock HR API. | All infrastructures |
| [AuthX Pro][sample-authx-pro] | Authentication and role-based authorization in a mock product with multiple APIs and policy fragments. | All infrastructures |
| [Azure Maps][sample-azure-maps] | Proxying calls to Azure Maps with APIM policies. | All infrastructures |
| [Costing & Showback][sample-costing] | Track and allocate API costs per business unit using APIM subscriptions, Log Analytics, and Cost Management. | All infrastructures |
| [Credential Manager (with Spotify)][sample-oauth-3rd-party] | Authenticate with APIM which then uses its Credential Manager with Spotify's REST API. | All infrastructures |
| [General][sample-general] | Basic demo of APIM sample setup and policy usage. | All infrastructures |
| [Load Balancing][sample-load-balancing] | Priority and weighted load balancing across backends. | apim-aca, afd-apim (with ACA) |
| [Secure Blob Access][sample-secure-blob-access] | Secure blob access via the [valet key pattern][valet-key-pattern]. | All infrastructures |
| [Credential Manager (with Spotify)][sample-oauth-3rd-party] | Authenticate with APIM which then uses its Credential Manager with Spotify's REST API. | All infrastructures |
| [Azure Maps][sample-azure-maps] | Proxying calls to Azure Maps with APIM policies. | All infrastructures |
</details>

### Compatibility Matrix
Expand All @@ -96,7 +97,7 @@ Use the interactive APIM Samples Developer CLI to verify setup, run tests, and m
This menu-driven interface provides quick access to:
- **Setup**: Complete environment setup and verify local setup
- **Verify**: Show Azure account info, list soft-deleted resources, and list deployed infrastructures
- **Tests**: Run pylint, pytest, and full Python checks
- **Tests**: Run ruff, pytest, and full Python checks

<img src="./assets/dev-cli-lint-test-results.png" alt="APIM Samples Developer CLI showing final linting, test, and code coverage results" title="APIM Samples Developer CLI Final Results" />

Expand Down Expand Up @@ -252,7 +253,7 @@ The repo uses the bicep linter and has rules defined in `bicepconfig.json`. See

### 🔍 Code Quality & Linting

The repository uses [pylint][pylint-docs] to maintain Python code quality standards. The configuration is located in `.pylintrc`, and the APIM Samples Developer CLI supports linting.
The repository uses [Ruff][ruff-docs] to maintain Python code quality standards. The configuration is located in `pyproject.toml` under `[tool.ruff]`, and the APIM Samples Developer CLI supports linting.

### 🧪 Testing & Code Coverage

Expand All @@ -279,6 +280,8 @@ Furthermore, [Houssem Dellai][houssem-dellai] was instrumental in setting up a w

[Andrew Redman][andrew-redman] for contributing the _Azure Maps_ sample.

[Naga Venkata Cheruvu][naga-cheruvu] for contributing the _Costing & Showback_ sample.

The original author of this project is [Simon Kurtz][simon-kurtz].


Expand Down Expand Up @@ -315,6 +318,7 @@ _For much more API Management content, please also check out [APIM Love](https:/
[bicep-linter-docs]: https://learn.microsoft.com/azure/azure-resource-manager/bicep/bicep-config-linter
[houssem-dellai]: https://github.com/HoussemDellai
[import-troubleshooting]: .devcontainer/IMPORT-TROUBLESHOOTING.md
[naga-cheruvu]: https://github.com/ncheruvu-MSFT
[infra-afd-apim-pe]: ./infrastructure/afd-apim-pe
[infra-apim-aca]: ./infrastructure/apim-aca
[infra-appgw-apim]: ./infrastructure/appgw-apim/
Expand All @@ -323,11 +327,12 @@ _For much more API Management content, please also check out [APIM Love](https:/
[openssf]: https://www.bestpractices.dev/projects/11057
[pytest-docs]: https://docs.pytest.org/
[pytest-docs-versioned]: https://docs.pytest.org/en/8.2.x/
[pylint-docs]: https://pylint.pycqa.org/
[ruff-docs]: https://docs.astral.sh/ruff/
[python]: https://www.python.org/
[sample-authx]: ./samples/authX/README.md
[sample-authx-pro]: ./samples/authX-pro/README.md
[sample-azure-maps]: ./samples/azure-maps/README.md
[sample-costing]: ./samples/costing/README.md
[sample-general]: ./samples/general/README.md
[sample-load-balancing]: ./samples/load-balancing/README.md
[sample-oauth-3rd-party]: ./samples/oauth-3rd-party/README.md
Expand Down
Loading
Loading