feat: add Gitea workflow integration with quality gates and closed-loop execution
This commit is contained in:
279
.kilo/WORKFLOW_AUDIT.md
Normal file
279
.kilo/WORKFLOW_AUDIT.md
Normal file
@@ -0,0 +1,279 @@
|
||||
# Workflow Completeness Audit
|
||||
|
||||
## Summary
|
||||
|
||||
| Workflow | Closed Loop | Gitea Integration | Quality Gates | Error Recovery | Final Delivery |
|
||||
|----------|-------------|-------------------|---------------|----------------|----------------|
|
||||
| `/landing-page` | ⚠️ Partial | ⚠️ Comments only | ❌ Missing | ❌ Missing | ✅ Docs |
|
||||
| `/commerce` | ⚠️ Partial | ❌ Missing | ❌ Missing | ❌ Missing | ✅ Docs |
|
||||
| `/blog` | ⚠️ Partial | ❌ Missing | ❌ Missing | ❌ Missing | ✅ Docs |
|
||||
| `/booking` | ⚠️ Partial | ❌ Missing | ❌ Missing | ❌ Missing | ❌ Missing |
|
||||
| `/workflow` | ✅ Full | ✅ Full | ✅ Full | ✅ Full | ✅ Full |
|
||||
|
||||
## Issues Found
|
||||
|
||||
### Missing in All Business Workflows
|
||||
|
||||
1. **No Gitea Issue Creation** - Workflows don't create issues first
|
||||
2. **No Progress Comments** - No `post_comment()` to Gitea after steps
|
||||
3. **No Checkpoints** - Can skip steps without validation
|
||||
4. **No Error Blocking** - Errors don't block workflow
|
||||
5. **No Final Checklist** - No delivery validation before completion
|
||||
|
||||
## Required Additions
|
||||
|
||||
### For Each Workflow
|
||||
|
||||
Add at the start of each workflow:
|
||||
|
||||
```markdown
|
||||
## Step 0: Gitea Issue Creation
|
||||
|
||||
**MANDATORY** - Create Gitea issue before any work starts.
|
||||
|
||||
### Implementation
|
||||
|
||||
1. Check if `issue` parameter provided
|
||||
2. If not, create new issue with checklist
|
||||
3. Post initial status comment
|
||||
4. Add `status: new` label
|
||||
5. Return issue number
|
||||
|
||||
### Code
|
||||
|
||||
\`\`\`python
|
||||
def create_or_get_issue(project_name, workflow_type, issue=None):
|
||||
if issue:
|
||||
return issue
|
||||
|
||||
# Create issue via Gitea API
|
||||
issue = gitea.create_issue(
|
||||
title=f"[{workflow_type}] {project_name}",
|
||||
body=generate_checklist(workflow_type),
|
||||
labels=["workflow", workflow_type, "status: new"]
|
||||
)
|
||||
return issue.number
|
||||
\`\`\`
|
||||
|
||||
### Checklist Template
|
||||
|
||||
\`\`\`markdown
|
||||
## Workflow Progress
|
||||
|
||||
- [ ] Requirements
|
||||
- [ ] Architecture
|
||||
- [ ] Backend
|
||||
- [ ] Frontend
|
||||
- [ ] Testing
|
||||
- [ ] Review
|
||||
- [ ] Docker
|
||||
- [ ] Documentation
|
||||
- [ ] Delivery
|
||||
|
||||
## Quality Gates
|
||||
|
||||
| Gate | Status |
|
||||
|------|--------|
|
||||
| Requirements | ⏳ |
|
||||
| Architecture | ⏳ |
|
||||
| Implementation | ⏳ |
|
||||
| Testing | ⏳ |
|
||||
| Security | ⏳ |
|
||||
| Docker | ⏳ |
|
||||
| Documentation | ⏳ |
|
||||
| Delivery | ⏳ |
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
Add after each step:
|
||||
|
||||
```markdown
|
||||
### Gitea Progress Comment
|
||||
|
||||
\`\`\`python
|
||||
def post_step_comment(issue_number, step_name, result):
|
||||
gitea.post_comment(issue_number, f"""## ✅ {step_name} Complete
|
||||
|
||||
**Duration**: {result.duration}
|
||||
**Files**: {result.files}
|
||||
|
||||
### Gate: {step_name}
|
||||
| Check | Status |
|
||||
|-------|--------|
|
||||
{result.checks}
|
||||
|
||||
**Next**: {result.next_step}
|
||||
|
||||
**Progress**: {result.progress}%
|
||||
""")
|
||||
|
||||
# Update issue label
|
||||
gitea.remove_label(issue_number, f"status: {prev_status}")
|
||||
gitea.add_label(issue_number, f"status: {next_status}")
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
Add error handling:
|
||||
|
||||
```markdown
|
||||
### Error Handling
|
||||
|
||||
\`\`\`python
|
||||
def handle_step_error(issue_number, step_name, error):
|
||||
# BLOCK workflow - don't proceed
|
||||
gitea.post_comment(issue_number, f"""## ❌ {step_name} Failed
|
||||
|
||||
**Error**: {error.message}
|
||||
**Blocker**: {error.blocker}
|
||||
|
||||
### How to Fix:
|
||||
1. {error.fix_step_1}
|
||||
2. {error.fix_step_2}
|
||||
|
||||
### Cannot Proceed Until:
|
||||
- [ ] Error is resolved
|
||||
- [ ] Step is re-run successfully
|
||||
|
||||
**Workflow PAUSED** - waiting for fix.
|
||||
|
||||
Reply "retry" to re-run after fixing.
|
||||
""")
|
||||
|
||||
# Add blocked label
|
||||
gitea.add_label(issue_number, "status: blocked")
|
||||
|
||||
# Raise exception to stop workflow
|
||||
raise WorkflowBlockedError(step_name, error)
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
Add final delivery:
|
||||
|
||||
```markdown
|
||||
## Final Delivery Validation
|
||||
|
||||
### Pre-delivery Checklist
|
||||
|
||||
\`\`\`python
|
||||
def validate_final_delivery(project_path):
|
||||
checks = [
|
||||
("Source code exists", os.path.exists(f"{project_path}/src")),
|
||||
("Backend builds", run("npm run build --prefix backend")),
|
||||
("Frontend builds", run("npm run build --prefix frontend")),
|
||||
("Tests pass", run("npm test")),
|
||||
("Docker builds", run("docker-compose build")),
|
||||
("Health check", check_health()),
|
||||
("README exists", os.path.exists("README.md")),
|
||||
("API docs exist", os.path.exists("docs/API.md")),
|
||||
("Deployment guide exists", os.path.exists("docs/DEPLOYMENT.md")),
|
||||
]
|
||||
|
||||
results = {check[0]: check[1] for check in checks}
|
||||
all_passed = all(check[1] for check in checks)
|
||||
|
||||
if not all_passed:
|
||||
failed = [c[0] for c in checks if not c[1]]
|
||||
raise DeliveryError(f"Checks failed: {failed}")
|
||||
|
||||
return results
|
||||
\`\`\`
|
||||
|
||||
### Delivery Comment
|
||||
|
||||
\`\`\`python
|
||||
def post_delivery_comment(issue_number, project_name):
|
||||
gitea.post_comment(issue_number, f"""## 🎉 Workflow Complete - READY FOR DELIVERY
|
||||
|
||||
**Project**: {project_name}
|
||||
|
||||
## 📦 Delivery Package
|
||||
|
||||
### Source Code
|
||||
- Repository: UniqueSoft/APAW
|
||||
- Branch: main
|
||||
|
||||
### Docker
|
||||
- Image: {project_name}:latest
|
||||
- Health: ✅ Passing
|
||||
|
||||
### Tests
|
||||
- Unit: ✅ Passing
|
||||
- Integration: ✅ Passing
|
||||
- E2E: ✅ Passing
|
||||
|
||||
### Documentation
|
||||
- ✅ README.md
|
||||
- ✅ API.md
|
||||
- ✅ DEPLOYMENT.md
|
||||
- ✅ ADMIN.md
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
\`\`\`bash
|
||||
docker-compose up -d
|
||||
# Access: http://localhost
|
||||
\`\`\`
|
||||
|
||||
## ✅ Client Handoff Checklist
|
||||
|
||||
- [x] Source code in repository
|
||||
- [x] Docker builds successfully
|
||||
- [x] All tests passing
|
||||
- [x] Documentation complete
|
||||
- [x] Client can deploy independently
|
||||
|
||||
**Status**: 🟢 READY FOR CLIENT DELIVERY
|
||||
""")
|
||||
|
||||
# Close issue
|
||||
gitea.close_issue(issue_number, "Workflow completed successfully")
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
## Files to Update
|
||||
|
||||
### 1. landing-page.md
|
||||
Add:
|
||||
- Step 0: Issue Creation
|
||||
- Gitea comments after each step
|
||||
- Error handling with blocking
|
||||
- Final delivery validation
|
||||
|
||||
### 2. commerce.md
|
||||
Add:
|
||||
- Step 0: Issue Creation
|
||||
- Gitea comments after each step
|
||||
- Error handling with blocking
|
||||
- Final delivery validation
|
||||
|
||||
### 3. blog.md
|
||||
Add:
|
||||
- Step 0: Issue Creation
|
||||
- Gitea comments after each step
|
||||
- Error handling with blocking
|
||||
- Final delivery validation
|
||||
|
||||
### 4. booking.md
|
||||
Add:
|
||||
- Step 0: Issue Creation
|
||||
- Gitea comments after each step
|
||||
- Error handling with blocking
|
||||
- Final delivery validation
|
||||
|
||||
## Integration with workflow.md
|
||||
|
||||
The `/workflow` command orchestrates all other workflows:
|
||||
|
||||
```
|
||||
/workflow landing-page --project_name="MySite"
|
||||
/workflow commerce --project_name="MyShop"
|
||||
/workflow blog --project_name="MyBlog"
|
||||
/workflow booking --project_name="MySalon"
|
||||
```
|
||||
|
||||
Each workflow MUST:
|
||||
1. Accept `issue` parameter (auto-created if not provided)
|
||||
2. Call `post_comment()` after each step
|
||||
3. Block on errors
|
||||
4. Validate final delivery
|
||||
5. Close issue on completion
|
||||
@@ -40,9 +40,37 @@ Create a full-stack landing page CMS from HTML mockups with Node.js backend, Vue
|
||||
## Overview
|
||||
|
||||
```
|
||||
HTML Mockups → Architecture → Backend API → Vue Frontend → Admin Panel → E2E Tests → Docker → Documentation → Product
|
||||
Issue Creation → Analysis → Architecture → Backend → Frontend → Testing → Review → Docker → Docs → Delivery
|
||||
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
|
||||
Gitea #N Comment Comment Comment Comment Comment Comment Comment Comment Final
|
||||
Checkpoint Checkpoint Checkpoint Checkpoint Checkpoint Checkpoint Checkpoint Checkpoint
|
||||
```
|
||||
|
||||
## CRITICAL: Gitea Integration (MANDATORY)
|
||||
|
||||
This workflow REQUIRES Gitea issue tracking. No work starts without an issue.
|
||||
|
||||
### Pre-conditions
|
||||
|
||||
```python
|
||||
def ensure_gitea_issue(issue_number, project_name):
|
||||
"""MANDATORY: Create Gitea issue before any work"""
|
||||
if not issue_number:
|
||||
issue_number = create_gitea_issue(
|
||||
title=f"[landing-page] {project_name}",
|
||||
body=generate_workflow_checklist(),
|
||||
labels=["workflow", "landing-page", "status: new"]
|
||||
)
|
||||
return issue_number
|
||||
```
|
||||
|
||||
### Workflow Progress Tracking
|
||||
|
||||
Every step MUST post progress to Gitea:
|
||||
- 🔄 START: When step begins
|
||||
- ✅ SUCCESS: When step completes with validations
|
||||
- ❌ ERROR: When step fails (blocks workflow)
|
||||
|
||||
## Technology Stack
|
||||
|
||||
### Frontend
|
||||
@@ -75,24 +103,131 @@ HTML Mockups → Architecture → Backend API → Vue Frontend → Admin Panel
|
||||
| Mobile Safari | 14+ | Full |
|
||||
| Chrome Mobile | 90+ | Full |
|
||||
|
||||
## Step 0: Issue Creation (MANDATORY)
|
||||
|
||||
**This step is ALWAYS executed first. No exceptions.**
|
||||
|
||||
```python
|
||||
import urllib.request, json, base64, os
|
||||
|
||||
def create_workflow_issue(project_name, mockup_dir):
|
||||
"""Create Gitea issue for workflow tracking"""
|
||||
|
||||
# Get credentials
|
||||
username = os.environ.get('GITEA_USER', 'NW')
|
||||
password = os.environ.get('GITEA_PASS', 'eshkink0t')
|
||||
credentials = base64.b64encode(f"{username}:{password}".encode()).decode()
|
||||
|
||||
# Create token
|
||||
token_req = urllib.request.Request(
|
||||
"https://git.softuniq.eu/api/v1/users/NW/tokens",
|
||||
data=json.dumps({"name": f"landing-{os.getpid()}", "scopes": ["all"]}).encode(),
|
||||
headers={'Content-Type': 'application/json', 'Authorization': f'Basic {credentials}'},
|
||||
method='POST'
|
||||
)
|
||||
with urllib.request.urlopen(token_req) as r:
|
||||
token = json.loads(r.read())['sha1']
|
||||
|
||||
# Create issue
|
||||
body = f"""## Landing Page CMS Workflow
|
||||
|
||||
**Project**: {project_name}
|
||||
**Mockups**: {mockup_dir}
|
||||
|
||||
## Workflow Progress
|
||||
|
||||
| Step | Status | Agent | Duration |
|
||||
|------|--------|-------|----------|
|
||||
| 0. Issue Creation | ✅ | - | - |
|
||||
| 1. Analysis | ⏳ | @frontend-developer | - |
|
||||
| 2. Architecture | ⏳ | @system-analyst | - |
|
||||
| 3. Backend | ⏳ | @backend-developer | - |
|
||||
| 4. Frontend | ⏳ | @frontend-developer | - |
|
||||
| 5. Admin Panel | ⏳ | @frontend-developer | - |
|
||||
| 6. Testing | ⏳ | @sdet-engineer | - |
|
||||
| 7. Review | ⏳ | @code-skeptic, @security-auditor | - |
|
||||
| 8. Docker | ⏳ | @release-manager | - |
|
||||
| 9. Documentation | ⏳ | @system-analyst | - |
|
||||
| 10. Delivery | ⏳ | @evaluator | - |
|
||||
|
||||
## Quality Gates
|
||||
|
||||
| Gate | Status | Score |
|
||||
|------|--------|-------|
|
||||
| Requirements | ⏳ | - |
|
||||
| Architecture | ⏳ | - |
|
||||
| Implementation | ⏳ | - |
|
||||
| Testing | ⏳ | - |
|
||||
| Security | ⏳ | - |
|
||||
| Docker | ⏳ | - |
|
||||
| Documentation | ⏳ | - |
|
||||
| Delivery | ⏳ | - |
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] Mockups analyzed
|
||||
- [ ] Architecture designed
|
||||
- [ ] Backend API implemented
|
||||
- [ ] Frontend built
|
||||
- [ ] Admin panel created
|
||||
- [ ] Tests passing
|
||||
- [ ] Code reviewed
|
||||
- [ ] Docker ready
|
||||
- [ ] Documentation complete
|
||||
- [ ] Client delivery ready
|
||||
"""
|
||||
|
||||
issue_req = urllib.request.Request(
|
||||
"https://git.softuniq.eu/api/v1/repos/UniqueSoft/APAW/issues",
|
||||
data=json.dumps({
|
||||
"title": f"[landing-page] {project_name}",
|
||||
"body": body,
|
||||
"labels": ["workflow", "landing-page", "status: new"]
|
||||
}).encode(),
|
||||
headers={'Content-Type': 'application/json', 'Authorization': f'token {token}'},
|
||||
method='POST'
|
||||
)
|
||||
|
||||
with urllib.request.urlopen(issue_req) as r:
|
||||
issue = json.loads(r.read())
|
||||
|
||||
return issue['number']
|
||||
```
|
||||
|
||||
**Result**: Issue number for tracking all subsequent steps.
|
||||
|
||||
## Step 1: Analyze Mockups
|
||||
|
||||
**Agent**: `@FrontendDeveloper`
|
||||
**Pre-condition**: Issue created (Step 0 complete)
|
||||
|
||||
### Input Analysis
|
||||
### Execution
|
||||
|
||||
1. Scan mockup directory:
|
||||
1. Post START to Gitea:
|
||||
```python
|
||||
post_comment(issue_number, f"""## 🔄 Analysis Started
|
||||
|
||||
**Agent**: @frontend-developer
|
||||
**Step**: 1/10
|
||||
**Time**: {timestamp()}
|
||||
**Input**: {mockup_dir}
|
||||
|
||||
Analyzing HTML mockups...
|
||||
""")
|
||||
```
|
||||
|
||||
2. Scan mockup directory:
|
||||
```bash
|
||||
find {mockup_dir} -name "*.html" -o -name "*.htm" | head -20
|
||||
```
|
||||
|
||||
2. Extract content structure:
|
||||
3. Extract content structure:
|
||||
- Public pages (home, about, contact)
|
||||
- Content sections (hero, features, pricing, testimonials, footer)
|
||||
- Editable content areas (text, images, CTAs)
|
||||
- Forms (contact, newsletter, lead capture)
|
||||
|
||||
3. Create content model:
|
||||
4. Create content model:
|
||||
```markdown
|
||||
## Content Model
|
||||
|
||||
@@ -107,34 +242,72 @@ HTML Mockups → Architecture → Backend API → Vue Frontend → Admin Panel
|
||||
- Pricing: [{name, price, features, highlight}]
|
||||
- Testimonials: [{avatar, name, role, text}]
|
||||
- Footer: copyright, social_links, contact_info
|
||||
|
||||
### Dynamic Content
|
||||
- Blog/News posts
|
||||
- FAQ items
|
||||
- Team members
|
||||
- Portfolio items
|
||||
|
||||
### Forms
|
||||
- Contact: name, email, phone, message
|
||||
- Newsletter: email
|
||||
- Lead capture: name, email, phone, interest
|
||||
```
|
||||
|
||||
4. Post analysis to Gitea:
|
||||
```python
|
||||
post_gitea_comment(issue_number, """## 🎨 Mockup Analysis Complete
|
||||
|
||||
**Pages Identified**: {page_count}
|
||||
**Content Sections**: {section_count}
|
||||
**Editable Areas**: {editable_count}
|
||||
**Forms Detected**: {form_count}
|
||||
|
||||
### Content Model
|
||||
{content_model_summary}
|
||||
|
||||
**Next**: Architecture design
|
||||
""")
|
||||
5. Save artifacts:
|
||||
```
|
||||
.workflow/analysis/content-model.md
|
||||
.workflow/analysis/pages-summary.md
|
||||
```
|
||||
|
||||
6. Validate artifacts exist
|
||||
|
||||
7. Post SUCCESS to Gitea:
|
||||
```python
|
||||
post_comment(issue_number, f"""## ✅ Analysis Complete
|
||||
|
||||
**Duration**: {duration()}
|
||||
**Files Scanned**: {file_count}
|
||||
|
||||
### Content Model
|
||||
- **Pages**: {page_count}
|
||||
- **Sections**: {section_count}
|
||||
- **Editable Areas**: {editable_count}
|
||||
- **Forms**: {form_count}
|
||||
|
||||
### Artifacts
|
||||
- `.workflow/analysis/content-model.md`
|
||||
- `.workflow/analysis/pages-summary.md`
|
||||
|
||||
### Gate: Analysis
|
||||
| Check | Status |
|
||||
|-------|--------|
|
||||
| Mockups found | ✅ |
|
||||
| Content model created | ✅ |
|
||||
| Artifacts saved | ✅ |
|
||||
|
||||
**Next**: Architecture design
|
||||
|
||||
**Progress**: 10%
|
||||
""")
|
||||
|
||||
update_issue_label(issue_number, "status: analysis", "status: architecture")
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```python
|
||||
if error:
|
||||
post_comment(issue_number, f"""## ❌ Analysis Failed
|
||||
|
||||
**Error**: {error_message}
|
||||
**Blocker**: {blocker_description}
|
||||
|
||||
### How to Fix:
|
||||
1. Ensure mockups directory exists: `{mockup_dir}`
|
||||
2. Check HTML files are valid
|
||||
3. Verify file permissions
|
||||
|
||||
### Cannot Proceed Until:
|
||||
- [ ] Mockups directory exists
|
||||
- [ ] HTML files are readable
|
||||
- [ ] Content structure identified
|
||||
|
||||
**Workflow PAUSED**. Reply "retry" after fixing.
|
||||
""")
|
||||
add_label(issue_number, "status: blocked")
|
||||
raise WorkflowBlockedError("Analysis failed")
|
||||
```
|
||||
|
||||
## Step 2: Architecture Design
|
||||
|
||||
|
||||
615
.kilo/skills/gitea-workflow/SKILL.md
Normal file
615
.kilo/skills/gitea-workflow/SKILL.md
Normal file
@@ -0,0 +1,615 @@
|
||||
---
|
||||
name: gitea-workflow
|
||||
description: Gitea integration for all workflows - issue creation, progress tracking, quality gates, final delivery
|
||||
---
|
||||
|
||||
# Gitea Workflow Integration
|
||||
|
||||
Mandatory Gitea integration for ALL workflows. Ensures closed-loop execution, no partial results, complete delivery.
|
||||
|
||||
## Core Functions
|
||||
|
||||
### Create Issue
|
||||
|
||||
```python
|
||||
import urllib.request, json, base64, os, time
|
||||
|
||||
GITEA_URL = "https://git.softuniq.eu"
|
||||
GITEA_USER = os.environ.get('GITEA_USER', 'NW')
|
||||
GITEA_PASS = os.environ.get('GITEA_PASS', 'eshkink0t')
|
||||
|
||||
class GiteaClient:
|
||||
def __init__(self):
|
||||
self.token = self._get_token()
|
||||
|
||||
def _get_token(self):
|
||||
"""Get or create API token"""
|
||||
credentials = base64.b64encode(f"{GITEA_USER}:{GITEA_PASS}".encode()).decode()
|
||||
req = urllib.request.Request(
|
||||
f"{GITEA_URL}/api/v1/users/{GITEA_USER}/tokens",
|
||||
data=json.dumps({"name": f"workflow-{int(time.time())}", "scopes": ["all"]}).encode(),
|
||||
headers={'Content-Type': 'application/json', 'Authorization': f'Basic {credentials}'},
|
||||
method='POST'
|
||||
)
|
||||
with urllib.request.urlopen(req) as r:
|
||||
return json.loads(r.read())['sha1']
|
||||
|
||||
def create_issue(self, repo, title, body, labels):
|
||||
"""Create workflow issue"""
|
||||
url = f"{GITEA_URL}/api/v1/repos/{repo}/issues"
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
data=json.dumps({
|
||||
"title": title,
|
||||
"body": body,
|
||||
"labels": labels
|
||||
}).encode(),
|
||||
headers={
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': f'token {self.token}'
|
||||
},
|
||||
method='POST'
|
||||
)
|
||||
with urllib.request.urlopen(req) as r:
|
||||
return json.loads(r.read())
|
||||
|
||||
def post_comment(self, repo, issue_number, body):
|
||||
"""Post progress comment"""
|
||||
url = f"{GITEA_URL}/api/v1/repos/{repo}/issues/{issue_number}/comments"
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
data=json.dumps({"body": body}).encode(),
|
||||
headers={
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': f'token {self.token}'
|
||||
},
|
||||
method='POST'
|
||||
)
|
||||
with urllib.request.urlopen(req) as r:
|
||||
return json.loads(r.read())
|
||||
|
||||
def add_label(self, repo, issue_number, label):
|
||||
"""Add label to issue"""
|
||||
url = f"{GITEA_URL}/api/v1/repos/{repo}/issues/{issue_number}/labels"
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
data=json.dumps({"labels": [label]}).encode(),
|
||||
headers={
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': f'token {self.token}'
|
||||
},
|
||||
method='POST'
|
||||
)
|
||||
with urllib.request.urlopen(req) as r:
|
||||
return json.loads(r.read())
|
||||
|
||||
def close_issue(self, repo, issue_number, comment):
|
||||
"""Close issue with final comment"""
|
||||
self.post_comment(repo, issue_number, comment)
|
||||
url = f"{GITEA_URL}/api/v1/repos/{repo}/issues/{issue_number}"
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
data=json.dumps({"state": "closed"}).encode(),
|
||||
headers={
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': f'token {self.token}'
|
||||
},
|
||||
method='PATCH'
|
||||
)
|
||||
with urllib.request.urlopen(req) as r:
|
||||
return json.loads(r.read())
|
||||
|
||||
|
||||
def create_workflow_issue(workflow_type, project_name, issue_number=None):
|
||||
"""Create Gitea issue for workflow tracking - MANDATORY FIRST STEP"""
|
||||
|
||||
if issue_number:
|
||||
return issue_number
|
||||
|
||||
client = GiteaClient()
|
||||
|
||||
title = f"[{workflow_type}] {project_name}"
|
||||
|
||||
body = f"""## {workflow_type.title()} Workflow
|
||||
|
||||
**Project**: {project_name}
|
||||
|
||||
## Workflow Progress
|
||||
|
||||
| Step | Status | Agent | Duration |
|
||||
|------|--------|-------|----------|
|
||||
| 0. Issue Creation | ✅ | - | - |
|
||||
| 1. Requirements | ⏳ | @requirement-refiner | - |
|
||||
| 2. Architecture | ⏳ | @system-analyst | - |
|
||||
| 3. Backend | ⏳ | @backend-developer | - |
|
||||
| 4. Frontend | ⏳ | @frontend-developer | - |
|
||||
| 5. Testing | ⏳ | @sdet-engineer | - |
|
||||
| 6. Review | ⏳ | @code-skeptic | - |
|
||||
| 7. Security | ⏳ | @security-auditor | - |
|
||||
| 8. Docker | ⏳ | @release-manager | - |
|
||||
| 9. Documentation | ⏳ | @system-analyst | - |
|
||||
| 10. Delivery | ⏳ | @evaluator | - |
|
||||
|
||||
## Quality Gates
|
||||
|
||||
| Gate | Status | Score |
|
||||
|------|--------|-------|
|
||||
| Requirements | ⏳ Pending | - |
|
||||
| Architecture | ⏳ Pending | - |
|
||||
| Implementation | ⏳ Pending | - |
|
||||
| Testing | ⏳ Pending | - |
|
||||
| Security | ⏳ Pending | - |
|
||||
| Docker | ⏳ Pending | - |
|
||||
| Documentation | ⏳ Pending | - |
|
||||
| Delivery | ⏳ Pending | - |
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] Requirements gathered
|
||||
- [ ] Architecture designed
|
||||
- [ ] Backend implemented
|
||||
- [ ] Frontend implemented
|
||||
- [ ] Tests passing
|
||||
- [ ] Code reviewed
|
||||
- [ ] Security audited
|
||||
- [ ] Docker ready
|
||||
- [ ] Documentation complete
|
||||
- [ ] Final delivery validated
|
||||
|
||||
## Labels
|
||||
|
||||
- workflow
|
||||
- {workflow_type}
|
||||
- status: new
|
||||
"""
|
||||
|
||||
issue = client.create_issue(
|
||||
repo="UniqueSoft/APAW",
|
||||
title=title,
|
||||
body=body,
|
||||
labels=["workflow", workflow_type, "status: new"]
|
||||
)
|
||||
|
||||
return issue['number']
|
||||
```
|
||||
|
||||
## Comment Templates
|
||||
|
||||
### Step Start
|
||||
|
||||
```python
|
||||
def comment_start(issue_number, step_name, step_number, agent, files=None):
|
||||
"""Post step start comment"""
|
||||
body = f"""## 🔄 {step_name} Started
|
||||
|
||||
**Agent**: {agent}
|
||||
**Step**: {step_number}/10
|
||||
**Time**: {timestamp()}
|
||||
"""
|
||||
if files:
|
||||
body += f"\n**Files**:\n"
|
||||
for f in files:
|
||||
body += f"- `{f}`\n"
|
||||
|
||||
body += "\n---\n*Progress comment will be updated upon completion.*"
|
||||
|
||||
client = GiteaClient()
|
||||
client.post_comment("UniqueSoft/APAW", issue_number, body)
|
||||
```
|
||||
|
||||
### Step Success
|
||||
|
||||
```python
|
||||
def comment_success(issue_number, step_name, step_number, result):
|
||||
"""Post step completion comment"""
|
||||
body = f"""## ✅ {step_name} Complete
|
||||
|
||||
**Duration**: {result['duration']}
|
||||
**Files Changed**: {result['files_count']}
|
||||
|
||||
### Artifacts Created:
|
||||
"""
|
||||
for artifact in result['artifacts']:
|
||||
body += f"- `{artifact}`\n"
|
||||
|
||||
body += f"""
|
||||
### Tests:
|
||||
- Unit: {result['tests']['unit_passed']}/{result['tests']['unit_total']} passed
|
||||
- E2E: {result['tests']['e2e_passed']}/{result['tests']['e2e_total']} passed
|
||||
- Coverage: {result['tests']['coverage']}%
|
||||
|
||||
### Gate: {step_name}
|
||||
| Check | Status |
|
||||
|-------|--------|
|
||||
"""
|
||||
for check, passed in result['checks'].items():
|
||||
status = "✅" if passed else "❌"
|
||||
body += f"| {check} | {status} |\n"
|
||||
|
||||
body += f"""
|
||||
**Next Step**: {result['next_step']}
|
||||
|
||||
**Progress**: {step_number * 10}%
|
||||
"""
|
||||
|
||||
client = GiteaClient()
|
||||
client.post_comment("UniqueSoft/APAW", issue_number, body)
|
||||
|
||||
# Update labels
|
||||
client.add_label("UniqueSoft/APAW", issue_number, f"step: {step_number}")
|
||||
```
|
||||
|
||||
### Step Error
|
||||
|
||||
```python
|
||||
def comment_error(issue_number, step_name, step_number, error):
|
||||
"""Post step error comment - BLOCKS workflow"""
|
||||
body = f"""## ❌ {step_name} Failed
|
||||
|
||||
**Error**: {error['message']}
|
||||
**Type**: {error['type']}
|
||||
**File**: {error.get('file', 'N/A')}
|
||||
**Time**: {timestamp()}
|
||||
|
||||
### Stack Trace:
|
||||
```
|
||||
{error.get('stack_trace', 'N/A')}
|
||||
```
|
||||
|
||||
### Blocker:
|
||||
{error['blocker']}
|
||||
|
||||
### How to Fix:
|
||||
"""
|
||||
for i, step in enumerate(error['fix_steps'], 1):
|
||||
body += f"{i}. {step}\n"
|
||||
|
||||
body += f"""
|
||||
### Cannot Proceed Until:
|
||||
"""
|
||||
for req in error['requirements']:
|
||||
body += f"- [ ] {req}\n"
|
||||
|
||||
body += f"""
|
||||
**Workflow PAUSED** - waiting for resolution.
|
||||
|
||||
Reply with "retry" to re-run this step after fixing.
|
||||
"""
|
||||
|
||||
client = GiteaClient()
|
||||
client.post_comment("UniqueSoft/APAW", issue_number, body)
|
||||
client.add_label("UniqueSoft/APAW", issue_number, "status: blocked")
|
||||
```
|
||||
|
||||
### Final Delivery
|
||||
|
||||
```python
|
||||
def comment_delivery(issue_number, project_name, workflow_type, checks):
|
||||
"""Post final delivery comment"""
|
||||
body = f"""## 🎉 Workflow Complete - READY FOR CLIENT DELIVERY
|
||||
|
||||
**Project**: {project_name}
|
||||
**Type**: {workflow_type}
|
||||
**Issue**: #{issue_number}
|
||||
**Total Duration**: {checks['total_duration']}
|
||||
**Steps Completed**: 10/10
|
||||
|
||||
---
|
||||
|
||||
## 📦 Delivery Package
|
||||
|
||||
### Source Code
|
||||
- **Repository**: UniqueSoft/APAW
|
||||
- **Branch**: main
|
||||
- **Commit**: `{checks['commit_hash']}`
|
||||
|
||||
### Docker
|
||||
- **Image**: `{project_name}:latest`
|
||||
- **Size**: {checks['docker_size']}MB
|
||||
- **Health**: ✅ Passing
|
||||
|
||||
### Quality Score: {checks['score']}/100
|
||||
|
||||
---
|
||||
|
||||
## ✅ All Quality Gates Passed
|
||||
|
||||
| Gate | Status | Score |
|
||||
|------|--------|-------|
|
||||
| Requirements | ✅ | {checks['gates']['requirements']}/10 |
|
||||
| Architecture | ✅ | {checks['gates']['architecture']}/10 |
|
||||
| Implementation | ✅ | {checks['gates']['implementation']}/10 |
|
||||
| Testing | ✅ | {checks['gates']['testing']}/10 |
|
||||
| Security | ✅ | {checks['gates']['security']}/10 |
|
||||
| Docker | ✅ | {checks['gates']['docker']}/10 |
|
||||
| Documentation | ✅ | {checks['gates']['documentation']}/10 |
|
||||
| Delivery | ✅ | {checks['gates']['delivery']}/10 |
|
||||
| **Total** | ✅ | **{checks['total_score']}/80** |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
```bash
|
||||
# Clone and run
|
||||
git clone https://git.softuniq.eu/UniqueSoft/APAW.git
|
||||
cd {project_name}
|
||||
docker-compose up -d
|
||||
|
||||
# Access
|
||||
# Frontend: http://localhost
|
||||
# Admin: http://localhost/admin
|
||||
# API: http://localhost/api
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Client Handoff Checklist
|
||||
|
||||
- [x] Source code in repository
|
||||
- [x] Docker builds successfully
|
||||
- [x] All tests passing
|
||||
- [x] Documentation complete
|
||||
- [x] Security audit passed
|
||||
- [x] Client can deploy independently
|
||||
- [x] Demo recorded
|
||||
|
||||
---
|
||||
|
||||
**Status**: 🟢 READY FOR CLIENT DELIVERY
|
||||
**Completed**: {timestamp()}
|
||||
"""
|
||||
|
||||
client = GiteaClient()
|
||||
client.post_comment("UniqueSoft/APAW", issue_number, body)
|
||||
client.close_issue("UniqueSoft/APAW", issue_number, "Workflow completed successfully")
|
||||
```
|
||||
|
||||
## Progress Tracking
|
||||
|
||||
```python
|
||||
def update_progress_table(issue_number, step_number, step_name, status):
|
||||
"""Update progress table in issue body"""
|
||||
# Get current issue
|
||||
client = GiteaClient()
|
||||
issue = client.get_issue("UniqueSoft/APAW", issue_number)
|
||||
|
||||
# Parse and update progress table
|
||||
lines = issue['body'].split('\n')
|
||||
in_progress = False
|
||||
new_lines = []
|
||||
|
||||
for line in lines:
|
||||
if '| Step | Status |' in line:
|
||||
in_progress = True
|
||||
if in_progress and line.startswith(f'| {step_number}.'):
|
||||
parts = line.split('|')
|
||||
parts[2] = f' {status} '
|
||||
parts[3] = f' @{step_name.split()[0].lower()} '
|
||||
line = '|'.join(parts)
|
||||
new_lines.append(line)
|
||||
|
||||
# Update issue
|
||||
client.update_issue("UniqueSoft/APAW", issue_number, '\n'.join(new_lines))
|
||||
```
|
||||
|
||||
## Quality Gate Validation
|
||||
|
||||
```python
|
||||
class QualityGate:
|
||||
def __init__(self, gate_name, checks):
|
||||
self.gate_name = gate_name
|
||||
self.checks = checks
|
||||
|
||||
def validate(self):
|
||||
"""Validate all checks pass"""
|
||||
results = {}
|
||||
for check_name, check_fn in self.checks.items():
|
||||
try:
|
||||
results[check_name] = check_fn()
|
||||
except Exception as e:
|
||||
results[check_name] = False
|
||||
|
||||
all_passed = all(results.values())
|
||||
|
||||
if not all_passed:
|
||||
failed = [k for k, v in results.items() if not v]
|
||||
raise QualityGateError(f"Gate '{self.gate_name}' failed: {failed}")
|
||||
|
||||
return results
|
||||
|
||||
def score(self, results):
|
||||
"""Calculate gate score"""
|
||||
passed = sum(1 for v in results.values() if v)
|
||||
total = len(results)
|
||||
return round((passed / total) * 10)
|
||||
|
||||
# Define gates for each workflow step
|
||||
GATES = {
|
||||
'requirements': QualityGate('requirements', {
|
||||
'User stories defined': lambda: os.path.exists('.workflow/user-stories.md'),
|
||||
'Acceptance criteria': lambda: count_criteria() > 0,
|
||||
'Technical constraints': lambda: os.path.exists('.workflow/requirements.md'),
|
||||
'Stakeholders identified': lambda: check_stakeholders(),
|
||||
}),
|
||||
|
||||
'architecture': QualityGate('architecture', {
|
||||
'Database schema': lambda: os.path.exists('.workflow/database-schema.sql'),
|
||||
'API documented': lambda: os.path.exists('.workflow/api-endpoints.md'),
|
||||
'Tech stack decided': lambda: os.path.exists('.workflow/tech-stack.md'),
|
||||
'Project structure': lambda: os.path.exists('backend/') and os.path.exists('frontend/'),
|
||||
}),
|
||||
|
||||
'backend': QualityGate('backend', {
|
||||
'Code exists': lambda: os.path.exists('backend/src/app.js'),
|
||||
'Builds successfully': lambda: run('npm run build --prefix backend') == 0,
|
||||
'No TypeScript errors': lambda: run('npm run type-check --prefix backend') == 0,
|
||||
'Database connected': lambda: check_db_connection(),
|
||||
}),
|
||||
|
||||
'frontend': QualityGate('frontend', {
|
||||
'Pages created': lambda: count_files('frontend/src/views') > 0,
|
||||
'Components created': lambda: count_files('frontend/src/components') > 0,
|
||||
'Builds successfully': lambda: run('npm run build --prefix frontend') == 0,
|
||||
'No errors': lambda: run('npm run lint --prefix frontend') == 0,
|
||||
}),
|
||||
|
||||
'testing': QualityGate('testing', {
|
||||
'Unit tests pass': lambda: run('npm test') == 0,
|
||||
'E2E tests pass': lambda: run('npm run e2e') == 0,
|
||||
'Coverage > 80%': lambda: get_coverage() >= 80,
|
||||
'No critical bugs': lambda: count_critical_bugs() == 0,
|
||||
}),
|
||||
|
||||
'security': QualityGate('security', {
|
||||
'No vulnerabilities': lambda: run('npm audit') == 0,
|
||||
'No secrets in code': lambda: scan_for_secrets() == 0,
|
||||
'Auth working': lambda: test_auth() == True,
|
||||
'Input validation': lambda: check_input_validation(),
|
||||
}),
|
||||
|
||||
'docker': QualityGate('docker', {
|
||||
'Image builds': lambda: run('docker-compose build') == 0,
|
||||
'Container starts': lambda: run('docker-compose up -d') == 0,
|
||||
'Health check': lambda: check_health() == True,
|
||||
'Port accessible': lambda: check_port(8080),
|
||||
}),
|
||||
|
||||
'documentation': QualityGate('documentation', {
|
||||
'README exists': lambda: os.path.exists('README.md'),
|
||||
'API docs exist': lambda: os.path.exists('docs/API.md'),
|
||||
'Deployment guide': lambda: os.path.exists('docs/DEPLOYMENT.md'),
|
||||
'Admin guide': lambda: os.path.exists('docs/ADMIN.md'),
|
||||
}),
|
||||
|
||||
'delivery': QualityGate('delivery', {
|
||||
'All gates passed': lambda: all(g.validate() for g in GATES.values()),
|
||||
'Source in repo': lambda: run('git status --porcelain') == '',
|
||||
'Demo recorded': lambda: os.path.exists('.workflow/demo.md'),
|
||||
'Client can deploy': lambda: validate_deployment_instructions(),
|
||||
}),
|
||||
}
|
||||
```
|
||||
|
||||
## Workflow Execution Pattern
|
||||
|
||||
```python
|
||||
def run_workflow(workflow_type, project_name, issue_number=None):
|
||||
"""Execute workflow with Gitea tracking - CLOSED LOOP"""
|
||||
|
||||
# Step 0: Create Issue (MANDATORY)
|
||||
issue_number = create_workflow_issue(workflow_type, project_name, issue_number)
|
||||
|
||||
try:
|
||||
# Step 1: Requirements
|
||||
comment_start(issue_number, "Requirements", 1, "@requirement-refiner")
|
||||
result = execute_requirements(workflow_type)
|
||||
GATES['requirements'].validate()
|
||||
comment_success(issue_number, "Requirements", 1, result)
|
||||
|
||||
# Step 2: Architecture
|
||||
comment_start(issue_number, "Architecture", 2, "@system-analyst")
|
||||
result = execute_architecture(workflow_type)
|
||||
GATES['architecture'].validate()
|
||||
comment_success(issue_number, "Architecture", 2, result)
|
||||
|
||||
# Step 3: Backend
|
||||
comment_start(issue_number, "Backend", 3, "@backend-developer")
|
||||
result = execute_backend(workflow_type)
|
||||
GATES['backend'].validate()
|
||||
comment_success(issue_number, "Backend", 3, result)
|
||||
|
||||
# Step 4: Frontend
|
||||
comment_start(issue_number, "Frontend", 4, "@frontend-developer")
|
||||
result = execute_frontend(workflow_type)
|
||||
GATES['frontend'].validate()
|
||||
comment_success(issue_number, "Frontend", 4, result)
|
||||
|
||||
# Step 5: Testing
|
||||
comment_start(issue_number, "Testing", 5, "@sdet-engineer")
|
||||
result = execute_testing(workflow_type)
|
||||
GATES['testing'].validate()
|
||||
comment_success(issue_number, "Testing", 5, result)
|
||||
|
||||
# Step 6: Review
|
||||
comment_start(issue_number, "Review", 6, "@code-skeptic")
|
||||
result = execute_review(workflow_type)
|
||||
comment_success(issue_number, "Review", 6, result)
|
||||
|
||||
# Step 7: Security
|
||||
comment_start(issue_number, "Security", 7, "@security-auditor")
|
||||
result = execute_security(workflow_type)
|
||||
GATES['security'].validate()
|
||||
comment_success(issue_number, "Security", 7, result)
|
||||
|
||||
# Step 8: Docker
|
||||
comment_start(issue_number, "Docker", 8, "@release-manager")
|
||||
result = execute_docker(workflow_type)
|
||||
GATES['docker'].validate()
|
||||
comment_success(issue_number, "Docker", 8, result)
|
||||
|
||||
# Step 9: Documentation
|
||||
comment_start(issue_number, "Documentation", 9, "@system-analyst")
|
||||
result = execute_documentation(workflow_type)
|
||||
GATES['documentation'].validate()
|
||||
comment_success(issue_number, "Documentation", 9, result)
|
||||
|
||||
# Step 10: Delivery
|
||||
comment_start(issue_number, "Delivery", 10, "@evaluator")
|
||||
result = execute_delivery(workflow_type)
|
||||
GATES['delivery'].validate()
|
||||
comment_delivery(issue_number, project_name, workflow_type, result)
|
||||
|
||||
return {'success': True, 'issue': issue_number}
|
||||
|
||||
except QualityGateError as e:
|
||||
# BLOCK workflow - don't proceed
|
||||
comment_error(issue_number, e.gate_name, e.step_number, {
|
||||
'message': str(e),
|
||||
'type': 'QualityGateError',
|
||||
'blocker': f"Quality gate '{e.gate_name}' failed",
|
||||
'fix_steps': ['Review failed checks', 'Fix issues', 'Retry step'],
|
||||
'requirements': ['All quality checks must pass']
|
||||
})
|
||||
raise
|
||||
|
||||
except Exception as e:
|
||||
# BLOCK workflow - don't proceed
|
||||
comment_error(issue_number, "Unknown", 0, {
|
||||
'message': str(e),
|
||||
'type': type(e).__name__,
|
||||
'blocker': str(e),
|
||||
'fix_steps': ['Review error', 'Fix issue', 'Retry workflow'],
|
||||
'requirements': ['Error must be resolved']
|
||||
})
|
||||
raise
|
||||
```
|
||||
|
||||
## Usage in Workflows
|
||||
|
||||
Every workflow MUST follow this pattern:
|
||||
|
||||
```markdown
|
||||
## Step 0: Issue Creation (MANDATORY)
|
||||
|
||||
**Before any work starts:**
|
||||
|
||||
```python
|
||||
issue_number = create_workflow_issue(workflow_type, project_name)
|
||||
```
|
||||
|
||||
## Each Step MUST:
|
||||
|
||||
1. Post START comment to Gitea
|
||||
2. Execute step logic
|
||||
3. Validate quality gate
|
||||
4. Post SUCCESS/ERROR comment
|
||||
5. Update progress table
|
||||
|
||||
## Error Handling MUST:
|
||||
|
||||
1. Post ERROR comment to Gitea
|
||||
2. Block workflow (raise exception)
|
||||
3. NOT proceed to next step
|
||||
4. Wait for fix and retry
|
||||
```
|
||||
Reference in New Issue
Block a user