Reproducible Builds in Go
go-talks.appspot.com
go-talks.appspot.com
There was a video recorded at this meetup, but I'm not in control of that.
In the mean time, the getting started document [1] is the contents of the "demo time" slide.
1. https://github.com/constabulary/gb/blob/master/getting-start...
I'm completely with you. My own solution has similar ideals (per project versioned deps) though a different solution. https://github.com/joewalnes/go-getter/blob/master/README.md. Honestly I'm just glad others are finally talking about the elephant in the room.
> A project is the consumer of your own source code, and possibly dependencies that your code consumes; nothing consumes the code from a project.
This seems to imply that any code outside of a project (i.e. the code inside vendor/src) has no recourse for indicating the versions of their dependencies. This is nice in that it simplifies the problem, but to completely remove the ability for any and all libraries to indicate the versions of their dependencies seems unnecessarily restrictive. If I build a library for others to use, and I have a dependency, I want to be be able to lock to a specific version, or at least give my preference for a version.
Of course, this creates its own issues - what do you do when two libraries depend on two different versions of the same library? (Also known as the diamond dependency problem.) This is where the Go culture helps, where as long as you pick a later version, things are likely to work. But I'd rather have the tooling let me detect the two versions that the two libraries want, show that there is a mismatch, and give me the ability to override and pick one (probably the later one). Instead, the gb approach eliminates the ability for the libraries to even have the ability to indicate what version they would prefer, which makes it even more difficult to get a bunch of libraries that share dependencies to work correctly together.
godep (https://github.com/tools/godep) seems to have the best compromise: vendor dependencies without path rewriting (though with GOPATH rewriting), but also keep track of their versions in a Godep.json file. You can gracefully pick between conflicting versions upstream if need be.
IMO it is the best solution right now with just 1 issue, that it is not included with go. This is a pain with CI systems (e.g. Jenkins) where you have plugins to provide go itself, but have to figure out a way to get godep around to run your build. Right now I'm punting and just doing a go-get godep, then using it to build my project. I'm not happy with that though.
Suddenly you're able to have full control over a project, use private repos effortlessly (even with SSH protocol) and most importantly for me, not check in all the dependencies and their changes like with Godep, which just keeps up messing up the history although having nothing to do with the project.
Just Git submodules and you're done.
> Just Git submodules and you're done.
It would be a disaster if popular Go projects started using submodules, and consequently required users and contributors to play the submodule init/update/sync dance. Please, please -- prefer subtrees.People are smart, if they like the tool, they'll figure out how to integrate it into whatever DVCS they use.
The main issue with submodules (for me anyway) is that it seems to be all too easy to revert a change in a submodule if you aren't careful. It is not very people-friendly because you're just looking at hashes, and it isn't clear which one is newer and which is older.
I wonder if anyone in the git team has considered doing any updates on the porcelain for them.
edit: BTW, even though syncs may be separate (and let's be honest, they should be really infrequent). You can do the update/init thing in one command: git submodule update --init --recursive
I guess what you're saying is that submodules means that the diffs are better somehow, but I can't remember ever enjoying using submodules. This whole thing seems more like a git problem than anything else.
Is Go's definition of "backwards compatible" different from the usual one?
It's the go build tool that add semantics to the values of the import statement, not the language.
I guess theoretically it could be changed in such a way that both the older and newer syntax would be valid on newer compilers, but that adds additional complexity which is also an anti-Go idiom.
So sadly we're left with two alternatives: 1) code rewriting / generation, or 2) 3rd party compilers.
Based on what I'm reading, versioning is a serious pain point. Sure, you always have to make sure the payoff is worth the additional complexity (universally, not just in Go) but the author seems to categorically disqualify this option by incorrectly saying that it would break backwards compatibility.
It's not really same in my opinion. Slices already accepted multiple parameters - or even none. So adding another optional parameter doesn't affect the syntax. Where as import names are all stored in a single quoted string, so you'd have to rethink the entire structure of the import syntax (which leaves you with two import different methods of expressing imports - which is messy) or use some nasty kludge of including versioning within the import string.
What's more versioning is only half the story here. There's other issues with Go's imports that this author aims to resolve - such as the dependency on a static repo. Because import paths are based directly on the repo path (eg github.com) it makes it a pain to prototype code in a private repository before pushing changes public. And then there's issues with dependency on any specific resource (you can't use mirrored repos with go get), plus a whole slew of related issues regarding hard-coded repo paths.
So versioning is only half the story.
> Based on what I'm reading, versioning is a serious pain point. Sure, you always have to make sure the payoff is worth the additional complexity (universally, not just in Go) but the author seems to categorically disqualify this option by incorrectly saying that it would break backwards compatibility.
Well I've already argued that any clean solution would break backwards compatibility and any kludge would be messy and rather short sighted (plus potentially may not address the other issues with go get and import()).
So I think it's a little misjudged for you to say he's "incorrect" in his opinion in the way that you have.
import "github.com/pkg/term" "{hash,tag,version}"
And this (old) syntax: import "github.com/pkg/term"
Meaning the language would be backwards compatible. But you're saying that this wouldn't be backwards compatible - why ?Plus lumping everything within the second set of quotes means you don't have idiomatic styling nor a simple method for the gofmt to validate since you have a list handled as a string. So it's not really a clean fix in my opinion (which was one of the other points I raised).
Whatever we bake into the language will be set in stone for all of the foreseeable future - regardless of whether it's a good change or bad change. So I tend to think it makes more sense to use 3rd party tooling to address this problem for now, and then bring whatever method seems to work the best into the official language specifications for Go 2.0. Other people's opinions might differ, but that's why I personally prefer the approach this author it taking.
The new slicing syntax could have broken 3rd party pre-parsers or other tooling. Yet that change was considered backwards-compatible.
However the other key point is one I made earlier: slices already support a number of optional parameters so the language semantics didn't change even if the syntax of slices were expanded. adding 1 additional optional parameter to a property that already supports multiple optional parameters really isn't comparable to the import example which had a list of values represented in a new and completely non-idiomatic way (Ie initially without punctuation and then nested inside a string. Nothing else in Go follows that pattern, let alone the import strings already).
What we do is keep a copy of all packages we use and then assemble everything into a workspace as part of the automated build. Nothing is pulled from GitHub at build time. All those copies are versioned so you can have different projects use different versions. The dependencies are specified via some pre-existing build infrastructure but essentially there is one file in the project listing all the dependencies and where to find the packages.
Developers can still have their own personal workspace and build with different versions but the "real" build is always reproducible.
I think many people using Go or wanting to use Go in commercial/production environments are finding a little bit of impedance mismatch between the Go view of the world (the workspace and packages) and what their existing tooling does. It's not too terrible to deal with but it would be nice if we had a little more flexibility. go get could support some nicer/automated version of the above where you have the ability to download/mirror packages to a shared drive or to another source control system and pull from there to your workspace based on specific dependency versions. This is fairly minor friction though.
Out of scope: compiler doesn't produce byte for byte comparable binariesI probably shouldn't have mentioned it; if I hadn't, you wouldn't have thought about it, and then nobody would have had their hopes dashed.
I don't think that holds for mach-o or PE binaries, but I'm really speaking off the cuff here, as I said, others care about this and are working on it separately.
Honestly, the entire presentation was nearly lost on me by the weird comparison at the beginning. I recovered, but seriously, very strange comparison for anyone who understands what the GIL represents.
Edit: And really, was github the best target to go to for availability when it came to developer downtime on the edited XKCD? Github's downtime is pretty near 0. I think I remember roughly an hour of downtime in 2014 in the super early morning, and nothing before that until 2012 maybe.
The GIL was an implementation detail that was initially considered not a particularly big deal, but turned out to be a Really Big Deal later because no one thought about the problem from the beginning.
The lack of a package ecosystem for go and 'go get is good enough' is exactly the same; it was for quite some time simply considered to not really be a big problem. ...but it turns out, when you're doing complicated things, not having repeatable builds really is a Big Problem.
...but yes, they're in different leagues. Solving this one won't be anywhere near as troublesome.
There was something about 2 days of DDOS earlier this year, but again the specifics aren't important -- the important part is, if you are in charge of delivering a product written in Go, you're going to look like an ass if your build fails because some random part of the internet is having a bad day.
/.go
vendors # map simple import name to explicit (ugly) path
... # other stuff for additional e.g. generate features
Hierarchical: Think OO-ish shadowing of .go directives and metainfo.Is this clear?
not really
# ----------------------------------
# general layout of the land
# ----------------------------------
$GOPATH/src/.go
$GOPATH/src/.go/config
$GOPATH/src/.go/vendors # for all projects
# ----------------------------------
# $GOPATH/.go/vendors sketch
# ----------------------------------
lib/mq = github.com/lib/pq@master
mgo = labix.org/v2/mgo
... etc
# ----------------------------------
# specific project 1
# uses the global version of package labix.org/mgo
# uses a project specific package hotness
# ----------------------------------
$GOPATH/src/mygofoo
$GOPATH/src/mygofoo/foo.go
$GOPATH/src/mygofoo/.go
$GOPATH/src/mygofoo/.go/vendors
# ----------------------------------
# foo.go fragment
# ----------------------------------
package mygofoo
import (
"mgo" // version spec'd in global .go
"lib/pq" // version spec'd in project specific .go
"hotness" // version spec'd in project specific .go
...
)
...
# ----------------------------------
# $GOPATH/src/mygofoo/.go/vendors sketch
# ----------------------------------
lib/mq = github.com/lib/pq@experimental // lets pretend
hotness = bitbucket.org/wiz/hotness@master
... etc
# go tool
> cd $GOPATH/src/mygofoo
> go build -vendors
using:
mgo -> labix.org/v2/mgo
lib/pq -> github.com/lib/pq@experimental
hotness -> bitbucket.org/wiz/hotness@master
>
# go tool can obviously allow for command line mods of the .goIt's early for me to tell, but looks like gb is something like a mix between a gvm (virtual env) and godep (vendorizing).
I'm skeptical about things like "don't use the standard, use my thing", but I will try it anyway.
It's pretty much crazy not to, in my opinion. Stuff gets removed, and networks are unreliable, so locking/resolving dependencies to specific versions is insufficient.
edit; if you downvoted, maybe comment and explain why?
Thanks for your work.
If I publish my project on github and use gb with it, then people who want to contribute have to install gb as well.
Do you think that in the future there is a chance to do the building of a gb project without the gb itself?
* the major players all solve this specific problem outside the lang core
* Dave Cheney is so involved with the project, you could easily consider him part of the "go team." Both in contributions and community pull.
* This very article is not about a problem, it's about a solution. It's literally Dave Cheney saying: wow, look at this important problem, and look at all the reasons why it's important. Here is a solution and all its pros and cons. Regardless of how you feel, the timing of your comment simply could not have been further off.
* More generally: Go is one of the most batteries-included languages I know. From the stdlib to the tooling (which is still part of the official release), it's all in there. When did you last try to create platform and architecture independent binaries without any prior knowledge in, I don't know; C? Even Python?
I really want to like this comment, believe me I do. But it is so out of tune with the article, I can't.
This article is almost literally about the opposite of everything you just wrote.
Wait, is he not officially part of the go team?