Skip to content

Commit 5c9dc54

Browse files
authored
Merge pull request #19 from agamm/add-license-compliance-agent
Add License Compliance Agent
2 parents 97e4382 + 6009d4f commit 5c9dc54

File tree

11 files changed

+963
-0
lines changed

11 files changed

+963
-0
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# License Compliance Agent
2+
3+
🔐 Automated license compliance checking for your codebase. Prevents license violations before merging code.
4+
5+
## Features
6+
7+
- **Multi-Language Support**: Python, Node.js, Rust, Ruby, Go, Java
8+
- **Smart Detection**: Finds dependencies in manifests, imports, and uv scripts
9+
- **Flexible Policies**: Configurable allowed/blocked license lists
10+
- **CI/CD Ready**: Works with GitHub Actions, GitLab CI, Jenkins, pre-commit
11+
12+
## Quick Start
13+
14+
```bash
15+
# Basic usage
16+
qodo --agent-file=qodo-agent.toml -y --set directory=./src
17+
18+
# Custom policies
19+
qodo --agent-file=qodo-agent.toml -y \
20+
--set directory=./src \
21+
--set allowed_licenses="MIT,Apache-2.0" \
22+
--set blocked_licenses="GPL-3.0,AGPL-3.0"
23+
```
24+
25+
## Configuration
26+
27+
### Arguments
28+
29+
| Argument | Type | Required | Default | Description |
30+
|----------|------|----------|---------|-------------|
31+
| `directory` | string || - | Directory path to scan for dependencies |
32+
| `allowed_licenses` | string || `MIT,BSD-2-Clause,BSD-3-Clause,Apache-2.0,ISC,Unlicense` | Comma-separated list of allowed licenses |
33+
| `blocked_licenses` | string || `GPL-3.0,AGPL-3.0,SSPL-1.0` | Comma-separated list of blocked licenses |
34+
| `ignore_dev_dependencies` | boolean || `true` | Skip checking development-only dependencies |
35+
36+
## Test with Examples
37+
38+
```bash
39+
qodo --agent-file=qodo-agent.toml -y --set directory=./examples/python
40+
```
41+
42+
Expected: MIT license passes ✅, GPL license fails ❌
43+
44+
## CI/CD Integration
45+
46+
See complete examples in `examples/ci/`:
47+
- GitHub Actions: `github-actions.yml`
48+
- GitLab CI: `gitlab-ci.yml`
49+
- Jenkins: `Jenkinsfile`
50+
- Pre-commit: `pre-commit-config.yaml`
51+
52+
## Supported Package Managers
53+
54+
| Language | Package Manager | Registry API | License Detection |
55+
|----------|----------------|--------------|-------------------|
56+
| Python | pip/uv | PyPI | ✅ Comprehensive |
57+
| Node.js | npm/yarn | npm Registry | ✅ Comprehensive |
58+
| Rust | cargo | crates.io | ✅ Comprehensive |
59+
| Ruby | gem | RubyGems | ✅ Comprehensive |
60+
| Go | go mod | pkg.go.dev | ⚠️ Limited |
61+
| Java | maven/gradle | Maven Central | ⚠️ Limited |
62+
63+
## Troubleshooting
64+
65+
### Common Issues
66+
67+
**Issue**: `curl: command not found`
68+
```bash
69+
# Ubuntu/Debian
70+
sudo apt-get install curl
71+
72+
# macOS
73+
brew install curl
74+
75+
# Alpine
76+
apk add curl
77+
```
78+
79+
**Issue**: `jq: command not found`
80+
```bash
81+
# Ubuntu/Debian
82+
sudo apt-get install jq
83+
84+
# macOS
85+
brew install jq
86+
87+
# Alpine
88+
apk add jq
89+
```
90+
91+
**Issue**: Package not found in registry
92+
- Verify package name spelling
93+
- Check if package exists in the registry
94+
- Some packages may have different names in different registries
95+
96+
**Issue**: License information unavailable
97+
- The agent will flag these for manual review
98+
- Check the package's GitHub repository for license information
99+
- Contact package maintainers if license is unclear
100+
101+
### Debug Mode
102+
103+
For verbose output, you can modify the agent to include debug information:
104+
105+
```bash
106+
qodo chat qodo-agent.toml
107+
```
108+
109+
Then ask: "Please scan ./src directory with verbose logging enabled"
110+
111+
## Contributing
112+
113+
1. Fork the repository
114+
2. Create a feature branch
115+
3. Make your changes
116+
4. Test with the provided examples
117+
5. Submit a pull request
118+
119+
## License
120+
121+
This project is licensed under the MIT License - see the LICENSE file for details.
122+
123+
## Support
124+
125+
- 📚 [Qodo Documentation](https://docs.qodo.ai/)
126+
- 💬 [Discord Community](https://discord.gg/qodo)
127+
- 🐛 [Report Issues](https://github.com/qodo-ai/agents/issues)
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
version = "1.0"
2+
3+
[commands.license_compliance_check]
4+
description = "Analyze code changes and dependencies to ensure license compliance before merging code into projects."
5+
6+
instructions = """
7+
You are a license compliance specialist responsible for ensuring all dependencies in a directory meet legal requirements.
8+
9+
IMPORTANT: You are analyzing existing code, NOT writing new scripts. Manually review and check dependencies.
10+
11+
Your workflow:
12+
13+
1. DIRECTORY SCANNING PHASE
14+
- List all files in the specified directory
15+
- Identify Python files, package manifests (requirements.txt, pyproject.toml, setup.py), and other dependency files
16+
- For Python files, scan for import statements and uv script dependencies (# dependencies = [...] sections)
17+
18+
2. DEPENDENCY EXTRACTION PHASE
19+
- Extract all dependencies from:
20+
* Python import statements (import X, from X import Y)
21+
* uv script dependency declarations in comments
22+
* requirements.txt, setup.py, pyproject.toml files
23+
- Create a comprehensive list of all dependencies found
24+
25+
3. LICENSE RESEARCH PHASE
26+
- For each dependency, use the appropriate package registry API + bash command + jq to extract the license:
27+
28+
* Python (PyPI):
29+
curl -s https://pypi.org/pypi/PACKAGE_NAME/json | jq -r '
30+
.info.license
31+
//
32+
(.info.classifiers[]
33+
| select(test("^License ::"))
34+
| sub("^License ::[[:space:]]*"; ""))'
35+
36+
* Node.js (npm):
37+
curl -s https://registry.npmjs.org/PACKAGE_NAME | jq -r '.license // .latest.license // .versions[.["dist-tags"].latest].license'
38+
39+
* Rust (crates.io):
40+
curl -s https://crates.io/api/v1/crates/PACKAGE_NAME | jq -r '.crate.license'
41+
42+
* Ruby (RubyGems):
43+
curl -s https://rubygems.org/api/v1/gems/PACKAGE_NAME.json | jq -r '.licenses[]?'
44+
45+
* Go (pkg.go.dev):
46+
curl -s "https://api.pkg.go.dev/v1/module/MODULE_NAME" | jq -r '.License // "License information not available"'
47+
48+
* Java Maven:
49+
curl -s "https://search.maven.org/solrsearch/select?q=g:GROUP_ID+AND+a:ARTIFACT_ID&core=gav&rows=1&wt=json" | jq -r '.response.docs[0] | .license // "License information not available"'
50+
51+
- Build a comprehensive list of all licenses found
52+
- If license information is not available via API, note it for manual review
53+
54+
4. LICENSE COMPATIBILITY ASSESSMENT
55+
- Compare each discovered license against the allowed/blocked license lists
56+
- Apply standard license compatibility rules:
57+
* Permissive licenses (MIT, BSD, Apache 2.0) - generally safe
58+
* Weak copyleft (LGPL, MPL) - requires careful consideration
59+
* Strong copyleft (GPL v2/v3) - may require project to be GPL
60+
* Network copyleft (AGPL) - requires source disclosure for network services
61+
* Proprietary/Commercial - requires explicit licensing agreement
62+
- Flag any licenses that require legal review
63+
64+
5. COMPLIANCE REPORTING PHASE
65+
- Generate a detailed compliance report with:
66+
* Summary of compliance status (PASS/FAIL/REVIEW_REQUIRED)
67+
* List of all dependencies with their licenses
68+
* Any license violations or concerns found
69+
* Specific recommendations for resolving issues
70+
* Alternative dependency suggestions for problematic licenses
71+
- Provide clear guidance on compliance status
72+
73+
Always prioritize conservative compliance - when in doubt, flag for legal review rather than risking violations.
74+
"""
75+
76+
arguments = [
77+
{ name = "directory", type = "string", required = true, description = "Directory path to scan for dependencies and license compliance" },
78+
{ name = "allowed_licenses", type = "string", required = false, description = "Comma-separated list of explicitly allowed licenses", default = "MIT,BSD-2-Clause,BSD-3-Clause,Apache-2.0,ISC,Unlicense" },
79+
{ name = "blocked_licenses", type = "string", required = false, description = "Comma-separated list of blocked licenses that should never be allowed", default = "GPL-3.0,AGPL-3.0,SSPL-1.0" },
80+
{ name = "ignore_dev_dependencies", type = "boolean", required = false, description = "Skip license checking for development-only dependencies", default = true }
81+
]
82+
83+
mcpServers = """{}"""
84+
85+
tools = ["git", "filesystem", "shell"]
86+
87+
execution_strategy = "act"
88+
89+
output_schema = """
90+
{
91+
"properties": {
92+
"compliance_status": {
93+
"description": "Overall compliance status",
94+
"title": "Compliance Status",
95+
"type": "string",
96+
"enum": ["PASS", "FAIL", "REVIEW_REQUIRED"]
97+
},
98+
"new_dependencies": {
99+
"description": "New dependencies introduced in this change",
100+
"title": "New Dependencies",
101+
"type": "array",
102+
"items": {
103+
"type": "object",
104+
"properties": {
105+
"name": { "type": "string" },
106+
"license": { "type": "string" },
107+
"source": { "type": "string" },
108+
"status": {
109+
"type": "string",
110+
"enum": ["allowed", "blocked", "review_required", "unknown"]
111+
}
112+
},
113+
"required": ["name", "license", "status"]
114+
}
115+
},
116+
"license_violations": {
117+
"description": "License compliance violations found",
118+
"title": "License Violations",
119+
"type": "array",
120+
"items": {
121+
"type": "object",
122+
"properties": {
123+
"type": {
124+
"type": "string",
125+
"enum": ["blocked_license", "missing_license", "incompatible_license", "missing_attribution", "copyleft_risk"]
126+
},
127+
"severity": {
128+
"type": "string",
129+
"enum": ["high", "medium", "low"]
130+
},
131+
"description": { "type": "string" },
132+
"component": { "type": "string" },
133+
"recommendation": { "type": "string" }
134+
},
135+
"required": ["type", "severity", "description", "component"]
136+
}
137+
},
138+
"recommendations": {
139+
"description": "Specific recommendations for resolving issues",
140+
"title": "Recommendations",
141+
"type": "array",
142+
"items": { "type": "string" }
143+
},
144+
"summary": {
145+
"description": "Human-readable summary of the license compliance analysis",
146+
"title": "Summary",
147+
"type": "string"
148+
},
149+
"safe_to_merge": {
150+
"description": "Whether the changes can be safely merged without license concerns",
151+
"title": "Safe to Merge",
152+
"type": "boolean"
153+
}
154+
},
155+
"required": ["compliance_status", "new_dependencies", "license_violations", "summary", "safe_to_merge"]
156+
}
157+
"""
158+
159+
exit_expression = "safe_to_merge"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
pipeline {
2+
agent any
3+
4+
stages {
5+
stage('License Compliance') {
6+
steps {
7+
sh '''
8+
curl -fsSL https://install.qodo.ai | sh
9+
qodo --agent-file=qodo-agent.toml -y --set directory=./src
10+
'''
11+
}
12+
post {
13+
always {
14+
archiveArtifacts artifacts: 'license-report.json', allowEmptyArchive: true
15+
}
16+
failure {
17+
emailext (
18+
subject: "License Compliance Check Failed - ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
19+
body: "License compliance check failed. Please review the attached report.",
20+
to: "${env.CHANGE_AUTHOR_EMAIL}"
21+
)
22+
}
23+
}
24+
}
25+
}
26+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/bash
2+
# Docker-based license compliance check
3+
# Usage: ./docker-example.sh /path/to/project
4+
5+
PROJECT_DIR=${1:-$(pwd)}
6+
7+
docker run --rm \
8+
-v "$PROJECT_DIR:/workspace" \
9+
-w /workspace \
10+
ubuntu:latest \
11+
bash -c "
12+
apt-get update && apt-get install -y curl jq
13+
curl -fsSL https://install.qodo.ai | sh
14+
qodo --agent-file=qodo-agent.toml -y --set directory=./src
15+
"
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: License Compliance Check
2+
3+
on:
4+
pull_request:
5+
branches: [main, develop]
6+
push:
7+
branches: [main]
8+
9+
jobs:
10+
license-compliance:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v4
16+
17+
- name: Install dependencies
18+
run: |
19+
curl -fsSL https://install.qodo.ai | sh
20+
sudo apt-get update && sudo apt-get install -y jq
21+
22+
- name: Run License Compliance Check
23+
run: |
24+
qodo --agent-file=qodo-agent.toml -y --set directory=./src
25+
26+
- name: Upload compliance report
27+
if: always()
28+
uses: actions/upload-artifact@v3
29+
with:
30+
name: license-compliance-report
31+
path: license-report.json
32+
33+
- name: Comment PR with results
34+
if: github.event_name == 'pull_request' && failure()
35+
uses: actions/github-script@v6
36+
with:
37+
script: |
38+
github.rest.issues.createComment({
39+
issue_number: context.issue.number,
40+
owner: context.repo.owner,
41+
repo: context.repo.repo,
42+
body: '⚠️ License compliance check failed. Please review the license-compliance-report artifact for details.'
43+
})
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
license_compliance:
2+
stage: test
3+
image: ubuntu:latest
4+
before_script:
5+
- apt-get update && apt-get install -y curl jq
6+
- curl -fsSL https://install.qodo.ai | sh
7+
script:
8+
- qodo --agent-file=qodo-agent.toml -y --set directory=./src
9+
artifacts:
10+
reports:
11+
junit: license-report.xml
12+
when: always
13+
rules:
14+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
15+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

0 commit comments

Comments
 (0)