Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitlab-ci.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ workflow:
.nso-version-matrix:
parallel:
matrix:
- NSO_VERSION: ["6.5.7", "6.6.3"]
- NSO_VERSION: ["6.6.3", "6.7.3"]

.skip-on-release-updates:
rules:
Expand Down
58 changes: 40 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ pythonic declarations.
```
[dependency-groups]
dev = [
"ncs_py3",
"pkg_mgmt",
"ruff>=0.14.13",
"pyang>=2.7.1",
Expand Down Expand Up @@ -246,38 +245,61 @@ pythonic declarations.

```
[dependency-groups]
pyndev-nso = [
"pyndev-nso-6.6.3" = [
"arista-dcs-cli-5.30==5.30.7+nso6.6.3",
]
```

The pyndev-nso dependency group is a special placeholder for pyndev NSO
packages that are external to the current workspace. This separate group is
needed for the pyndev specific synchronization process and this array is
manually populated.
The `pyndev-nso-${NSO_VERSION}` dependency group is a special placeholder for
pyndev NSO packages that are external to the current workspace. This
separate group is needed for the pyndev specific synchronization process and
this array is manually populated.

The pyndev-nso dependency group name is version specific to support multiple
NSO versions in single `uv.lock` file. When more than one version of NSO is
declared there must exist a uv "conflicts" configuration to keep only a
single group installed in the local environment at time.

pyproject.toml

```
[dependency-groups]
"pyndev-nso-6.6.3" = [
"arista-dcs-cli-5.30==5.30.7+nso6.6.3",
]
"pyndev-nso-6.7.3" = [
"arista-dcs-cli-5.30==5.30.7+nso6.7.3",
]

[tool.uv]
conflicts = [
[
{ group = "pyndev-nso-6.6.3" },
{ group = "pyndev-nso-6.7.3" },
],
]
```

- NSO_VERSION

pyndev supports managing multiple NSO versions in python through the use of
PEP 440 "local version identifiers". During build all NSO packages are
appended with the version that was used for the package build. Internally
this is all driven by the top level `.env` variable `NSO_VERSION`. If your
repository only supports one version of NSO this identifier can be ignored
and left off of dependency version matching. If you are concurrently
repository only supports one version of NSO this package identifier can be
ignored and left off of dependency version matching. If you are concurrently
developing towards multiple NSO releases the full identifier should be
specified in the version.
specified in the package version.

> As previously mentioned, the pkg\_mgmt tooling does some special handling
> for the NSO project. One of the tricks that impacts the top level
> pyproject.toml is that during a `just sync` the `NSO_VERSION` variable is
> read and any NSO versions are updated to be synchronized with the declared
> project version. Eg. the arista NED above has a local version tag specific
> to set NSO version and a sync function will update that package version
> automatically to ensure consistency.
> The `NSO_VERSION` variable is referenced in all docker commands to select
> a tag for the build and prod versions of the Cisco official containers.
> Additionally, the `just sync` is pinned to interact with dependency-group
> of the form `pyndev-nso-${NSO_VERSION}` so both of these configuration
> items are required.

> Anytime the NSO_VERSION is updated locally the project needs to be
> re-initialized with a `just init` and a `just sync` to setup to the proper
> context.
> re-initialized with a `just init` and a `just sync` to install to the
> proper context.

This feature can be utilized in CI platforms with tools such as Github's
strategy matrix and Gitlabs parallel matrix. This skeleton has a
Expand Down
12 changes: 7 additions & 5 deletions docs/package-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,13 @@ package's own `src/` directory.

## Dependencies

Pyndev package pyproject.toml supports two kinds of dependencies. In either
case since uv is managing the project as workspace NSO packages can be
declared much more simply and uv will find the source based on the top level
pyproject.toml configuration for sources and inherit private repository
settings.
Pyndev package pyproject.toml supports two kinds of dependencies. For locking
and consistency per package NSO dependencies should also be declared in the
project top level pyproject.toml. When a top level pyproject.toml defines
versions including the nso version identifiers with or with out conflict groups
the child package pyproject.toml will inherit the state of the workspace. Thus,
it is redundant and not needed to set NSO versions for packages or the special
`pyndev-nso` dependency group name.

### Build Dependencies

Expand Down
2 changes: 1 addition & 1 deletion justfile
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ sync quiet='':
fi
just sync-pkg
uv sync --reinstall {{ if quiet != '' { '--quiet' } else { '' } }}
uv sync --group pyndev-nso {{ if quiet != '' { '--quiet' } else { '' } }}
uv sync --group "pyndev-nso-${NSO_VERSION}" {{ if quiet != '' { '--quiet' } else { '' } }}

# Build local packages, default is '--all-packages', optionally specify one or more package names to build serially
build *args:
Expand Down
2 changes: 1 addition & 1 deletion pkg_mgmt/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pkg_mgmt"
version = "0.3.0"
version = "0.4.0"
description = "pyndev package management helpers"
authors = [
{ name = "Bill Stephens", email = "bill@es.net" }
Expand Down
3 changes: 0 additions & 3 deletions pkg_mgmt/src/pkg_mgmt/sync_pkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,16 @@
from functools import partial
from pathlib import Path

from .toml import sync_pyproject
from .utils import sync_managed_files


def process_package(pkg: Path, nso_version: str) -> None:
"""Sync individual package managed files"""
sync_pyproject(nso_version, pkg.name)
sync_managed_files(pkg.name)


def main() -> None:
nso_version = os.getenv("NSO_VERSION", "0.0.0")
sync_pyproject(nso_version)
packages = Path("packages")
if packages.is_dir():
with ProcessPoolExecutor() as executor:
Expand Down
37 changes: 15 additions & 22 deletions pkg_mgmt/src/pkg_mgmt/toml.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,23 +173,6 @@ def update_nso_version(nso_version: str, deps: list[str]) -> None:
deps[i] = re.sub(r"\+nso[^.]+\.[^.]+(\.[^.]+)?", f"+nso{nso_version}", dep)


def sync_pyproject(nso_version: str, pkg_name: str | None = None) -> None:
"""Synchronize pyproject.toml with .env state"""
config = load_pyproject(pkg_name)

if "build-system" in config and "requires" in config["build-system"]:
update_nso_version(nso_version, config["build-system"]["requires"])

if "project" in config and "dependencies" in config["project"]:
update_nso_version(nso_version, config["project"]["dependencies"])

if "dependency-groups" in config:
for group_deps in config["dependency-groups"].values():
update_nso_version(nso_version, group_deps)

write_pyproject(config, pkg_name)


def update_pyproject(pkg_name: str, version: str) -> None:
"""Update pyndev pyproject.toml with pkg_name"""
config = load_pyproject()
Expand Down Expand Up @@ -245,11 +228,16 @@ def get_pyndev_description(pkg_name: str) -> str:
return description


def get_nso_dependencies() -> str:
def get_nso_dependencies() -> list[str]:
"""Pull out project NSO package dependencies"""
config = load_pyproject()
deps = [dep.split("==")[0] for dep in config.get("project", {}).get("dependencies", [])]
pyndev_nso = [dep.split("==")[0] for dep in config.get("dependency-groups", {}).get("pyndev-nso", [])]
pyndev_nso = [
dep.split("==")[0]
for group, group_deps in config.get("dependency-groups", {}).items()
if "pyndev-nso" in group
for dep in group_deps
]

return deps + pyndev_nso

Expand All @@ -262,16 +250,21 @@ def get_pkg_version(pkg_name: str) -> str:
return pkg_version


def get_pkg_dependencies(pkg_name: str) -> str:
def get_pkg_dependencies(pkg_name: str) -> list[str]:
"""Pull out package project.dependencies"""
config = load_pyproject(pkg_name)
pyndev_nso = [dep.split("==")[0] for dep in config.get("dependency-groups", {}).get("pyndev-nso", [])]
pyndev_nso = [
dep.split("==")[0]
for group, group_deps in config.get("dependency-groups", {}).items()
if "pyndev-nso" in group
for dep in group_deps
]
nso_deps = get_nso_dependencies()

return [dep for dep in pyndev_nso if dep in nso_deps]


def get_pkg_build_dependencies(pkg_name: str) -> str:
def get_pkg_build_dependencies(pkg_name: str) -> list[str]:
"""Pull out package build-system.requires"""
config = load_pyproject(pkg_name)
deps = [dep.split("==")[0] for dep in config.get("build-system", {}).get("requires", [])]
Expand Down
8 changes: 6 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pyndev"
dynamic = ["version"]
version = "0.4.0"
description = "Python wrapped NSO development environment"
readme = "README.md"
authors = [
Expand All @@ -18,7 +18,11 @@ dev = [
"mdformat>=1.0.0",
"mdformat-gfm>=1.0.0",
]
pyndev-nso = [
"pyndev-nso-6.6.3" = [
]

[tool.uv]
conflicts = [
]

[[tool.uv.index]]
Expand Down