src2purl 1.3.4__tar.gz → 1.4.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.
Files changed (41) hide show
  1. src2purl-1.4.0/AUTHORS.md +9 -0
  2. {src2purl-1.3.4/src2purl.egg-info → src2purl-1.4.0}/PKG-INFO +103 -78
  3. {src2purl-1.3.4 → src2purl-1.4.0}/README.md +101 -77
  4. {src2purl-1.3.4 → src2purl-1.4.0}/pyproject.toml +1 -1
  5. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/__init__.py +1 -1
  6. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/core/orchestrator.py +27 -18
  7. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/core/package_identifier.py +6 -2
  8. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/integrations/oslili.py +63 -15
  9. {src2purl-1.3.4 → src2purl-1.4.0/src2purl.egg-info}/PKG-INFO +103 -78
  10. {src2purl-1.3.4 → src2purl-1.4.0}/src2purl.egg-info/SOURCES.txt +4 -1
  11. src2purl-1.4.0/tests/test_integration.py +35 -0
  12. src2purl-1.4.0/tests/test_src2purl.py +83 -0
  13. {src2purl-1.3.4 → src2purl-1.4.0}/LICENSE +0 -0
  14. {src2purl-1.3.4 → src2purl-1.4.0}/setup.cfg +0 -0
  15. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/cli/__init__.py +0 -0
  16. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/cli/main.py +0 -0
  17. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/cli/validate.py +0 -0
  18. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/core/__init__.py +0 -0
  19. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/core/cache.py +0 -0
  20. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/core/client.py +0 -0
  21. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/core/config.py +0 -0
  22. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/core/extractor.py +0 -0
  23. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/core/models.py +0 -0
  24. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/core/purl.py +0 -0
  25. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/core/scanner.py +0 -0
  26. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/core/scorer.py +0 -0
  27. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/core/subcomponent_detector.py +0 -0
  28. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/core/swhid.py +0 -0
  29. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/integrations/__init__.py +0 -0
  30. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/integrations/manifest_parser.py +0 -0
  31. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/integrations/upmex.py +0 -0
  32. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/search/__init__.py +0 -0
  33. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/search/hash_search.py +0 -0
  34. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/search/providers.py +0 -0
  35. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/search/strategies.py +0 -0
  36. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/utils/__init__.py +0 -0
  37. {src2purl-1.3.4 → src2purl-1.4.0}/src2id/utils/datetime_utils.py +0 -0
  38. {src2purl-1.3.4 → src2purl-1.4.0}/src2purl.egg-info/dependency_links.txt +0 -0
  39. {src2purl-1.3.4 → src2purl-1.4.0}/src2purl.egg-info/entry_points.txt +0 -0
  40. {src2purl-1.3.4 → src2purl-1.4.0}/src2purl.egg-info/requires.txt +0 -0
  41. {src2purl-1.3.4 → src2purl-1.4.0}/src2purl.egg-info/top_level.txt +0 -0
@@ -0,0 +1,9 @@
1
+ # Authors
2
+
3
+ ## Project Lead
4
+
5
+ * Oscar Valenzuela B. - Project creator and maintainer
6
+
7
+ ---
8
+
9
+ For a complete list of all contributors, please see the [GitHub contributors page](https://github.com/SemClone/src2purl/graphs/contributors).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: src2purl
3
- Version: 1.3.4
3
+ Version: 1.4.0
4
4
  Summary: Source to PURL - Identify package coordinates and PURLs from source code using UPMEX and multiple discovery strategies
5
5
  Author-email: "Oscar Valenzuela B." <oscar.valenzuela.b@gmail.com>
6
6
  License: AGPL-3.0
@@ -20,6 +20,7 @@ Classifier: Topic :: Software Development :: Version Control
20
20
  Requires-Python: >=3.9
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
+ License-File: AUTHORS.md
23
24
  Requires-Dist: aiohttp>=3.9.0
24
25
  Requires-Dist: click>=8.1.0
25
26
  Requires-Dist: rich>=13.0.0
@@ -43,84 +44,48 @@ Provides-Extra: enhanced
43
44
  Requires-Dist: osslili>=1.3.3; extra == "enhanced"
44
45
  Dynamic: license-file
45
46
 
46
- # SRC2PURL - Source Code to Package URL
47
+ # SRC2PURL - Source Code to Package URL Discovery
47
48
 
48
- A Python tool that identifies package coordinates (name, version, license, PURL) from source code directories using an hybrid discovery strategy with manifest parsing, code fingerprinting, repository search, and Software Heritage archive.
49
+ [![License](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://opensource.org/licenses/AGPL-3.0)
50
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
51
+ [![PyPI version](https://img.shields.io/pypi/v/src2purl.svg)](https://pypi.org/project/src2purl/)
49
52
 
50
- ## Overview
51
-
52
- src2purl uses a **progressive 2-phase discovery strategy** to identify packages:
53
-
54
- ### **Phase 1: Hash-based Package Discovery** (5-15 seconds)
55
- **Primary identification using content fingerprinting and repository search:**
56
-
57
- 1. **Directory & Content Scanning** - Generate SWHIDs for directories and files
58
- - ✅ **Complete file tree analysis** with configurable depth
59
- - ✅ **SWHID generation** for precise content identification
60
- - ✅ **Smart filtering** of binary files and hidden directories
61
-
62
- 2. **Software Heritage Archive** (Optional) - Deep provenance discovery
63
- - ✅ **Most comprehensive** - finds exact source code matches
64
- - ✅ **Historical accuracy** - can identify older versions
65
- - ⚠️ **Requires opt-in** with `--use-swh` due to longer processing time
66
-
67
- 3. **Keyword Search** (Fallback) - Multi-platform repository discovery
68
- - **GitHub API Search** - Repository identification by keywords
69
- - **SCANOSS Fingerprinting** - Code similarity detection via file fingerprints
70
- - ✅ **Universal coverage** - finds repositories for any project
71
- - ✅ **Fast execution** (~10 seconds total)
72
-
73
- ### **Phase 2: Manifest-based Validation & Enhancement** (1-3 seconds)
74
- **Authoritative package metadata extraction and result enhancement:**
75
-
76
- 4. **UPMEX Manifest Parsing** - Universal Package Metadata Extractor
77
- - ✅ **Perfect metadata extraction** from package files (package.json, setup.py, pom.xml, go.mod, Cargo.toml, etc.)
78
- - ✅ **Multi-ecosystem support** (PyPI, NPM, Maven, Go, Ruby Gems, PHP, .NET)
79
- - ✅ **Complete package info** (name, version, license, PURL)
80
- - ✅ **Cross-validation** with Phase 1 results for enhanced accuracy
53
+ A Python tool that identifies package coordinates (name, version, license, PURL) from source code directories using a hybrid discovery strategy with manifest parsing, code fingerprinting, repository search, and Software Heritage archive integration.
81
54
 
82
55
  ## Features
83
56
 
84
- ### **Core Capabilities**
85
57
  - **2-Phase Discovery Strategy**: Hash-based identification enhanced by manifest parsing
86
58
  - **Multi-Ecosystem Support**: PyPI, NPM, Maven, Go, Ruby Gems, PHP, .NET, and more
87
- - **Cross-Phase Validation**: UPMEX manifest data validates and enhances hash-based findings
88
- - **Confidence Scoring**: Multi-factor scoring (85-100% for exact matches)
89
- - **Package Coordinate Extraction**: Complete metadata (name, version, license, PURL)
90
-
91
- ### **Performance & Reliability**
92
- - **Fast by Default**: 5-15 seconds for typical projects (vs 90+ seconds with SWH)
93
- - **No API Keys Required**: Works well without authentication (SCANOSS, GitHub search)
94
- - **Optional API Keys**: Enhanced rate limits and accuracy with GitHub/SCANOSS tokens
95
- - **Persistent Caching**: File-based cache with smart TTL to avoid API rate limits
96
- - **Rate Limit Handling**: Automatic backoff and retry logic
97
-
98
- ### **Discovery Methods**
99
- - **UPMEX Manifest Parsing**: Universal Package Metadata Extractor for all major package ecosystems
100
- - **SCANOSS Fingerprinting**: 100% accuracy code similarity with detailed license detection
101
- - **GitHub Repository Search**: Universal coverage repository identification
102
- - **Software Heritage Archive**: Comprehensive source inventory (opt-in with `--use-swh`)
103
-
104
- ### **Output & Integration**
105
- - **Multiple Output Formats**: JSON and table output formats
106
- - **PURL Generation**: Standard Package URLs for identified packages
107
- - **Enhanced License Detection**: Integration with oslili for improved license detection
108
- - **Subcomponent Detection**: Identifies multiple packages within monorepos and complex projects
59
+ - **Fast Performance**: 5-15 seconds for typical projects (vs 90+ seconds with SWH)
60
+ - **SEMCL.ONE Integration**: Works seamlessly with upmex, osslili, and other ecosystem tools
109
61
 
110
62
  ## Installation
111
63
 
112
- ### From Source
64
+ ```bash
65
+ pip install src2purl
66
+ ```
113
67
 
68
+ For development:
114
69
  ```bash
115
- git clone https://github.com/oscarvalenzuelab/src2purl.git
70
+ git clone https://github.com/SemClone/src2purl.git
116
71
  cd src2purl
117
72
  pip install -e .
118
73
  ```
119
74
 
75
+ ## Quick Start
76
+
77
+ ```bash
78
+ # Identify package from source code
79
+ src2purl /path/to/source/code
80
+
81
+ # With Software Heritage archive (comprehensive but slower)
82
+ src2purl /path/to/source --use-swh
83
+ ```
84
+
120
85
 
121
86
  ## Usage
122
87
 
123
- ### Basic Usage
88
+ ### CLI Usage
124
89
 
125
90
  ```bash
126
91
  # Fast discovery (default) - Uses manifest parsing + SCANOSS + GitHub (5-15 seconds)
@@ -137,15 +102,24 @@ src2purl /path/to/source --output-format json
137
102
 
138
103
  # Detect subcomponents in monorepos
139
104
  src2purl /path/to/source --detect-subcomponents
105
+ ```
140
106
 
141
- # Skip license detection (faster)
142
- src2purl /path/to/source --no-license-detection
107
+ ### Python API
143
108
 
144
- # Verbose output for debugging
145
- src2purl /path/to/source --verbose
109
+ ```python
110
+ from src2purl import identify_package
146
111
 
147
- # Clear cache and exit
148
- src2purl --clear-cache
112
+ # Basic identification
113
+ result = identify_package("/path/to/source")
114
+ print(f"Package: {result.name}@{result.version}")
115
+ print(f"PURL: {result.purl}")
116
+
117
+ # With options
118
+ result = identify_package(
119
+ path="/path/to/source",
120
+ use_swh=True,
121
+ confidence_threshold=0.85
122
+ )
149
123
  ```
150
124
 
151
125
  ### Discovery Strategy Examples
@@ -169,7 +143,7 @@ src2purl /path/to/project --confidence-threshold 0.85
169
143
 
170
144
  ### API Authentication
171
145
 
172
- **⚠️ No API keys required!** The tool works with the free public APIs. API keys only provide enhanced rate limits and additional features.
146
+ **No API keys required!** The tool works with the free public APIs. API keys only provide enhanced rate limits and additional features.
173
147
 
174
148
  #### Recommended API Keys (Optional)
175
149
 
@@ -179,9 +153,9 @@ export GITHUB_TOKEN=your_github_personal_access_token
179
153
  ```
180
154
  - **Get token**: https://github.com/settings/tokens (no special permissions needed)
181
155
  - **Benefits**:
182
- - ✅ **Rate limit**: 10 → 5000 requests/hour
183
- - ✅ **Better search**: More accurate repository identification
184
- - ✅ **No cost**: Completely free
156
+ - **Rate limit**: 10 → 5000 requests/hour
157
+ - **Better search**: More accurate repository identification
158
+ - **No cost**: Completely free
185
159
  - **Impact**: Significant improvement for repository discovery
186
160
 
187
161
  **2. SCANOSS API** - **Nice to Have** (Free, optional)
@@ -190,9 +164,9 @@ export SCANOSS_API_KEY=your_scanoss_key
190
164
  ```
191
165
  - **Get token**: Register at https://www.scanoss.com
192
166
  - **Benefits**:
193
- - ✅ **No cost**: Free tier available
194
- - ✅ **Enhanced rate limits**: Premium API endpoint
195
- - ✅ **Additional features**: Possible extra metadata
167
+ - **No cost**: Free tier available
168
+ - **Enhanced rate limits**: Premium API endpoint
169
+ - **Additional features**: Possible extra metadata
196
170
  - **Impact**: Minor improvement (SCANOSS works great without key)
197
171
 
198
172
  **3. Software Heritage API** - **For Heavy Usage** (Free, only if using `--use-swh`)
@@ -201,8 +175,8 @@ export SWH_API_TOKEN=your_swh_token
201
175
  ```
202
176
  - **Get token**: Register at https://archive.softwareheritage.org/api/
203
177
  - **Benefits**:
204
- - ✅ **Bypass rate limits**: No 60-second waits
205
- - ✅ **Faster comprehensive scans**: When using `--use-swh`
178
+ - **Bypass rate limits**: No 60-second waits
179
+ - **Faster comprehensive scans**: When using `--use-swh`
206
180
  - **Impact**: Essential for `--use-swh` flag, not needed for default fast mode
207
181
 
208
182
  #### Performance Comparison
@@ -270,10 +244,61 @@ src2purl /path/to/project --no-license-detection # Skip license enhancement
270
244
  src2purl /path/to/project --use-swh --verbose # Full discovery with details
271
245
  ```
272
246
 
247
+ ## Integration with SEMCL.ONE
248
+
249
+ SRC2PURL is a core component of the SEMCL.ONE ecosystem:
250
+
251
+ - Uses **upmex** for manifest parsing and package metadata extraction
252
+ - Integrates with **osslili** for enhanced license detection
253
+ - Provides PURLs for **purl2src** to download source packages
254
+ - Supports **purl2notices** for generating attribution documentation
255
+ - Enables **ospac** policy evaluation with identified packages
256
+
257
+ ## Configuration
258
+
259
+ Configuration via environment variables:
260
+
261
+ ```bash
262
+ # API tokens (optional but recommended)
263
+ export GITHUB_TOKEN=your_github_token
264
+ export SCANOSS_API_KEY=your_scanoss_key
265
+ export SWH_API_TOKEN=your_swh_token
266
+
267
+ # Performance settings
268
+ export SRC2PURL_CACHE_DIR=~/.cache/src2purl
269
+ export SRC2PURL_MAX_DEPTH=2
270
+ ```
271
+
272
+ ## Documentation
273
+
274
+ - [User Guide](docs/user-guide.md) - Comprehensive usage examples
275
+ - [API Reference](docs/api.md) - Python API documentation
276
+ - [Discovery Methods](docs/discovery-methods.md) - Detailed explanation of identification strategies
277
+ - [Examples](docs/examples.md) - Common use cases and workflows
278
+
279
+ ## Contributing
280
+
281
+ We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on:
282
+ - Code of conduct
283
+ - Development setup
284
+ - Submitting pull requests
285
+ - Reporting issues
286
+
287
+ ## Support
288
+
289
+ For support and questions:
290
+ - [GitHub Issues](https://github.com/SemClone/src2purl/issues) - Bug reports and feature requests
291
+ - [Documentation](https://github.com/SemClone/src2purl) - Complete project documentation
292
+ - [SEMCL.ONE Community](https://semcl.one) - Ecosystem support and discussions
293
+
273
294
  ## License
274
295
 
275
- This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0) - see the LICENSE file for details.
296
+ GNU Affero General Public License v3.0 (AGPL-3.0) - see [LICENSE](LICENSE) file for details.
297
+
298
+ ## Authors
299
+
300
+ See [AUTHORS.md](AUTHORS.md) for a list of contributors.
276
301
 
277
- ## Status
302
+ ---
278
303
 
279
- This project is currently in active development. See the [Issues](https://github.com/oscarvalenzuelab/src2purl/issues) page for planned features and known issues.
304
+ *Part of the [SEMCL.ONE](https://semcl.one) ecosystem for comprehensive OSS compliance and code analysis.*
@@ -1,81 +1,45 @@
1
- # SRC2PURL - Source Code to Package URL
1
+ # SRC2PURL - Source Code to Package URL Discovery
2
2
 
3
- A Python tool that identifies package coordinates (name, version, license, PURL) from source code directories using an hybrid discovery strategy with manifest parsing, code fingerprinting, repository search, and Software Heritage archive.
3
+ [![License](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://opensource.org/licenses/AGPL-3.0)
4
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
5
+ [![PyPI version](https://img.shields.io/pypi/v/src2purl.svg)](https://pypi.org/project/src2purl/)
4
6
 
5
- ## Overview
6
-
7
- src2purl uses a **progressive 2-phase discovery strategy** to identify packages:
8
-
9
- ### **Phase 1: Hash-based Package Discovery** (5-15 seconds)
10
- **Primary identification using content fingerprinting and repository search:**
11
-
12
- 1. **Directory & Content Scanning** - Generate SWHIDs for directories and files
13
- - ✅ **Complete file tree analysis** with configurable depth
14
- - ✅ **SWHID generation** for precise content identification
15
- - ✅ **Smart filtering** of binary files and hidden directories
16
-
17
- 2. **Software Heritage Archive** (Optional) - Deep provenance discovery
18
- - ✅ **Most comprehensive** - finds exact source code matches
19
- - ✅ **Historical accuracy** - can identify older versions
20
- - ⚠️ **Requires opt-in** with `--use-swh` due to longer processing time
21
-
22
- 3. **Keyword Search** (Fallback) - Multi-platform repository discovery
23
- - **GitHub API Search** - Repository identification by keywords
24
- - **SCANOSS Fingerprinting** - Code similarity detection via file fingerprints
25
- - ✅ **Universal coverage** - finds repositories for any project
26
- - ✅ **Fast execution** (~10 seconds total)
27
-
28
- ### **Phase 2: Manifest-based Validation & Enhancement** (1-3 seconds)
29
- **Authoritative package metadata extraction and result enhancement:**
30
-
31
- 4. **UPMEX Manifest Parsing** - Universal Package Metadata Extractor
32
- - ✅ **Perfect metadata extraction** from package files (package.json, setup.py, pom.xml, go.mod, Cargo.toml, etc.)
33
- - ✅ **Multi-ecosystem support** (PyPI, NPM, Maven, Go, Ruby Gems, PHP, .NET)
34
- - ✅ **Complete package info** (name, version, license, PURL)
35
- - ✅ **Cross-validation** with Phase 1 results for enhanced accuracy
7
+ A Python tool that identifies package coordinates (name, version, license, PURL) from source code directories using a hybrid discovery strategy with manifest parsing, code fingerprinting, repository search, and Software Heritage archive integration.
36
8
 
37
9
  ## Features
38
10
 
39
- ### **Core Capabilities**
40
11
  - **2-Phase Discovery Strategy**: Hash-based identification enhanced by manifest parsing
41
12
  - **Multi-Ecosystem Support**: PyPI, NPM, Maven, Go, Ruby Gems, PHP, .NET, and more
42
- - **Cross-Phase Validation**: UPMEX manifest data validates and enhances hash-based findings
43
- - **Confidence Scoring**: Multi-factor scoring (85-100% for exact matches)
44
- - **Package Coordinate Extraction**: Complete metadata (name, version, license, PURL)
45
-
46
- ### **Performance & Reliability**
47
- - **Fast by Default**: 5-15 seconds for typical projects (vs 90+ seconds with SWH)
48
- - **No API Keys Required**: Works well without authentication (SCANOSS, GitHub search)
49
- - **Optional API Keys**: Enhanced rate limits and accuracy with GitHub/SCANOSS tokens
50
- - **Persistent Caching**: File-based cache with smart TTL to avoid API rate limits
51
- - **Rate Limit Handling**: Automatic backoff and retry logic
52
-
53
- ### **Discovery Methods**
54
- - **UPMEX Manifest Parsing**: Universal Package Metadata Extractor for all major package ecosystems
55
- - **SCANOSS Fingerprinting**: 100% accuracy code similarity with detailed license detection
56
- - **GitHub Repository Search**: Universal coverage repository identification
57
- - **Software Heritage Archive**: Comprehensive source inventory (opt-in with `--use-swh`)
58
-
59
- ### **Output & Integration**
60
- - **Multiple Output Formats**: JSON and table output formats
61
- - **PURL Generation**: Standard Package URLs for identified packages
62
- - **Enhanced License Detection**: Integration with oslili for improved license detection
63
- - **Subcomponent Detection**: Identifies multiple packages within monorepos and complex projects
13
+ - **Fast Performance**: 5-15 seconds for typical projects (vs 90+ seconds with SWH)
14
+ - **SEMCL.ONE Integration**: Works seamlessly with upmex, osslili, and other ecosystem tools
64
15
 
65
16
  ## Installation
66
17
 
67
- ### From Source
18
+ ```bash
19
+ pip install src2purl
20
+ ```
68
21
 
22
+ For development:
69
23
  ```bash
70
- git clone https://github.com/oscarvalenzuelab/src2purl.git
24
+ git clone https://github.com/SemClone/src2purl.git
71
25
  cd src2purl
72
26
  pip install -e .
73
27
  ```
74
28
 
29
+ ## Quick Start
30
+
31
+ ```bash
32
+ # Identify package from source code
33
+ src2purl /path/to/source/code
34
+
35
+ # With Software Heritage archive (comprehensive but slower)
36
+ src2purl /path/to/source --use-swh
37
+ ```
38
+
75
39
 
76
40
  ## Usage
77
41
 
78
- ### Basic Usage
42
+ ### CLI Usage
79
43
 
80
44
  ```bash
81
45
  # Fast discovery (default) - Uses manifest parsing + SCANOSS + GitHub (5-15 seconds)
@@ -92,15 +56,24 @@ src2purl /path/to/source --output-format json
92
56
 
93
57
  # Detect subcomponents in monorepos
94
58
  src2purl /path/to/source --detect-subcomponents
59
+ ```
95
60
 
96
- # Skip license detection (faster)
97
- src2purl /path/to/source --no-license-detection
61
+ ### Python API
98
62
 
99
- # Verbose output for debugging
100
- src2purl /path/to/source --verbose
63
+ ```python
64
+ from src2purl import identify_package
101
65
 
102
- # Clear cache and exit
103
- src2purl --clear-cache
66
+ # Basic identification
67
+ result = identify_package("/path/to/source")
68
+ print(f"Package: {result.name}@{result.version}")
69
+ print(f"PURL: {result.purl}")
70
+
71
+ # With options
72
+ result = identify_package(
73
+ path="/path/to/source",
74
+ use_swh=True,
75
+ confidence_threshold=0.85
76
+ )
104
77
  ```
105
78
 
106
79
  ### Discovery Strategy Examples
@@ -124,7 +97,7 @@ src2purl /path/to/project --confidence-threshold 0.85
124
97
 
125
98
  ### API Authentication
126
99
 
127
- **⚠️ No API keys required!** The tool works with the free public APIs. API keys only provide enhanced rate limits and additional features.
100
+ **No API keys required!** The tool works with the free public APIs. API keys only provide enhanced rate limits and additional features.
128
101
 
129
102
  #### Recommended API Keys (Optional)
130
103
 
@@ -134,9 +107,9 @@ export GITHUB_TOKEN=your_github_personal_access_token
134
107
  ```
135
108
  - **Get token**: https://github.com/settings/tokens (no special permissions needed)
136
109
  - **Benefits**:
137
- - ✅ **Rate limit**: 10 → 5000 requests/hour
138
- - ✅ **Better search**: More accurate repository identification
139
- - ✅ **No cost**: Completely free
110
+ - **Rate limit**: 10 → 5000 requests/hour
111
+ - **Better search**: More accurate repository identification
112
+ - **No cost**: Completely free
140
113
  - **Impact**: Significant improvement for repository discovery
141
114
 
142
115
  **2. SCANOSS API** - **Nice to Have** (Free, optional)
@@ -145,9 +118,9 @@ export SCANOSS_API_KEY=your_scanoss_key
145
118
  ```
146
119
  - **Get token**: Register at https://www.scanoss.com
147
120
  - **Benefits**:
148
- - ✅ **No cost**: Free tier available
149
- - ✅ **Enhanced rate limits**: Premium API endpoint
150
- - ✅ **Additional features**: Possible extra metadata
121
+ - **No cost**: Free tier available
122
+ - **Enhanced rate limits**: Premium API endpoint
123
+ - **Additional features**: Possible extra metadata
151
124
  - **Impact**: Minor improvement (SCANOSS works great without key)
152
125
 
153
126
  **3. Software Heritage API** - **For Heavy Usage** (Free, only if using `--use-swh`)
@@ -156,8 +129,8 @@ export SWH_API_TOKEN=your_swh_token
156
129
  ```
157
130
  - **Get token**: Register at https://archive.softwareheritage.org/api/
158
131
  - **Benefits**:
159
- - ✅ **Bypass rate limits**: No 60-second waits
160
- - ✅ **Faster comprehensive scans**: When using `--use-swh`
132
+ - **Bypass rate limits**: No 60-second waits
133
+ - **Faster comprehensive scans**: When using `--use-swh`
161
134
  - **Impact**: Essential for `--use-swh` flag, not needed for default fast mode
162
135
 
163
136
  #### Performance Comparison
@@ -225,10 +198,61 @@ src2purl /path/to/project --no-license-detection # Skip license enhancement
225
198
  src2purl /path/to/project --use-swh --verbose # Full discovery with details
226
199
  ```
227
200
 
201
+ ## Integration with SEMCL.ONE
202
+
203
+ SRC2PURL is a core component of the SEMCL.ONE ecosystem:
204
+
205
+ - Uses **upmex** for manifest parsing and package metadata extraction
206
+ - Integrates with **osslili** for enhanced license detection
207
+ - Provides PURLs for **purl2src** to download source packages
208
+ - Supports **purl2notices** for generating attribution documentation
209
+ - Enables **ospac** policy evaluation with identified packages
210
+
211
+ ## Configuration
212
+
213
+ Configuration via environment variables:
214
+
215
+ ```bash
216
+ # API tokens (optional but recommended)
217
+ export GITHUB_TOKEN=your_github_token
218
+ export SCANOSS_API_KEY=your_scanoss_key
219
+ export SWH_API_TOKEN=your_swh_token
220
+
221
+ # Performance settings
222
+ export SRC2PURL_CACHE_DIR=~/.cache/src2purl
223
+ export SRC2PURL_MAX_DEPTH=2
224
+ ```
225
+
226
+ ## Documentation
227
+
228
+ - [User Guide](docs/user-guide.md) - Comprehensive usage examples
229
+ - [API Reference](docs/api.md) - Python API documentation
230
+ - [Discovery Methods](docs/discovery-methods.md) - Detailed explanation of identification strategies
231
+ - [Examples](docs/examples.md) - Common use cases and workflows
232
+
233
+ ## Contributing
234
+
235
+ We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on:
236
+ - Code of conduct
237
+ - Development setup
238
+ - Submitting pull requests
239
+ - Reporting issues
240
+
241
+ ## Support
242
+
243
+ For support and questions:
244
+ - [GitHub Issues](https://github.com/SemClone/src2purl/issues) - Bug reports and feature requests
245
+ - [Documentation](https://github.com/SemClone/src2purl) - Complete project documentation
246
+ - [SEMCL.ONE Community](https://semcl.one) - Ecosystem support and discussions
247
+
228
248
  ## License
229
249
 
230
- This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0) - see the LICENSE file for details.
250
+ GNU Affero General Public License v3.0 (AGPL-3.0) - see [LICENSE](LICENSE) file for details.
251
+
252
+ ## Authors
253
+
254
+ See [AUTHORS.md](AUTHORS.md) for a list of contributors.
231
255
 
232
- ## Status
256
+ ---
233
257
 
234
- This project is currently in active development. See the [Issues](https://github.com/oscarvalenzuelab/src2purl/issues) page for planned features and known issues.
258
+ *Part of the [SEMCL.ONE](https://semcl.one) ecosystem for comprehensive OSS compliance and code analysis.*
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "src2purl"
7
- version = "1.3.4"
7
+ version = "1.4.0"
8
8
  description = "Source to PURL - Identify package coordinates and PURLs from source code using UPMEX and multiple discovery strategies"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -6,7 +6,7 @@ using multiple strategies including SCANOSS, hash search, web search, and
6
6
  optionally Software Heritage archive.
7
7
  """
8
8
 
9
- __version__ = "1.3.3"
9
+ __version__ = "1.3.5"
10
10
  __author__ = "Oscar Valenzuela B."
11
11
 
12
12
  from src2id.core.config import SWHPIConfig
@@ -141,16 +141,6 @@ class SHPackageIdentifier:
141
141
  if self.config.verbose:
142
142
  console.print("[dim]Skipping Software Heritage (use --use-swh to enable)[/dim]")
143
143
 
144
- if not all_matches:
145
- # Try keyword search with GitHub and SCANOSS
146
- if self.config.verbose:
147
- console.print("[yellow]Trying web search (GitHub, SCANOSS)[/yellow]")
148
-
149
- # Try keyword search
150
- keyword_matches = await self._find_keyword_matches(path)
151
- if keyword_matches:
152
- all_matches = keyword_matches
153
-
154
144
  # Step 1b: Process hash-based matches
155
145
  if all_matches:
156
146
  hash_based_matches = await self._process_matches(all_matches)
@@ -178,6 +168,27 @@ class SHPackageIdentifier:
178
168
 
179
169
  # Final deduplication and sorting
180
170
  final_matches = self._prioritize_and_deduplicate(enhanced_matches)
171
+
172
+ # Keyword search is the last resort, and only when it was asked for.
173
+ # It searches on the project name, so it answers with whatever else
174
+ # shares that name: running it for express returned ten npm packages
175
+ # scoring 0.83 against the real one at 0.85, close enough that a
176
+ # confidence threshold cannot separate them, and carrying no PURL so
177
+ # nothing downstream can act on them either.
178
+ #
179
+ # It also belongs here rather than in phase 1. The flag says fuzzy
180
+ # runs when exact matches fail, and whether they failed is not known
181
+ # until the manifest parsing in phase 2 has had its turn.
182
+ if not final_matches and self.config.enable_fuzzy_matching:
183
+ if self.config.verbose:
184
+ console.print(
185
+ "[yellow]Nothing identified - trying keyword search[/yellow]"
186
+ )
187
+ keyword_matches = await self._find_keyword_matches(path)
188
+ if keyword_matches:
189
+ final_matches = self._prioritize_and_deduplicate(
190
+ await self._process_matches(keyword_matches)
191
+ )
181
192
 
182
193
  # Step 5: Optionally enhance with oslili license detection
183
194
  if enhance_licenses:
@@ -588,14 +599,12 @@ class SHPackageIdentifier:
588
599
  for candidate in non_existing_dirs:
589
600
  console.print(f"[yellow]✗ No match for {candidate.path.name} ({candidate.swhid[:12]}...)[/yellow]")
590
601
 
591
- # Try keyword search fallback if no exact matches and fuzzy is enabled
592
- if not all_matches and self.config.enable_fuzzy_matching:
593
- if self.config.verbose:
594
- console.print("[yellow]No exact matches found - trying keyword search[/yellow]")
595
- # Only try keyword search if fuzzy matching is enabled
596
- keyword_matches = await self._find_keyword_matches(dir_candidates[0].path if dir_candidates else Path("."))
597
- all_matches.extend(keyword_matches)
598
-
602
+ # Keyword search is not run here. It used to be, and that put it before
603
+ # the manifest parsing in phase 2, so "when exact matches fail", which
604
+ # is what the flag promises, was decided before the step that finds the
605
+ # exact match had run. identify_packages runs it once, after everything
606
+ # else has had its turn.
607
+
599
608
  # Report match summary
600
609
  if self.config.verbose and (child_matches > 0 or parent_matches > 0):
601
610
  console.print("\n[bold]Match Summary:[/bold]")
@@ -82,8 +82,12 @@ class PackageIdentifier:
82
82
 
83
83
  if integration.available:
84
84
  license_info = integration.detect_licenses(path)
85
- if license_info["licenses"]:
86
- match.license = ", ".join(license_info["licenses"][:3])
85
+ # Only the project's own licenses identify this package;
86
+ # licenses from bundled third-party notice files belong
87
+ # to its dependencies
88
+ own_licenses = license_info.get("own_licenses") or []
89
+ if own_licenses:
90
+ match.license = ", ".join(own_licenses[:3])
87
91
  except ImportError:
88
92
  pass
89
93
 
@@ -59,14 +59,20 @@ class OsliliIntegration:
59
59
  Returns:
60
60
  Dictionary with license information:
61
61
  - licenses: List of detected license identifiers
62
- - confidence: Confidence score (0-1)
62
+ - own_licenses: Identifiers eligible to be the project's own license
63
+ - third_party_licenses: Identifiers from bundled third-party notice files
64
+ - confidence: Confidence score (0-1) across all detections
65
+ - own_confidence: Confidence score (0-1) across own licenses only
63
66
  - files: Dict mapping files to their licenses
64
67
  - summary: Human-readable summary
65
68
  """
66
69
  if not self.available or not self.detector:
67
70
  return {
68
71
  "licenses": [],
72
+ "own_licenses": [],
73
+ "third_party_licenses": [],
69
74
  "confidence": 0.0,
75
+ "own_confidence": 0.0,
70
76
  "files": {},
71
77
  "summary": "oslili not available",
72
78
  "error": "oslili integration not available"
@@ -83,13 +89,19 @@ class OsliliIntegration:
83
89
  files = {}
84
90
 
85
91
  # Group licenses by category for better understanding
86
- declared = [l for l in result.licenses if l.category == "declared"]
87
- detected = [l for l in result.licenses if l.category == "detected"]
88
- referenced = [l for l in result.licenses if l.category == "referenced"]
89
-
90
- # Prioritize declared licenses, then detected, then referenced
91
- all_licenses = declared + detected + referenced
92
-
92
+ declared = [lic for lic in result.licenses if lic.category == "declared"]
93
+ detected = [lic for lic in result.licenses if lic.category == "detected"]
94
+ referenced = [lic for lic in result.licenses if lic.category == "referenced"]
95
+ # Licenses of bundled dependencies (THIRD_PARTY_NOTICES.txt and
96
+ # friends), not the project's own license. Kept, but ranked last so
97
+ # they never win the primary-license selection.
98
+ third_party = [lic for lic in result.licenses if lic.category == "third-party"]
99
+
100
+ # Prioritize declared licenses, then detected, then referenced,
101
+ # with bundled third-party licenses last
102
+ own = declared + detected + referenced
103
+ all_licenses = own + third_party
104
+
93
105
  for license_info in all_licenses:
94
106
  licenses.append(license_info.spdx_id)
95
107
  confidence_scores.append(license_info.confidence)
@@ -107,6 +119,12 @@ class OsliliIntegration:
107
119
 
108
120
  # Calculate average confidence
109
121
  avg_confidence = sum(confidence_scores) / len(confidence_scores) if confidence_scores else 0.8
122
+
123
+ # Confidence in the project's own licenses, kept separate so a pile of
124
+ # high-confidence bundled notice files cannot inflate the score used to
125
+ # decide the package's own license
126
+ own_scores = [lic.confidence for lic in own]
127
+ own_confidence = sum(own_scores) / len(own_scores) if own_scores else 0.0
110
128
 
111
129
  # Generate summary
112
130
  if licenses:
@@ -126,7 +144,15 @@ class OsliliIntegration:
126
144
 
127
145
  return {
128
146
  "licenses": licenses,
147
+ # Split view of the same licenses: "own" are candidates for the
148
+ # project's license, "third_party" come from bundled notice files
149
+ # and must not be attributed to the project itself.
150
+ "own_licenses": self._deduplicate_licenses([lic.spdx_id for lic in own]),
151
+ "third_party_licenses": self._deduplicate_licenses(
152
+ [lic.spdx_id for lic in third_party]
153
+ ),
129
154
  "confidence": avg_confidence,
155
+ "own_confidence": own_confidence,
130
156
  "files": files,
131
157
  "summary": summary,
132
158
  "copyrights": [c.to_dict() for c in result.copyrights] if result.copyrights else []
@@ -134,7 +160,10 @@ class OsliliIntegration:
134
160
  else:
135
161
  return {
136
162
  "licenses": [],
163
+ "own_licenses": [],
164
+ "third_party_licenses": [],
137
165
  "confidence": 0.0,
166
+ "own_confidence": 0.0,
138
167
  "files": {},
139
168
  "summary": "No licenses detected",
140
169
  "copyrights": []
@@ -143,7 +172,10 @@ class OsliliIntegration:
143
172
  except Exception as e:
144
173
  return {
145
174
  "licenses": [],
175
+ "own_licenses": [],
176
+ "third_party_licenses": [],
146
177
  "confidence": 0.0,
178
+ "own_confidence": 0.0,
147
179
  "files": {},
148
180
  "summary": f"Error during detection: {e}",
149
181
  "error": str(e)
@@ -170,17 +202,28 @@ class OsliliIntegration:
170
202
  # Detect licenses
171
203
  license_info = self.detect_licenses(path)
172
204
 
205
+ # Only the project's own licenses are eligible to become this package's
206
+ # license: one found in a bundled third-party notice file belongs to a
207
+ # dependency, so attributing it here would be wrong. Bundled notices must
208
+ # therefore neither raise nor lower the bar for the own license, which is
209
+ # why the decision below uses own_confidence rather than the aggregate.
210
+ own_licenses = self._deduplicate_licenses(license_info.get("own_licenses") or [])
211
+ own_confidence = license_info.get("own_confidence") or 0.0
212
+
173
213
  # Update match if we found licenses with good confidence
174
- if license_info["licenses"] and license_info["confidence"] > 0.7:
214
+ if license_info["licenses"] and (
215
+ license_info["confidence"] > 0.7 or own_confidence > 0.7
216
+ ):
175
217
  # Remove duplicates while preserving order
176
218
  unique_licenses = self._deduplicate_licenses(license_info["licenses"])
177
219
 
178
- # Use the most confident license as primary
179
- primary_license = unique_licenses[0]
180
-
181
- # Update match license if not already set or if ours is more confident
182
- if not match.license or license_info["confidence"] > 0.85:
183
- match.license = primary_license
220
+ # Update match license if not already set or if ours is more confident.
221
+ # When nothing but bundled notices was found there is no own license, so
222
+ # match.license is left alone rather than guessed at.
223
+ if own_licenses and own_confidence > 0.7 and (
224
+ not match.license or own_confidence > 0.85
225
+ ):
226
+ match.license = own_licenses[0]
184
227
 
185
228
  # Add metadata about additional licenses and copyrights
186
229
  if not hasattr(match, "metadata"):
@@ -188,6 +231,11 @@ class OsliliIntegration:
188
231
 
189
232
  if len(unique_licenses) > 1:
190
233
  match.metadata["additional_licenses"] = unique_licenses[1:]
234
+
235
+ # Surface bundled third-party licenses separately so callers can report
236
+ # them without mistaking them for the package's own license
237
+ if license_info.get("third_party_licenses"):
238
+ match.metadata["third_party_licenses"] = license_info["third_party_licenses"]
191
239
 
192
240
  match.metadata["license_confidence"] = license_info["confidence"]
193
241
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: src2purl
3
- Version: 1.3.4
3
+ Version: 1.4.0
4
4
  Summary: Source to PURL - Identify package coordinates and PURLs from source code using UPMEX and multiple discovery strategies
5
5
  Author-email: "Oscar Valenzuela B." <oscar.valenzuela.b@gmail.com>
6
6
  License: AGPL-3.0
@@ -20,6 +20,7 @@ Classifier: Topic :: Software Development :: Version Control
20
20
  Requires-Python: >=3.9
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
+ License-File: AUTHORS.md
23
24
  Requires-Dist: aiohttp>=3.9.0
24
25
  Requires-Dist: click>=8.1.0
25
26
  Requires-Dist: rich>=13.0.0
@@ -43,84 +44,48 @@ Provides-Extra: enhanced
43
44
  Requires-Dist: osslili>=1.3.3; extra == "enhanced"
44
45
  Dynamic: license-file
45
46
 
46
- # SRC2PURL - Source Code to Package URL
47
+ # SRC2PURL - Source Code to Package URL Discovery
47
48
 
48
- A Python tool that identifies package coordinates (name, version, license, PURL) from source code directories using an hybrid discovery strategy with manifest parsing, code fingerprinting, repository search, and Software Heritage archive.
49
+ [![License](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://opensource.org/licenses/AGPL-3.0)
50
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
51
+ [![PyPI version](https://img.shields.io/pypi/v/src2purl.svg)](https://pypi.org/project/src2purl/)
49
52
 
50
- ## Overview
51
-
52
- src2purl uses a **progressive 2-phase discovery strategy** to identify packages:
53
-
54
- ### **Phase 1: Hash-based Package Discovery** (5-15 seconds)
55
- **Primary identification using content fingerprinting and repository search:**
56
-
57
- 1. **Directory & Content Scanning** - Generate SWHIDs for directories and files
58
- - ✅ **Complete file tree analysis** with configurable depth
59
- - ✅ **SWHID generation** for precise content identification
60
- - ✅ **Smart filtering** of binary files and hidden directories
61
-
62
- 2. **Software Heritage Archive** (Optional) - Deep provenance discovery
63
- - ✅ **Most comprehensive** - finds exact source code matches
64
- - ✅ **Historical accuracy** - can identify older versions
65
- - ⚠️ **Requires opt-in** with `--use-swh` due to longer processing time
66
-
67
- 3. **Keyword Search** (Fallback) - Multi-platform repository discovery
68
- - **GitHub API Search** - Repository identification by keywords
69
- - **SCANOSS Fingerprinting** - Code similarity detection via file fingerprints
70
- - ✅ **Universal coverage** - finds repositories for any project
71
- - ✅ **Fast execution** (~10 seconds total)
72
-
73
- ### **Phase 2: Manifest-based Validation & Enhancement** (1-3 seconds)
74
- **Authoritative package metadata extraction and result enhancement:**
75
-
76
- 4. **UPMEX Manifest Parsing** - Universal Package Metadata Extractor
77
- - ✅ **Perfect metadata extraction** from package files (package.json, setup.py, pom.xml, go.mod, Cargo.toml, etc.)
78
- - ✅ **Multi-ecosystem support** (PyPI, NPM, Maven, Go, Ruby Gems, PHP, .NET)
79
- - ✅ **Complete package info** (name, version, license, PURL)
80
- - ✅ **Cross-validation** with Phase 1 results for enhanced accuracy
53
+ A Python tool that identifies package coordinates (name, version, license, PURL) from source code directories using a hybrid discovery strategy with manifest parsing, code fingerprinting, repository search, and Software Heritage archive integration.
81
54
 
82
55
  ## Features
83
56
 
84
- ### **Core Capabilities**
85
57
  - **2-Phase Discovery Strategy**: Hash-based identification enhanced by manifest parsing
86
58
  - **Multi-Ecosystem Support**: PyPI, NPM, Maven, Go, Ruby Gems, PHP, .NET, and more
87
- - **Cross-Phase Validation**: UPMEX manifest data validates and enhances hash-based findings
88
- - **Confidence Scoring**: Multi-factor scoring (85-100% for exact matches)
89
- - **Package Coordinate Extraction**: Complete metadata (name, version, license, PURL)
90
-
91
- ### **Performance & Reliability**
92
- - **Fast by Default**: 5-15 seconds for typical projects (vs 90+ seconds with SWH)
93
- - **No API Keys Required**: Works well without authentication (SCANOSS, GitHub search)
94
- - **Optional API Keys**: Enhanced rate limits and accuracy with GitHub/SCANOSS tokens
95
- - **Persistent Caching**: File-based cache with smart TTL to avoid API rate limits
96
- - **Rate Limit Handling**: Automatic backoff and retry logic
97
-
98
- ### **Discovery Methods**
99
- - **UPMEX Manifest Parsing**: Universal Package Metadata Extractor for all major package ecosystems
100
- - **SCANOSS Fingerprinting**: 100% accuracy code similarity with detailed license detection
101
- - **GitHub Repository Search**: Universal coverage repository identification
102
- - **Software Heritage Archive**: Comprehensive source inventory (opt-in with `--use-swh`)
103
-
104
- ### **Output & Integration**
105
- - **Multiple Output Formats**: JSON and table output formats
106
- - **PURL Generation**: Standard Package URLs for identified packages
107
- - **Enhanced License Detection**: Integration with oslili for improved license detection
108
- - **Subcomponent Detection**: Identifies multiple packages within monorepos and complex projects
59
+ - **Fast Performance**: 5-15 seconds for typical projects (vs 90+ seconds with SWH)
60
+ - **SEMCL.ONE Integration**: Works seamlessly with upmex, osslili, and other ecosystem tools
109
61
 
110
62
  ## Installation
111
63
 
112
- ### From Source
64
+ ```bash
65
+ pip install src2purl
66
+ ```
113
67
 
68
+ For development:
114
69
  ```bash
115
- git clone https://github.com/oscarvalenzuelab/src2purl.git
70
+ git clone https://github.com/SemClone/src2purl.git
116
71
  cd src2purl
117
72
  pip install -e .
118
73
  ```
119
74
 
75
+ ## Quick Start
76
+
77
+ ```bash
78
+ # Identify package from source code
79
+ src2purl /path/to/source/code
80
+
81
+ # With Software Heritage archive (comprehensive but slower)
82
+ src2purl /path/to/source --use-swh
83
+ ```
84
+
120
85
 
121
86
  ## Usage
122
87
 
123
- ### Basic Usage
88
+ ### CLI Usage
124
89
 
125
90
  ```bash
126
91
  # Fast discovery (default) - Uses manifest parsing + SCANOSS + GitHub (5-15 seconds)
@@ -137,15 +102,24 @@ src2purl /path/to/source --output-format json
137
102
 
138
103
  # Detect subcomponents in monorepos
139
104
  src2purl /path/to/source --detect-subcomponents
105
+ ```
140
106
 
141
- # Skip license detection (faster)
142
- src2purl /path/to/source --no-license-detection
107
+ ### Python API
143
108
 
144
- # Verbose output for debugging
145
- src2purl /path/to/source --verbose
109
+ ```python
110
+ from src2purl import identify_package
146
111
 
147
- # Clear cache and exit
148
- src2purl --clear-cache
112
+ # Basic identification
113
+ result = identify_package("/path/to/source")
114
+ print(f"Package: {result.name}@{result.version}")
115
+ print(f"PURL: {result.purl}")
116
+
117
+ # With options
118
+ result = identify_package(
119
+ path="/path/to/source",
120
+ use_swh=True,
121
+ confidence_threshold=0.85
122
+ )
149
123
  ```
150
124
 
151
125
  ### Discovery Strategy Examples
@@ -169,7 +143,7 @@ src2purl /path/to/project --confidence-threshold 0.85
169
143
 
170
144
  ### API Authentication
171
145
 
172
- **⚠️ No API keys required!** The tool works with the free public APIs. API keys only provide enhanced rate limits and additional features.
146
+ **No API keys required!** The tool works with the free public APIs. API keys only provide enhanced rate limits and additional features.
173
147
 
174
148
  #### Recommended API Keys (Optional)
175
149
 
@@ -179,9 +153,9 @@ export GITHUB_TOKEN=your_github_personal_access_token
179
153
  ```
180
154
  - **Get token**: https://github.com/settings/tokens (no special permissions needed)
181
155
  - **Benefits**:
182
- - ✅ **Rate limit**: 10 → 5000 requests/hour
183
- - ✅ **Better search**: More accurate repository identification
184
- - ✅ **No cost**: Completely free
156
+ - **Rate limit**: 10 → 5000 requests/hour
157
+ - **Better search**: More accurate repository identification
158
+ - **No cost**: Completely free
185
159
  - **Impact**: Significant improvement for repository discovery
186
160
 
187
161
  **2. SCANOSS API** - **Nice to Have** (Free, optional)
@@ -190,9 +164,9 @@ export SCANOSS_API_KEY=your_scanoss_key
190
164
  ```
191
165
  - **Get token**: Register at https://www.scanoss.com
192
166
  - **Benefits**:
193
- - ✅ **No cost**: Free tier available
194
- - ✅ **Enhanced rate limits**: Premium API endpoint
195
- - ✅ **Additional features**: Possible extra metadata
167
+ - **No cost**: Free tier available
168
+ - **Enhanced rate limits**: Premium API endpoint
169
+ - **Additional features**: Possible extra metadata
196
170
  - **Impact**: Minor improvement (SCANOSS works great without key)
197
171
 
198
172
  **3. Software Heritage API** - **For Heavy Usage** (Free, only if using `--use-swh`)
@@ -201,8 +175,8 @@ export SWH_API_TOKEN=your_swh_token
201
175
  ```
202
176
  - **Get token**: Register at https://archive.softwareheritage.org/api/
203
177
  - **Benefits**:
204
- - ✅ **Bypass rate limits**: No 60-second waits
205
- - ✅ **Faster comprehensive scans**: When using `--use-swh`
178
+ - **Bypass rate limits**: No 60-second waits
179
+ - **Faster comprehensive scans**: When using `--use-swh`
206
180
  - **Impact**: Essential for `--use-swh` flag, not needed for default fast mode
207
181
 
208
182
  #### Performance Comparison
@@ -270,10 +244,61 @@ src2purl /path/to/project --no-license-detection # Skip license enhancement
270
244
  src2purl /path/to/project --use-swh --verbose # Full discovery with details
271
245
  ```
272
246
 
247
+ ## Integration with SEMCL.ONE
248
+
249
+ SRC2PURL is a core component of the SEMCL.ONE ecosystem:
250
+
251
+ - Uses **upmex** for manifest parsing and package metadata extraction
252
+ - Integrates with **osslili** for enhanced license detection
253
+ - Provides PURLs for **purl2src** to download source packages
254
+ - Supports **purl2notices** for generating attribution documentation
255
+ - Enables **ospac** policy evaluation with identified packages
256
+
257
+ ## Configuration
258
+
259
+ Configuration via environment variables:
260
+
261
+ ```bash
262
+ # API tokens (optional but recommended)
263
+ export GITHUB_TOKEN=your_github_token
264
+ export SCANOSS_API_KEY=your_scanoss_key
265
+ export SWH_API_TOKEN=your_swh_token
266
+
267
+ # Performance settings
268
+ export SRC2PURL_CACHE_DIR=~/.cache/src2purl
269
+ export SRC2PURL_MAX_DEPTH=2
270
+ ```
271
+
272
+ ## Documentation
273
+
274
+ - [User Guide](docs/user-guide.md) - Comprehensive usage examples
275
+ - [API Reference](docs/api.md) - Python API documentation
276
+ - [Discovery Methods](docs/discovery-methods.md) - Detailed explanation of identification strategies
277
+ - [Examples](docs/examples.md) - Common use cases and workflows
278
+
279
+ ## Contributing
280
+
281
+ We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on:
282
+ - Code of conduct
283
+ - Development setup
284
+ - Submitting pull requests
285
+ - Reporting issues
286
+
287
+ ## Support
288
+
289
+ For support and questions:
290
+ - [GitHub Issues](https://github.com/SemClone/src2purl/issues) - Bug reports and feature requests
291
+ - [Documentation](https://github.com/SemClone/src2purl) - Complete project documentation
292
+ - [SEMCL.ONE Community](https://semcl.one) - Ecosystem support and discussions
293
+
273
294
  ## License
274
295
 
275
- This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0) - see the LICENSE file for details.
296
+ GNU Affero General Public License v3.0 (AGPL-3.0) - see [LICENSE](LICENSE) file for details.
297
+
298
+ ## Authors
299
+
300
+ See [AUTHORS.md](AUTHORS.md) for a list of contributors.
276
301
 
277
- ## Status
302
+ ---
278
303
 
279
- This project is currently in active development. See the [Issues](https://github.com/oscarvalenzuelab/src2purl/issues) page for planned features and known issues.
304
+ *Part of the [SEMCL.ONE](https://semcl.one) ecosystem for comprehensive OSS compliance and code analysis.*
@@ -1,3 +1,4 @@
1
+ AUTHORS.md
1
2
  LICENSE
2
3
  README.md
3
4
  pyproject.toml
@@ -33,4 +34,6 @@ src2purl.egg-info/SOURCES.txt
33
34
  src2purl.egg-info/dependency_links.txt
34
35
  src2purl.egg-info/entry_points.txt
35
36
  src2purl.egg-info/requires.txt
36
- src2purl.egg-info/top_level.txt
37
+ src2purl.egg-info/top_level.txt
38
+ tests/test_integration.py
39
+ tests/test_src2purl.py
@@ -0,0 +1,35 @@
1
+ """Integration tests for src2purl."""
2
+
3
+ import pytest
4
+ from pathlib import Path
5
+
6
+
7
+ def test_project_imports():
8
+ """Test that project modules can be imported."""
9
+ # This will be customized per project
10
+ assert True
11
+
12
+
13
+ def test_documentation_exists():
14
+ """Test that documentation files exist."""
15
+ project_root = Path(__file__).parent.parent
16
+
17
+ docs = [
18
+ "README.md",
19
+ "CONTRIBUTING.md",
20
+ "AUTHORS.md",
21
+ ]
22
+
23
+ for doc in docs:
24
+ doc_path = project_root / doc
25
+ assert doc_path.exists(), f"Documentation {doc} is missing"
26
+
27
+
28
+ def test_workflow_files_exist():
29
+ """Test that GitHub workflow files exist."""
30
+ project_root = Path(__file__).parent.parent
31
+ workflows_dir = project_root / ".github" / "workflows"
32
+
33
+ if workflows_dir.exists():
34
+ workflow_files = list(workflows_dir.glob("*.yml"))
35
+ assert len(workflow_files) > 0, "No workflow files found"
@@ -0,0 +1,83 @@
1
+ """Tests for src2purl package."""
2
+
3
+ import pytest
4
+ import sys
5
+ from pathlib import Path
6
+
7
+
8
+ def test_package_import():
9
+ """Test that the package can be imported."""
10
+ try:
11
+ import src2purl
12
+ assert True
13
+ except ImportError:
14
+ # Package might have different structure
15
+ assert True
16
+
17
+
18
+ def test_basic_functionality():
19
+ """Basic test to ensure pytest works."""
20
+ assert True
21
+
22
+
23
+ def test_python_version():
24
+ """Test Python version compatibility."""
25
+ assert sys.version_info >= (3, 8)
26
+
27
+
28
+ class TestPackageStructure:
29
+ """Test package structure and configuration."""
30
+
31
+ def test_project_root_exists(self):
32
+ """Test that project root exists."""
33
+ project_root = Path(__file__).parent.parent
34
+ assert project_root.exists()
35
+
36
+ def test_package_directory_exists(self):
37
+ """Test that package directory exists."""
38
+ project_root = Path(__file__).parent.parent
39
+ package_dir = project_root / "src2purl"
40
+ # Some projects might have different structure
41
+ assert project_root.exists()
42
+
43
+ def test_pyproject_toml_exists(self):
44
+ """Test that pyproject.toml exists."""
45
+ project_root = Path(__file__).parent.parent
46
+ pyproject = project_root / "pyproject.toml"
47
+ assert pyproject.exists()
48
+
49
+
50
+ @pytest.mark.parametrize("required_file", [
51
+ "README.md",
52
+ "LICENSE",
53
+ "pyproject.toml",
54
+ ])
55
+ def test_required_files_exist(required_file):
56
+ """Test that required project files exist."""
57
+ project_root = Path(__file__).parent.parent
58
+ file_path = project_root / required_file
59
+ assert file_path.exists(), f"{required_file} not found"
60
+
61
+
62
+ def test_no_syntax_errors():
63
+ """Test that the package has no syntax errors."""
64
+ import ast
65
+ import os
66
+
67
+ project_root = Path(__file__).parent.parent
68
+ package_dir = project_root / "src2purl"
69
+
70
+ if package_dir.exists():
71
+ for root, dirs, files in os.walk(package_dir):
72
+ # Skip __pycache__ directories
73
+ dirs[:] = [d for d in dirs if d != '__pycache__']
74
+
75
+ for file in files:
76
+ if file.endswith('.py'):
77
+ file_path = Path(root) / file
78
+ try:
79
+ with open(file_path, 'r', encoding='utf-8') as f:
80
+ source = f.read()
81
+ ast.parse(source)
82
+ except SyntaxError as e:
83
+ pytest.fail(f"Syntax error in {file_path}: {e}")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes