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.
Files changed (39) hide show
  1. ipkgs-0.1.0/.github/workflows/ci.yml +46 -0
  2. ipkgs-0.1.0/.github/workflows/publish.yml +34 -0
  3. ipkgs-0.1.0/.gitignore +40 -0
  4. ipkgs-0.1.0/PKG-INFO +144 -0
  5. ipkgs-0.1.0/README.md +111 -0
  6. ipkgs-0.1.0/pyproject.toml +69 -0
  7. ipkgs-0.1.0/src/ipkgs/__init__.py +10 -0
  8. ipkgs-0.1.0/src/ipkgs/__main__.py +3 -0
  9. ipkgs-0.1.0/src/ipkgs/cli/__init__.py +0 -0
  10. ipkgs-0.1.0/src/ipkgs/cli/cmd_auth.py +36 -0
  11. ipkgs-0.1.0/src/ipkgs/cli/cmd_info.py +81 -0
  12. ipkgs-0.1.0/src/ipkgs/cli/cmd_init.py +81 -0
  13. ipkgs-0.1.0/src/ipkgs/cli/cmd_install.py +142 -0
  14. ipkgs-0.1.0/src/ipkgs/cli/cmd_list.py +77 -0
  15. ipkgs-0.1.0/src/ipkgs/cli/cmd_publish.py +83 -0
  16. ipkgs-0.1.0/src/ipkgs/cli/cmd_search.py +59 -0
  17. ipkgs-0.1.0/src/ipkgs/cli/cmd_uninstall.py +67 -0
  18. ipkgs-0.1.0/src/ipkgs/cli/cmd_update.py +116 -0
  19. ipkgs-0.1.0/src/ipkgs/cli/main.py +75 -0
  20. ipkgs-0.1.0/src/ipkgs/core/__init__.py +0 -0
  21. ipkgs-0.1.0/src/ipkgs/core/installer.py +109 -0
  22. ipkgs-0.1.0/src/ipkgs/core/lockfile.py +48 -0
  23. ipkgs-0.1.0/src/ipkgs/core/manifest.py +99 -0
  24. ipkgs-0.1.0/src/ipkgs/core/package.py +32 -0
  25. ipkgs-0.1.0/src/ipkgs/core/resolver.py +85 -0
  26. ipkgs-0.1.0/src/ipkgs/exceptions.py +59 -0
  27. ipkgs-0.1.0/src/ipkgs/registry/__init__.py +0 -0
  28. ipkgs-0.1.0/src/ipkgs/registry/auth.py +47 -0
  29. ipkgs-0.1.0/src/ipkgs/registry/client.py +123 -0
  30. ipkgs-0.1.0/src/ipkgs/utils/__init__.py +0 -0
  31. ipkgs-0.1.0/src/ipkgs/utils/console.py +43 -0
  32. ipkgs-0.1.0/src/ipkgs/utils/fs.py +72 -0
  33. ipkgs-0.1.0/src/ipkgs/utils/semver.py +81 -0
  34. ipkgs-0.1.0/tests/__init__.py +0 -0
  35. ipkgs-0.1.0/tests/conftest.py +50 -0
  36. ipkgs-0.1.0/tests/test_lockfile.py +61 -0
  37. ipkgs-0.1.0/tests/test_manifest.py +60 -0
  38. ipkgs-0.1.0/tests/test_resolver.py +103 -0
  39. 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"]
@@ -0,0 +1,10 @@
1
+ """ipkgs — Verilog IP core package manager."""
2
+
3
+ from importlib.metadata import version, PackageNotFoundError
4
+
5
+ try:
6
+ __version__ = version("ipkgs")
7
+ except PackageNotFoundError:
8
+ __version__ = "0.0.0"
9
+
10
+ __all__ = ["__version__"]
@@ -0,0 +1,3 @@
1
+ from ipkgs.cli.main import cli
2
+
3
+ cli()
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
+ )