diff --git a/docs/Gopkg.toml.md b/docs/Gopkg.toml.md index a40f813a83..7eca5ab72f 100644 --- a/docs/Gopkg.toml.md +++ b/docs/Gopkg.toml.md @@ -110,7 +110,7 @@ ignored = ["github.com/user/project/badpkg*"] `metadata` declarations are ignored by dep and are meant for usage by other independent systems. -The root `metadata` declaration defines informatino about the project itself, while a `metadata` declaration under a `[[constraint]]` or an `[[override]]` defines metadata about that rule, for the `name`d project. +The root `metadata` declaration defines information about the project itself, while a `metadata` declaration under a `[[constraint]]` or an `[[override]]` defines metadata about that rule, for the `name`d project. ```toml [metadata] key1 = "value that convey data to other systems" diff --git a/docs/day-to-day-dep.md b/docs/day-to-day-dep.md index c5d6893093..b115d6252d 100644 --- a/docs/day-to-day-dep.md +++ b/docs/day-to-day-dep.md @@ -1,37 +1,37 @@ --- -title: Day-to-day dep +title: Daily Dep --- -In keeping with Go's philosophy of minimizing knobs, dep has a sparse interface; there are only two commands you're likely to run regularly: +This guide is an introduction to the day-to-day use of dep. If you haven't set up a Go project at all yet, though, run through [Creating a New Project](new-project.md) first. + +Dep is a tool you'll use regularly in the course of normal Go development. Regularly, but briefly - dependency management is never the place we want to be spending our time or energy! In keeping with Go's philosophy of minimizing knobs, dep has a sparse interface; there are only two commands you're likely to run regularly: * `dep ensure` is the primary workhorse command, and is the only command that changes disk state. -* `dep status` reports on the state of your project, and the universe of Go dependencies. +* `dep status` reports on the state of your project, and the visible universe of Go software projects. -This guide primarily centers on `dep ensure`, as that's the command you run to effect changes on your project. The [ensure mechanics](ensure-mechanics.md) reference details how the command actually works, and is worth reading if you're encountering a confusing `dep ensure` behavior (or just curious!). This guide is more of a high-level tour for folks trying to get a basic handle on using dep effectively. +This guide primarily centers on `dep ensure`, as that's the command you run to effect changes on your project. The [ensure mechanics](ensure-mechanics.md) reference document details how the things work under the hood, and is worth reading if you're encountering a confusing `dep ensure` behavior (or just curious!). ## Basics -Let's start with some semantics: the verb is "ensure" to emphasize that the action being taken is not only performing a single, discrete action (like adding a dependency), but rather enforcing a kind of broader guarantee. To put that guarantee in narrative terms, running `dep ensure` is like saying: - -> Hey dep, please make sure that my project is [in sync](glossary.md#sync): that `Gopkg.lock` satisfies all the imports in my project, and all the rules in `Gopkg.toml`, and that `vendor/` contains exactly what `Gopkg.lock` says it should." +Let's start with words! -As the narrative indicates, `dep ensure` is a holistic operation; rather than offering a series of commands that you run in succession to incrementally achieve a some final state, each run of `dep ensure` delivers a complete, consistent final state with respect to the inputs of your project. It's a bit like a frog, hopping from lilypad to lilypad: `dep ensure` moves your project from one safe (transitively complete import graph, with all constraints satisfied, and a fully populated `vendor`) island to the the next, or it doesn't move at all. There are no known intermediate failure states. This makes `dep ensure` fine to run at most any time, as it will always drive towards a safe, known good state. +Dep's main command is `dep ensure`. The verb is "ensure" to imply that the action is not just some single, discrete action (like adding a dependency), but enforcing some kind of broader guarantee. If we wanted to express the `dep ensure` guarantee as a sentence, it would go something like this: -General guidelines for using dep: +> Hey dep, please make sure that [my project](glossary.md#current-project) is [in sync](glossary.md#sync): that [`Gopkg.lock`](Gopkg.lock.md) satisfies all the imports in my project, and all the rules in[ `Gopkg.toml`](Gopkg.toml.md), and that `vendor/` contains exactly what `Gopkg.lock` says it should." -* Never directly edit anything in `vendor/`; dep will unconditionally overwrite such changes. -* `dep ensure` is almost never the wrong thing to run; if you're not sure what's going on, running it will bring you back to safety, or fail informatively. +As the narrative indicates, `dep ensure` is a holistic operation; rather than offering a series of commands that you run in succession to incrementally achieve a some final state, each run of `dep ensure` delivers a safe, complete, and reproducible set of dependencies with respect to the current state of your project. You might imagine repeated runs of `dep ensure` as being a bit like a frog, hopping from one lilypad to the next. + `dep ensure` also guarantees that, barring `kill -9`, power failure, or a critical bug, its disk writes are all-or-nothing: on any given run, either nothing changes (and you get an error), or you're on the nearest safe lilypad. This makes `dep ensure` fine to run at most any time. ## Using `dep ensure` There are four times when you'll run `dep ensure`: -- We want to add a new dependency -- We want to update an existing dependency -- We've imported a package for the first time, or removed the last import of a package -- We've made a change to a rule in `Gopkg.toml` +- To add a new dependency +- To update an existing dependency +- To catch up after importing a package for the first time in your project, or removing the last import of a package in your project +- To catch up to a change to a rule in `Gopkg.toml` There's also an implicit fifth time: when you're not sure if one of the above has happened. Running `dep ensure` without any additional flags will get your project back in sync - a known good state. As such, it's generally safe to defensively run `dep ensure` as a way of simply making sure that your project is in that state. @@ -54,11 +54,17 @@ This should succeed, resulting in an updated `Gopkg.lock` and `vendor/` director If you run "dep ensure" again before actually importing it, it will disappear from Gopkg.lock and vendor/. ``` -As the warning suggests, you should introduce an `import "github.com/pkg/errors"` in your code, the sooner the better. If you don't, a later `dep ensure` run will interpret your newly-added dependency as unused, and automatically remove it from `Gopkg.lock` and `vendor/`. This is because, in contrast to other dependency management tools that rely on a metadata file to indicate which dependencies are required, dep considers the import statements it discovers through static analysis of your project's code to be the canonical indicator of what dependencies must be present. +As the warning suggests, you should introduce an `import "github.com/pkg/errors"` in your code, the sooner the better. If you don't, a later `dep ensure` run will interpret your newly-added dependency as unused, and automatically remove it from `Gopkg.lock` and `vendor/`. This also means that if you want to add multiple dependencies at once, you'll need to do it in a single command, rather than one after the other: + +``` +$ dep ensure -add github.com/pkg/errors github.com/foo/bar +``` + +Dep works this way because it considers the import statements it discovers through static analysis of your project's code to be the canonical indicator of what dependencies must be present. That choice does add some pain at this moment, but it reduces friction and automates cleanup elsewhere. Tradeoffs! -Note that you do not _have to_ use `dep ensure -add` to add new dependencies - you can also just add an appropriate `import` statement in your code, then run `dep ensure`. This approach doesn't always play nicely with [`goimports`](https://godoc.org/golang.org/x/tools/cmd/goimports), and also won't append a `[[constraint]]` into `Gopkg.toml`. Still, it can be useful at times, often for rapid iteration and off-the-cuff experimenting. +Of course, given this model, you don't _have to_ use `dep ensure -add` to add new dependencies - you can also just add an appropriate `import` statement in your code, then run `dep ensure`. However, this approach doesn't always play nicely with [`goimports`](https://godoc.org/golang.org/x/tools/cmd/goimports), and also won't append a `[[constraint]]` into `Gopkg.toml`. Still, it can be useful at times, often for rapid iteration and off-the-cuff experimenting. -The [ensure mechanics section on `-add`](ensure-mechanics.md#add) has more detail on internals, as well as some subtle variations in `dep ensure -add`'s behavior. +The [ensure mechanics section on `-add`](ensure-mechanics.md#add) has a more thorough exploration, including some ways that `dep ensure -add`'s behavior subtly varies depending on the state of your project. ### Updating dependencies @@ -97,7 +103,7 @@ Only if it is the first/last import of a project being added/removed - cases 3 a `Gopkg.toml` files contain five basic types of rules. The [`Gopkg.toml` docs](#gopkg.toml.md) explain them in detail, but here's an overview: -* `required`, which are mostly equivalent to import statements in code, except it's OK to include a `main` package +* `required`, which are mostly equivalent to `import` statements in `.go` files, except that it's OK to list a `main` package here * `ignored`, which causes dep to black hole an import path (and any imports it uniquely introduces) * `[[constraint]]`, stanzas that express version constraints and some other rules on a per-project dependency basis * `[[override]]`, stanzas identical to `[[constraint]]` except that only the current project can express them and they supersede `[[constraint]]` in both the current project and dependencies @@ -105,4 +111,17 @@ Only if it is the first/last import of a project being added/removed - cases 3 a Changes to any one of these rules will likely necessitate changes in `Gopkg.lock` and `vendor/`; a single successful `dep ensure` run will incorporate all such changes at once, bringing your project back in sync. +## Key Takeaways + +Here are the key takeaways from this guide: + +- `dep ensure -update` is the preferred way to update dependencies, though it's less effective for projects that don't publish semver releases. +- `dep ensure -add` is usually the easiest way to introduce new dependencies, though it's not the only one. To add more than one at a time, you'll need to use multiple arguments, not multiple invocations - and make sure to add real `import` statements for the projects after the command completes! +- If you ever make a manual change in `Gopkg.toml`, it's best to run `dep ensure` to make sure everything's in sync. +- `dep ensure` is almost never the wrong thing to run; if you're not sure what's going on, running it will bring you back to safety ("the nearest lilypad"), or fail informatively. + +Also, a couple other miscellaneous tidbits: + +- As in the Go toolchain generally, avoid symlinks within your own project. dep tolerates a bit of this, but like the Go toolchain itself, is generally not terribly supportive of symlinks. +- Never directly edit anything in `vendor/`; dep will unconditionally overwrite such changes. If you need to modify a dependency, fork it and do it properly. diff --git a/docs/deduction.md b/docs/deduction.md index fd7d560f3c..acfc16b939 100644 --- a/docs/deduction.md +++ b/docs/deduction.md @@ -9,4 +9,35 @@ Deduction is dep's algorithm for looking at an import path and determining the p The set of hosts supported by static deduction are the same as [those supported by `go get`](). -If the static logic cannot identify the root for a given import path, the algorithm continues to a dynamic component, where it makes an HTTP(S) request to the import path, and a server is expected to return a response that indicates the root import path. This, again, is equivalent to the behavior of `go get`. \ No newline at end of file +If the static logic cannot identify the root for a given import path, the algorithm continues to a dynamic component, where it makes an HTTP(S) request to the import path, and a server is expected to return a response that indicates the root import path. This, again, is equivalent to the behavior of `go get`. + + + +Import path deduction is applied to all of the following: + +* `import` statements found in all `.go` files +* Import paths named in the [`required`](gopkg.toml.md#required) property in `Gopkg.toml` +* `name` properties in both [`[[constraint]]`](Gopkg.toml.md#constraint) and [`[[override]]`](Gopkg.toml.md#override) stanzas in `Gopkg.toml`. This is solely for validation purposes, enforcing that these names correspond strictly to source roots. + + + + +The results of import path deduction are, in practice, almost entirely fixed; it's easy to imagine why. In the public ecosystem, even dynamic deductions rarely change in practice, as it would either require: + +- a `go-get` metadata service to intentionally change its mappings, or +- a `go-get` metadata service to disappear. + +`go get` itself is only partially resilient to these cases, but each is potentially catastrophic for a package's retrievability across the ecosystem, sooner rather than later. This steep and abrupt downside makes it nearly impossible for projects only accessible via an unreliable metadata service to ever become popular or widely used in the ecosystem. Thus, in the public ecosystem, we almost only ever see reliable, well-behaved services. + + + + + + + + + + +Because deduction has a dynamic component, the deducibility of any given path necessarily cannot be fixed. However, because the + +## Static deduction \ No newline at end of file diff --git a/docs/dep-basics.md b/docs/dep-basics.md deleted file mode 100644 index af84291d02..0000000000 --- a/docs/dep-basics.md +++ /dev/null @@ -1 +0,0 @@ -Normal work with dep will have \ No newline at end of file diff --git a/docs/ensure-mechanics.md b/docs/ensure-mechanics.md index fdf8934b4e..a85e9f1620 100644 --- a/docs/ensure-mechanics.md +++ b/docs/ensure-mechanics.md @@ -1,23 +1,27 @@ --- -title: dep ensure mechanics +title: Models and Mechanics --- -As `dep ensure` is dep's sole state-mutating command, its mechanics are essentially the mechanics of dep as a whole. +While dep has many discrete components and moving parts, all of these parts revolve around a central model. This document explains that model, then explores the major mechanisms of dep as they relate to it. -## Functional flow +## States and flows -Dep's operation centers around the idea of the "four state system" - a model for organizing the on-disk state a package manager deals with, originally articulated in [this (very long) article](https://medium.com/@sdboyer/so-you-want-to-write-a-package-manager-4ae9c17d9527). Those states are: +Dep is centered around the idea of the "four state system" - a model for classifying and organizing the on-disk state with which a package manager interacts. This was first articulated as a coherent, general model in [this (long) article](https://medium.com/@sdboyer/so-you-want-to-write-a-package-manager-4ae9c17d9527), though many of the principles in the four state model were derived from existing package managers. + +Briefly, the four states are: 1. The [current project's](glossary.md#current-project) source code. -2. A [manifest](glossary.md#manifest) - a file describing the current project's dependency requirements. In dep, this is the `Gopkg.toml` file. -3. A [lock](glossary.md#lock) - a file containing a transitively-complete, reproducible description of the dependency graph. In dep, this is the `Gopkg.lock` file. +2. A [manifest](glossary.md#manifest) - a file describing the current project's dependency requirements. In dep, this is the [`Gopkg.toml`](Gopkg.toml.md) file. +3. A [lock](glossary.md#lock) - a file containing a transitively-complete, reproducible description of the dependency graph. In dep, this is the [`Gopkg.lock`](Gopkg.lock.md) file. 4. The source code of the dependences themselves. In dep's current design, this is the `vendor/` directory. -Let's visually represent these four states as follows: +We can visually represent these four states as follows: ![dep's four states](img/four-states.png) -It's best to think of `dep ensure` as a unidirectional series of functions, analyzing and transforming inputs into outputs. Specifically, there are two functions: +### Functional flow + +It's useful to think of dep as a system that imposes a unidirectional, functional flow on the relationships between these states. These functions treat the above states as inputs and outputs, moving them from left to right. Specifically, there are two functions: * A _solving function_, that takes as its input the set of imports in the current project and the rules in `Gopkg.toml`, and returns as its output a transitively-complete, immutable dependency graph - the information in a `Gopkg.lock`. * A _vendoring function_, that takes the information in a `Gopkg.lock` as its input and ensures an on-disk arrangement of source files such that the compiler will use the versions designated in the lock. @@ -26,6 +30,8 @@ We can represent these two functions visually: ![dep's two main functions](img/annotated-func-arrows.png) +This is `dep ensure` - the typical flow, used when a `Gopkg.toml` already exists. When a project does not yet have a `Gopkg.toml`, `dep init` can generate one. The essential flow remains the same, but with changed inputs: instead of reading from an existing `Gopkg.toml` file, `dep init` constructs one out of data inferred from the user's GOPATH, and/or [a metadata file from another tool](). (In other words, `dep init` automatically migrates a project from other approaches to organizing dependencies.) + This diagram directly corresponds directly to code, as well. The solving function is actually split into a constructor and a method - we first create a [`Solver`](https://godoc.org/github.com/golang/dep/gps#Solver) type, then call its `Solve()` method. The inputs to the constructor are wrapped up in a [`SolveParameters`](https://godoc.org/github.com/golang/dep/gps#SolveParameters), which should look familiar: ```go @@ -36,36 +42,26 @@ type SolveParameters struct { } ``` -The vendoring function is [`gps.WriteDepTree()`](https://godoc.org/github.com/golang/dep/gps#WriteDepTree). It takes a handful of arguments, but the key one is a [`Lock`](https://godoc.org/github.com/golang/dep/gps#Lock) - that is, the data held in `Gopkg.lock`. +The vendoring function is [`gps.WriteDepTree()`](https://godoc.org/github.com/golang/dep/gps#WriteDepTree). While it takes a handful of arguments, the relevant one is a [`gps.Lock`](https://godoc.org/github.com/golang/dep/gps#Lock) - an interface representing an abstracted form of the data held in a `Gopkg.lock`. -Almost all of dep's behaviors are best understood with respect to this functional model. If you want to understand dep's mechanics, keep this model centered in your mind. +The four state system, and these functional flows through it, are the foundation on which all of dep's behavior is built. If you want to understand dep's mechanics, keep this model at the forefront of your mind. -## Functional optimizations +### Staying in sync -It is one of dep's foundational design goals that both of its functions do as little work as possible, and result in as little change for their outputs as possible. Consequently, both "functions" peek ahead at the their current result to understand what work, if any, actually needs to be done: +One of dep's design goals is that both of its "functions" minimize both the work they do, and the change they induce in their respective results. (Note: "minimize" is not currently formally defined with respect to a cost function.) Consequently, both functions peek ahead at the pre-existing result to understand what work actually needs to be done: * The solving function checks the existing `Gopkg.lock` to determine if all of its inputs (project import statements + `Gopkg.toml` rules) are satisfied. If they are, the solving function can be bypassed entirely. If not, the solving function proceeds, but attempts to change as few of the selections in `Gopkg.lock` as possible. * WIP: The current implementation's check relies on a coarse heuristic check that can be wrong in some cases. There is a [plan to fix this](https://github.com/golang/dep/issues/1496). * The vendoring function hashes each discrete project already in `vendor/` to see if the code present on disk is what `Gopkg.lock` indicates it should be. Only projects that deviate from expectations are written out. * WIP: the hashing check is generally referred to as "vendor verification," and [is not yet complete](https://github.com/golang/dep/issues/121). Without this verification, dep is blind to whether code in `vendor/` is correct or not; as such, dep must defensively re-write all projects to ensure the state of `vendor/` is correct. -## Sync - -It's easy to think about - - - -## Imports, and the package tree - - - -## Flags and behavior variations - -Each of `dep ensure`'s various flags affects the behavior of these functions - or even whether they run at all. +Of course, it's possible that, in peeking ahead, either function might discover that the pre-existing result is already correct - so no work need be done at all. Either way, when each function completes, we can be sure that the output, changed or not, is correct with respect to the inputs. In other words, the inputs and outputs are "in sync." Indeed, being in sync is the "known good state" of dep; `dep ensure` (without flags) guarantees that if it exits 0, all four states in the project are in sync. +## `dep ensure` flags and behavior variations +Each of `dep ensure`'s various flags affects the behavior of the solving and vendoring functions - or even whether they run at all. Some flags can also marginally push the project out of sync, temporarily. Thinking about these effects in the context of dep's basic model is the fastest path to understanding. -## `-no-vendor` and `-vendor-only` +### `-no-vendor` and `-vendor-only` These two flags are mutually exclusive, and determine which of `dep ensure`'s two functions are actually performed. Passing `-no-vendor` will cause only the solving function to be run, resulting in the creation of a new `Gopkg.lock`; `-vendor-only` will skip solving and run only the vendoring function, causing `vendor/` to be repopulated from the pre-existing `Gopkg.lock`. @@ -73,7 +69,7 @@ These two flags are mutually exclusive, and determine which of `dep ensure`'s tw Passing `-no-vendor` has the additional effect of causing the solving function to run unconditionally, bypassing the pre-check ordinarily made against `Gopkg.lock` to see if it already satisfies all inputs. -## `-add` +### `-add` The general purpose of `dep ensure -add` is to facilitate the introduction of new dependencies into the depgraph. Whereas `-update` is restricted to [source roots](glossary.md#source-root), (e.g. `github.com/foo/bar`), `-add` can take any package import path as an argument (e.g. `github.com/foo/bar` OR `github.com/foo/bar/baz`). @@ -123,17 +119,17 @@ $ dep ensure -add github.com/foo/bar If you run "dep ensure" again before actually importing it, it will disappear from Gopkg.lock and vendor/. ``` -## `-update` +### `-update` The behavior of `dep ensure -update` is intimately linked to the behavior of the solver itself. Full detail on that is a topic for the [solver reference material](the-solver.md), but for the purposes of understanding `-update`, we can simplify a bit. -First, to solidify an implication in the discussion of [functional optimizations](#functional-optimizations), the solving function actually takes into account the pre-existing `Gopkg.lock` when it runs: +First, to solidify an implication in the discussion of [functional optimizations](#staying-in-sync), the solving function actually takes into account the pre-existing `Gopkg.lock` when it runs: ![Pre-existing lock feeds back into solving function](img/lock-back.png) Injecting `Gopkg.lock` into the solver is a necessity. If we want the solver to preserve previously-selected versions by default, then the solver has to learn about the existing `Gopkg.lock` from somewhere. Otherwise, it wouldn't know what to preserve! -As such, the lock is another one of the properties encoded onto the [previously-discussed](#functional-flow) `SolveParameters` struct. That, plus two other properties, are the salient ones for `-update`: +As such, the lock is another one of the properties encoded onto the [previously-discussed]() `SolveParameters` struct. That, plus two other properties, are the salient ones for `-update`: ```go type SolveParameters struct { @@ -173,7 +169,7 @@ And `v1.1.0` will be selected again, unless some other condition is presented th So, barring some other conflict, `v1.2.0` is selected, resulting in the desired update. -### `-update` and constraint types +#### `-update` and constraint types Continuing with our example, it's important to note that updates with `-update` are achieved incidentally - the solver never explicitly targets a newer version. It just skips adding a hint from the lock, then selects the first version in the queue that satisfies constraints. Consequently, `-update` is only effective with certain types of constraints. diff --git a/docs/failure-modes.md b/docs/failure-modes.md index ceeb6ba57c..7039f8cc8a 100644 --- a/docs/failure-modes.md +++ b/docs/failure-modes.md @@ -16,6 +16,8 @@ In general, these problems aren't things we can reasonably program around in dep ### Network failures +> **Remediation tl;dr:** most network issues are ephemeral, even if they may last for a few minutes, and can be addressed simply by re-running the same command. Always try this before attempting more invasive solutions. + dep talks to the network at several different points. These vary somewhat depending on source (VCS) type and local disk state, but this list of operations is generally instructive: * When dep cannot [statically deduce](deduction.md#static-deduction) the source root of an import path, it issues a `go-get` HTTP metadata request to a URL constructed from the import path. @@ -29,12 +31,11 @@ Network failures that you actually may observe are biased towards the earlier it #### Persistent network failures -Most of the time, errors from the above list will be ephemeral, and **simply re-running the command will result in success**. However, there are three well-defined cases where that's not true: +Although most network failures are ephemeral, there are three well-defined cases where that's not true. These are those cases, and their respective remediations: * **The network on which the source resides is permanently unreachable from the user's location:** in practice, this generally means one of two things: you've forgotten to log into your company VPN, or you're behind [the GFW](https://en.wikipedia.org/wiki/Great_Firewall). In the latter case, setting the *de facto* standard HTTP proxy environment variables that [`http.ProxyFromEnvironment()`](https://golang.org/pkg/net/http/#ProxyFromEnvironment) respects will cause dep's `go-get` HTTP metadata requests, as well as git, bzr, and hg subcommands, to utilize the proxy. - -* * Remediation is also exactly the same when the custom `go-get` HTTP metadata service for a source is similarly unreachable. The failure messages, however, will look like [deduction failures](#deduction-failures). + * Remediation is also exactly the same when the custom `go-get` HTTP metadata service for a source is similarly unreachable. The failure messages, however, will look like [deduction failures](#deduction-failures). * **The source has been permanently deleted or moved:** these are [left-pad](https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/) events, though note that [GitHub automatically redirects traffic after renames](https://help.github.com/articles/renaming-a-repository/), mitigating the rename problem. But, if an upstream source is removed, dep will be unable to proceed until a new upstream source is established for the import path. To that end: @@ -50,6 +51,8 @@ The exact error text will vary depending on which of the operations is running, #### Hangs +> **Remediation tl;dr:** hangs are almost always network congestion, or sheer amount of network data to fetch. Wait, or cancel and try again with `-v` to try to get more context. + Almost any case where a dep command, run with `-v`, hangs for more than ten minutes will ultimately be a bug. However, the most common explanation for an apparent dep hangs is actually normal behavior: because dep's operation requires that it keep its own copies of upstream sources hidden away in the [local cache](glossary.md#local-cache), the first run of dep against a project, especially large projects, can take a long time while it populates the cache. The only known case where dep may hang indefinitely is if one of the underlying VCS binaries it calls is prompting for some kind of input. Typically this means credentials (though not always - make sure to accept remote hosts' SSH keys into your known hosts!), and dep's normal assumption is that necessary credentials have been provided via environmental mechanisms - [configuration files or daemons](FAQ.md#how-do-i-get-dep-to-authenticate-to-a-git-repo), SSH agents, etc. This assumption is necessary for dep's concurrent network activity to work. If your use case absolutely cannot support the use of any such environmental caching mechanism, [please weigh in on this issue](). @@ -58,6 +61,8 @@ Unfortunately, until dep [improves the observability of its ongoing I/O operatio ### Bad local cache state +> **Remediation tl;dr:** Remove the local cache dir: `rm -rf $GOPATH/pkg/dep/sources`. + It is possible for parts of the [local cache](glossary.md#local-cache) maintained by dep to get into a bad state. This primarily happens when dep processes are forcibly terminated (e.g. Ctrl-C). This can, for example, terminate a `git` command partway through, leaving bad state on disk. By dep's definition, a [dirty git working copy]() is bad state. The error messages arising from bad local cache state often do not include full paths, so it may not be immediately obvious that problems are originating in the local cache. If full paths aren't included, then the best hint tends to be that the errors look like local VCS errors, but they're not on files from your own project. @@ -72,7 +77,7 @@ There are no known cases where, in the course of normal operations, dep can irre ### `vendor` write errors -dep may encounter errors while attempting to write out the `vendor` directory itself (any such errors will result in a full rollback; causing no changes to be made to disk). To help pinpoint where the problem may be, know that this is the flow for populating `vendor`: +Dep may encounter errors while attempting to write out the `vendor` directory itself (any such errors will result in a full rollback; causing no changes to be made to disk). To help pinpoint where the problem may be, know that this is the flow for populating `vendor`: 1. Allocate a new temporary directory within the system temporary directory. 2. Rename the existing `vendor` directory to `vendor.orig`. Do this within the current project's root directory if possible; if not, rename and move it to the tempdir. @@ -117,6 +122,8 @@ In all of these cases, your last recourse will be to add a [`source`](Gopkg.toml #### Undeducible paths +> **Remediation tl;dr:** You made a typo; fix it. If not, you may need a `source`, but be sparing with those. + The most likely cause of deduction failure is minor user error. Specifically, the user is the _current_ user (you), and the error is there is a mistyped import path somewhere in the current (your) project. The problem may be in your `Gopkg.toml`, or one of your imports, but the error message should point you directly at the problem, and the solution is usually obvious - e.g., "gihtub". Validation of the inputs from the current project are made fast and up front in dep, so these errors will tend to present themselves immediately. Between this fast validation, and the fact that projects are typically uncompilable, or at least not `go get`-able, with these kinds of errors, they tend to be caught early. This is why truly undeducible paths pop up primarily as temporary accidents while hacking on your own projects - you have to fix them to move on. @@ -142,6 +149,8 @@ If you think you've encountered a defunct metadata service, try probing the doma #### Static rule changes +> **Remediation tl;dr:** make sure you have the latest released version of dep. + Static rule changes are very unlikely to be the cause of your deduction failures. It is plausible that dep will add new static deduction rules in the future. And it is possible that, if you have an older version of dep, and you collaborate with or pull in code from someone using a newer version of dep, then their code may take advantage of new import path patterns that your dep doesn't know about yet. But very, very few static rules additions are likely to ever be made to dep over its lifetime - and getting access to them is just a question of updating once. @@ -168,13 +177,28 @@ init failed: unable to solve the dependency graph: Solving failure: No versions master: (another error) ``` -It means that the solver was unable to find a combination of versions for all dependencies that satisfy all the rules enforced by the solver. These rules are described in detail in the section on [solver invariants](the-solver.md#solving-invariants), but here's a summary: +_Note: all three of the other hard failure types can sometimes be reported as the errors for individual versions in a list like this. This primarily happens because dep is in need of a [thorough refactor of its error handling](https://github.com/golang/dep/issues/288)._ + +It means that the solver was unable to find a combination of versions for all dependencies that satisfy all the rules enforced by the solver. It is crucial to note that, just because dep provides a big list of reasons why each version failed _doesn't mean_ you have to address each one! That's just dep telling you why it ultimately couldn't use each of those versions in a solution. + +These rules, and specific remediations for failing to meet them, are described in detail in the section on [solver invariants](the-solver.md#solving-invariants). This section is about the steps to take when solving failures occur in general. But, to set context, here's a quick summary: * **`[[constraint]]` conflicts:** when projects in the dependency graph disagree on what [versions](gopkg.toml.md#version-rules) are acceptable for a project, or where to [source](gopkg.toml.md#source) it from. -* **Package validity failure:** when an imported package is quite obviously not capable of being built. + * Remediation will usually be either changing a `[[constraint]]` or adding an `[[override]]`, but genuine conflicts may require forking and hacking code. +* **Package validity failure:** when an imported package is quite obviously not capable of being built. + * There usually isn't much remediation here beyond "stop importing that," as it indicates something broken at a particular version. * **Import comment failure:** when the import path used to address a package differs from the [import comment](https://golang.org/cmd/go/#hdr-Import_path_checking) the package uses to specify how it should be imported. + * Remediation is to use the specified import path, instead of whichever one you used. * **Case-only import variation failure:** when two equal-except-for-case imports exist in the same build. + * Remediation is to pick one case variation to use throughout your project, then manually update all projects in your depgraph to use the new casing. +Let's break down the process of addressing a solving failure into a series of steps: +1. First, look through the failed versions list for a version of the dependency that works for you (or a failure that seems fixable), then try to work that one out. Often enough, you'll see a single failure repeated across the entire version list, which makes it pretty clear what problem you need to solve. +2. Take the remediation steps specific to that failure. +3. Re-run the same command you ran that produced the failure. There are three possible outcomes: + 1. Success! + 2. Your fix was ineffective - the same failure re-occurs. Either re-examine your fix (step 2), or look for a new failure to fix (step 1). + 3. Your fix was effective, but some new failure arose. Return to step 1 with the new failure list. diff --git a/docs/glossary.md b/docs/glossary.md index a8e5f72ad8..d9e4bbaf06 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -11,6 +11,7 @@ dep uses some specialized terminology. Learn about it here! * [Current Project](#current-project) * [Deduction](#deduction) * [Direct Dependency](#direct-dependency) +* [External Import](#external-import) * [GPS](#gps) * [Local cache](#local-cache) * [Lock](#lock) @@ -57,6 +58,10 @@ Deduction is the process of determining the subset of an import path that corres A project's direct dependencies are those that it imports from one or more of its packages, or includes in its [`required`](Gopkg.toml.md#required) list in `Gopkg.toml`. If each letter in `A -> B -> C -> D` represents a project, then only `B` is `A`'s direct dependency. +## External Import + +An `import` statement that points to a package in a project other than the one in which it originates. For example, an `import` in package `github.com/foo/bar` will be considered an external import if it points to anything _other_ than stdlib or `github.com/foo/bar/*`. + ## GPS Stands for "Go packaging solver", it is [a subtree of library-style packages within dep](https://godoc.org/github.com/golang/dep/gps), and is the engine around which dep is built. Most commonly referred to as "gps." @@ -118,9 +123,9 @@ The portion of an import path that corresponds to the network location of a sour ## Sync -dep's interaction model is based around the idea of maintaining a well-defined relationship between your project's import statements and `Gopkg.toml`, and your project's `Gopkg.lock` - keeping them "in sync". When the `Gopkg.lock` has more or fewer entries than are necessary, or entries that are incompatible with constraint rules established in `Gopkg.toml`, your project is "out of sync". +Dep's interaction model is based around the idea of maintaining a well-defined relationship between your project's import statements and `Gopkg.toml`, and your project's `Gopkg.lock` - keeping them "in sync". When the `Gopkg.lock` has more or fewer entries than are necessary, or entries that are incompatible with constraint rules established in `Gopkg.toml`, your project is "out of sync". -When your project is out of sync, running `dep ensure` will always either fail with an informative message, or bring your project back in sync. +This concept is explored in detail on [the ensure mechanics reference page](ensure-mechanics.md#staying-in-sync). ## Transitive Dependency diff --git a/docs/introduction.md b/docs/introduction.md index 6e2fb8e4a7..83df675ad5 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -4,6 +4,11 @@ title: Getting Started --- -Welcome! This is documentation for dep, the "official experiment" dependency management tool for the Go language. dep is a tool intended primarily for use by developers, to support the work of actually writing and shipping code. It is not intended for end users who are installing Go software - that's what go get does. +Welcome! This is documentation for dep, the "official experiment" dependency management tool for the Go language. Dep is a tool intended primarily for use by developers, to support the work of actually writing and shipping code. It is not intended for end users who are installing Go software - that's what `go get` does. -This documentation contains both guides and reference material. The guides are practical explanations of how to actually do things with dep, whereas the reference material provides deeper dives on specific topics. Of particular note is the [glossary](#glossary.md) - if you're unfamiliar with terminology used in this documentation, make sure to check there! +This site has both guides and reference documents. The guides are practical explanations of how to actually do things with dep, whereas the reference material provides deeper dives on specific topics. Of particular note is the [glossary](#glossary.md) - if you're unfamiliar with terminology used in this documentation, make sure to check there! + + + +* link to guides +* installing dep \ No newline at end of file diff --git a/docs/migrating.md b/docs/migrating.md index cf196a32ae..bc6443170b 100644 --- a/docs/migrating.md +++ b/docs/migrating.md @@ -9,13 +9,13 @@ $ cd $GOPATH/src/path/to/project/root $ dep init ``` -For many projects, this will just work. `dep init` will make educated guesses about what versions to use for your dependencies, generate sane `Gopkg.toml`, `Gopkg.lock`, and `vendor/`, and if your tests pass and builds work, then you're probably all done. +For many projects, this will just work. `dep init` will make educated guesses about what versions to use for your dependencies, generate sane `Gopkg.toml`, `Gopkg.lock`, and `vendor/`, and if your tests pass and builds work, then you're probably done. (Congratulations! You should check out [Daily Dep](daily-dep.md) next.) The migration process is still difficult for some projects. If you're trying dep for the first time, this can be particularly frustrating, as you're trying to simultaneously learn how to use dep, and how your project *should* be managed in dep. The good news is, `dep init` is usually the big difficulty hump; once you're over it, things get much easier. The goal of this guide is to provide enough information for you to reason about what's happening during `dep init`, so that you can at least understand what class of problems you're encountering, and what steps you might take to address them. To that end, we'll start with an overview of what `dep init` is doing. -One advisory up front, though: the first run of `dep init` can take quite a long time, as dep is creating fresh clones of all your dependencies into a special location, `$GOPATH/pkg/dep/sources/`. This is necessary for dep's normal operations, and is a one-time cost. +> Note: the first run of `dep init` can take quite a long time, as dep is creating fresh clones of all your dependencies into a special location, `$GOPATH/pkg/dep/sources/`. This is necessary for dep's normal operations, and is a one-time cost. ## `dep init` mechanics @@ -63,7 +63,7 @@ First and foremost, make sure that you're running `dep init` with the `-v` flag. While dep contributors have invested enormous effort into creating automated migration paths into dep, these paths will always best-effort and imprecise. It's simply not always possible to convert from other tools or GOPATH with full fidelity. dep is an opinionated tool, with a correspondingly opinionated model, and that model does sometimes fundamentally differ from that of other tools. Sometimes these model mismatches result in hard failures, sometimes soft, and sometimes there's no harm at all. -Because these are deep assumptions, their symptoms can be varied and surprising. Keeping these assumptions in mind could save you some hair-pulling later on. +Because these are deep assumptions, their symptoms can be varied and surprising. Keeping these assumptions in mind could save you some hair-pulling later on. - dep does not allow nested `vendor/` directories; it flattens all dependencies to the topmost `vendor/` directory, at the root of your project. This is foundational to dep's model, and cannot be disabled. - dep wholly controls `vendor`, and will blow away any manual changes or additions made to it that deviate from the version of an upstream source dep selects. @@ -75,43 +75,23 @@ A small number of projects that have reported being unable, thus far, to find a ### Hard failures -Hard failures come in four flavors: +Hard failures involve actual -* Deduction failures -* Local cache errors -* Network errors -* Solving failures +All of these failure modes are covered extensively in the reference on [failure modes](failure-modes.md) -Deduction failures occur when dep can't figure out what remote source corresponds to an import path it encounters in either your project, or one of your dependencies. As dep [follows the same rules for deduction as `go get`](), these types of errors generally only occur if a project contains dependencies that cannot be fetched by `go get`. This primarily occurs only when your project was relying on a capability provided by your previous tool to specify an alternate source location. This is [currently a problem in dep](https://github.com/golang/dep/issues/860), as the `source` field can only specify an alternate location for import paths that are already deducible. If this is a hard requirement for your case, then unfortunately, you will need to wait for this to be fixed before migrating - or, you need a server that properly responds to `go-get` HTTP metadata requests. +Because the solver, and all its possible failures, are the same for `dep init` as for `dep ensure`, there's a separate section for understanding and dealing with them: [dealing with solving failures](failure-modes.md#solving-failures). It can be trickier with `dep init`, however, as many remediations require tweaking `Gopkg.toml`. -Local cache errors occur when dep somehow mishandles the state of its local repository cache (`$GOPATH/pkg/dep/sources` by default). They generally manifest as VCS complaints about some files existing when they shouldn't, or vice-versa. These errors are quite rare now, as dep has implemented adaptive recovery logic that kicks in when it detects such problems. If an issue does occur, it's always safe to `rm -rf $GOPATH/pkg/dep/sources`; dep will automatically reconstruct it on the next run. (Also, please file an issue!) +Unfortunately, `dep init` does not write out a partial `Gopkg.toml` when it fails. This is a known, critical problem, and [we have an open issue (help wanted!)](https://github.com/golang/dep/issues/909). -Network problems are almost always transient, so re-executing the command will usually fix them. They may manifest as hangs, or as network i/o timeouts (though these usually indicate non-ephemeral firewall issues), or underlying VCS (usually git) errors. The ephemeral issues, especially hangs, occur a disproportionately high percentage of the time on `dep init`, as your local cache tends to be empty, which means dep has a lot of cloning to do. - -Other hard failures are most likely going to be actual solving failures. These occur when some invariant that dep enforces (e.g. version constraints are met, imported packages have Go code, [et al.]() SOLVER MODEL LINK) prevent dep from finding a solution - that is, a transitively complete dependency graph. The CLI output of solving failures looks something like this: - -``` -$ dep init -init failed: unable to solve the dependency graph: Solving failure: No versions of github.com/foo/bar met constraints: - v1.0.1: Could not introduce github.com/foo/bar@v1.13.1, as its subpackage github.com/foo/bar/foo is missing. (Package is required by (root).) - v1.0.0: Could not introduce github.com/foo/bar@v1.13.0, as... - v0.1.0: (another error) - master: (another error) -``` - -_Note: all three of the other hard failure types can sometimes be reported as the errors for individual versions in a list like this. This primarily happens because dep is in need of a [thorough refactor of its error handling](https://github.com/golang/dep/issues/288)._ - -Because the solver, and all its possible failures, are the same for `dep init` as for `dep ensure`, there's a separate section for understanding and dealing with them: [dealing with solving failures](). It can be trickier with `dep init`, however, as many remediations require tweaking `Gopkg.toml`. - -Unfortunately, `dep init` does not write out a partial `Gopkg.toml` when it fails ([though we are working on this](https://github.com/golang/dep/issues/909)), so if the particular errors you are encountering do entail such tweaks, you may need to skip `dep init` entirely, create an empty `Gopkg.toml`, and populate it with rules by hand; the [`Gopkg.toml` reference documentation](#gopkg.toml.md) explains the various properties. Before resorting to that, make sure you've run `dep init` with various combinations of the inferencing flags (`-skip-tools` and `-gopath`) to see if they can at least give you something to start from. +In the meantime, if the particular errors you are encountering do entail `Gopkg.toml` tweaks, you unfortunately may have to do without the automation of `dep init`: create an empty [`Gopkg.toml`](Gopkg.toml.md), and populate it with rules by hand. Before resorting to that, make sure you've run `dep init` with various combinations of the inferencing flags (`-skip-tools` and `-gopath`) to see if they can at least give you something to start from. ### Soft failures -Soft failures are usually obvious: `dep init` exits cleanly, but subsequent builds or tests fail. dep's soft failures are usually more drastically than subtly wrong - an explosion of type errors when you try to build, because a wildly incorrect version for some dependency got selected. +Soft failures are cases where `dep init` appears to exit cleanly, but a subsequent `go build` or `go test` fails. Dep's soft failures are usually more drastically than subtly wrong - e.g., an explosion of type errors when you try to build, because a wildly incorrect version for some dependency got selected. If you do encounter problems like this, `dep status` is your first diagnostic step; it will report what versions were selected for all your dependencies. It may be clear which dependencies are a problem simply from your building or testing error messages. If not, compare the `dep status` list against the versions recorded by your previous tool to find the differences. -Once you've identified the problematic dependenc(ies), the next step is exerting appropriate controls over them via `Gopkg.toml`. (Note - this advice is intentionally terse; look at [Zen of Constraints and Locks]() if you want a deeper understanding of how to optimally utilize dep's controls) +Once you've identified the problematic dependenc(ies), the next step is exerting appropriate controls over them via `Gopkg.toml`. (Note - this advice is intentionally terse; check out [Zen of Dep]() if you want a deeper understanding of how to optimally utilize dep's controls) For each of the following items, assume that you should run `dep ensure` after making the suggested change. If that fails, consult [dealing with solving failures](). diff --git a/docs/new-project.md b/docs/new-project.md index 6df0831a5b..ce4ef8e435 100644 --- a/docs/new-project.md +++ b/docs/new-project.md @@ -1,5 +1,5 @@ --- -title: Creating a new project +title: Creating a New Project --- First, we need a working Go workspace and GOPATH. If you're unfamiliar with Go workspaces and GOPATH, have a look at [the language documentation](https://golang.org/doc/code.html#Organization) and get your local workspace set up. (dep's model could eventually lead to being able to work without GOPATH, but we're not there yet.) @@ -8,8 +8,8 @@ Next, we need to pick a root directory for our project. This is primarily about 1. A project that is now or eventually may be shared with or imported by other projects/people. In this case, pick the import path corresponding to the VCS root of its intended network location, e.g., `$GOPATH/src/github.com/golang/dep`. 2. An entirely local project - one that you have no intention of pushing to a central server (like GitHub). In this case, any subdirectory beneath `$GOPATH/src` will do. -3. A project that needs to live within a large repository, such as a company monorepo. This is possible, but gets more complicated - see [dep in monorepos](). -4. Treat the entire GOPATH as a single project, where `$GOPATH/src` is the root. dep does not currently support this - it needs a non-empty import path to treat as the root of the import namespace. +3. A project that needs to live within a large repository, such as a company monorepo. This may be possible, but gets more complicated. (Unfortunately, no docs on this yet - coming soon!) +4. Treat the entire GOPATH as a single project, where `$GOPATH/src` is the root. dep [does not currently support this](https://github.com/golang/dep/issues/417) - it needs a non-empty import path to treat as the root of your project's import namespace. We'll assume the first case, as it's the most common. Create and move into the directory: @@ -26,7 +26,9 @@ $ ls Gopkg.toml Gopkg.lock vendor/ ``` -In a new project like this one, both files and the `vendor` directory will be effectively empty. +In a new project like this one, both files and the `vendor` directory will be effectively empty. + +This would also be a good time to set up a version control, such as [git](https://git-scm.com/). While dep in no way requires version control for your project, it can make inspecting the changes made by normal dep operations easier. Plus, it's basically best practice #1 of modern software development! At this point, we're initialized and ready to start writing code! You can open up a `.go` file in an editor and start hacking away. Or, if you already know some projects you'll need, you can pre-populate your `vendor` directory with them: @@ -34,6 +36,4 @@ At this point, we're initialized and ready to start writing code! You can open u $ dep ensure -add github.com/foo/bar github.com/baz/quux ``` -Note that, because of how `dep ensure -add` works, you'll have to list all dependencies you want to add in a single `dep ensure -add` execution, rather than running it repeatedly. But the `-add` approach is ultimately equivalent to just jumping directly into editing `.go` files, so this needn't be a huge problem. - -The [day-to-day dep]() guide is a good next stop now that your project is set up, and has more detail on these `-add` patterns. \ No newline at end of file +Great, your project's all set up! You're ready to move on to [Daily Dep](daily-dep.md). \ No newline at end of file diff --git a/docs/the-solver.md b/docs/the-solver.md index ff9748e354..43f173d3fb 100644 --- a/docs/the-solver.md +++ b/docs/the-solver.md @@ -4,7 +4,7 @@ title: The Solver At the heart of dep is a constraint solving engine - a [CDCL]()-style [SMT]() solver, tailored specifically to the domain of Go package management. It lives in the `github.com/golang/dep/gps` package, and is where the work of determining a valid, transitively complete dependency graph (aka, the contents of `Gopkg.lock`) is performed. -This page will eventually detail the solver's mechanics, but in the meantime, there are [docs for an older version of the solver]() that are still accurate enough to provide a rough picture of its behavior. +This page will eventually detail the solver's mechanics, but in the meantime, there are [docs for an older version of the solver](https://github.com/sdboyer/gps/wiki/gps-for-Contributors) that are still accurate enough to provide a rough picture of its behavior. ## Solving invariants @@ -19,7 +19,7 @@ The solver is an iterative algorithm, working its way project-by-project through ### `[[constraint]]` rules -As described in the `Gopkg.toml` docs, each [`[[constraint]]`](gopkg.toml.md#constraint) stanza is associated with a single project, and each stanza can contain both [a version rule](gopkg.toml.md#version-rules) and a [source rule](gopkg.toml.md#source). For any given project `P`, all dependers on `P` whose constraint rules are "activated" must express mutually compatible rules. That means: +As described in the `Gopkg.toml` docs, each [`[[constraint]]`](gopkg.toml.md#constraint) stanza is associated with a single project, and each stanza can contain both [a version rule](Gopkg.toml.md#version-rules) and a [source rule](Gopkg.toml.md#source). For any given project `P`, all dependers on `P` whose constraint rules are "activated" must express mutually compatible rules. That means: * For version rules, all activated constraints on `P` must [intersect](https://en.wikipedia.org/wiki/Intersection_(set_theory)), and and there must be at least one published version must exist in the intersecting space. Intersection varies depending on version rule type: * For `revision` and `branch`, it must be a string-literal match. @@ -69,7 +69,9 @@ will result in an error, as `C` imports a package that will necessarily result i Go 1.4 introduced [import comments](https://golang.org/cmd/go/#hdr-Import_path_checking), which allow a package to specify the import path that must be used when addressing it. For example, `import "github.com/golang/net/dict"` would point to a valid package, but because [it uses an import comment](https://github.com/golang/net/blob/42fe2e1c20de1054d3d30f82cc9fb5b41e2e3767/dict/dict.go#L7) to enforce that it must be imported as `golang.org/x/net/dict`, dep would reject any project attempting to import it directly through its github address. -**Remediation:** Because most projects are consistent about their import comment use over time, this issue typically only occurs when adding a new dependency or attempting to revive an older project. However, if you do encounter it, your only recourse is to change the code by fixing the offending import paths. If the offending import paths are in a dependency, not your/the current project, that may entail forking the dependency. +Because most projects are consistent about their import comment use over time, this issue typically only occurs when adding a new dependency or attempting to revive an older project. + +**Remediation:** change the code by fixing the offending import paths. If the offending import paths are not in the current project and you don't directly control the dependency, you'll have to fork and fix it yourself, then use `source` to point to your fork. ### Import path casing @@ -77,17 +79,4 @@ The standard Go toolchain compiler [does not](https://github.com/golang/go/issue The solver keeps track of the accepted case variant for each import path it's processed. Any subsequent projects it sees that introduces a case-only variation for a known import path will be rejected. -**Remediation:** - ---- - - - -The solving algorithm works by iteratively constructing a depgraph, working one project at a time. For each project, it walks through a [sorted queue]() of the project's published versions, searching for a version that satisfies all of the these invariants. The first version encountered that satisfies all invariants will be selected. - -At any given point in the algorithm, it is impossible to know _a priori_ if all the rules that will ultimately be discovered for a given project have been discovered. As such, it is possible that, if new dependencies are discovered on an already-selected - -whether all rules that will eventually be needed for a given project are known at the time that project is visited, it is possible that a project for which a version is selected may need to be revisited later, through a backtracking process. - -This process is complicated by the fact that, for any given project being evaluated by the algorithm, it cannot be known _a priori_ whether we know all of the invariants for a project. - +**Remediation:** Pick a casing variation (all lowercase is usually the right answer), and enforce it universally across the depgraph. As it has to be respected in all dependencies, as well, this may necessitate pull requests and possibly forking of dependencies, if you don't control them directly. \ No newline at end of file