ipkgs 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ipkgs-0.1.0/.github/workflows/ci.yml +46 -0
- ipkgs-0.1.0/.github/workflows/publish.yml +34 -0
- ipkgs-0.1.0/.gitignore +40 -0
- ipkgs-0.1.0/PKG-INFO +144 -0
- ipkgs-0.1.0/README.md +111 -0
- ipkgs-0.1.0/pyproject.toml +69 -0
- ipkgs-0.1.0/src/ipkgs/__init__.py +10 -0
- ipkgs-0.1.0/src/ipkgs/__main__.py +3 -0
- ipkgs-0.1.0/src/ipkgs/cli/__init__.py +0 -0
- ipkgs-0.1.0/src/ipkgs/cli/cmd_auth.py +36 -0
- ipkgs-0.1.0/src/ipkgs/cli/cmd_info.py +81 -0
- ipkgs-0.1.0/src/ipkgs/cli/cmd_init.py +81 -0
- ipkgs-0.1.0/src/ipkgs/cli/cmd_install.py +142 -0
- ipkgs-0.1.0/src/ipkgs/cli/cmd_list.py +77 -0
- ipkgs-0.1.0/src/ipkgs/cli/cmd_publish.py +83 -0
- ipkgs-0.1.0/src/ipkgs/cli/cmd_search.py +59 -0
- ipkgs-0.1.0/src/ipkgs/cli/cmd_uninstall.py +67 -0
- ipkgs-0.1.0/src/ipkgs/cli/cmd_update.py +116 -0
- ipkgs-0.1.0/src/ipkgs/cli/main.py +75 -0
- ipkgs-0.1.0/src/ipkgs/core/__init__.py +0 -0
- ipkgs-0.1.0/src/ipkgs/core/installer.py +109 -0
- ipkgs-0.1.0/src/ipkgs/core/lockfile.py +48 -0
- ipkgs-0.1.0/src/ipkgs/core/manifest.py +99 -0
- ipkgs-0.1.0/src/ipkgs/core/package.py +32 -0
- ipkgs-0.1.0/src/ipkgs/core/resolver.py +85 -0
- ipkgs-0.1.0/src/ipkgs/exceptions.py +59 -0
- ipkgs-0.1.0/src/ipkgs/registry/__init__.py +0 -0
- ipkgs-0.1.0/src/ipkgs/registry/auth.py +47 -0
- ipkgs-0.1.0/src/ipkgs/registry/client.py +123 -0
- ipkgs-0.1.0/src/ipkgs/utils/__init__.py +0 -0
- ipkgs-0.1.0/src/ipkgs/utils/console.py +43 -0
- ipkgs-0.1.0/src/ipkgs/utils/fs.py +72 -0
- ipkgs-0.1.0/src/ipkgs/utils/semver.py +81 -0
- ipkgs-0.1.0/tests/__init__.py +0 -0
- ipkgs-0.1.0/tests/conftest.py +50 -0
- ipkgs-0.1.0/tests/test_lockfile.py +61 -0
- ipkgs-0.1.0/tests/test_manifest.py +60 -0
- ipkgs-0.1.0/tests/test_resolver.py +103 -0
- ipkgs-0.1.0/tests/test_semver.py +51 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.12"
|
|
16
|
+
- run: pip install ruff
|
|
17
|
+
- run: ruff check src/ tests/
|
|
18
|
+
- run: ruff format --check src/ tests/
|
|
19
|
+
|
|
20
|
+
typecheck:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: "3.12"
|
|
27
|
+
- run: pip install -e ".[dev]"
|
|
28
|
+
- run: mypy src/
|
|
29
|
+
|
|
30
|
+
test:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
strategy:
|
|
33
|
+
matrix:
|
|
34
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- uses: actions/setup-python@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: ${{ matrix.python-version }}
|
|
40
|
+
- name: Cache pip
|
|
41
|
+
uses: actions/cache@v4
|
|
42
|
+
with:
|
|
43
|
+
path: ~/.cache/pip
|
|
44
|
+
key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }}
|
|
45
|
+
- run: pip install -e ".[dev]"
|
|
46
|
+
- run: pytest --tb=short -q
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.12"
|
|
16
|
+
- run: pip install hatchling
|
|
17
|
+
- run: python -m hatchling build
|
|
18
|
+
- uses: actions/upload-artifact@v4
|
|
19
|
+
with:
|
|
20
|
+
name: dist
|
|
21
|
+
path: dist/
|
|
22
|
+
|
|
23
|
+
publish:
|
|
24
|
+
needs: build
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
environment: pypi
|
|
27
|
+
permissions:
|
|
28
|
+
id-token: write
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/download-artifact@v4
|
|
31
|
+
with:
|
|
32
|
+
name: dist
|
|
33
|
+
path: dist/
|
|
34
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
ipkgs-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
.Python
|
|
7
|
+
*.egg-info/
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
.eggs/
|
|
11
|
+
*.egg
|
|
12
|
+
pip-wheel-metadata/
|
|
13
|
+
.installed.cfg
|
|
14
|
+
|
|
15
|
+
# Virtual environments
|
|
16
|
+
.venv/
|
|
17
|
+
venv/
|
|
18
|
+
env/
|
|
19
|
+
ENV/
|
|
20
|
+
|
|
21
|
+
# Testing
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.coverage
|
|
24
|
+
htmlcov/
|
|
25
|
+
.tox/
|
|
26
|
+
|
|
27
|
+
# Type checking
|
|
28
|
+
.mypy_cache/
|
|
29
|
+
.dmypy.json
|
|
30
|
+
|
|
31
|
+
# IDE
|
|
32
|
+
.vscode/
|
|
33
|
+
.idea/
|
|
34
|
+
*.swp
|
|
35
|
+
*.swo
|
|
36
|
+
|
|
37
|
+
# ipkgs runtime
|
|
38
|
+
ip_modules/
|
|
39
|
+
ipkgs.lock
|
|
40
|
+
.ipkgs_cache/
|
ipkgs-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ipkgs
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A package manager for Verilog IP cores
|
|
5
|
+
Project-URL: Homepage, https://ipkgs.com
|
|
6
|
+
Project-URL: Repository, https://github.com/ipkgs/ipkgs
|
|
7
|
+
Author-email: "ipkgs.com" <hi@ipkgs.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Keywords: eda,fpga,ip-core,package-manager,rtl,verilog
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: click<9,>=8.1
|
|
19
|
+
Requires-Dist: httpx<1,>=0.27
|
|
20
|
+
Requires-Dist: keyring<26,>=25
|
|
21
|
+
Requires-Dist: platformdirs<5,>=4
|
|
22
|
+
Requires-Dist: pydantic<3,>=2.6
|
|
23
|
+
Requires-Dist: rich<15,>=13
|
|
24
|
+
Requires-Dist: semver<4,>=3.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: mypy>=1.9; extra == 'dev'
|
|
27
|
+
Requires-Dist: pyfakefs>=5.4; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# ipkgs
|
|
35
|
+
|
|
36
|
+
**The package manager for Verilog IP cores.**
|
|
37
|
+
|
|
38
|
+
`ipkgs` is to Verilog what `npm` is to JavaScript — a CLI tool and registry for sharing, installing, and publishing reusable RTL IP cores for FPGA and ASIC projects.
|
|
39
|
+
|
|
40
|
+
Registry: **[ipkgs.com](https://ipkgs.com)**
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
pip install ipkgs
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
# Initialize a new IP core project
|
|
54
|
+
ipkgs init
|
|
55
|
+
|
|
56
|
+
# Search for IP cores
|
|
57
|
+
ipkgs search uart
|
|
58
|
+
|
|
59
|
+
# Install a package
|
|
60
|
+
ipkgs install uart-core
|
|
61
|
+
|
|
62
|
+
# Install a specific version
|
|
63
|
+
ipkgs install fifo-sync@^2.0.0
|
|
64
|
+
|
|
65
|
+
# List installed packages
|
|
66
|
+
ipkgs list
|
|
67
|
+
|
|
68
|
+
# Publish your core to ipkgs.com
|
|
69
|
+
ipkgs login
|
|
70
|
+
ipkgs publish
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## `ipkgs.json`
|
|
74
|
+
|
|
75
|
+
Every IP core project has an `ipkgs.json` manifest:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"name": "uart-core",
|
|
80
|
+
"version": "1.2.0",
|
|
81
|
+
"description": "Parameterized UART TX/RX for FPGA targets",
|
|
82
|
+
"author": "Your Name <you@example.com>",
|
|
83
|
+
"license": "MIT",
|
|
84
|
+
"top_module": "uart_top",
|
|
85
|
+
"platforms": ["ice40", "ecp5", "xc7", "generic"],
|
|
86
|
+
"source_files": [
|
|
87
|
+
"rtl/uart_top.sv",
|
|
88
|
+
"rtl/uart_tx.sv",
|
|
89
|
+
"rtl/uart_rx.sv"
|
|
90
|
+
],
|
|
91
|
+
"parameters": {
|
|
92
|
+
"BAUD_RATE": "115200",
|
|
93
|
+
"DATA_BITS": "8"
|
|
94
|
+
},
|
|
95
|
+
"dependencies": {
|
|
96
|
+
"fifo-sync": "^2.0.0"
|
|
97
|
+
},
|
|
98
|
+
"scripts": {
|
|
99
|
+
"sim": "iverilog -g2012 -o sim.out rtl/uart_top.sv && vvp sim.out",
|
|
100
|
+
"lint": "verilator --lint-only rtl/uart_top.sv"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Installed packages land in `ip_modules/` (add to `.gitignore`).
|
|
106
|
+
|
|
107
|
+
## Commands
|
|
108
|
+
|
|
109
|
+
| Command | Description |
|
|
110
|
+
|---|---|
|
|
111
|
+
| `ipkgs init` | Scaffold a new IP core project |
|
|
112
|
+
| `ipkgs install [pkg[@ver]]` | Install packages |
|
|
113
|
+
| `ipkgs uninstall <pkg>` | Remove packages |
|
|
114
|
+
| `ipkgs update [pkg]` | Update to latest within semver range |
|
|
115
|
+
| `ipkgs list` | List installed packages |
|
|
116
|
+
| `ipkgs search <query>` | Search the registry |
|
|
117
|
+
| `ipkgs info <pkg>` | Show package details |
|
|
118
|
+
| `ipkgs publish` | Publish to ipkgs.com |
|
|
119
|
+
| `ipkgs login` | Authenticate with ipkgs.com |
|
|
120
|
+
| `ipkgs logout` | Remove stored credentials |
|
|
121
|
+
|
|
122
|
+
## Version ranges
|
|
123
|
+
|
|
124
|
+
`ipkgs` uses standard semver ranges:
|
|
125
|
+
|
|
126
|
+
| Range | Meaning |
|
|
127
|
+
|---|---|
|
|
128
|
+
| `^1.2.0` | Compatible: `>=1.2.0 <2.0.0` |
|
|
129
|
+
| `~1.2.0` | Patch-level: `>=1.2.0 <1.3.0` |
|
|
130
|
+
| `>=1.0.0 <2.0.0` | Explicit range |
|
|
131
|
+
| `1.2.3` | Exact version |
|
|
132
|
+
|
|
133
|
+
> **Note on version conflicts:** Unlike npm, `ipkgs` hard-fails on incompatible version conflicts. Verilog IP cores compile into a single netlist — duplicate module definitions would cause synthesis errors. If two packages require incompatible versions of a dependency, you must resolve the conflict manually.
|
|
134
|
+
|
|
135
|
+
## Environment variables
|
|
136
|
+
|
|
137
|
+
| Variable | Description |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `IPKGS_TOKEN` | Auth token (for CI/CD, skips keyring) |
|
|
140
|
+
| `IPKGS_REGISTRY` | Override registry URL (default: `https://api.ipkgs.com/v1`) |
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT
|
ipkgs-0.1.0/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# ipkgs
|
|
2
|
+
|
|
3
|
+
**The package manager for Verilog IP cores.**
|
|
4
|
+
|
|
5
|
+
`ipkgs` is to Verilog what `npm` is to JavaScript — a CLI tool and registry for sharing, installing, and publishing reusable RTL IP cores for FPGA and ASIC projects.
|
|
6
|
+
|
|
7
|
+
Registry: **[ipkgs.com](https://ipkgs.com)**
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
pip install ipkgs
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
# Initialize a new IP core project
|
|
21
|
+
ipkgs init
|
|
22
|
+
|
|
23
|
+
# Search for IP cores
|
|
24
|
+
ipkgs search uart
|
|
25
|
+
|
|
26
|
+
# Install a package
|
|
27
|
+
ipkgs install uart-core
|
|
28
|
+
|
|
29
|
+
# Install a specific version
|
|
30
|
+
ipkgs install fifo-sync@^2.0.0
|
|
31
|
+
|
|
32
|
+
# List installed packages
|
|
33
|
+
ipkgs list
|
|
34
|
+
|
|
35
|
+
# Publish your core to ipkgs.com
|
|
36
|
+
ipkgs login
|
|
37
|
+
ipkgs publish
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## `ipkgs.json`
|
|
41
|
+
|
|
42
|
+
Every IP core project has an `ipkgs.json` manifest:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"name": "uart-core",
|
|
47
|
+
"version": "1.2.0",
|
|
48
|
+
"description": "Parameterized UART TX/RX for FPGA targets",
|
|
49
|
+
"author": "Your Name <you@example.com>",
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"top_module": "uart_top",
|
|
52
|
+
"platforms": ["ice40", "ecp5", "xc7", "generic"],
|
|
53
|
+
"source_files": [
|
|
54
|
+
"rtl/uart_top.sv",
|
|
55
|
+
"rtl/uart_tx.sv",
|
|
56
|
+
"rtl/uart_rx.sv"
|
|
57
|
+
],
|
|
58
|
+
"parameters": {
|
|
59
|
+
"BAUD_RATE": "115200",
|
|
60
|
+
"DATA_BITS": "8"
|
|
61
|
+
},
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"fifo-sync": "^2.0.0"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"sim": "iverilog -g2012 -o sim.out rtl/uart_top.sv && vvp sim.out",
|
|
67
|
+
"lint": "verilator --lint-only rtl/uart_top.sv"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Installed packages land in `ip_modules/` (add to `.gitignore`).
|
|
73
|
+
|
|
74
|
+
## Commands
|
|
75
|
+
|
|
76
|
+
| Command | Description |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `ipkgs init` | Scaffold a new IP core project |
|
|
79
|
+
| `ipkgs install [pkg[@ver]]` | Install packages |
|
|
80
|
+
| `ipkgs uninstall <pkg>` | Remove packages |
|
|
81
|
+
| `ipkgs update [pkg]` | Update to latest within semver range |
|
|
82
|
+
| `ipkgs list` | List installed packages |
|
|
83
|
+
| `ipkgs search <query>` | Search the registry |
|
|
84
|
+
| `ipkgs info <pkg>` | Show package details |
|
|
85
|
+
| `ipkgs publish` | Publish to ipkgs.com |
|
|
86
|
+
| `ipkgs login` | Authenticate with ipkgs.com |
|
|
87
|
+
| `ipkgs logout` | Remove stored credentials |
|
|
88
|
+
|
|
89
|
+
## Version ranges
|
|
90
|
+
|
|
91
|
+
`ipkgs` uses standard semver ranges:
|
|
92
|
+
|
|
93
|
+
| Range | Meaning |
|
|
94
|
+
|---|---|
|
|
95
|
+
| `^1.2.0` | Compatible: `>=1.2.0 <2.0.0` |
|
|
96
|
+
| `~1.2.0` | Patch-level: `>=1.2.0 <1.3.0` |
|
|
97
|
+
| `>=1.0.0 <2.0.0` | Explicit range |
|
|
98
|
+
| `1.2.3` | Exact version |
|
|
99
|
+
|
|
100
|
+
> **Note on version conflicts:** Unlike npm, `ipkgs` hard-fails on incompatible version conflicts. Verilog IP cores compile into a single netlist — duplicate module definitions would cause synthesis errors. If two packages require incompatible versions of a dependency, you must resolve the conflict manually.
|
|
101
|
+
|
|
102
|
+
## Environment variables
|
|
103
|
+
|
|
104
|
+
| Variable | Description |
|
|
105
|
+
|---|---|
|
|
106
|
+
| `IPKGS_TOKEN` | Auth token (for CI/CD, skips keyring) |
|
|
107
|
+
| `IPKGS_REGISTRY` | Override registry URL (default: `https://api.ipkgs.com/v1`) |
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
MIT
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.26"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ipkgs"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A package manager for Verilog IP cores"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "ipkgs.com", email = "hi@ipkgs.com" }]
|
|
13
|
+
keywords = ["verilog", "fpga", "ip-core", "package-manager", "eda", "rtl"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
dependencies = [
|
|
25
|
+
"click>=8.1,<9",
|
|
26
|
+
"httpx>=0.27,<1",
|
|
27
|
+
"rich>=13,<15",
|
|
28
|
+
"semver>=3.0,<4",
|
|
29
|
+
"pydantic>=2.6,<3",
|
|
30
|
+
"keyring>=25,<26",
|
|
31
|
+
"platformdirs>=4,<5",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = [
|
|
36
|
+
"pytest>=8",
|
|
37
|
+
"pytest-asyncio>=0.23",
|
|
38
|
+
"pytest-httpx>=0.30",
|
|
39
|
+
"pyfakefs>=5.4",
|
|
40
|
+
"mypy>=1.9",
|
|
41
|
+
"ruff>=0.4",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
ipkgs = "ipkgs.cli.main:cli"
|
|
46
|
+
|
|
47
|
+
[project.urls]
|
|
48
|
+
Homepage = "https://ipkgs.com"
|
|
49
|
+
Repository = "https://github.com/ipkgs/ipkgs"
|
|
50
|
+
|
|
51
|
+
[tool.hatch.build.targets.wheel]
|
|
52
|
+
packages = ["src/ipkgs"]
|
|
53
|
+
|
|
54
|
+
[tool.hatch.version]
|
|
55
|
+
path = "src/ipkgs/__init__.py"
|
|
56
|
+
|
|
57
|
+
[tool.ruff]
|
|
58
|
+
line-length = 100
|
|
59
|
+
|
|
60
|
+
[tool.ruff.lint]
|
|
61
|
+
select = ["E", "F", "I", "UP"]
|
|
62
|
+
|
|
63
|
+
[tool.mypy]
|
|
64
|
+
python_version = "3.10"
|
|
65
|
+
strict = true
|
|
66
|
+
|
|
67
|
+
[tool.pytest.ini_options]
|
|
68
|
+
asyncio_mode = "auto"
|
|
69
|
+
testpaths = ["tests"]
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""ipkgs login / logout — registry authentication."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from ipkgs.cli.main import IpkgsContext
|
|
10
|
+
from ipkgs.exceptions import IpkgsError
|
|
11
|
+
from ipkgs.registry.auth import AuthManager
|
|
12
|
+
from ipkgs.utils.console import print_success, print_error
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.command("login")
|
|
16
|
+
@click.option("--username", "-u", prompt=True)
|
|
17
|
+
@click.option("--password", "-p", prompt=True, hide_input=True)
|
|
18
|
+
@click.pass_obj
|
|
19
|
+
def login(ctx: IpkgsContext, username: str, password: str) -> None:
|
|
20
|
+
"""Authenticate with the ipkgs.com registry."""
|
|
21
|
+
try:
|
|
22
|
+
auth = AuthManager(ctx.registry)
|
|
23
|
+
token = asyncio.run(auth.login(username, password))
|
|
24
|
+
print_success(ctx.console, f"Logged in as [bold]{username}[/]")
|
|
25
|
+
except IpkgsError as exc:
|
|
26
|
+
print_error(ctx.console, str(exc))
|
|
27
|
+
raise SystemExit(1)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@click.command("logout")
|
|
31
|
+
@click.pass_obj
|
|
32
|
+
def logout(ctx: IpkgsContext) -> None:
|
|
33
|
+
"""Remove stored credentials for the registry."""
|
|
34
|
+
auth = AuthManager(ctx.registry)
|
|
35
|
+
auth.clear_token()
|
|
36
|
+
print_success(ctx.console, "Logged out.")
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""ipkgs info — show package details."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
from ipkgs.cli.main import IpkgsContext
|
|
12
|
+
from ipkgs.exceptions import IpkgsError
|
|
13
|
+
from ipkgs.registry.client import RegistryClient
|
|
14
|
+
from ipkgs.utils.console import print_error
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@click.command("info")
|
|
18
|
+
@click.argument("package")
|
|
19
|
+
@click.pass_obj
|
|
20
|
+
def info(ctx: IpkgsContext, package: str) -> None:
|
|
21
|
+
"""Show details about an IP core package.
|
|
22
|
+
|
|
23
|
+
\b
|
|
24
|
+
Examples:
|
|
25
|
+
ipkgs info uart-core
|
|
26
|
+
ipkgs info uart-core@1.2.0
|
|
27
|
+
"""
|
|
28
|
+
try:
|
|
29
|
+
asyncio.run(_info(ctx, package))
|
|
30
|
+
except IpkgsError as exc:
|
|
31
|
+
print_error(ctx.console, str(exc))
|
|
32
|
+
raise SystemExit(1)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async def _info(ctx: IpkgsContext, package: str) -> None:
|
|
36
|
+
name, _, version = package.partition("@")
|
|
37
|
+
client = RegistryClient(base_url=ctx.registry)
|
|
38
|
+
meta = await client.get_package(name)
|
|
39
|
+
|
|
40
|
+
target_version = version or meta.latest
|
|
41
|
+
pkg = meta.versions.get(target_version)
|
|
42
|
+
|
|
43
|
+
ctx.console.print(
|
|
44
|
+
Panel(
|
|
45
|
+
f"[bold cyan]{meta.name}[/] [green]{target_version}[/]\n"
|
|
46
|
+
f"{meta.description or '[dim]No description[/]'}",
|
|
47
|
+
expand=False,
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
grid = Table.grid(padding=(0, 2))
|
|
52
|
+
grid.add_column(style="bold")
|
|
53
|
+
grid.add_column()
|
|
54
|
+
grid.add_row("Author", meta.author or "-")
|
|
55
|
+
grid.add_row("License", meta.license or "-")
|
|
56
|
+
grid.add_row("Homepage", meta.dict().get("homepage") or "-")
|
|
57
|
+
if pkg:
|
|
58
|
+
grid.add_row("Published", str(pkg.published_at.date()) if pkg.published_at else "-")
|
|
59
|
+
grid.add_row("Downloads", str(pkg.download_count))
|
|
60
|
+
ctx.console.print(grid)
|
|
61
|
+
|
|
62
|
+
if pkg and pkg.dependencies:
|
|
63
|
+
dep_table = Table(title="Dependencies", show_header=True, header_style="bold")
|
|
64
|
+
dep_table.add_column("Package", style="cyan")
|
|
65
|
+
dep_table.add_column("Range", style="green")
|
|
66
|
+
for dep_name, constraint in pkg.dependencies.items():
|
|
67
|
+
dep_table.add_row(dep_name, constraint)
|
|
68
|
+
ctx.console.print(dep_table)
|
|
69
|
+
|
|
70
|
+
# Versions list
|
|
71
|
+
versions = sorted(meta.versions.keys(), reverse=True)[:10]
|
|
72
|
+
ver_table = Table(title="Recent versions", show_header=True, header_style="bold")
|
|
73
|
+
ver_table.add_column("Version", style="cyan")
|
|
74
|
+
ver_table.add_column("Downloads")
|
|
75
|
+
for v in versions:
|
|
76
|
+
pv = meta.versions[v]
|
|
77
|
+
tag = " [bold green](latest)[/]" if v == meta.latest else ""
|
|
78
|
+
ver_table.add_row(v + tag, str(pv.download_count))
|
|
79
|
+
ctx.console.print(ver_table)
|
|
80
|
+
|
|
81
|
+
ctx.console.print(f"\n[dim]Install:[/] [cyan]ipkgs install {meta.name}[/]")
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""ipkgs init — scaffold a new IP core project."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
|
|
10
|
+
from ipkgs.cli.main import IpkgsContext
|
|
11
|
+
from ipkgs.core.manifest import IpkgsManifest, MANIFEST_FILENAME
|
|
12
|
+
from ipkgs.utils.fs import ensure_ip_modules_dir
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.command("init")
|
|
16
|
+
@click.option("--yes", "-y", is_flag=True, help="Accept all defaults non-interactively.")
|
|
17
|
+
@click.option("--force", "-f", is_flag=True, help="Overwrite existing ipkgs.json.")
|
|
18
|
+
@click.pass_obj
|
|
19
|
+
def init(ctx: IpkgsContext, yes: bool, force: bool) -> None:
|
|
20
|
+
"""Scaffold a new Verilog IP core project."""
|
|
21
|
+
cwd = Path.cwd()
|
|
22
|
+
manifest_path = cwd / MANIFEST_FILENAME
|
|
23
|
+
|
|
24
|
+
if manifest_path.exists() and not force:
|
|
25
|
+
ctx.console.print(
|
|
26
|
+
f"[yellow]{MANIFEST_FILENAME} already exists.[/] Use --force to overwrite."
|
|
27
|
+
)
|
|
28
|
+
raise SystemExit(1)
|
|
29
|
+
|
|
30
|
+
def ask(prompt: str, default: str, skip: bool = False) -> str:
|
|
31
|
+
if skip:
|
|
32
|
+
return default
|
|
33
|
+
return click.prompt(prompt, default=default)
|
|
34
|
+
|
|
35
|
+
name = ask("Package name", cwd.name.lower().replace("_", "-"), yes)
|
|
36
|
+
version = ask("Version", "0.1.0", yes)
|
|
37
|
+
description = ask("Description", "", yes)
|
|
38
|
+
author = ask("Author", "", yes)
|
|
39
|
+
license_ = ask("License", "MIT", yes)
|
|
40
|
+
top_module = ask("Top module name", name.replace("-", "_"), yes)
|
|
41
|
+
|
|
42
|
+
if yes:
|
|
43
|
+
platforms = ["generic"]
|
|
44
|
+
else:
|
|
45
|
+
ctx.console.print(
|
|
46
|
+
"Platforms [dim](comma-separated: ice40, ecp5, xc7, generic)[/]"
|
|
47
|
+
)
|
|
48
|
+
raw = click.prompt("Platforms", default="generic")
|
|
49
|
+
platforms = [p.strip() for p in raw.split(",") if p.strip()]
|
|
50
|
+
|
|
51
|
+
manifest = IpkgsManifest(
|
|
52
|
+
name=name,
|
|
53
|
+
version=version,
|
|
54
|
+
description=description,
|
|
55
|
+
author=author,
|
|
56
|
+
license=license_,
|
|
57
|
+
top_module=top_module,
|
|
58
|
+
platforms=platforms,
|
|
59
|
+
)
|
|
60
|
+
manifest.save(manifest_path)
|
|
61
|
+
|
|
62
|
+
ensure_ip_modules_dir(cwd)
|
|
63
|
+
|
|
64
|
+
# Add ip_modules/ to .gitignore if not present
|
|
65
|
+
gitignore = cwd / ".gitignore"
|
|
66
|
+
if gitignore.exists():
|
|
67
|
+
content = gitignore.read_text()
|
|
68
|
+
if "ip_modules/" not in content:
|
|
69
|
+
gitignore.write_text(content + "\nip_modules/\n")
|
|
70
|
+
else:
|
|
71
|
+
gitignore.write_text("ip_modules/\n")
|
|
72
|
+
|
|
73
|
+
ctx.console.print(
|
|
74
|
+
Panel(
|
|
75
|
+
f"[green]Created[/] [bold]{MANIFEST_FILENAME}[/]\n"
|
|
76
|
+
f"[green]Created[/] ip_modules/\n"
|
|
77
|
+
f"[green]Updated[/] .gitignore",
|
|
78
|
+
title=f"[bold cyan]ipkgs init[/] — {name}@{version}",
|
|
79
|
+
expand=False,
|
|
80
|
+
)
|
|
81
|
+
)
|