Skip to content
This repository was archived by the owner on Sep 9, 2020. It is now read-only.
This repository was archived by the owner on Sep 9, 2020. It is now read-only.

The dep workflow: default constraints, minimal manifest? #213

Closed
@nathany

Description

@nathany

This is a meta-issue after some discussion with @sdboyer on #vendor to better understand the dep tool and to both explain my understanding and present what I think the workflow could be like based on what I currently know. Currently I'm only tackling the simple case of building a new app.

The dep tool is unique compared to its contemporaries because it is aware of the import graph contained in source code. That means the import statements in your source code are the source of truth. Bundler, Hex, NPM, etc. look to a manifest file for a list dependencies you manually maintain. This impacts what the so-named (#168) "manifest" file is actually for.

So to use dep, you can start by just writing code. At some point you need to dep init to create the manifest and lock file. The lock file is a full transitive list of all imported code with commit SHAs that make reproducible builds possible. The "manifest" need not list all the dependencies and constraints, in fact it could be essentially empty†. Let me explain.

Say you import "foo/bar" and run dep ensure to ensure that everything imported is correctly vendored and tracked.

The dep tool can be smart enough to fetch the latest stable version v1.3.4 of "foo/bar" rather than pulling master (as with go get) and rather than pulling v2.0.0.beta.1. Pre-releases are always opt-in, as the solver gives preference to stable versions.

At this point there is no need for anything to exist in the manifest, but the lock file will contain a specific commit SHA for "foo/bar" which you commit along with your code (and optionally the vendored copy of "foo/bar" for the hermetic view of the world).

Now lets say you import "baz/qux". Even if "foo/bar" v1.3.5 is available, running dep ensure should vendor "baz/qux" but not touch "foo/bar" at all.

A separate command should exist to dep update foo/bar, which would upgrade to v1.3.5. If no constraints are manually specified, dep update x always gets you the latest stable version, so once v2.0.0 is out, dep update foo/bar would give you that.

IMO, this should be fine for most cases. Upgrading versions is always done in a controlled manner and builds are reproducible thanks to the lock file. Additionally, you can use dep status to see what's going to happen before running dep update x and simply choose not to update to new major versions immediately.

The "manifest" comes into play when the default behaviour isn't what you want.

  • To opt-in to a pre-release version of a dependency ^v2.0.0.beta.1
  • When two downstream dependencies have conflicting constraints, the manifest can specify an override to determine which one wins.
  • Or generally to override the preferences of a downstream dependency if you know better.
  • To lock to an older version due to an incompatibility, license change, or other consideration.
  • To use a temporary fork in place of a given dependency.

These are all exceptions to the general rule of latest stable version. In all these cases, it is helpful to include a comment in the manifest indicating why the constraint is specified.

[dependencies]
"foo/bar" = "^v1.3.5" # v2 is incompatible with baz/qux

Next time you run dep ensure, the constraints in the manifest will override the default behaviour (latest stable version) when solving. However, if v2.0.0 is already vendored, you may see an error, and need to run dep update to update to the older specified version.

How does this sound so far?

If this makes sense, I'd suggest a few changes that could help direct some open issues:

  • The "manifest" should be a human-editable file that is created on init and never modified by the tool. Comments and organization of the file are up to the humans maintaining it. The initial file should include a wall of comments as documentation on usage. To support comments and for readability, the format preferably wouldn't be JSON (Move to TOML for manifest and lock #119), but the lock file could remain JSON.
  • The dep ensure foo/bar variant should go away in favour of a separate update command. Neither command should alter the manifest. To add a dependency, write an import statement in your Go code, and run dep ensure to vendor the latest stable version.
  • The "manifest" probably shouldn't be called "manifest" (Discuss renaming lock.json and manifest.json #168). It provides a way to override default behaviours. It's not a full list of everything (the lock file is).
  • The manifest is for specifying constraints and overrides. It is an error if the manifest specifies a package that isn't ever imported (much like unused variable/import in Go).
  • If supported, dep update foo/bar v1.3.4 could override any constraints specified in the manifest too, vendoring v1.3.4. But assuming this doesn't update the manifest (not worth the trouble), maybe it's not worth supporting constraints on the CLI.

The end result of all this is a manifest designed for humans but that rarely needs editing. For the common case, just write your code, run dep ensure, and commit your work as usual. That's as simple as it gets.

† Yet to confirm that the solver can cope with a large number of unspecified constraints (empty manifest) with a preference for the latest stable version.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions