amigo_sdk 0.100.0__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.
- amigo_sdk-0.100.0/.github/workflows/auto-release.yml +183 -0
- amigo_sdk-0.100.0/.github/workflows/release.yml +215 -0
- amigo_sdk-0.100.0/.github/workflows/test.yml +86 -0
- amigo_sdk-0.100.0/.gitignore +18 -0
- amigo_sdk-0.100.0/CONTRIBUTING.md +137 -0
- amigo_sdk-0.100.0/LICENSE +21 -0
- amigo_sdk-0.100.0/PKG-INFO +264 -0
- amigo_sdk-0.100.0/README.md +240 -0
- amigo_sdk-0.100.0/pyproject.toml +146 -0
- amigo_sdk-0.100.0/scripts/__init__.py +0 -0
- amigo_sdk-0.100.0/scripts/aliases.json +5 -0
- amigo_sdk-0.100.0/scripts/check.py +102 -0
- amigo_sdk-0.100.0/scripts/gen_models.py +122 -0
- amigo_sdk-0.100.0/specs/openapi-baseline.json +1 -0
- amigo_sdk-0.100.0/src/amigo_sdk/__init__.py +4 -0
- amigo_sdk-0.100.0/src/amigo_sdk/_retry_utils.py +69 -0
- amigo_sdk-0.100.0/src/amigo_sdk/auth.py +48 -0
- amigo_sdk-0.100.0/src/amigo_sdk/config.py +48 -0
- amigo_sdk-0.100.0/src/amigo_sdk/errors.py +163 -0
- amigo_sdk-0.100.0/src/amigo_sdk/generated/model.py +15903 -0
- amigo_sdk-0.100.0/src/amigo_sdk/http_client.py +380 -0
- amigo_sdk-0.100.0/src/amigo_sdk/models.py +1 -0
- amigo_sdk-0.100.0/src/amigo_sdk/resources/conversation.py +361 -0
- amigo_sdk-0.100.0/src/amigo_sdk/resources/organization.py +34 -0
- amigo_sdk-0.100.0/src/amigo_sdk/resources/service.py +44 -0
- amigo_sdk-0.100.0/src/amigo_sdk/resources/user.py +111 -0
- amigo_sdk-0.100.0/src/amigo_sdk/sdk_client.py +187 -0
- amigo_sdk-0.100.0/tests/__init__.py +1 -0
- amigo_sdk-0.100.0/tests/conftest.py +20 -0
- amigo_sdk-0.100.0/tests/integration/test_conversation_integration.py +349 -0
- amigo_sdk-0.100.0/tests/integration/test_organization_integration.py +179 -0
- amigo_sdk-0.100.0/tests/integration/test_user_integration.py +208 -0
- amigo_sdk-0.100.0/tests/resources/__init__.py +1 -0
- amigo_sdk-0.100.0/tests/resources/helpers.py +363 -0
- amigo_sdk-0.100.0/tests/resources/test_conversation.py +889 -0
- amigo_sdk-0.100.0/tests/resources/test_organization.py +95 -0
- amigo_sdk-0.100.0/tests/resources/test_service.py +90 -0
- amigo_sdk-0.100.0/tests/resources/test_user.py +271 -0
- amigo_sdk-0.100.0/tests/test_auth.py +172 -0
- amigo_sdk-0.100.0/tests/test_config.py +132 -0
- amigo_sdk-0.100.0/tests/test_errors.py +117 -0
- amigo_sdk-0.100.0/tests/test_http_client.py +743 -0
- amigo_sdk-0.100.0/tests/test_retry_utils.py +86 -0
- amigo_sdk-0.100.0/tests/test_sdk_client.py +128 -0
- amigo_sdk-0.100.0/uv.lock +1318 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
name: Auto Release (Backend API changes)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
repository_dispatch:
|
|
5
|
+
types: [openapi-updated]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
detect:
|
|
12
|
+
name: Detect OpenAPI change
|
|
13
|
+
runs-on: blacksmith-4vcpu-ubuntu-2404
|
|
14
|
+
outputs:
|
|
15
|
+
changed: ${{ steps.diff.outputs.changed }}
|
|
16
|
+
spec_url: ${{ steps.spec.outputs.spec_url }}
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Ensure jq
|
|
21
|
+
run: sudo apt-get update && sudo apt-get install -y jq
|
|
22
|
+
|
|
23
|
+
- name: Resolve spec URL
|
|
24
|
+
id: spec
|
|
25
|
+
run: |
|
|
26
|
+
SPEC_URL="${{ github.event.client_payload.spec_url }}"
|
|
27
|
+
if [ -z "$SPEC_URL" ]; then
|
|
28
|
+
SPEC_URL="https://api.amigo.ai/v1/openapi.json"
|
|
29
|
+
fi
|
|
30
|
+
echo "spec_url=$SPEC_URL" >> $GITHUB_OUTPUT
|
|
31
|
+
|
|
32
|
+
- name: Fetch current production spec
|
|
33
|
+
run: |
|
|
34
|
+
mkdir -p specs
|
|
35
|
+
curl -fSL -o specs/openapi-new.json "${{ steps.spec.outputs.spec_url }}"
|
|
36
|
+
|
|
37
|
+
- name: Check baseline presence
|
|
38
|
+
id: baseline
|
|
39
|
+
run: |
|
|
40
|
+
mkdir -p specs
|
|
41
|
+
if [ ! -f specs/openapi-baseline.json ]; then
|
|
42
|
+
echo "missing=true" >> $GITHUB_OUTPUT
|
|
43
|
+
else
|
|
44
|
+
echo "missing=false" >> $GITHUB_OUTPUT
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
- name: Prepare baseline
|
|
48
|
+
run: |
|
|
49
|
+
mkdir -p specs
|
|
50
|
+
if [ ! -f specs/openapi-baseline.json ]; then
|
|
51
|
+
cp specs/openapi-new.json specs/openapi-baseline.json
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
- name: Diagnostics (spec and baseline)
|
|
55
|
+
run: |
|
|
56
|
+
echo "Resolved spec_url: ${{ steps.spec.outputs.spec_url }}"
|
|
57
|
+
echo "Baseline missing: ${{ steps.baseline.outputs.missing }}"
|
|
58
|
+
echo "jq version: $(jq --version)"
|
|
59
|
+
jq -S '
|
|
60
|
+
def sort_deep:
|
|
61
|
+
if type == "array" then
|
|
62
|
+
map(sort_deep) | sort_by(tostring)
|
|
63
|
+
elif type == "object" then
|
|
64
|
+
with_entries(.value |= sort_deep)
|
|
65
|
+
else . end;
|
|
66
|
+
(del(.info) | del(.servers)) | sort_deep
|
|
67
|
+
' specs/openapi-baseline.json > baseline.norm.json || true
|
|
68
|
+
jq -S '
|
|
69
|
+
def sort_deep:
|
|
70
|
+
if type == "array" then
|
|
71
|
+
map(sort_deep) | sort_by(tostring)
|
|
72
|
+
elif type == "object" then
|
|
73
|
+
with_entries(.value |= sort_deep)
|
|
74
|
+
else . end;
|
|
75
|
+
(del(.info) | del(.servers)) | sort_deep
|
|
76
|
+
' specs/openapi-new.json > new.norm.json
|
|
77
|
+
if [ -f baseline.norm.json ]; then
|
|
78
|
+
echo "BASELINE_NORM_SHA=$(sha256sum baseline.norm.json | awk '{print $1}')"
|
|
79
|
+
else
|
|
80
|
+
echo "BASELINE_NORM_SHA=(none)"
|
|
81
|
+
fi
|
|
82
|
+
echo "NEW_NORM_SHA=$(sha256sum new.norm.json | awk '{print $1}')"
|
|
83
|
+
if [ -f baseline.norm.json ]; then
|
|
84
|
+
if diff -q baseline.norm.json new.norm.json >/dev/null 2>&1; then
|
|
85
|
+
echo "Quick diff: identical"
|
|
86
|
+
else
|
|
87
|
+
echo "Quick diff: differs"
|
|
88
|
+
fi
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
# Section-level hashes and key diffs
|
|
92
|
+
echo "\n== Section-level hashes =="
|
|
93
|
+
if [ -f specs/openapi-baseline.json ]; then
|
|
94
|
+
BASELINE_PATHS_SHA=$(jq -cS '(.paths // {})' specs/openapi-baseline.json | sha256sum | awk '{print $1}')
|
|
95
|
+
BASELINE_SCHEMAS_SHA=$(jq -cS '(.components.schemas // {})' specs/openapi-baseline.json | sha256sum | awk '{print $1}')
|
|
96
|
+
BASELINE_PATHS_COUNT=$(jq -r '(.paths // {}) | keys | length' specs/openapi-baseline.json)
|
|
97
|
+
BASELINE_SCHEMAS_COUNT=$(jq -r '(.components.schemas // {}) | keys | length' specs/openapi-baseline.json)
|
|
98
|
+
else
|
|
99
|
+
BASELINE_PATHS_SHA=(none)
|
|
100
|
+
BASELINE_SCHEMAS_SHA=(none)
|
|
101
|
+
BASELINE_PATHS_COUNT=0
|
|
102
|
+
BASELINE_SCHEMAS_COUNT=0
|
|
103
|
+
fi
|
|
104
|
+
NEW_PATHS_SHA=$(jq -cS '(.paths // {})' specs/openapi-new.json | sha256sum | awk '{print $1}')
|
|
105
|
+
NEW_SCHEMAS_SHA=$(jq -cS '(.components.schemas // {})' specs/openapi-new.json | sha256sum | awk '{print $1}')
|
|
106
|
+
NEW_PATHS_COUNT=$(jq -r '(.paths // {}) | keys | length' specs/openapi-new.json)
|
|
107
|
+
NEW_SCHEMAS_COUNT=$(jq -r '(.components.schemas // {}) | keys | length' specs/openapi-new.json)
|
|
108
|
+
echo "BASELINE paths: count=$BASELINE_PATHS_COUNT sha=$BASELINE_PATHS_SHA"
|
|
109
|
+
echo "NEW paths: count=$NEW_PATHS_COUNT sha=$NEW_PATHS_SHA"
|
|
110
|
+
echo "BASELINE schemas: count=$BASELINE_SCHEMAS_COUNT sha=$BASELINE_SCHEMAS_SHA"
|
|
111
|
+
echo "NEW schemas: count=$NEW_SCHEMAS_COUNT sha=$NEW_SCHEMAS_SHA"
|
|
112
|
+
|
|
113
|
+
echo "\n== Key diffs (paths) =="
|
|
114
|
+
if [ -f specs/openapi-baseline.json ]; then
|
|
115
|
+
jq -r '(.paths // {}) | keys | sort[]' specs/openapi-baseline.json > baseline.paths.txt || true
|
|
116
|
+
else
|
|
117
|
+
: > baseline.paths.txt
|
|
118
|
+
fi
|
|
119
|
+
jq -r '(.paths // {}) | keys | sort[]' specs/openapi-new.json > new.paths.txt
|
|
120
|
+
echo "Removed paths (in baseline, not in new):"; comm -23 baseline.paths.txt new.paths.txt || true
|
|
121
|
+
echo "Added paths (in new, not in baseline):"; comm -13 baseline.paths.txt new.paths.txt || true
|
|
122
|
+
|
|
123
|
+
echo "\n== Key diffs (components.schemas) =="
|
|
124
|
+
if [ -f specs/openapi-baseline.json ]; then
|
|
125
|
+
jq -r '(.components.schemas // {}) | keys | sort[]' specs/openapi-baseline.json > baseline.schemas.txt || true
|
|
126
|
+
else
|
|
127
|
+
: > baseline.schemas.txt
|
|
128
|
+
fi
|
|
129
|
+
jq -r '(.components.schemas // {}) | keys | sort[]' specs/openapi-new.json > new.schemas.txt
|
|
130
|
+
echo "Removed schemas (in baseline, not in new):"; comm -23 baseline.schemas.txt new.schemas.txt || true
|
|
131
|
+
echo "Added schemas (in new, not in baseline):"; comm -13 baseline.schemas.txt new.schemas.txt || true
|
|
132
|
+
|
|
133
|
+
- name: Compare normalized specs
|
|
134
|
+
id: diff
|
|
135
|
+
run: |
|
|
136
|
+
if [ "${{ steps.baseline.outputs.missing }}" = "true" ]; then
|
|
137
|
+
echo "changed=true" >> $GITHUB_OUTPUT
|
|
138
|
+
exit 0
|
|
139
|
+
fi
|
|
140
|
+
jq -S '
|
|
141
|
+
def sort_deep:
|
|
142
|
+
if type == "array" then
|
|
143
|
+
map(sort_deep) | sort_by(tostring)
|
|
144
|
+
elif type == "object" then
|
|
145
|
+
with_entries(.value |= sort_deep)
|
|
146
|
+
else . end;
|
|
147
|
+
(del(.info) | del(.servers)) | sort_deep
|
|
148
|
+
' specs/openapi-baseline.json > baseline.norm.json
|
|
149
|
+
jq -S '
|
|
150
|
+
def sort_deep:
|
|
151
|
+
if type == "array" then
|
|
152
|
+
map(sort_deep) | sort_by(tostring)
|
|
153
|
+
elif type == "object" then
|
|
154
|
+
with_entries(.value |= sort_deep)
|
|
155
|
+
else . end;
|
|
156
|
+
(del(.info) | del(.servers)) | sort_deep
|
|
157
|
+
' specs/openapi-new.json > new.norm.json
|
|
158
|
+
if diff -q baseline.norm.json new.norm.json >/dev/null 2>&1; then
|
|
159
|
+
echo "changed=false" >> $GITHUB_OUTPUT
|
|
160
|
+
else
|
|
161
|
+
echo "changed=true" >> $GITHUB_OUTPUT
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
- name: Upload normalized specs
|
|
165
|
+
if: always()
|
|
166
|
+
uses: actions/upload-artifact@v4
|
|
167
|
+
with:
|
|
168
|
+
name: normalized-specs
|
|
169
|
+
path: |
|
|
170
|
+
baseline.norm.json
|
|
171
|
+
new.norm.json
|
|
172
|
+
retention-days: 7
|
|
173
|
+
|
|
174
|
+
release-on-dispatch:
|
|
175
|
+
name: Trigger SDK release (minor)
|
|
176
|
+
needs: detect
|
|
177
|
+
if: needs.detect.outputs.changed == 'true'
|
|
178
|
+
uses: ./.github/workflows/release.yml
|
|
179
|
+
with:
|
|
180
|
+
version_type: minor
|
|
181
|
+
dry_run: ${{ github.event.client_payload.dry_run == true }}
|
|
182
|
+
spec_url: ${{ needs.detect.outputs.spec_url }}
|
|
183
|
+
secrets: inherit
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
version_type:
|
|
7
|
+
description: "Version increment type"
|
|
8
|
+
required: true
|
|
9
|
+
default: "patch"
|
|
10
|
+
type: choice
|
|
11
|
+
options:
|
|
12
|
+
- patch
|
|
13
|
+
- minor
|
|
14
|
+
- major
|
|
15
|
+
dry_run:
|
|
16
|
+
description: "Dry run (skip upload)"
|
|
17
|
+
required: false
|
|
18
|
+
default: false
|
|
19
|
+
type: boolean
|
|
20
|
+
workflow_call:
|
|
21
|
+
inputs:
|
|
22
|
+
version_type:
|
|
23
|
+
description: "Version increment type (patch|minor|major)"
|
|
24
|
+
required: true
|
|
25
|
+
type: string
|
|
26
|
+
default: "patch"
|
|
27
|
+
dry_run:
|
|
28
|
+
description: "Dry run (skip upload)"
|
|
29
|
+
required: false
|
|
30
|
+
type: boolean
|
|
31
|
+
default: false
|
|
32
|
+
spec_url:
|
|
33
|
+
description: "OpenAPI spec URL to use for generation"
|
|
34
|
+
required: false
|
|
35
|
+
type: string
|
|
36
|
+
default: ""
|
|
37
|
+
|
|
38
|
+
permissions:
|
|
39
|
+
contents: write
|
|
40
|
+
|
|
41
|
+
jobs:
|
|
42
|
+
# Reuse the existing test workflow
|
|
43
|
+
test:
|
|
44
|
+
uses: ./.github/workflows/test.yml
|
|
45
|
+
with:
|
|
46
|
+
skip_codecov: true
|
|
47
|
+
secrets: inherit
|
|
48
|
+
|
|
49
|
+
release:
|
|
50
|
+
runs-on: blacksmith-4vcpu-ubuntu-2404
|
|
51
|
+
needs: test
|
|
52
|
+
|
|
53
|
+
steps:
|
|
54
|
+
- name: Create GitHub App token (release-bot)
|
|
55
|
+
id: app-token
|
|
56
|
+
uses: actions/create-github-app-token@v2
|
|
57
|
+
with:
|
|
58
|
+
app-id: ${{ secrets.RELEASE_BOT_APP_ID }}
|
|
59
|
+
private-key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }}
|
|
60
|
+
|
|
61
|
+
- name: Checkout code
|
|
62
|
+
uses: actions/checkout@v4
|
|
63
|
+
with:
|
|
64
|
+
# Fetch full history for version tagging
|
|
65
|
+
fetch-depth: 0
|
|
66
|
+
# Use a token that can push tags and create releases
|
|
67
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
68
|
+
|
|
69
|
+
- name: Ensure jq
|
|
70
|
+
run: sudo apt-get update && sudo apt-get install -y jq
|
|
71
|
+
|
|
72
|
+
- name: Fetch OpenAPI spec
|
|
73
|
+
id: fetch_spec
|
|
74
|
+
run: |
|
|
75
|
+
SPEC_URL="${{ inputs.spec_url }}"
|
|
76
|
+
if [ -z "$SPEC_URL" ]; then
|
|
77
|
+
SPEC_URL="https://api.amigo.ai/v1/openapi.json"
|
|
78
|
+
fi
|
|
79
|
+
mkdir -p specs
|
|
80
|
+
curl -fSL -o specs/openapi-new.json "$SPEC_URL"
|
|
81
|
+
cp specs/openapi-new.json specs/openapi-baseline.json
|
|
82
|
+
|
|
83
|
+
# Generator always fetches remote; no local spec path needed
|
|
84
|
+
|
|
85
|
+
- name: Setup Python
|
|
86
|
+
uses: actions/setup-python@v5
|
|
87
|
+
with:
|
|
88
|
+
python-version: "3.13"
|
|
89
|
+
cache: "pip"
|
|
90
|
+
|
|
91
|
+
- name: Install dependencies
|
|
92
|
+
run: |
|
|
93
|
+
python -m pip install --upgrade pip
|
|
94
|
+
pip install -e ".[dev]"
|
|
95
|
+
pip install hatch
|
|
96
|
+
|
|
97
|
+
- name: Configure git
|
|
98
|
+
run: |
|
|
99
|
+
git config --global user.name "github-actions[bot]"
|
|
100
|
+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
101
|
+
|
|
102
|
+
- name: Get current version
|
|
103
|
+
id: current_version
|
|
104
|
+
run: |
|
|
105
|
+
CURRENT_VERSION=$(hatch version)
|
|
106
|
+
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
|
|
107
|
+
echo "Current version: $CURRENT_VERSION"
|
|
108
|
+
|
|
109
|
+
- name: Generate models
|
|
110
|
+
run: |
|
|
111
|
+
echo "🔄 Generating models from API spec..."
|
|
112
|
+
python -m scripts.gen_models
|
|
113
|
+
echo "✅ Models generated successfully"
|
|
114
|
+
|
|
115
|
+
- name: Increment version
|
|
116
|
+
id: new_version
|
|
117
|
+
run: |
|
|
118
|
+
echo "🔄 Incrementing ${{ inputs.version_type }} version..."
|
|
119
|
+
hatch version ${{ inputs.version_type }}
|
|
120
|
+
NEW_VERSION=$(hatch version)
|
|
121
|
+
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
|
122
|
+
echo "✅ Version updated: ${{ steps.current_version.outputs.version }} → $NEW_VERSION"
|
|
123
|
+
|
|
124
|
+
- name: Build package
|
|
125
|
+
run: |
|
|
126
|
+
echo "🔄 Building package..."
|
|
127
|
+
# Clean any existing build artifacts
|
|
128
|
+
rm -rf dist/
|
|
129
|
+
hatch build
|
|
130
|
+
echo "✅ Package built successfully"
|
|
131
|
+
ls -la dist/
|
|
132
|
+
|
|
133
|
+
- name: Upload to PyPI
|
|
134
|
+
if: ${{ !inputs.dry_run }}
|
|
135
|
+
env:
|
|
136
|
+
HATCH_INDEX_USER: __token__
|
|
137
|
+
HATCH_INDEX_AUTH: ${{ secrets.PYPI_API_TOKEN }}
|
|
138
|
+
run: |
|
|
139
|
+
echo "🔄 Uploading to PyPI..."
|
|
140
|
+
hatch publish --repo https://upload.pypi.org/legacy/
|
|
141
|
+
echo "✅ Package uploaded to PyPI"
|
|
142
|
+
|
|
143
|
+
- name: Commit version bump
|
|
144
|
+
if: ${{ !inputs.dry_run }}
|
|
145
|
+
run: |
|
|
146
|
+
git add -A
|
|
147
|
+
git commit -m "Bump version to ${{ steps.new_version.outputs.version }}"
|
|
148
|
+
git push origin HEAD
|
|
149
|
+
|
|
150
|
+
- name: Create and push tag
|
|
151
|
+
if: ${{ !inputs.dry_run }}
|
|
152
|
+
run: |
|
|
153
|
+
git tag -a "v${{ steps.new_version.outputs.version }}" -m "Release v${{ steps.new_version.outputs.version }}"
|
|
154
|
+
git push origin "v${{ steps.new_version.outputs.version }}"
|
|
155
|
+
|
|
156
|
+
- name: Create GitHub Release
|
|
157
|
+
if: ${{ !inputs.dry_run }}
|
|
158
|
+
uses: actions/create-release@v1
|
|
159
|
+
env:
|
|
160
|
+
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
|
161
|
+
with:
|
|
162
|
+
tag_name: "v${{ steps.new_version.outputs.version }}"
|
|
163
|
+
release_name: "Release v${{ steps.new_version.outputs.version }}"
|
|
164
|
+
body: |
|
|
165
|
+
## Changes in v${{ steps.new_version.outputs.version }}
|
|
166
|
+
|
|
167
|
+
This release was automatically created by the GitHub Actions release workflow.
|
|
168
|
+
|
|
169
|
+
### Package Information
|
|
170
|
+
- **Version**: ${{ steps.new_version.outputs.version }}
|
|
171
|
+
- **Target**: PyPI
|
|
172
|
+
- **Type**: ${{ inputs.version_type }} release
|
|
173
|
+
|
|
174
|
+
### Links
|
|
175
|
+
${{ format('- [View on PyPI](https://pypi.org/project/amigo_sdk/{0}/)', steps.new_version.outputs.version) }}
|
|
176
|
+
|
|
177
|
+
### Installation
|
|
178
|
+
```bash
|
|
179
|
+
pip install amigo_sdk
|
|
180
|
+
```
|
|
181
|
+
draft: false
|
|
182
|
+
|
|
183
|
+
- name: Upload build artifacts
|
|
184
|
+
if: always()
|
|
185
|
+
uses: actions/upload-artifact@v4
|
|
186
|
+
with:
|
|
187
|
+
name: dist-${{ steps.new_version.outputs.version }}
|
|
188
|
+
path: dist/
|
|
189
|
+
retention-days: 30
|
|
190
|
+
|
|
191
|
+
- name: Release Summary
|
|
192
|
+
run: |
|
|
193
|
+
echo "## 🎉 Release Summary" >> $GITHUB_STEP_SUMMARY
|
|
194
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
195
|
+
echo "- **Version**: ${{ steps.new_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
|
196
|
+
echo "- **Previous**: ${{ steps.current_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
|
197
|
+
echo "- **Type**: ${{ inputs.version_type }} release" >> $GITHUB_STEP_SUMMARY
|
|
198
|
+
echo "- **Target**: PyPI" >> $GITHUB_STEP_SUMMARY
|
|
199
|
+
echo "- **Dry Run**: ${{ inputs.dry_run }}" >> $GITHUB_STEP_SUMMARY
|
|
200
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
201
|
+
|
|
202
|
+
if [ "${{ inputs.dry_run }}" = "false" ]; then
|
|
203
|
+
echo "### 📦 Package Links" >> $GITHUB_STEP_SUMMARY
|
|
204
|
+
echo "- [PyPI Package](https://pypi.org/project/amigo_sdk/${{ steps.new_version.outputs.version }}/)" >> $GITHUB_STEP_SUMMARY
|
|
205
|
+
echo "- [GitHub Release](https://github.com/${{ github.repository }}/releases/tag/v${{ steps.new_version.outputs.version }})" >> $GITHUB_STEP_SUMMARY
|
|
206
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
207
|
+
echo "### 🚀 Next Steps" >> $GITHUB_STEP_SUMMARY
|
|
208
|
+
echo "The package has been successfully released and is available for installation!" >> $GITHUB_STEP_SUMMARY
|
|
209
|
+
else
|
|
210
|
+
echo "### 🚫 Dry Run Results" >> $GITHUB_STEP_SUMMARY
|
|
211
|
+
echo "- All steps completed successfully" >> $GITHUB_STEP_SUMMARY
|
|
212
|
+
echo "- Version would be bumped to: ${{ steps.new_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
|
213
|
+
echo "- Package built and ready for upload" >> $GITHUB_STEP_SUMMARY
|
|
214
|
+
echo "- No commits, tags, or uploads were made" >> $GITHUB_STEP_SUMMARY
|
|
215
|
+
fi
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
permissions:
|
|
4
|
+
contents: read
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
pull_request:
|
|
10
|
+
branches: [main]
|
|
11
|
+
workflow_call:
|
|
12
|
+
inputs:
|
|
13
|
+
skip_codecov:
|
|
14
|
+
description: "Skip Codecov upload"
|
|
15
|
+
required: false
|
|
16
|
+
default: true
|
|
17
|
+
type: boolean
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
test:
|
|
21
|
+
runs-on: blacksmith-4vcpu-ubuntu-2404
|
|
22
|
+
|
|
23
|
+
steps:
|
|
24
|
+
- name: Checkout code
|
|
25
|
+
uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- name: Setup Python
|
|
28
|
+
uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: "3.13"
|
|
31
|
+
cache: "pip"
|
|
32
|
+
|
|
33
|
+
- name: Install dependencies
|
|
34
|
+
run: |
|
|
35
|
+
python -m pip install --upgrade pip
|
|
36
|
+
pip install -e ".[dev]"
|
|
37
|
+
|
|
38
|
+
- name: Run linter
|
|
39
|
+
run: ruff check .
|
|
40
|
+
|
|
41
|
+
- name: Run formatter check
|
|
42
|
+
run: ruff format --check .
|
|
43
|
+
|
|
44
|
+
- name: Run tests with coverage
|
|
45
|
+
run: pytest --cov=src --cov-report=xml --cov-report=term
|
|
46
|
+
|
|
47
|
+
- name: Upload coverage reports to Codecov
|
|
48
|
+
if: inputs.skip_codecov != true
|
|
49
|
+
uses: codecov/codecov-action@v5
|
|
50
|
+
with:
|
|
51
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
52
|
+
files: ./coverage.xml
|
|
53
|
+
fail_ci_if_error: true
|
|
54
|
+
|
|
55
|
+
integration-test:
|
|
56
|
+
runs-on: blacksmith-4vcpu-ubuntu-2404
|
|
57
|
+
needs: test # Only run if unit tests pass
|
|
58
|
+
# Skip for PRs from forks (they don't have access to secrets)
|
|
59
|
+
# This runs on: pushes to main, PRs from same repo
|
|
60
|
+
if: >
|
|
61
|
+
github.event_name == 'push' ||
|
|
62
|
+
github.event.pull_request.head.repo.full_name == github.repository
|
|
63
|
+
|
|
64
|
+
steps:
|
|
65
|
+
- name: Checkout code
|
|
66
|
+
uses: actions/checkout@v4
|
|
67
|
+
|
|
68
|
+
- name: Setup Python
|
|
69
|
+
uses: actions/setup-python@v5
|
|
70
|
+
with:
|
|
71
|
+
python-version: "3.13"
|
|
72
|
+
cache: "pip"
|
|
73
|
+
|
|
74
|
+
- name: Install dependencies
|
|
75
|
+
run: |
|
|
76
|
+
python -m pip install --upgrade pip
|
|
77
|
+
pip install -e ".[dev]"
|
|
78
|
+
|
|
79
|
+
- name: Run integration tests
|
|
80
|
+
env:
|
|
81
|
+
AMIGO_API_KEY: ${{ secrets.AMIGO_API_KEY }}
|
|
82
|
+
AMIGO_API_KEY_ID: ${{ secrets.AMIGO_API_KEY_ID }}
|
|
83
|
+
AMIGO_ORGANIZATION_ID: ${{ secrets.AMIGO_ORGANIZATION_ID }}
|
|
84
|
+
AMIGO_USER_ID: ${{ secrets.AMIGO_USER_ID }}
|
|
85
|
+
AMIGO_BASE_URL: ${{ secrets.AMIGO_BASE_URL }}
|
|
86
|
+
run: pytest -m integration -v -s
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.venv
|
|
2
|
+
.env
|
|
3
|
+
.pytest_cache
|
|
4
|
+
.ruff_cache
|
|
5
|
+
__pycache__
|
|
6
|
+
|
|
7
|
+
.vscode
|
|
8
|
+
!.vscode/settings.json
|
|
9
|
+
|
|
10
|
+
dist/
|
|
11
|
+
|
|
12
|
+
# Coverage files
|
|
13
|
+
htmlcov/
|
|
14
|
+
coverage.xml
|
|
15
|
+
.coverage
|
|
16
|
+
|
|
17
|
+
# CI/Temp OpenAPI spec (fetched during workflows)
|
|
18
|
+
specs/openapi-new.json
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Contributing Guide
|
|
2
|
+
|
|
3
|
+
## Quick Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Create and activate virtual environment
|
|
7
|
+
python -m venv .venv
|
|
8
|
+
source .venv/bin/activate
|
|
9
|
+
|
|
10
|
+
# Install the project in development mode
|
|
11
|
+
pip install -e ".[dev]"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Development Commands
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
check # Run all checks (format, lint, tests)
|
|
18
|
+
check --fix # Auto-fix issues and run all checks
|
|
19
|
+
check --fast # Format + lint only (skip tests)
|
|
20
|
+
|
|
21
|
+
gen-models # Generate models from API spec
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Workflow
|
|
25
|
+
|
|
26
|
+
1. **Before committing:** Run `check --fix` to auto-fix issues
|
|
27
|
+
2. **During development:** Use `check --fast` for quick validation
|
|
28
|
+
3. **Update models:** Run `gen-models` when API changes
|
|
29
|
+
|
|
30
|
+
## Release Process
|
|
31
|
+
|
|
32
|
+
### GitHub Actions Release
|
|
33
|
+
|
|
34
|
+
1. Go to the **Actions** tab in GitHub
|
|
35
|
+
2. Select the **Release** workflow
|
|
36
|
+
3. Click **Run workflow** and choose:
|
|
37
|
+
- **Version type**: `patch` (default), `minor`, or `major`
|
|
38
|
+
- **Dry run**: Test the release process without publishing
|
|
39
|
+
|
|
40
|
+
The workflow will automatically:
|
|
41
|
+
|
|
42
|
+
- ✅ Run all tests, linting, and formatting checks (reuses existing test workflow)
|
|
43
|
+
- 🔄 Generate fresh models from the API spec
|
|
44
|
+
- 📈 Increment the version using Hatch
|
|
45
|
+
- 📦 Build the package
|
|
46
|
+
- 🚀 Upload to PyPI
|
|
47
|
+
- 🏷️ Create a Git tag and GitHub release
|
|
48
|
+
- 📋 Provide detailed summary with links
|
|
49
|
+
|
|
50
|
+
### Required Repository Secrets
|
|
51
|
+
|
|
52
|
+
Configure these secrets in your GitHub repository settings:
|
|
53
|
+
|
|
54
|
+
- `PYPI_API_TOKEN`: Token for https://pypi.org/ (production releases)
|
|
55
|
+
- `CODECOV_TOKEN`: Token for Codecov uploads (used by CI test workflow)
|
|
56
|
+
- `RELEASE_BOT_APP_ID`: GitHub App ID used to create tags/releases
|
|
57
|
+
- `RELEASE_BOT_PRIVATE_KEY`: GitHub App private key (PEM) for the release bot
|
|
58
|
+
|
|
59
|
+
### Getting API Tokens
|
|
60
|
+
|
|
61
|
+
1. **PyPI**: Go to https://pypi.org/manage/account/token/
|
|
62
|
+
2. Create a token with upload permissions
|
|
63
|
+
3. Add the token to your repository secrets
|
|
64
|
+
|
|
65
|
+
## Auto-Release (OpenAPI changes)
|
|
66
|
+
|
|
67
|
+
When the backend OpenAPI spec changes, the SDK can auto-release a new version.
|
|
68
|
+
|
|
69
|
+
- Trigger: a `repository_dispatch` event with type `openapi-updated`
|
|
70
|
+
- Detect job: fetches the provided `spec_url` (or defaults to `https://api.amigo.ai/v1/openapi.json`), normalizes and compares against `specs/openapi-baseline.json`
|
|
71
|
+
- If changed: invokes the release workflow with `version_type=minor` and passes `spec_url` (supports `dry_run`)
|
|
72
|
+
- Release workflow: regenerates models, bumps version, builds, publishes to PyPI, pushes tag, and creates a GitHub release
|
|
73
|
+
|
|
74
|
+
Manual trigger example (repository_dispatch):
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
curl -X POST \
|
|
78
|
+
-H "Accept: application/vnd.github+json" \
|
|
79
|
+
-H "Authorization: Bearer $GH_TOKEN" \
|
|
80
|
+
https://api.github.com/repos/<OWNER>/<REPO>/dispatches \
|
|
81
|
+
-d '{
|
|
82
|
+
"event_type": "openapi-updated",
|
|
83
|
+
"client_payload": {
|
|
84
|
+
"spec_url": "https://api.amigo.ai/v1/openapi.json",
|
|
85
|
+
"dry_run": true
|
|
86
|
+
}
|
|
87
|
+
}'
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Notes:
|
|
91
|
+
|
|
92
|
+
- The baseline file `specs/openapi-baseline.json` is created on first run and updated as part of the release workflow.
|
|
93
|
+
- Ensure the secrets listed above are configured so the workflow can push tags, create releases, and publish to PyPI.
|
|
94
|
+
|
|
95
|
+
### Manual trigger using GitHub CLI (gh)
|
|
96
|
+
|
|
97
|
+
Requires `gh` authenticated with a token that can dispatch events on the SDK repos.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Values that mirror the workflow
|
|
101
|
+
OWNER="amigo-ai"
|
|
102
|
+
BACKEND_REPO="$OWNER/backend"
|
|
103
|
+
COMMIT_SHA="$(git rev-parse HEAD 2>/dev/null || echo manual-test)"
|
|
104
|
+
RUN_ID="$(date +%s)" # stand-in for GitHub Actions run_id
|
|
105
|
+
|
|
106
|
+
for repo in amigo-typescript-sdk amigo-python-sdk; do
|
|
107
|
+
echo "Notifying $repo..."
|
|
108
|
+
gh api "repos/$OWNER/$repo/dispatches" \
|
|
109
|
+
--method POST \
|
|
110
|
+
--header 'Accept: application/vnd.github+json' \
|
|
111
|
+
--input - <<EOF || echo "⚠️ Failed to notify $repo"
|
|
112
|
+
{
|
|
113
|
+
"event_type": "openapi-updated",
|
|
114
|
+
"client_payload": {
|
|
115
|
+
"spec_url": "https://api.amigo.ai/v1/openapi.json",
|
|
116
|
+
"commit_sha": "$COMMIT_SHA",
|
|
117
|
+
"backend_repo": "$BACKEND_REPO",
|
|
118
|
+
"backend_run_id": "$RUN_ID"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
EOF
|
|
122
|
+
done
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## IDE Setup (VS Code)
|
|
126
|
+
|
|
127
|
+
Install extensions:
|
|
128
|
+
|
|
129
|
+
- [Ruff](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff)
|
|
130
|
+
- [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python)
|
|
131
|
+
|
|
132
|
+
## Troubleshooting
|
|
133
|
+
|
|
134
|
+
- **Command not found:** Activate virtual environment with `source .venv/bin/activate`
|
|
135
|
+
- **Linting failures:** Run `check --fix` to auto-fix issues
|
|
136
|
+
- **Model import errors:** Run `gen-models` to regenerate models
|
|
137
|
+
- **Release failures:** Check API tokens are configured in repository secrets and try a dry run first
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Amigo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|