fabric-dw 0.1.dev77__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fabric_dw-0.1.dev77/.devcontainer/devcontainer.json +27 -0
- fabric_dw-0.1.dev77/.env.example +8 -0
- fabric_dw-0.1.dev77/.github/CODEOWNERS +1 -0
- fabric_dw-0.1.dev77/.github/ISSUE_TEMPLATE/bug_report.yml +33 -0
- fabric_dw-0.1.dev77/.github/ISSUE_TEMPLATE/feature_request.yml +30 -0
- fabric_dw-0.1.dev77/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- fabric_dw-0.1.dev77/.github/dependabot.yml +25 -0
- fabric_dw-0.1.dev77/.github/release-drafter.yml +28 -0
- fabric_dw-0.1.dev77/.github/workflows/ci.yml +73 -0
- fabric_dw-0.1.dev77/.github/workflows/docker.yml +58 -0
- fabric_dw-0.1.dev77/.github/workflows/integration.yml +189 -0
- fabric_dw-0.1.dev77/.github/workflows/publish.yml +44 -0
- fabric_dw-0.1.dev77/.github/workflows/release-drafter.yml +16 -0
- fabric_dw-0.1.dev77/.github/workflows/security.yml +38 -0
- fabric_dw-0.1.dev77/.gitignore +50 -0
- fabric_dw-0.1.dev77/.python-version +1 -0
- fabric_dw-0.1.dev77/CODE_OF_CONDUCT.md +83 -0
- fabric_dw-0.1.dev77/CONTRIBUTING.md +92 -0
- fabric_dw-0.1.dev77/Dockerfile +18 -0
- fabric_dw-0.1.dev77/LICENSE +21 -0
- fabric_dw-0.1.dev77/PKG-INFO +143 -0
- fabric_dw-0.1.dev77/README.md +92 -0
- fabric_dw-0.1.dev77/SECURITY.md +16 -0
- fabric_dw-0.1.dev77/docs/assets/logo.svg +17 -0
- fabric_dw-0.1.dev77/docs/authentication.md +112 -0
- fabric_dw-0.1.dev77/docs/cli.md +702 -0
- fabric_dw-0.1.dev77/docs/code-of-conduct.md +5 -0
- fabric_dw-0.1.dev77/docs/completion.md +73 -0
- fabric_dw-0.1.dev77/docs/contributing.md +5 -0
- fabric_dw-0.1.dev77/docs/index.md +31 -0
- fabric_dw-0.1.dev77/docs/install.md +45 -0
- fabric_dw-0.1.dev77/docs/license.md +5 -0
- fabric_dw-0.1.dev77/docs/mcp.md +325 -0
- fabric_dw-0.1.dev77/docs/security.md +5 -0
- fabric_dw-0.1.dev77/docs/troubleshooting.md +179 -0
- fabric_dw-0.1.dev77/docs_build/cloudflare_pages.sh +9 -0
- fabric_dw-0.1.dev77/justfile +19 -0
- fabric_dw-0.1.dev77/overrides/partials/integrations/analytics/custom.html +18 -0
- fabric_dw-0.1.dev77/pyproject.toml +144 -0
- fabric_dw-0.1.dev77/src/fabric_dw/__init__.py +5 -0
- fabric_dw-0.1.dev77/src/fabric_dw/_version.py +24 -0
- fabric_dw-0.1.dev77/src/fabric_dw/auth.py +112 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cache.py +259 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cli/__init__.py +10 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cli/_context.py +29 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cli/_main.py +82 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cli/_render.py +104 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cli/commands/__init__.py +0 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cli/commands/_utils.py +90 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cli/commands/audit.py +144 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cli/commands/cache.py +26 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cli/commands/completion.py +94 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cli/commands/config.py +120 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cli/commands/endpoints.py +112 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cli/commands/queries.py +109 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cli/commands/snapshots.py +228 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cli/commands/warehouses.py +236 -0
- fabric_dw-0.1.dev77/src/fabric_dw/cli/commands/workspaces.py +126 -0
- fabric_dw-0.1.dev77/src/fabric_dw/config.py +159 -0
- fabric_dw-0.1.dev77/src/fabric_dw/exceptions.py +34 -0
- fabric_dw-0.1.dev77/src/fabric_dw/http_client.py +337 -0
- fabric_dw-0.1.dev77/src/fabric_dw/logging.py +90 -0
- fabric_dw-0.1.dev77/src/fabric_dw/mcp/__init__.py +13 -0
- fabric_dw-0.1.dev77/src/fabric_dw/mcp/server.py +602 -0
- fabric_dw-0.1.dev77/src/fabric_dw/models.py +119 -0
- fabric_dw-0.1.dev77/src/fabric_dw/py.typed +0 -0
- fabric_dw-0.1.dev77/src/fabric_dw/resolver.py +241 -0
- fabric_dw-0.1.dev77/src/fabric_dw/services/__init__.py +5 -0
- fabric_dw-0.1.dev77/src/fabric_dw/services/audit.py +154 -0
- fabric_dw-0.1.dev77/src/fabric_dw/services/ownership.py +40 -0
- fabric_dw-0.1.dev77/src/fabric_dw/services/queries.py +121 -0
- fabric_dw-0.1.dev77/src/fabric_dw/services/snapshots.py +311 -0
- fabric_dw-0.1.dev77/src/fabric_dw/services/sql_endpoints.py +124 -0
- fabric_dw-0.1.dev77/src/fabric_dw/services/warehouses.py +238 -0
- fabric_dw-0.1.dev77/src/fabric_dw/services/workspaces.py +131 -0
- fabric_dw-0.1.dev77/src/fabric_dw/sql.py +193 -0
- fabric_dw-0.1.dev77/tests/__init__.py +0 -0
- fabric_dw-0.1.dev77/tests/fixtures/__init__.py +0 -0
- fabric_dw-0.1.dev77/tests/fixtures/api_payloads.py +273 -0
- fabric_dw-0.1.dev77/tests/integration/__init__.py +0 -0
- fabric_dw-0.1.dev77/tests/integration/conftest.py +67 -0
- fabric_dw-0.1.dev77/tests/integration/test_cross_cutting.py +47 -0
- fabric_dw-0.1.dev77/tests/integration/test_services_audit.py +45 -0
- fabric_dw-0.1.dev77/tests/integration/test_services_ownership.py +22 -0
- fabric_dw-0.1.dev77/tests/integration/test_services_queries.py +18 -0
- fabric_dw-0.1.dev77/tests/integration/test_services_snapshots.py +26 -0
- fabric_dw-0.1.dev77/tests/integration/test_services_warehouses.py +42 -0
- fabric_dw-0.1.dev77/tests/integration/test_services_workspaces.py +45 -0
- fabric_dw-0.1.dev77/tests/integration/test_smoke.py +17 -0
- fabric_dw-0.1.dev77/tests/unit/__init__.py +0 -0
- fabric_dw-0.1.dev77/tests/unit/cli/__init__.py +0 -0
- fabric_dw-0.1.dev77/tests/unit/cli/commands/__init__.py +0 -0
- fabric_dw-0.1.dev77/tests/unit/cli/commands/test_audit.py +304 -0
- fabric_dw-0.1.dev77/tests/unit/cli/commands/test_config.py +162 -0
- fabric_dw-0.1.dev77/tests/unit/cli/commands/test_endpoints.py +278 -0
- fabric_dw-0.1.dev77/tests/unit/cli/commands/test_queries.py +240 -0
- fabric_dw-0.1.dev77/tests/unit/cli/commands/test_snapshots.py +424 -0
- fabric_dw-0.1.dev77/tests/unit/cli/commands/test_warehouses.py +414 -0
- fabric_dw-0.1.dev77/tests/unit/cli/commands/test_workspaces.py +286 -0
- fabric_dw-0.1.dev77/tests/unit/cli/test_cache.py +74 -0
- fabric_dw-0.1.dev77/tests/unit/cli/test_completion.py +62 -0
- fabric_dw-0.1.dev77/tests/unit/cli/test_main.py +85 -0
- fabric_dw-0.1.dev77/tests/unit/cli/test_render.py +100 -0
- fabric_dw-0.1.dev77/tests/unit/mcp/__init__.py +0 -0
- fabric_dw-0.1.dev77/tests/unit/mcp/test_server.py +754 -0
- fabric_dw-0.1.dev77/tests/unit/services/__init__.py +0 -0
- fabric_dw-0.1.dev77/tests/unit/services/test_audit.py +263 -0
- fabric_dw-0.1.dev77/tests/unit/services/test_ownership.py +109 -0
- fabric_dw-0.1.dev77/tests/unit/services/test_queries.py +395 -0
- fabric_dw-0.1.dev77/tests/unit/services/test_snapshots.py +601 -0
- fabric_dw-0.1.dev77/tests/unit/services/test_sql_endpoints.py +423 -0
- fabric_dw-0.1.dev77/tests/unit/services/test_warehouses.py +688 -0
- fabric_dw-0.1.dev77/tests/unit/services/test_workspaces.py +248 -0
- fabric_dw-0.1.dev77/tests/unit/test_auth.py +242 -0
- fabric_dw-0.1.dev77/tests/unit/test_cache.py +367 -0
- fabric_dw-0.1.dev77/tests/unit/test_config.py +133 -0
- fabric_dw-0.1.dev77/tests/unit/test_http_client.py +443 -0
- fabric_dw-0.1.dev77/tests/unit/test_logging.py +74 -0
- fabric_dw-0.1.dev77/tests/unit/test_models.py +250 -0
- fabric_dw-0.1.dev77/tests/unit/test_resolver.py +549 -0
- fabric_dw-0.1.dev77/tests/unit/test_smoke.py +6 -0
- fabric_dw-0.1.dev77/tests/unit/test_sql.py +289 -0
- fabric_dw-0.1.dev77/uv.lock +1868 -0
- fabric_dw-0.1.dev77/wrangler.toml +3 -0
- fabric_dw-0.1.dev77/zensical.toml +133 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fabric-dw",
|
|
3
|
+
"image": "ghcr.io/astral-sh/uv:python3.13-bookworm",
|
|
4
|
+
"features": {
|
|
5
|
+
"ghcr.io/devcontainers/features/azure-cli:1": {},
|
|
6
|
+
"ghcr.io/devcontainers/features/github-cli:1": {},
|
|
7
|
+
"ghcr.io/devcontainers/features/common-utils:2": { "username": "vscode" }
|
|
8
|
+
},
|
|
9
|
+
"postCreateCommand": "uv sync",
|
|
10
|
+
"customizations": {
|
|
11
|
+
"vscode": {
|
|
12
|
+
"extensions": [
|
|
13
|
+
"charliermarsh.ruff",
|
|
14
|
+
"ms-python.python",
|
|
15
|
+
"ms-python.mypy-type-checker",
|
|
16
|
+
"tamasfe.even-better-toml",
|
|
17
|
+
"GitHub.copilot",
|
|
18
|
+
"GitHub.copilot-chat"
|
|
19
|
+
],
|
|
20
|
+
"settings": {
|
|
21
|
+
"python.defaultInterpreterPath": "/usr/local/bin/python",
|
|
22
|
+
"[python]": { "editor.defaultFormatter": "charliermarsh.ruff" }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"remoteUser": "vscode"
|
|
27
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
FABRIC_TEST_WORKSPACE_ID=
|
|
2
|
+
FABRIC_AUTH=default
|
|
3
|
+
# AZURE_TENANT_ID=
|
|
4
|
+
# AZURE_CLIENT_ID=
|
|
5
|
+
# AZURE_CLIENT_SECRET=
|
|
6
|
+
# Override the shared multi-tenant app used for interactive browser sign-in:
|
|
7
|
+
# FABRIC_INTERACTIVE_CLIENT_ID=f666e5ee-2149-4c6a-87eb-13c9e1fdc70d
|
|
8
|
+
# FABRIC_INTERACTIVE_TENANT_ID=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @sdebruyn
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Report a bug or unexpected behavior
|
|
3
|
+
title: "bug: "
|
|
4
|
+
labels: [bug]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: |
|
|
9
|
+
Thanks for taking the time to report a bug!
|
|
10
|
+
- type: textarea
|
|
11
|
+
id: description
|
|
12
|
+
attributes:
|
|
13
|
+
label: Description
|
|
14
|
+
description: What happened? What did you expect to happen?
|
|
15
|
+
validations:
|
|
16
|
+
required: true
|
|
17
|
+
- type: textarea
|
|
18
|
+
id: steps
|
|
19
|
+
attributes:
|
|
20
|
+
label: Steps to reproduce
|
|
21
|
+
description: How can we reproduce the issue?
|
|
22
|
+
placeholder: |
|
|
23
|
+
1. Run `fabric-dw ...`
|
|
24
|
+
2. See error
|
|
25
|
+
validations:
|
|
26
|
+
required: true
|
|
27
|
+
- type: textarea
|
|
28
|
+
id: environment
|
|
29
|
+
attributes:
|
|
30
|
+
label: Environment
|
|
31
|
+
description: "Python version, OS, package version"
|
|
32
|
+
validations:
|
|
33
|
+
required: false
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest a new feature or enhancement
|
|
3
|
+
title: "feat: "
|
|
4
|
+
labels: [enhancement]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: |
|
|
9
|
+
Thanks for suggesting a feature!
|
|
10
|
+
- type: textarea
|
|
11
|
+
id: problem
|
|
12
|
+
attributes:
|
|
13
|
+
label: Problem or motivation
|
|
14
|
+
description: What problem does this feature solve?
|
|
15
|
+
validations:
|
|
16
|
+
required: true
|
|
17
|
+
- type: textarea
|
|
18
|
+
id: solution
|
|
19
|
+
attributes:
|
|
20
|
+
label: Proposed solution
|
|
21
|
+
description: Describe the feature you'd like to see.
|
|
22
|
+
validations:
|
|
23
|
+
required: true
|
|
24
|
+
- type: textarea
|
|
25
|
+
id: alternatives
|
|
26
|
+
attributes:
|
|
27
|
+
label: Alternatives considered
|
|
28
|
+
description: Any alternative solutions or workarounds you've considered?
|
|
29
|
+
validations:
|
|
30
|
+
required: false
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
<!-- Describe what this PR does and why. -->
|
|
4
|
+
|
|
5
|
+
## Test plan
|
|
6
|
+
|
|
7
|
+
- [ ] Pre-commit hooks pass (`uv run pre-commit run --all-files`)
|
|
8
|
+
- [ ] Unit tests pass (`uv run pytest tests/unit`)
|
|
9
|
+
- [ ] Integration tests pass where applicable (`uv run pytest tests/integration`)
|
|
10
|
+
- [ ] Manual testing steps (if any):
|
|
11
|
+
|
|
12
|
+
## Linked issue
|
|
13
|
+
|
|
14
|
+
Closes #
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: pip
|
|
4
|
+
directory: /
|
|
5
|
+
schedule:
|
|
6
|
+
interval: weekly
|
|
7
|
+
groups:
|
|
8
|
+
patch-and-minor:
|
|
9
|
+
patterns:
|
|
10
|
+
- "*"
|
|
11
|
+
update-types:
|
|
12
|
+
- minor
|
|
13
|
+
- patch
|
|
14
|
+
|
|
15
|
+
- package-ecosystem: github-actions
|
|
16
|
+
directory: /
|
|
17
|
+
schedule:
|
|
18
|
+
interval: weekly
|
|
19
|
+
groups:
|
|
20
|
+
patch-and-minor:
|
|
21
|
+
patterns:
|
|
22
|
+
- "*"
|
|
23
|
+
update-types:
|
|
24
|
+
- minor
|
|
25
|
+
- patch
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name-template: 'v$RESOLVED_VERSION'
|
|
2
|
+
tag-template: 'v$RESOLVED_VERSION'
|
|
3
|
+
categories:
|
|
4
|
+
- title: 'Features'
|
|
5
|
+
labels: ['type:feature']
|
|
6
|
+
commitish:
|
|
7
|
+
- "feat"
|
|
8
|
+
- title: 'Fixes'
|
|
9
|
+
labels: ['type:fix', 'bug']
|
|
10
|
+
commitish:
|
|
11
|
+
- "fix"
|
|
12
|
+
- title: 'Documentation'
|
|
13
|
+
labels: ['area:docs', 'area:docs-site']
|
|
14
|
+
commitish:
|
|
15
|
+
- "docs"
|
|
16
|
+
- title: 'CI / Tooling'
|
|
17
|
+
labels: ['area:ci', 'area:bootstrap']
|
|
18
|
+
commitish:
|
|
19
|
+
- "ci"
|
|
20
|
+
- "chore"
|
|
21
|
+
- title: 'Other'
|
|
22
|
+
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
|
|
23
|
+
change-title-escapes: '\<*_&'
|
|
24
|
+
version-resolver:
|
|
25
|
+
default: patch
|
|
26
|
+
template: |
|
|
27
|
+
## Changes
|
|
28
|
+
$CHANGES
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
lint:
|
|
18
|
+
name: Lint
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v6
|
|
22
|
+
with:
|
|
23
|
+
fetch-depth: 0
|
|
24
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
25
|
+
with:
|
|
26
|
+
enable-cache: true
|
|
27
|
+
- uses: actions/setup-python@v6
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.11"
|
|
30
|
+
- run: uv sync --frozen
|
|
31
|
+
- run: uv run ruff check .
|
|
32
|
+
- run: uv run ruff format --check .
|
|
33
|
+
|
|
34
|
+
type:
|
|
35
|
+
name: Type check
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v6
|
|
39
|
+
with:
|
|
40
|
+
fetch-depth: 0
|
|
41
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
42
|
+
with:
|
|
43
|
+
enable-cache: true
|
|
44
|
+
- uses: actions/setup-python@v6
|
|
45
|
+
with:
|
|
46
|
+
python-version: "3.11"
|
|
47
|
+
- run: uv sync --frozen
|
|
48
|
+
- run: uv run mypy src tests
|
|
49
|
+
|
|
50
|
+
unit:
|
|
51
|
+
name: Unit tests
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
strategy:
|
|
54
|
+
matrix:
|
|
55
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v6
|
|
58
|
+
with:
|
|
59
|
+
fetch-depth: 0
|
|
60
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
61
|
+
with:
|
|
62
|
+
enable-cache: true
|
|
63
|
+
- uses: actions/setup-python@v6
|
|
64
|
+
with:
|
|
65
|
+
python-version: ${{ matrix.python-version }}
|
|
66
|
+
- run: uv sync --frozen
|
|
67
|
+
- run: uv run pytest tests/unit --cov=fabric_dw --cov-report=xml
|
|
68
|
+
- name: Coverage report
|
|
69
|
+
run: uv run coverage report
|
|
70
|
+
- uses: actions/upload-artifact@v7
|
|
71
|
+
with:
|
|
72
|
+
name: coverage-${{ matrix.python-version }}
|
|
73
|
+
path: coverage.xml
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: Docker
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ["v[0-9]+.[0-9]+.[0-9]+"]
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
packages: write
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build-and-push:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v6
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
|
|
21
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
22
|
+
with:
|
|
23
|
+
enable-cache: true
|
|
24
|
+
|
|
25
|
+
- uses: actions/setup-python@v6
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.13"
|
|
28
|
+
|
|
29
|
+
- run: uv sync --frozen --no-dev
|
|
30
|
+
|
|
31
|
+
- name: Compute Docker tags
|
|
32
|
+
id: docker_tags
|
|
33
|
+
run: |
|
|
34
|
+
VERSION=$(uv run python -c "import fabric_dw; print(fabric_dw.__version__)")
|
|
35
|
+
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
|
|
36
|
+
echo "tags=ghcr.io/sdebruyn/fabric-dw:$VERSION,ghcr.io/sdebruyn/fabric-dw:latest" >> $GITHUB_OUTPUT
|
|
37
|
+
else
|
|
38
|
+
echo "tags=ghcr.io/sdebruyn/fabric-dw:$VERSION,ghcr.io/sdebruyn/fabric-dw:main" >> $GITHUB_OUTPUT
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
- uses: docker/setup-qemu-action@v3
|
|
42
|
+
- uses: docker/setup-buildx-action@v3
|
|
43
|
+
|
|
44
|
+
- uses: docker/login-action@v3
|
|
45
|
+
with:
|
|
46
|
+
registry: ghcr.io
|
|
47
|
+
username: ${{ github.actor }}
|
|
48
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
49
|
+
|
|
50
|
+
- name: Build & push
|
|
51
|
+
uses: docker/build-push-action@v6
|
|
52
|
+
with:
|
|
53
|
+
context: .
|
|
54
|
+
platforms: linux/amd64,linux/arm64
|
|
55
|
+
push: true
|
|
56
|
+
tags: ${{ steps.docker_tags.outputs.tags }}
|
|
57
|
+
cache-from: type=gha
|
|
58
|
+
cache-to: type=gha,mode=max
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
name: Integration tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [labeled, synchronize]
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
id-token: write
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: integration-tests
|
|
15
|
+
cancel-in-progress: false
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
integration:
|
|
19
|
+
if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'integration') }}
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
environment: integration
|
|
22
|
+
timeout-minutes: 45
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v6
|
|
25
|
+
with:
|
|
26
|
+
fetch-depth: 0
|
|
27
|
+
|
|
28
|
+
- uses: azure/login@v2
|
|
29
|
+
with:
|
|
30
|
+
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
|
31
|
+
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
|
32
|
+
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
|
|
33
|
+
|
|
34
|
+
- name: Resume Fabric capacity
|
|
35
|
+
run: |
|
|
36
|
+
set -e
|
|
37
|
+
CAPACITY_ID="${{ vars.FABRIC_TEST_CAPACITY_ID }}"
|
|
38
|
+
|
|
39
|
+
# Wait for the capacity to leave any transitional state, up to 5 min
|
|
40
|
+
for i in {1..30}; do
|
|
41
|
+
STATE=$(az resource show --ids "$CAPACITY_ID" --query 'properties.state' -o tsv 2>/dev/null || echo "Unknown")
|
|
42
|
+
echo "Pre-resume state: $STATE"
|
|
43
|
+
case "$STATE" in
|
|
44
|
+
Active) echo "Already Active, skipping resume"; exit 0 ;;
|
|
45
|
+
Paused) break ;;
|
|
46
|
+
*) sleep 10 ;;
|
|
47
|
+
esac
|
|
48
|
+
done
|
|
49
|
+
|
|
50
|
+
# Issue resume, but tolerate transient BadRequest
|
|
51
|
+
for attempt in {1..6}; do
|
|
52
|
+
if az resource invoke-action --ids "$CAPACITY_ID" --action resume; then
|
|
53
|
+
break
|
|
54
|
+
fi
|
|
55
|
+
echo "Resume attempt $attempt failed (likely transient), waiting 15s..."
|
|
56
|
+
sleep 15
|
|
57
|
+
done
|
|
58
|
+
|
|
59
|
+
# Poll until Active, max 5 min
|
|
60
|
+
for i in {1..30}; do
|
|
61
|
+
STATE=$(az resource show --ids "$CAPACITY_ID" --query 'properties.state' -o tsv)
|
|
62
|
+
echo "Post-resume state: $STATE"
|
|
63
|
+
[[ "$STATE" == "Active" ]] && exit 0
|
|
64
|
+
sleep 10
|
|
65
|
+
done
|
|
66
|
+
|
|
67
|
+
echo "Capacity did not reach Active state within 5 minutes" >&2
|
|
68
|
+
exit 1
|
|
69
|
+
|
|
70
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
71
|
+
with:
|
|
72
|
+
enable-cache: true
|
|
73
|
+
|
|
74
|
+
- uses: actions/setup-python@v6
|
|
75
|
+
with:
|
|
76
|
+
python-version: "3.13"
|
|
77
|
+
|
|
78
|
+
- run: uv sync --frozen
|
|
79
|
+
|
|
80
|
+
- name: Pre-test sweep of stale pytest-* items
|
|
81
|
+
env:
|
|
82
|
+
FABRIC_TEST_WORKSPACE_ID: ${{ secrets.FABRIC_TEST_WORKSPACE_ID }}
|
|
83
|
+
FABRIC_AUTH: default
|
|
84
|
+
run: |
|
|
85
|
+
uv run python -c "
|
|
86
|
+
import anyio, os
|
|
87
|
+
from datetime import datetime, timedelta, UTC
|
|
88
|
+
from fabric_dw.auth import get_credential
|
|
89
|
+
from fabric_dw.http_client import FabricHttpClient
|
|
90
|
+
from fabric_dw.services import warehouses, snapshots
|
|
91
|
+
|
|
92
|
+
async def main() -> None:
|
|
93
|
+
ws = os.environ['FABRIC_TEST_WORKSPACE_ID']
|
|
94
|
+
cutoff = datetime.now(UTC) - timedelta(hours=1)
|
|
95
|
+
cred = get_credential()
|
|
96
|
+
async with FabricHttpClient(cred) as http:
|
|
97
|
+
items = await warehouses.list_warehouses(http, ws)
|
|
98
|
+
for w in items:
|
|
99
|
+
if w.name.startswith('pytest-') and (w.created_date is None or w.created_date < cutoff):
|
|
100
|
+
print(f'Sweeping {w.kind} {w.name} ({w.id})')
|
|
101
|
+
try:
|
|
102
|
+
if w.kind == 'WarehouseSnapshot':
|
|
103
|
+
await snapshots.delete(http, ws, w.id)
|
|
104
|
+
else:
|
|
105
|
+
await warehouses.delete(http, ws, w.id)
|
|
106
|
+
except Exception as exc:
|
|
107
|
+
print(f' skip: {exc}')
|
|
108
|
+
|
|
109
|
+
anyio.run(main)
|
|
110
|
+
"
|
|
111
|
+
|
|
112
|
+
- name: Run integration tests
|
|
113
|
+
env:
|
|
114
|
+
FABRIC_TEST_WORKSPACE_ID: ${{ secrets.FABRIC_TEST_WORKSPACE_ID }}
|
|
115
|
+
FABRIC_AUTH: default
|
|
116
|
+
run: uv run pytest tests/integration -m integration -v
|
|
117
|
+
|
|
118
|
+
- name: Post-test sweep of pytest-* items
|
|
119
|
+
if: always()
|
|
120
|
+
env:
|
|
121
|
+
FABRIC_TEST_WORKSPACE_ID: ${{ secrets.FABRIC_TEST_WORKSPACE_ID }}
|
|
122
|
+
FABRIC_AUTH: default
|
|
123
|
+
run: |
|
|
124
|
+
uv run python -c "
|
|
125
|
+
import anyio, os
|
|
126
|
+
from fabric_dw.auth import get_credential
|
|
127
|
+
from fabric_dw.http_client import FabricHttpClient
|
|
128
|
+
from fabric_dw.services import warehouses, snapshots
|
|
129
|
+
|
|
130
|
+
async def main() -> None:
|
|
131
|
+
ws = os.environ['FABRIC_TEST_WORKSPACE_ID']
|
|
132
|
+
cred = get_credential()
|
|
133
|
+
failures = []
|
|
134
|
+
async with FabricHttpClient(cred) as http:
|
|
135
|
+
items = await warehouses.list_warehouses(http, ws)
|
|
136
|
+
for w in items:
|
|
137
|
+
if w.name.startswith('pytest-'):
|
|
138
|
+
print(f'Deleting {w.kind} {w.name} ({w.id})')
|
|
139
|
+
try:
|
|
140
|
+
if w.kind == 'WarehouseSnapshot':
|
|
141
|
+
await snapshots.delete(http, ws, w.id)
|
|
142
|
+
else:
|
|
143
|
+
await warehouses.delete(http, ws, w.id)
|
|
144
|
+
except Exception as exc:
|
|
145
|
+
failures.append((w.name, str(exc)))
|
|
146
|
+
if failures:
|
|
147
|
+
for name, err in failures:
|
|
148
|
+
print(f'FAIL: {name}: {err}')
|
|
149
|
+
raise SystemExit(1)
|
|
150
|
+
|
|
151
|
+
anyio.run(main)
|
|
152
|
+
"
|
|
153
|
+
|
|
154
|
+
- name: Suspend Fabric capacity
|
|
155
|
+
if: always()
|
|
156
|
+
run: |
|
|
157
|
+
set -e
|
|
158
|
+
CAPACITY_ID="${{ vars.FABRIC_TEST_CAPACITY_ID }}"
|
|
159
|
+
|
|
160
|
+
# Wait for the capacity to leave any transitional state, up to 5 min
|
|
161
|
+
for i in {1..30}; do
|
|
162
|
+
STATE=$(az resource show --ids "$CAPACITY_ID" --query 'properties.state' -o tsv 2>/dev/null || echo "Unknown")
|
|
163
|
+
echo "Pre-suspend state: $STATE"
|
|
164
|
+
case "$STATE" in
|
|
165
|
+
Paused) echo "Already Paused, skipping suspend"; exit 0 ;;
|
|
166
|
+
Active) break ;;
|
|
167
|
+
*) sleep 10 ;;
|
|
168
|
+
esac
|
|
169
|
+
done
|
|
170
|
+
|
|
171
|
+
# Issue suspend, but tolerate transient BadRequest
|
|
172
|
+
for attempt in {1..6}; do
|
|
173
|
+
if az resource invoke-action --ids "$CAPACITY_ID" --action suspend; then
|
|
174
|
+
break
|
|
175
|
+
fi
|
|
176
|
+
echo "Suspend attempt $attempt failed (likely transient), waiting 15s..."
|
|
177
|
+
sleep 15
|
|
178
|
+
done
|
|
179
|
+
|
|
180
|
+
# Poll until Paused, max 5 min
|
|
181
|
+
for i in {1..30}; do
|
|
182
|
+
STATE=$(az resource show --ids "$CAPACITY_ID" --query 'properties.state' -o tsv)
|
|
183
|
+
echo "Post-suspend state: $STATE"
|
|
184
|
+
[[ "$STATE" == "Paused" ]] && exit 0
|
|
185
|
+
sleep 10
|
|
186
|
+
done
|
|
187
|
+
|
|
188
|
+
echo "Capacity did not reach Paused state within 5 minutes" >&2
|
|
189
|
+
exit 1
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ["v[0-9]+.[0-9]+.[0-9]+"]
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
environment: pypi
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v6
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
|
|
21
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
22
|
+
with:
|
|
23
|
+
enable-cache: true
|
|
24
|
+
|
|
25
|
+
- uses: actions/setup-python@v6
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.13"
|
|
28
|
+
|
|
29
|
+
- run: uv sync --frozen --no-dev
|
|
30
|
+
|
|
31
|
+
- run: uv build
|
|
32
|
+
|
|
33
|
+
- name: Publish to PyPI (OIDC)
|
|
34
|
+
run: uv publish --trusted-publishing always
|
|
35
|
+
|
|
36
|
+
- name: Create GitHub Release (tag only)
|
|
37
|
+
if: github.ref_type == 'tag'
|
|
38
|
+
uses: softprops/action-gh-release@v3
|
|
39
|
+
with:
|
|
40
|
+
files: |
|
|
41
|
+
dist/*.whl
|
|
42
|
+
dist/*.tar.gz
|
|
43
|
+
generate_release_notes: true
|
|
44
|
+
name: ${{ github.ref_name }}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: Release Drafter
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
types: [opened, reopened, synchronize]
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
pull-requests: read
|
|
10
|
+
jobs:
|
|
11
|
+
update_release_draft:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: release-drafter/release-drafter@v7
|
|
15
|
+
env:
|
|
16
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: Security
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
permissions:
|
|
7
|
+
contents: read
|
|
8
|
+
pull-requests: read
|
|
9
|
+
jobs:
|
|
10
|
+
bandit:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v6
|
|
14
|
+
with:
|
|
15
|
+
fetch-depth: 0
|
|
16
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
17
|
+
with: { enable-cache: true }
|
|
18
|
+
- run: uvx --quiet bandit -r src/ -ll
|
|
19
|
+
dependency-review:
|
|
20
|
+
if: github.event_name == 'pull_request'
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v6
|
|
24
|
+
with:
|
|
25
|
+
fetch-depth: 0
|
|
26
|
+
- uses: actions/dependency-review-action@v5
|
|
27
|
+
with:
|
|
28
|
+
fail-on-severity: high
|
|
29
|
+
pip-audit:
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v6
|
|
33
|
+
with:
|
|
34
|
+
fetch-depth: 0
|
|
35
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
36
|
+
with: { enable-cache: true }
|
|
37
|
+
- run: uv sync --frozen
|
|
38
|
+
- run: uvx --quiet pip-audit
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
src/fabric_dw/_version.py
|
|
4
|
+
*.py[cod]
|
|
5
|
+
*$py.class
|
|
6
|
+
*.so
|
|
7
|
+
.Python
|
|
8
|
+
build/
|
|
9
|
+
dist/
|
|
10
|
+
*.egg-info/
|
|
11
|
+
.eggs/
|
|
12
|
+
.installed.cfg
|
|
13
|
+
*.egg
|
|
14
|
+
|
|
15
|
+
# uv / pip
|
|
16
|
+
.venv/
|
|
17
|
+
venv/
|
|
18
|
+
env/
|
|
19
|
+
|
|
20
|
+
# Test / coverage
|
|
21
|
+
.pytest_cache/
|
|
22
|
+
.coverage
|
|
23
|
+
.coverage.*
|
|
24
|
+
htmlcov/
|
|
25
|
+
coverage.xml
|
|
26
|
+
.tox/
|
|
27
|
+
.nox/
|
|
28
|
+
.cache/
|
|
29
|
+
|
|
30
|
+
# Type checkers
|
|
31
|
+
.mypy_cache/
|
|
32
|
+
.pyright/
|
|
33
|
+
.ruff_cache/
|
|
34
|
+
|
|
35
|
+
# IDE
|
|
36
|
+
.idea/
|
|
37
|
+
.vscode/
|
|
38
|
+
*.swp
|
|
39
|
+
|
|
40
|
+
# OS
|
|
41
|
+
.DS_Store
|
|
42
|
+
Thumbs.db
|
|
43
|
+
|
|
44
|
+
# Env / secrets
|
|
45
|
+
.env
|
|
46
|
+
.env.*
|
|
47
|
+
!.env.example
|
|
48
|
+
|
|
49
|
+
# Docs build output
|
|
50
|
+
docs_build/site/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|