Documentation
¶
Overview ¶
Package manifest holds the vocabulary the scanner and writer modules share: the dependency-field kinds a manifest declares and the file-name rules both sides must agree on. It exists so the reading and writing halves of dispat's manifest support can never drift apart on what a kind is called or which files count as manifests.
Index ¶
- Variables
- func ComposeIdentity(services []ComposeService) (repository, tag string)
- func IsDockerfile(name string) bool
- func IsRequirementsFile(name string) bool
- func NameWords(base string) []string
- func NormalizePyName(name string) string
- func PathSuffix(f Format) (string, bool)
- func ValidTag(tag string) bool
- type ComposeService
- type DockerRef
- type Format
- type ImageRef
- type Kind
Constants ¶
This section is empty.
Variables ¶
var Formats = []Format{ FormatNpm, FormatGoMod, FormatCargo, FormatPyProject, FormatRequirements, FormatComposer, FormatMaven, FormatMSBuildProject, FormatNuSpec, FormatPackagesProps, FormatPackagesConfig, FormatPubspec, FormatPlist, FormatAndroidManifest, FormatGradleCatalog, FormatGradleBuild, FormatXcodeProject, FormatPodfile, FormatPodspec, FormatGemfile, FormatGemspec, FormatDockerfile, FormatCompose, FormatUnityPackages, FormatUnityProjectSettings, FormatGodotProject, FormatGodotPlugin, FormatGodotExportPresets, FormatUnrealProject, FormatUnrealPlugin, FormatUnrealGameConfig, FormatUnrealEngineConfig, FormatDefoldProject, FormatO3DEProject, FormatO3DEGem, }
Formats lists every recognised format. Both halves range over it to prove they cover the same ground.
Functions ¶
func ComposeIdentity ¶
func ComposeIdentity(services []ComposeService) (repository, tag string)
ComposeIdentity picks the image a compose file declares as its own, and returns its repository and tag. Both are empty when the file declares no identity, which is the honest answer for a compose file that only wires third-party services together.
A compose file lists many images and says nothing about which one the folder ships, so the choice is made by two rules in order.
First, the service that both builds and tags: a service with a build section and a tagged image is producing that image here, which is as close to "this is my package" as compose gets. Second, when nothing builds, the tagged repository the most services name — a scaled service appears several times under one image, and the third-party ones it sits beside appear once each.
Ties in either rule go to the lowest service name. A YAML mapping has no order worth trusting, so the tie-break has to come from the data rather than from how it was decoded, or the answer would change between runs.
func IsDockerfile ¶
IsDockerfile reports a container build file. Unlike every other format here, this one has no fixed name and no extension: Docker takes the base name "Dockerfile", the convention for a variant is a suffix ("Dockerfile.dev"), and the convention for keeping several in one folder is a prefix ("api.Dockerfile"). Podman's "Containerfile" is the same format spelled differently and is accepted on the same terms.
The comparison ignores case, because "-f dockerfile" builds exactly as "-f Dockerfile" does and repositories spell it both ways.
func IsRequirementsFile ¶
IsRequirementsFile reports a pip requirements file: a .txt whose base name starts or ends with the word "requirements" (requirements.txt, requirements-dev.txt, dev-requirements.txt). A name merely containing the word somewhere in the middle (OLD-REQUIREMENTS-NOTES.txt) is prose, not a manifest.
func NormalizePyName ¶
NormalizePyName renders a Python distribution name in its PEP 503 normalised form: lowercased, every run of "-", "_" and "." collapsed to a single "-", so "Acme_Core" and "acme.core" name the same package.
func PathSuffix ¶ added in v1.1.1
PathSuffix returns the slash-anchored path a path-qualified format is always kept at, and whether the format has one at all.
The four that do are the formats whose base name means something else everywhere else, which is why their folder is part of the name rather than a place the author chose: a Unity project's settings are ProjectSettings/ProjectSettings.asset or they are not those settings. A caller deciding whether a manifest belongs to the folder it scanned needs that distinction, because such a file is nested and still the folder's own.
func ValidTag ¶
ValidTag reports text a registry would accept as a tag: up to 128 characters of letters, digits, underscores, periods and dashes, not opening with a separator. A writer checks it before splicing so a version that cannot be a tag is refused outright instead of producing a file that no longer builds.
Types ¶
type ComposeService ¶
type ComposeService struct {
// Name is the service's key in the services map.
Name string
// Image is the declared image reference, empty when the service declares
// none.
Image string
// Builds reports a service that declares a build section.
Builds bool
}
ComposeService is one Compose service reduced to the facts that decide which image a compose file is *about*: what it is called, what image it declares, and whether it builds that image here rather than pulling it.
The type exists so the two halves of the tooling can share the decision without sharing the extraction. The reader gets these facts from a YAML decode; the writer gets them from the line walk it needs anyway to splice a scalar in place. Both then call ComposeIdentity, so they cannot disagree about which service owns the file's version — a disagreement that would show up as dispat writing a version into a service it never read one from.
type DockerRef ¶
DockerRef is one image reference located inside a Dockerfile: which line holds it and which bytes of that line it occupies.
The offsets are what let the writer splice a tag without rebuilding the line around it, and they are why locating references lives here rather than in each half separately. The reader only needs to know *which* images a file depends on and the writer only needs to know *where* they are, but both must agree on the answer to the first question — a reference the reader counts as a dependency and the writer cannot find would silently never be reconciled.
func DockerfileRefs ¶
DockerfileRefs finds every image reference a Dockerfile depends on.
Three instructions name an image. FROM names the base of a stage. COPY --from and RUN --mount=…,from= pull files out of one. Each may instead name an earlier stage, by its AS alias or by its position, and a stage is part of this file rather than something it depends on, so those are filtered out as they are met — along with `scratch`, which is a keyword.
Instructions may run across several physical lines through a trailing backslash, and a splice needs a physical line, so the walk tracks the instruction it is inside rather than joining the lines up first. Stage aliases come into scope as they are defined, which means an alias only shadows an image for the instructions below it. That is the scope the builder gives them, and the reason `FROM tools:1.0 AS first` above `FROM alpine:3 AS tools` still reports a dependency on tools:1.0.
type Format ¶
type Format string
Format identifies one manifest file format. It exists so the reader and the writer agree on which files count as manifests and what each one is, instead of each keeping its own list of names. Those lists drifted before this type did the job: a format could be readable and silently unwritable, and adding one meant remembering to edit two modules.
const ( FormatNpm Format = "npm" // package.json FormatGoMod Format = "gomod" // go.mod FormatCargo Format = "cargo" // Cargo.toml FormatPyProject Format = "pyproject" // pyproject.toml FormatRequirements Format = "requirements" // requirements*.txt FormatComposer Format = "composer" // composer.json FormatMaven Format = "maven" // pom.xml FormatMSBuildProject Format = "msbuild-project" // *.csproj, *.fsproj, *.vbproj FormatNuSpec Format = "nuspec" // *.nuspec FormatPackagesProps Format = "packages-props" // Directory.Packages.props FormatPackagesConfig Format = "packages-config" // packages.config FormatPubspec Format = "pubspec" // pubspec.yaml, pubspec.yml FormatPlist Format = "plist" // Info.plist FormatAndroidManifest Format = "android-manifest" // AndroidManifest.xml FormatGradleCatalog Format = "gradle-catalog" // libs.versions.toml FormatGradleBuild Format = "gradle-build" // build.gradle, build.gradle.kts FormatXcodeProject Format = "xcode-project" // project.pbxproj FormatPodfile Format = "podfile" // Podfile FormatPodspec Format = "podspec" // *.podspec FormatGemfile Format = "gemfile" // Gemfile FormatGemspec Format = "gemspec" // *.gemspec FormatDockerfile Format = "dockerfile" // Dockerfile, Dockerfile.*, *.Dockerfile FormatCompose Format = "compose" // compose.yaml, docker-compose.yml, ... // The game engines. Each keeps its version somewhere a package manager // would not look, and several of them declare dependencies beside it. FormatUnityPackages Format = "unity-packages" // Packages/manifest.json FormatUnityProjectSettings Format = "unity-project-settings" // ProjectSettings/ProjectSettings.asset FormatGodotProject Format = "godot-project" // project.godot FormatGodotPlugin Format = "godot-plugin" // plugin.cfg FormatGodotExportPresets Format = "godot-export-presets" // export_presets.cfg FormatUnrealProject Format = "unreal-project" // *.uproject FormatUnrealPlugin Format = "unreal-plugin" // *.uplugin FormatUnrealGameConfig Format = "unreal-game-config" // Config/DefaultGame.ini FormatUnrealEngineConfig Format = "unreal-engine-config" // Config/DefaultEngine.ini FormatDefoldProject Format = "defold-project" // game.project FormatO3DEProject Format = "o3de-project" // project.json FormatO3DEGem Format = "o3de-gem" // gem.json )
The formats both halves recognise. The value is a stable identifier rather than a file name, because several names map onto one format.
func FormatOf ¶
FormatOf resolves a file's base name onto its format: by exact name, then by extension, then by the two families whose names vary. A name that matches nothing is not a manifest.
func FormatOfPath ¶ added in v1.1.0
FormatOfPath resolves a file's path onto its format: first the formats only recognisable by where they sit, then FormatOf on the base name. The path may be relative or absolute, and may use either separator.
It is the entry point a walk uses, because four formats cannot be told from their base name alone. FormatOf stays the answer where only a name is known, and deliberately never learns those four: a bare manifest.json is a web app manifest, and treating it as Unity's would be a guess.
type ImageRef ¶
type ImageRef struct {
// Repository is everything before the tag: the registry, its optional
// port, and the path. "redis", "ghcr.io/acme/api", "localhost:5000/api".
Repository string
// Tag is the declared tag, empty when the reference names none.
Tag string
// Digest is the declared digest ("sha256:..."), empty when the reference
// pins none.
Digest string
// TagStart and TagEnd are the byte offsets of Tag inside the reference the
// split was given, so a writer can splice the tag without rebuilding the
// text around it. Both are -1 when there is no tag.
TagStart, TagEnd int
}
ImageRef is one Docker image reference split into the parts a manifest declares. A reference packs up to four things into one string — registry, repository path, tag and digest — and the reader and the writer must agree byte for byte on where each one starts, so the split lives here rather than in either half.
The parts are kept exactly as written. No registry is inferred, no "latest" is filled in and "library/" is never prepended: a manifest declares what it declares, and a writer that normalised the text would rewrite lines nobody asked it to touch.
func ParseImageRef ¶
ParseImageRef splits an image reference.
Two separators do the work, and the order matters. The digest is cut at the first "@", because everything after it belongs to the digest. The tag is then the text after a ":" that comes *after the last* "/" — that one rule is what tells the port in "localhost:5000/api" apart from the tag in "redis:7.2", since a registry's port can only appear before the first slash and a tag only after the last one.
A reference is never rejected. Anything unrecognisable comes back as a bare repository with no tag, which is inert: nothing matches it and no writer can splice it.
func (ImageRef) Interpolated ¶
Interpolated reports a reference whose repository or tag defers to a build argument or an environment variable ("${BASE}:${TAG}", "$IMAGE"). The value is resolved outside the file, and writing a literal over it would sever the indirection it exists for.
type Kind ¶
type Kind string
Kind is the manifest dependency field a declaration came from (or an edit targets). The zero value is the plain `dependencies` field, mirroring the config model's dependency kinds, so the two convert by a cast.
const ( KindDependencies Kind = "" KindDevDependencies Kind = "devDependencies" KindPeerDependencies Kind = "peerDependencies" KindOptionalDependencies Kind = "optionalDependencies" )
Dependency kinds, spelled exactly like the manifest fields they stand for.
func ParseKind ¶
ParseKind maps a dependency-field spelling onto its Kind. The four field names are accepted, with "dependencies" read as the long spelling of the zero value, so a caller carrying a word from a config file or a command line never has to know the zero value stands for the plain field.