gofetch-client 0.1.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.
- gofetch_client-0.1.0/LICENSE +21 -0
- gofetch_client-0.1.0/PKG-INFO +456 -0
- gofetch_client-0.1.0/README.md +408 -0
- gofetch_client-0.1.0/pyproject.toml +146 -0
- gofetch_client-0.1.0/setup.cfg +4 -0
- gofetch_client-0.1.0/src/gofetch/__init__.py +94 -0
- gofetch_client-0.1.0/src/gofetch/actor.py +312 -0
- gofetch_client-0.1.0/src/gofetch/client.py +305 -0
- gofetch_client-0.1.0/src/gofetch/constants.py +93 -0
- gofetch_client-0.1.0/src/gofetch/dataset.py +219 -0
- gofetch_client-0.1.0/src/gofetch/exceptions.py +182 -0
- gofetch_client-0.1.0/src/gofetch/http.py +411 -0
- gofetch_client-0.1.0/src/gofetch/log.py +72 -0
- gofetch_client-0.1.0/src/gofetch/py.typed +0 -0
- gofetch_client-0.1.0/src/gofetch/run.py +229 -0
- gofetch_client-0.1.0/src/gofetch/scrapers/__init__.py +7 -0
- gofetch_client-0.1.0/src/gofetch/scrapers/base.py +48 -0
- gofetch_client-0.1.0/src/gofetch/types.py +181 -0
- gofetch_client-0.1.0/src/gofetch/webhook.py +268 -0
- gofetch_client-0.1.0/src/gofetch/webhook_client.py +277 -0
- gofetch_client-0.1.0/src/gofetch_client.egg-info/PKG-INFO +456 -0
- gofetch_client-0.1.0/src/gofetch_client.egg-info/SOURCES.txt +32 -0
- gofetch_client-0.1.0/src/gofetch_client.egg-info/dependency_links.txt +1 -0
- gofetch_client-0.1.0/src/gofetch_client.egg-info/requires.txt +25 -0
- gofetch_client-0.1.0/src/gofetch_client.egg-info/top_level.txt +1 -0
- gofetch_client-0.1.0/tests/test_actor.py +395 -0
- gofetch_client-0.1.0/tests/test_client.py +73 -0
- gofetch_client-0.1.0/tests/test_http.py +187 -0
- gofetch_client-0.1.0/tests/test_log.py +107 -0
- gofetch_client-0.1.0/tests/test_parity.py +87 -0
- gofetch_client-0.1.0/tests/test_run.py +191 -0
- gofetch_client-0.1.0/tests/test_types.py +76 -0
- gofetch_client-0.1.0/tests/test_webhook.py +104 -0
- gofetch_client-0.1.0/tests/test_webhook_client.py +275 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yevhenii
|
|
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.
|
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gofetch-client
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for GoFetch.io social media scraping API - Drop-in replacement for apify-client
|
|
5
|
+
Author-email: Yevhenii Molodtsov <yevhenii@go-fetch.io>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://go-fetch.io
|
|
8
|
+
Project-URL: Documentation, https://github.com/YevheniiM/gofetch-client#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/YevheniiM/gofetch-client
|
|
10
|
+
Project-URL: Issues, https://github.com/YevheniiM/gofetch-client/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/YevheniiM/gofetch-client/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: scraping,instagram,tiktok,youtube,social-media,api-client,gofetch,web-scraping
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: httpx>=0.25.0
|
|
29
|
+
Requires-Dist: pydantic>=2.0.0
|
|
30
|
+
Requires-Dist: python-dateutil>=2.8.0
|
|
31
|
+
Requires-Dist: eval_type_backport>=0.1.3; python_version < "3.10"
|
|
32
|
+
Provides-Extra: async
|
|
33
|
+
Requires-Dist: httpx[http2]>=0.25.0; extra == "async"
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pytest-httpx>=0.21.0; extra == "dev"
|
|
38
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
39
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
40
|
+
Requires-Dist: mypy>=1.5.0; extra == "dev"
|
|
41
|
+
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
|
|
42
|
+
Provides-Extra: docs
|
|
43
|
+
Requires-Dist: mkdocs>=1.5.0; extra == "docs"
|
|
44
|
+
Requires-Dist: mkdocs-material>=9.0.0; extra == "docs"
|
|
45
|
+
Provides-Extra: all
|
|
46
|
+
Requires-Dist: gofetch-client[async,dev,docs]; extra == "all"
|
|
47
|
+
Dynamic: license-file
|
|
48
|
+
|
|
49
|
+
# GoFetch Client
|
|
50
|
+
|
|
51
|
+
[](https://badge.fury.io/py/gofetch-client)
|
|
52
|
+
[](https://pypi.org/project/gofetch-client/)
|
|
53
|
+
[](https://opensource.org/licenses/MIT)
|
|
54
|
+
|
|
55
|
+
**Python client for [GoFetch.io](https://go-fetch.io) social media scraping API.**
|
|
56
|
+
|
|
57
|
+
A drop-in replacement for `apify-client` that uses the GoFetch.io infrastructure.
|
|
58
|
+
|
|
59
|
+
## Features
|
|
60
|
+
|
|
61
|
+
- **Drop-in replacement** for `apify-client` - minimal code changes required
|
|
62
|
+
- **Multiple platforms**: Instagram, TikTok, YouTube
|
|
63
|
+
- **Sync and async** execution modes
|
|
64
|
+
- **Webhook support** for asynchronous job notifications
|
|
65
|
+
- **Full type hints** for better IDE support
|
|
66
|
+
- **Automatic retries** with exponential backoff
|
|
67
|
+
|
|
68
|
+
## Installation
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install gofetch-client
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
For async support with HTTP/2:
|
|
75
|
+
```bash
|
|
76
|
+
pip install gofetch-client[async]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Quick Start
|
|
80
|
+
|
|
81
|
+
### Basic Usage
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from gofetch import GoFetchClient
|
|
85
|
+
|
|
86
|
+
# Initialize client
|
|
87
|
+
client = GoFetchClient(api_key="sk_scr_your_api_key")
|
|
88
|
+
|
|
89
|
+
# Create an actor for Instagram scraping
|
|
90
|
+
actor = client.actor("instagram")
|
|
91
|
+
|
|
92
|
+
# Run synchronously (blocks until complete)
|
|
93
|
+
run = actor.call(run_input={
|
|
94
|
+
"directUrls": ["https://www.instagram.com/nike/"],
|
|
95
|
+
"maxPosts": 10,
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
# Fetch results
|
|
99
|
+
dataset = client.dataset(run["defaultDatasetId"])
|
|
100
|
+
for item in dataset.iterate_items():
|
|
101
|
+
print(item["id"], item.get("caption", "")[:50])
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Async Execution with Webhooks
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from gofetch import GoFetchClient
|
|
108
|
+
|
|
109
|
+
client = GoFetchClient(api_key="sk_scr_your_api_key")
|
|
110
|
+
actor = client.actor("instagram")
|
|
111
|
+
|
|
112
|
+
# Start async job with webhook notification
|
|
113
|
+
run = actor.start(
|
|
114
|
+
run_input={
|
|
115
|
+
"directUrls": ["https://www.instagram.com/nike/"],
|
|
116
|
+
"maxPosts": 100,
|
|
117
|
+
},
|
|
118
|
+
webhooks=[{
|
|
119
|
+
"request_url": "https://your-app.com/webhook",
|
|
120
|
+
"event_types": ["ACTOR.RUN.SUCCEEDED", "ACTOR.RUN.FAILED"]
|
|
121
|
+
}]
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
print(f"Job started: {run['id']}")
|
|
125
|
+
# Your webhook will be called when the job completes
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Migration from Apify
|
|
129
|
+
|
|
130
|
+
GoFetch Client is designed as a drop-in replacement for `apify-client`.
|
|
131
|
+
|
|
132
|
+
### Before (Apify)
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from apify_client import ApifyClient
|
|
136
|
+
|
|
137
|
+
client = ApifyClient(token="apify_api_xxx")
|
|
138
|
+
actor = client.actor("apify/instagram-scraper")
|
|
139
|
+
run = actor.call(run_input={"directUrls": [...]})
|
|
140
|
+
dataset = client.dataset(run["defaultDatasetId"])
|
|
141
|
+
items = list(dataset.iterate_items())
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### After (GoFetch)
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from gofetch import GoFetchClient # Only import changes!
|
|
148
|
+
|
|
149
|
+
client = GoFetchClient(api_key="sk_scr_xxx") # Use GoFetch API key
|
|
150
|
+
actor = client.actor("apify/instagram-scraper") # Same actor URL works!
|
|
151
|
+
run = actor.call(run_input={"directUrls": [...]})
|
|
152
|
+
dataset = client.dataset(run["defaultDatasetId"])
|
|
153
|
+
items = list(dataset.iterate_items())
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The client automatically translates Apify actor URLs to GoFetch scrapers:
|
|
157
|
+
- `apify/instagram-scraper` → `instagram`
|
|
158
|
+
- `apify/instagram-profile-scraper` → `instagram_profile`
|
|
159
|
+
- `clockworks/tiktok-profile-scraper` → `tiktok`
|
|
160
|
+
- `streamers/youtube-scraper` → `youtube`
|
|
161
|
+
|
|
162
|
+
## Supported Platforms
|
|
163
|
+
|
|
164
|
+
### Instagram
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
actor = client.actor("instagram")
|
|
168
|
+
run = actor.call(run_input={
|
|
169
|
+
"directUrls": ["https://www.instagram.com/nike/"],
|
|
170
|
+
"onlyPostsNewerThan": "2024-01-01",
|
|
171
|
+
"maxPosts": 50,
|
|
172
|
+
})
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### TikTok
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
actor = client.actor("tiktok")
|
|
179
|
+
run = actor.call(run_input={
|
|
180
|
+
"profiles": ["khaby.lame", "charlidamelio"],
|
|
181
|
+
"oldestPostDate": "2024-01-01",
|
|
182
|
+
"maxVideosPerProfile": 50,
|
|
183
|
+
})
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### YouTube
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
actor = client.actor("youtube")
|
|
190
|
+
run = actor.call(run_input={
|
|
191
|
+
"startUrls": [{"url": "https://www.youtube.com/@MrBeast"}],
|
|
192
|
+
"oldestPostDate": "2024-01-01",
|
|
193
|
+
})
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Webhook Handling
|
|
197
|
+
|
|
198
|
+
### Verifying Webhook Signatures
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from gofetch import verify_webhook_signature
|
|
202
|
+
|
|
203
|
+
def webhook_handler(request):
|
|
204
|
+
payload = request.body
|
|
205
|
+
signature = request.headers.get("X-Webhook-Signature", "")
|
|
206
|
+
|
|
207
|
+
if not verify_webhook_signature(payload, signature, "your_webhook_secret"):
|
|
208
|
+
return Response("Invalid signature", status=401)
|
|
209
|
+
|
|
210
|
+
# Process webhook...
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Transforming Webhook Payloads
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
from gofetch import transform_webhook_payload
|
|
217
|
+
|
|
218
|
+
def webhook_handler(request):
|
|
219
|
+
gofetch_payload = json.loads(request.body)
|
|
220
|
+
|
|
221
|
+
# Transform to Apify-compatible format
|
|
222
|
+
apify_payload = transform_webhook_payload(gofetch_payload)
|
|
223
|
+
|
|
224
|
+
if apify_payload["eventType"] == "ACTOR.RUN.SUCCEEDED":
|
|
225
|
+
dataset_id = apify_payload["resource"]["defaultDatasetId"]
|
|
226
|
+
# Fetch results...
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Error Handling
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
from gofetch import (
|
|
233
|
+
GoFetchClient,
|
|
234
|
+
GoFetchError,
|
|
235
|
+
AuthenticationError,
|
|
236
|
+
RateLimitError,
|
|
237
|
+
JobError,
|
|
238
|
+
TimeoutError,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
try:
|
|
242
|
+
client = GoFetchClient(api_key="sk_scr_xxx")
|
|
243
|
+
run = client.actor("instagram").call(run_input={...})
|
|
244
|
+
|
|
245
|
+
except AuthenticationError:
|
|
246
|
+
print("Invalid API key")
|
|
247
|
+
|
|
248
|
+
except RateLimitError as e:
|
|
249
|
+
print(f"Rate limited. Retry after {e.retry_after} seconds")
|
|
250
|
+
|
|
251
|
+
except TimeoutError as e:
|
|
252
|
+
print(f"Job timed out: {e.job_id}")
|
|
253
|
+
|
|
254
|
+
except JobError as e:
|
|
255
|
+
print(f"Job failed: {e.error_message}")
|
|
256
|
+
|
|
257
|
+
except GoFetchError as e:
|
|
258
|
+
print(f"GoFetch error: {e.message}")
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Development
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# Clone the repository
|
|
265
|
+
git clone https://github.com/YevheniiM/gofetch-client.git
|
|
266
|
+
cd gofetch-client
|
|
267
|
+
|
|
268
|
+
# Install in development mode
|
|
269
|
+
pip install -e ".[dev]"
|
|
270
|
+
|
|
271
|
+
# Run tests
|
|
272
|
+
pytest
|
|
273
|
+
|
|
274
|
+
# Run linting
|
|
275
|
+
ruff check src/
|
|
276
|
+
|
|
277
|
+
# Run type checking
|
|
278
|
+
mypy src/
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Agentic Development Infrastructure
|
|
284
|
+
|
|
285
|
+
This project ships with a complete [Claude Code](https://claude.ai/claude-code) agentic infrastructure — specialized agents, one-command skills, safety hooks, and a regression test framework. Everything lives under `.claude/` and activates automatically when you open a Claude Code session.
|
|
286
|
+
|
|
287
|
+
```
|
|
288
|
+
.claude/
|
|
289
|
+
├── settings.local.json # Permissions + hook wiring
|
|
290
|
+
├── hooks/ # 6 safety & automation hooks
|
|
291
|
+
├── agents/ # 7 specialized agent definitions
|
|
292
|
+
└── skills/ # 8 invocable skill workflows
|
|
293
|
+
docs/
|
|
294
|
+
└── REGRESSION_TESTING.md # 90 test cases across 8 modules
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Hooks
|
|
298
|
+
|
|
299
|
+
Hooks run automatically — you never call them directly. They intercept operations in real-time.
|
|
300
|
+
|
|
301
|
+
#### Safety hooks
|
|
302
|
+
|
|
303
|
+
| Hook | Trigger | Behavior |
|
|
304
|
+
|------|---------|----------|
|
|
305
|
+
| `no-ai-attribution.sh` | Before `git commit` | Blocks commits containing `Co-Authored-By` AI references |
|
|
306
|
+
| `destructive-command-confirm.sh` | Before any shell command | Blocks `git push --force`, `reset --hard`, `rm -rf /`, `clean -f`, `branch -D`, `checkout .` |
|
|
307
|
+
| `protect-sensitive-files.sh` | Before editing a file | Blocks edits to `.env`, `.pem`, `.key`, `credentials.*`, `secrets.*` |
|
|
308
|
+
|
|
309
|
+
#### Automation hooks
|
|
310
|
+
|
|
311
|
+
| Hook | Trigger | Behavior |
|
|
312
|
+
|------|---------|----------|
|
|
313
|
+
| `test-before-commit.sh` | Before `git commit` | Non-blocking reminder listing staged `.py` files |
|
|
314
|
+
| `auto-lint.sh` | After editing a `.py` file | Runs `ruff check --fix` silently on the file |
|
|
315
|
+
| `session-context.sh` | Session start | Prints version, branch, working tree, recent commits, available skills/agents |
|
|
316
|
+
|
|
317
|
+
Hooks work invisibly during normal development. For example, editing `src/gofetch/http.py` will trigger `protect-sensitive-files.sh` (passes — not a secret file), then after the edit `auto-lint.sh` fixes import ordering automatically. Committing triggers `no-ai-attribution.sh`, `destructive-command-confirm.sh`, and `test-before-commit.sh` in sequence.
|
|
318
|
+
|
|
319
|
+
### Agents
|
|
320
|
+
|
|
321
|
+
Seven role-specific experts, each with deep knowledge of their domain and strict boundaries on what they should and shouldn't touch.
|
|
322
|
+
|
|
323
|
+
| Agent | Role | When to Use |
|
|
324
|
+
|-------|------|-------------|
|
|
325
|
+
| **sdk-developer** | Core development | Features, bugs, refactoring across all modules |
|
|
326
|
+
| **api-compatibility-specialist** | Apify interface guardian | Verifying/fixing `apify-client` compatibility |
|
|
327
|
+
| **qa-engineer** | Manual QA (reporter only) | Hands-on testing — produces reports, never modifies code |
|
|
328
|
+
| **test-engineer** | Automated test writer | Writing pytest suites, closing coverage gaps |
|
|
329
|
+
| **async-specialist** | Async/await expert | Async implementations, sync/async parity |
|
|
330
|
+
| **docs-writer** | Documentation | README, CHANGELOG, docstrings, migration guides |
|
|
331
|
+
| **release-manager** | Version + publishing | Version bumps, changelog, tagging, PyPI releases |
|
|
332
|
+
|
|
333
|
+
**The `qa-engineer` is special** — it has strict reporter-only rules. It NEVER modifies source or test files, NEVER suggests patches, and NEVER commits. It runs 12 structured testing phases and produces a severity-rated report. This separation is intentional: QA finds problems, developers fix them.
|
|
334
|
+
|
|
335
|
+
#### Agent selection
|
|
336
|
+
|
|
337
|
+
```
|
|
338
|
+
Writing/fixing code?
|
|
339
|
+
├── Async-specific? → async-specialist
|
|
340
|
+
├── Apify compat? → api-compatibility-specialist
|
|
341
|
+
└── General → sdk-developer
|
|
342
|
+
|
|
343
|
+
Testing?
|
|
344
|
+
├── Writing tests → test-engineer
|
|
345
|
+
└── Manual QA → qa-engineer
|
|
346
|
+
|
|
347
|
+
Documentation? → docs-writer
|
|
348
|
+
Releasing? → release-manager
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Skills
|
|
352
|
+
|
|
353
|
+
Skills are one-command workflows invoked with `/skill-name` in Claude Code.
|
|
354
|
+
|
|
355
|
+
| Skill | Purpose | Duration |
|
|
356
|
+
|-------|---------|----------|
|
|
357
|
+
| `/self-test` | Lint + types + targeted tests on changed files | ~30 sec |
|
|
358
|
+
| `/regression-lite` | ~50 core tests from every module | ~15-20 min |
|
|
359
|
+
| `/regression-full` | All 90+ tests with parametrized variants (150+ effective) | ~45-60 min |
|
|
360
|
+
| `/review-changes` | Pre-commit review: security, types, compatibility, style, coverage | ~2-5 min |
|
|
361
|
+
| `/debug` | Hypothesis-driven debugging: reproduce, hypothesize, investigate, isolate | varies |
|
|
362
|
+
| `/fix-from-qa` | Parse a QA report, triage, fix by severity | varies |
|
|
363
|
+
| `/manual-qa [scope]` | Launch qa-engineer with scope: `client`, `http`, `webhook`, `async`, `compat`, `all` | ~20-30 min |
|
|
364
|
+
| `/release [major\|minor\|patch]` | Full release: validate, version, changelog, tag, publish | ~10-15 min |
|
|
365
|
+
|
|
366
|
+
### Real-World Workflows
|
|
367
|
+
|
|
368
|
+
#### Adding a new feature
|
|
369
|
+
|
|
370
|
+
```
|
|
371
|
+
1. Implement in src/gofetch/ (maintain sync/async parity)
|
|
372
|
+
2. /self-test → quick validation
|
|
373
|
+
3. /review-changes → pre-commit check (catches compat issues, missing tests)
|
|
374
|
+
4. Write tests → use test-engineer agent
|
|
375
|
+
5. Commit
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
**Example — adding a new scraper type (e.g., Twitter/X):**
|
|
379
|
+
|
|
380
|
+
The `sdk-developer` agent plans changes across `types.py`, `constants.py`, and actor URL resolution. After implementation, `/self-test` catches type errors from the new enum. `/review-changes` flags the missing test coverage. The `test-engineer` agent writes parametrized tests. `/manual-qa compat` verifies the new actor URL resolves correctly through the Apify compatibility layer. On commit, `no-ai-attribution.sh` checks the message and `test-before-commit.sh` lists the staged Python files.
|
|
381
|
+
|
|
382
|
+
#### Debugging a production issue
|
|
383
|
+
|
|
384
|
+
```
|
|
385
|
+
1. /debug <description> → structured investigation
|
|
386
|
+
2. Fix the root cause
|
|
387
|
+
3. /self-test → verify fix + no regressions
|
|
388
|
+
4. /regression-lite → broader regression check
|
|
389
|
+
5. Commit
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**Example — "Jobs time out even though the API shows them as completed":**
|
|
393
|
+
|
|
394
|
+
`/debug` reproduces the issue with mock HTTP, generates 3-5 hypotheses ranked by likelihood, and investigates each one. It might find that the polling loop compares raw GoFetch status `"completed"` against Apify status `"SUCCEEDED"` — a mapping issue in `actor.py:_wait_for_completion()`. After fixing, `/self-test` confirms the fix and `/regression-lite` runs all actor polling tests (ACT-04 through ACT-06).
|
|
395
|
+
|
|
396
|
+
#### Full QA cycle before a release
|
|
397
|
+
|
|
398
|
+
```
|
|
399
|
+
1. /manual-qa all → 12-phase QA, produces severity-rated report
|
|
400
|
+
2. /fix-from-qa → parse report, fix SEV-1 first, then SEV-2, SEV-3
|
|
401
|
+
3. /manual-qa all → re-run to verify all fixes
|
|
402
|
+
4. /regression-full → 150+ tests, coverage threshold check (80%)
|
|
403
|
+
5. /release minor → validate, bump, changelog, tag, push
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
**Example — shipping v0.2.0:**
|
|
407
|
+
|
|
408
|
+
`/manual-qa all` runs through environment verification, unit smoke tests, client instantiation, actor testing, dataset pagination, HTTP retries with mock transports, webhook signatures, exception hierarchy, async clients, edge cases, mypy, and ruff. It produces a report finding 5 issues. `/fix-from-qa` processes the report, fixing the SEV-1 (async client not raising `AuthenticationError`) first, then SEV-2s and SEV-3s — each fix includes a test. Re-running `/manual-qa all` comes back clean. `/regression-full` confirms 150+ tests pass at 83% coverage. `/release minor` bumps `0.1.0 → 0.2.0`, updates both version locations, prepares the changelog, commits, tags, and pauses for confirmation before pushing.
|
|
409
|
+
|
|
410
|
+
#### Maintaining sync/async parity
|
|
411
|
+
|
|
412
|
+
```
|
|
413
|
+
1. Change a sync method (e.g., HTTPClient.get)
|
|
414
|
+
2. /review-changes → flags that AsyncHTTPClient.get wasn't updated
|
|
415
|
+
3. Mirror the change in the async counterpart
|
|
416
|
+
4. /manual-qa async → verifies both behave identically
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
The `async-specialist` agent knows every sync/async class pair and common pitfalls — like accidentally using `time.sleep()` instead of `asyncio.sleep()` in async code, or forgetting to `await` a coroutine.
|
|
420
|
+
|
|
421
|
+
### Regression Testing
|
|
422
|
+
|
|
423
|
+
The full specification lives at [`docs/REGRESSION_TESTING.md`](docs/REGRESSION_TESTING.md) — 90 test cases with unique IDs:
|
|
424
|
+
|
|
425
|
+
| Module | IDs | Count | Priority |
|
|
426
|
+
|--------|-----|-------|----------|
|
|
427
|
+
| Client | CLI-01..10 | 10 | Medium |
|
|
428
|
+
| Actor | ACT-01..15 | 15 | High |
|
|
429
|
+
| Dataset | DAT-01..10 | 10 | High |
|
|
430
|
+
| HTTP | HTTP-01..12 | 12 | Highest |
|
|
431
|
+
| Webhook | WHK-01..10 | 10 | Medium |
|
|
432
|
+
| Async | ASY-01..15 | 15 | High |
|
|
433
|
+
| Compatibility | CMP-01..10 | 10 | Medium |
|
|
434
|
+
| Exceptions | EXC-01..08 | 8 | Low |
|
|
435
|
+
|
|
436
|
+
Each test specifies preconditions, steps, and expected results. Many use `@pytest.mark.parametrize` — effective count exceeds 150.
|
|
437
|
+
|
|
438
|
+
Reference test IDs in PRs and bug reports: *"This PR fixes the issue exposed by ACT-06 (polling backoff timing)"* or *"Blocked on HTTP-08 — need backoff timing test before shipping retry changes."*
|
|
439
|
+
|
|
440
|
+
### Customizing the Infrastructure
|
|
441
|
+
|
|
442
|
+
**Add a hook:** Create a script in `.claude/hooks/`, `chmod +x` it, wire it in `settings.local.json` under `PreToolUse`, `PostToolUse`, or `SessionStart`. Exit `0` to allow, `2` to block.
|
|
443
|
+
|
|
444
|
+
**Add an agent:** Create a markdown file in `.claude/agents/` with sections: Identity, Codebase Knowledge, Responsibilities, Boundaries.
|
|
445
|
+
|
|
446
|
+
**Add a skill:** Create `.claude/skills/<name>/SKILL.md` with YAML frontmatter (`name`, `description`, `user_invocable: true`) and step-by-step procedure.
|
|
447
|
+
|
|
448
|
+
## License
|
|
449
|
+
|
|
450
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
451
|
+
|
|
452
|
+
## Links
|
|
453
|
+
|
|
454
|
+
- **Documentation**: https://github.com/YevheniiM/gofetch-client#readme
|
|
455
|
+
- **Issues**: https://github.com/YevheniiM/gofetch-client/issues
|
|
456
|
+
- **GoFetch.io**: https://go-fetch.io
|