Repository navigation
feat: syspi — SysML Package Installer (identity, VCS-neutral resolution, verification, install, lock, index) - #1
Merged
Merged
Conversation
Hand-parse pkg:<vcs>/<host>/<path>[@<version>][#<subpath>] for git, svn and hg, normalise plain clone URLs to it, refuse provider types (pkg:github, pkg:gitlab) and unsupported systems (pkg:cvs). Add strict semver and constraint parsing (exact, caret, tilde, comparison ranges), one Source interface with a git, svn and hg implementation shelling out to the user's executables, and the deterministic tree digest a lock records. Fixture tests prove the same package served from the three systems exports identical trees. Co-Authored-By: jason.han <hanhuijun@gmail.com>
Contributor
Author
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
…ndles and lock Read and validate .project.json (KerML 10.3 Table 12) and .meta.json (Table 13), generate metadata with a package index and SHA-256 member checksums, verify checksums before any content is used, open .kpar archives with path-traversal, symlink, duplicate and size checks, write byte-identical bundles honouring SOURCE_DATE_EPOCH, and define the VCS-neutral .project.lock.json with deterministic bytes. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…e syspi commands Resolve the usages of a project to exact packages from git, svn or hg tags, local directories, local and remote .kpar files, OMG standard library IRIs and a configured sysand static index; verify every tree and archive before use; install trees under lib/<publisher>-<name>_<version>/ and archives beside them; record the closure in .project.lock.json with a lock entry winning over a constraint; honour an existing .sysand/lib. Add the syspi command with init, add, install, sync, lock, bundle and index emit, and an end-to-end suite on local git, hg and svn fixtures. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…guides, changelog fragment ci: Go gate and end-to-end suite on Linux and macOS with mercurial and subversion installed, and a release workflow on v* tags building linux/darwin/windows x amd64/arm64 binaries with a SHA256SUMS file. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…al coverage KerML 10.3 Table 13 lets the checksum dictionary cover some or all of the files and specifies a list per path; sysand and the packages built with it write a single object with algorithm NONE. Read both shapes, treat NONE as unrecorded, verify what is recorded and report the files whose integrity rests on the tree hash. Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…y sysand copies, reject install-slot collisions Lock usages record the resource and versionConstraint as declared, so a reused package keeps its constraints and transport when the closure is walked from the lock. New packages are staged under lib/ and published only after every usage resolved; a failing run leaves lib/ and the lock untouched. Two resources installing as the same <publisher>-<name>_<version> with different digests are an error. A .sysand/lib copy is reused only with the locked tree digest. Archives read every member so the ZIP CRC covers unrecorded ones; bundle excludes its own output and takes its created time from the checkout when SOURCE_DATE_EPOCH is unset; clone URLs accept @Version with #subpath; credentials never reach error text. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ory constraints, index package identity and exact tag text A locked local .kpar already in lib/ is reused without reading its source; a second usage of an editable directory is checked against the resolved version; an index archive must carry the publisher and name it was requested as; the tag text must equal .project.json's version exactly, build metadata included. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…d, credential proof tests Index files and .kpar URLs take HTTP Basic credentials from the netrc (NETRC, else ~/.netrc or %USERPROFILE%\_netrc) matched per request host, so a cross-host redirect never carries them. Authentication failures of git, svn, hg and HTTP each end in one actionable hint. End-to-end tests prove the user's credentials follow through each backend: git over dumb HTTP behind Basic auth with a credential helper from GIT_CONFIG_*, hg over static-http with an [auth] section from HGRCPATH under HGPLAIN=1, svn against svnserve with a seeded auth cache, and the index and .kpar downloads with a netrc. git falls back to a full clone where a remote cannot serve a shallow one; static-http:// is accepted as an hg address. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…X and SYSAND_CRED_* credentials
Index locations are parsed and discovered as sysand does (sysand-index-config.json,
{path}/{path_raw} templates, absolute http(s) only), versions.json is validated
with sysand's rules (descending semver, no build metadata, sha256:<64-hex>,
duplicates, removed/yanked status), the index chain reads -index/SYSPI_INDEX,
SYSAND_INDEX and SYSAND_DEFAULT_INDEX (default https://sysand.com; -no-index).
HTTP downloads take SYSAND_CRED_<LABEL> glob credentials (bearer or Basic,
anonymous first, retry on 4xx except 429, ambiguity warned and tried in order,
same-host redirects only) ahead of netrc.
Co-Authored-By: jason.han <hanhuijun@gmail.com>
A netrc entry for the repository host runs svn with --username <login> --password-from-stdin --no-auth-cache (svn 1.14+), the password on stdin only, so non-interactive installs work where svn stores no plaintext passwords. The svnserve proof test now runs everywhere instead of skipping. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ry and credentials Publication parks each replaced package and restores it when a later rename or the lock write fails (lock written via temp file + rename); the HTTP client and each index are opened lazily so offline runs never read credentials and a down default index cannot block a configured one; cmdAdd validates its index options before writing .project.json; version-1 locks with string usages still parse; local index reads are bounded; versions.json digests are normalised to lowercase; plain index roots keep their query; URL queries are redacted from diagnostics; a credential never follows a redirect to another port or an https->http downgrade. Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ids refused with a reason README, docs/packages.md and the origin package comment cite ECMA-427 / purl-spec types/git-definition.json for pkg:git and state that pkg:svn, pkg:hg and pkg:sysand are not registered types used by analogy; a commit id after @ is refused with an error saying SysPI resolves only a tag naming a semantic version. The four official examples are pinned in TestPurlGitExamples. Co-Authored-By: jason.han <hanhuijun@gmail.com>
… design Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The
syspicommand-line package manager for KerML / SysML v2 project-interchange packages — Phase 1. SysPI fetches, verifies, installs and locks packages; OpenSysML only reads what it installs, and nothing here is shared code with OpenSysML or sysand.Contract. A package is any git, svn or hg repository (or
#<subpath>within one) whose tagged tree has.project.json(KerML 1.1 §10.3) and whoseversionequals the tag (v<semver>preferred, bare<semver>accepted). No.project.jsonis an error; nothing is synthesised.Identity (
internal/origin, hand-parsed, no purl library;pkg:gitis the registered Package-URL typegitof ECMA-427 / purl-spectypes/git-definition.jsonwith the version restricted to a tag naming a semver — a commit sha is refused saying why, the other official examples parse unchanged (TestPurlGitExamples);pkg:svn/pkg:hg/pkg:sysandare not registered types and use the same shape by analogy):pkg:<vcs>/<host>/<path>[@<version>][#<subpath>]. Plain clone addresses (https://…/repo.git@1.0.0,ssh://git@host/path@1.0.0,git@host:path@1.0.0,file:///path@1.0.0,svn+ssh://…) normalise to it;pkg:github/…/pkg:gitlab/…are refused pointing atpkg:git/<host>/<path>;pkg:cvs/…is refused naming git, svn, hg.Resolution (
internal/vcs): oneSourceinterface —ListVersions,Export(version, dst),Revision(version)— with a git (ls-remote --tagspeeled,clone --depth 1 --branch), hg (identify --debug -r,clone -r <node>,hg archiveminus.hg_archival.txt/.hgtags) and svn (svn ls <url>/tags,svn info --show-item last-changed-revision,svn export <url>/tags/<tag>@<rev>) implementation, each shelling out to the user's executable; a missing executable is an error naming the binary. Constraints (internal/version): exact,^,~, comparison ranges;@<version>withoutversionConstraintis exact; the looser npm forms are refused.Verification before use (
internal/project,internal/digest):.meta.jsonchecksums (list or sysand's object shape;algorithm: NONEcounts as unrecorded; Table 13's "some or all" coverage is allowed and the uncovered count is reported), tag == version, recorded revision, path-traversal / symlink / duplicate / size checks on every archive and export, and the tree hash — sorted relative paths, length-prefixed path and content, mode-free, VCS metadata skipped (documented indocs/packages.md), identical for the same package served from any VCS.Install and lock (
internal/install,internal/lock): trees go tolib/<publisher>-<name>_<version>/, archives tolib/<publisher>-<name>_<version>.kpar, beside the consuming.project.json; an existing.sysand/lib/satisfies a locked package and is never written..project.lock.jsonis deterministic (sorted, fixed key order) with VCS-neutral entries:{ "resource": "pkg:git/github.com/MBSE4U/sysmod-sysmlv2", "kind": "vcs", "publisher": "mbse4u", "name": "sysmod", "version": "5.1.5", "vcs": "git", "source": "https://github.com/MBSE4U/sysmod-sysmlv2.git", "tag": "5.1.5", "revision": "07286ecd2128b64f53fc3d05aee811e98b991a85", "sha256": "38455a9a…" }A lock entry wins over a constraint;
usageper entry lets an offline run walk the closure from the lock alone. Other sources: local directory (editable,kind: local), local/remote.kpar, OMG standard-library IRIs (kind: standard, never installed),pkg:sysand/<publisher>/<name>through a static index (internal/index, which also emits that layout).Commands (
cmd/syspi, standard-libraryflag):init,add,install,sync,lock,bundle(byte-identical,SOURCE_DATE_EPOCH, checks tag == version in a tagged checkout),index emit.-offline,-C,-index/$SYSPI_INDEXon project commands.Repository hygiene: README (contract on one screen, Future section),
docs/packages.md,AGENTS.md,CONTRIBUTING.md,changes/unreleased/fragment, SPDX headers, CI on Linux and macOS with mercurial and subversion installed andSYSPI_REQUIRE_VCS=1, release workflow onv*building linux/darwin/windows × amd64/arm64 withSHA256SUMS.Gate
tests/e2e(local fixtures only, no network): bare git repository withv1.0.0/v1.1.0plus a#packages/gearssub-package, a.kparbuilt bysyspi bundle(byte-identical twice) and served overhttptest;init → add pkg:git//<path>@1.0.0 → add #packages/gears ~1.1.0 → add <url>.kpar → add <OMG IRI> → install → lock;lib/layout as above; lock byte-identical on a fresh clone with and without the lock; offlineinstall/lock/syncafter the server and repository are gone; refusals for missing.project.jsonat the tag, tag/version mismatch, checksum mismatch in an archive, path traversal in an archive,pkg:github/…,pkg:cvs/…, an unknown host, and nogitonPATH(error names the binary, no panic), each leaving.project.jsonand the lock untouched.TestSameTreeFromGitHgSvn: the same package in a git, an hg (hg init+hg tag) and an svn repository (svnadmin create,svn importtotrunk/,svn copytotags/v1.1.0) installs byte-identicallib/trees with the samesha256and differentrevisions.TestIndexEmitAndResolve:index emitthenadd -index <url> pkg:sysand/acme/tools ^3.0.0.Interop gate (OpenSysML
sysml -validate, no flags)sysmlbuilt from OpenSysMLdevelop(15ac97cff) and, for comparison, from the head of OpenSysML PR #1517 (6df2fca8f). Project:syspi init,add pkg:git//…/widgets.git@1.0.0,add pkg:git//…/widgets.git#packages/gears 1.0.0,App.sysmlimportingWidgets::*andGears::*and typing parts byFrameandGear.The PR #1517 build gives the same two results. Every usage resolves from
lib/with no OpenSysML flags; see the first note below on how thedevelopbuild finds it.Manual check: a real public package, read-only
(The repository is
MBSE4U/sysmod-sysmlv2; its tags are bare semver, which the tag rule accepts.)Credentials follow through each backend
SysPI never prompts and stores no credentials of its own; the user's
git,hgandsvnrun withthe user's environment, and SysPI's own HTTP downloads (index files,
.kparURLs) take HTTP Basicfrom the netrc (
$NETRC, else~/.netrc/%USERPROFILE%\_netrc), matched per request host so across-host redirect never carries them (
internal/netrc, asserted innetrc_test.go). Eachauthentication failure ends in one hint (
vcs.AuthHint,index.AuthHint).tests/e2e/auth_test.goproves the paths against local servers (
SYSPI_REQUIRE_VCS=1 go test -v ./tests/e2e -run TestAuth):Credentials in a URL stay refused; URL user-info is redacted from error text.
docs/packages.mdand the README carry a Credentials section with the per-backend fix and what is deliberately not
done. svn: a netrc entry for the repository host is passed as
--username <login> --password-from-stdin --no-auth-cachewhen svn is 1.14 or newer (svn --version --quiet, checkedonce; older svn runs as before and the hint names the cache and
svn+ssh://), so hosts whose svnstores no plaintext password (Debian, Ubuntu, CI containers) install non-interactively over
svn://andhttps://;svn+ssh://andfile://take no netrc.internal/vcs/svn_auth_test.gopins the argument shape (password never in argv). The svnserve proof no longer skips anywhere.
sysand index discovery and credentials (sensmetry/sysand read, nothing copied)
Behaviour matched from sysand's sources:
core/src/env/discovery.rs(discovery document,index_root/api_rootvalidation and error texts, 404 → flat root),core/src/index_location.rs({path}/{path_raw}URL templates, absolute http(s) only, no userinfo, redaction),core/src/env/index.rs(versions.jsonrules: non-semver, build metadata,sha256:<64-hex>, duplicates, descending precedence,removedhard error,yankednot newly selected),core/src/auth.rs(RestrictAuthentication/SequenceAuthentication: anonymous first, retry on 4xx except 429, ambiguous globs warned and tried in order, same-host redirects only, literal-separator globs),sysand/src/cred_env.rsandsysand/src/cli.rs(SYSAND_CRED_*grammar and validation messages;SYSAND_INDEX/SYSAND_DEFAULT_INDEXcomma-delimited, empty-entry error),design/index-protocol.mdanddesign/credential-storage.md(precedence ofSYSAND_CRED_*over anonymous; default-index chain).internal/index/location.go:ParseLocation,Discover,Endpoints(api_rootrecorded, unused by design);internal/index/index.go:Open,ReadVersionswith sysand's messages,Lookup(removed → error).internal/httpauth:FromEnv(sysand grammar), glob compiler,Transport(env credential → netrc → anonymous),Hintnaming both mechanisms.syspi:-index/$SYSPI_INDEXcomma-separated, then$SYSAND_INDEX, then$SYSAND_DEFAULT_INDEXelsehttps://sysand.com;-no-index. The e2e harness pinsSYSAND_DEFAULT_INDEXto an empty local directory so no test reaches sysand.com.Unit tests:
internal/httpauth(grammar errors, globs, anonymous-first/retry/429/ambiguity, same-origin redirects — another host, another port or an https → http downgrade never sees a credential, URL query redaction),internal/index(locations incl. a plain root with a query, templates, discovery errors, versions.json rules incl. digest case, bounded local reads). Review round 2 (11 findings) fixed in ccb9bc2: atomic publication with rollback and a temp-file lock write, lazy HTTP client (offline never reads credentials), lazy per-index discovery (a down default never blocks a configured index),addvalidates its options before writing.project.json, legacy stringusageentries in a lock still parse. Gate on the head:go build ./...,go vet ./...,gofmt -l .empty,SYSPI_REQUIRE_VCS=1 go test ./...all ok.Notes for OpenSysML
wellKnownLibraryDirsondevelop(15ac97cff) still lists only.sysand/lib;lib/arrives with PR #1517. Thedevelopbuild above resolveslib/becausesysmlloads every model file under the project directory, not through the well-known-directory mechanism — the result is the same, but alib/outside the directory tree (or-library-root) needs #1517.#<subpath>package is also contained in its parent's tagged tree. When both are installed (lib/acme-widgets_1.0.0/packages/gears/andlib/acme-gears_1.0.0/),sysmlloadsGears.sysmltwice and reports "Gears is opened by more than one loaded file". The reader could skip a nested directory that has its own.project.jsonwhen loading an installed project (SysPI must not alter the tree: its hash is the tagged tree)..meta.jsonas sysand writes it (and asMBSE4U/sysmod-sysmlv2ships it) recordschecksumaspath → {value, algorithm}— one object, not the list Table 13 specifies — with"algorithm": "NONE"and an empty value, and covers only the four.sysmlfiles.internal/workspace/project/archive.godecodesmap[string][]Checksum, so such a.meta.jsonwould be rejected; SysPI reads both shapes and treatsNONEas unrecorded.sysmod5.1.5 installed and a model that doesprivate import SYSMOD::*;,sysml -validate(both builds) reportserror[unresolved]forSYSMODViews::ProjectView,::ProjectAIView,::ProblemStatementView,::ProblemStatementAIViewatSYSMOD.sysml:50–76, althoughview def ProblemStatementAIViewis declared at the top level oflibrary package SYSMODViews(SYSMODViews.sysml:504) andSYSMODViewsitself doesprivate import SYSMOD::*;— a mutual import between two installed library packages that looks like a resolution gap rather than an installation problem (the files are all present and loaded, as the earlier lines show).