Hacker News new | past | comments | ask | show | jobs | submit login
Setting Go variables from the outside (cloudflare.com)
146 points by FiloSottile on July 1, 2015 | hide | past | favorite | 19 comments



The CloudFlare blog is awesome. This post was succinct, but super valuable to teams who need to manage versioning, etc. (really there are tons of use cases). I like how they admitted that they were doing things inefficiently and explained what they learned to make their dev lives easier. Thanks again, CloudFlare!


I just learned about this two days ago and used it for the first time to set the version in an CLI app.

In my main app I set var Version = "unreleased" and the build scripts sets the correct version.

https://github.com/groob/CommPhone/blob/master/release.sh#L1...


I used to use ldflags heavily for things like that, but nowa days i tend to avoid them - depending on the use case atleast.

Why? Well, many of my projects i don't want to distribute a binary for. I don't want to have to make a whole build process where i build multiple OSs, host it (github upload, etc), and update corresponding documents.

`npm install -g foo` doesn't require that, `go install foo` doesn't require that, so why am i putting myself through a large headache over nothing?

So these days, if i'm writing a Go binary that i intend to mostly use myself, or have other Go developers use, i bake the version/etc information into the files via a build script. For those projects, the Go source is a 100% complete, valid binary. ldflags are still awesome for certain things, but it is really, really nice when your source code is a proper representation of the output result.

Anyway, not bashing ldflags, just talking about my experience :)


As a sysadmin who's embraced Go for a lot of my day to day work, it really bugs me when Go developers do not publish binary releases of their apps.

1) Go makes this easy. 2) One of the advantages of Go is that the program is compiled. Most of the people who want to use your app are not going to be other go developers. 3) Versioning your code is a good practice and if there's a hosted binary release somewhere I can rely on it in my docker/config management process.


Maybe there is a market for "go get" in the cloud where you could paste a repository link and get back binaries for a selected distribution. It should be pretty easy to set up too.


I can see it now:

Hmmmmm, why are these downloading from bastion.nsa.gov?


> 1) Go makes this easy.

Correction, Go makes compiling and dependencies easy. It does not provide me with a platform for incremental distribution. If i could `go push v1.0 myapp` and you could download my 1.0 binary from Google? Well, i'd do that gladly.

And in general, we all spend as much time as we're willing to on free OSS. Usually it's for problems we solve, for ourselves. Unless you come to my repo and ask me for a binary, i have no information about anyone who is unable to build the binaries - or what their platform/arch might even be.

Do i build for everything, all the time, on the off chance that some random sysadmin might want to use `curl -O myrepo/bin` instead of `go get myrepo/bin`? I choose not to personally - but if the project gets popular, and there is a legitimate need for people to be able to download the bin, i'd gladly manage it. This is just not the default thing for me to do.

> Most of the people who want to use your app are not going to be other go developers.

In my example, you're wrong there. As i said, I'm choosing to do this because i'm targeting go developers. If i was writing a windows GUI app, of course i would release a binary.

> Versioning your code is a good practice and if there's a hosted binary release somewhere I can rely on it in my docker/config management process.

I'm not sure i follow here. I do version my code. Infact, it's even more versioned than if i used ldflags (imo) - because by injecting the version from Git at compile time, my binary has the version, but my code doesn't. You have to have my git repo, to know what version my code is at.

Moreover, with ldflags, you have to download my binary. You can't simply `go get foo/bar` to get the bin.

I understand your frustration, bins are nice - but strictly speaking from an OSS perspective, there is too much resistance in the process of releasing binaries (to my knowledge).

Perhaps there is a tool to automatically push a binary for a specific git tag to Github?


And I suppose you're going to expect the binaries to be packaged for every platform as well. Supporting Various Linux distros, Windows, and OS/X all with various combinations of target processors and word size is a pain that becomes a management nightmare which doesn't have to be endured with a dependable build system.


We've been doing this in our makefiles and exposing it to the user https://github.com/prometheus/prometheus/blob/0e383b0b9f4ff0...

Cloudflare have been putting out quite a few high-quality posts recently.


Nice and clean solution. I usually write the version string of a program with a git pre-commit hook.

Like this:

    HASH="$(git rev-parse --short HEAD)"
    sed 's/Version = .*/Version = "'"$HASH"'+"/' -i version.go
    git add version.go


This is one area that the Go ecosystem leaves me wanting a bit more. Idiomatic Go says that my package should be go-gettable, which AFAIK just uses `go install` command after fetching dependencies.

Is there an idiomatic way to include build options/tasks, while remaining go-gettable?


You can use ldflags with go get

go get -u -ldflags "-X main.Version foo" github.com/influxdb/telegraf/cmd/telegraf


This doesn't affect `go get`. You can use the same options when fetching the package, setting the variable or leaving it as the default.

     go get -ldflags="-X main.Who CloudFlare" import/path/package


I guess that's pretty reasonable. Especially coupled with sensible defaults for your variables like "unreleased" in the article. If you're building for a release you're probably a lot more up-to-speed on the needs for a release build anyway.


What's the advantage of doing that vs. just using a macro defined at compile-time?


In my build scripts, I like to have this added, so I can see what commit was used in building the binary:

    go build -ldflags "-X main.versionId `git rev-parse HEAD`"


    git describe --tags --always --dirty="-dev"
This will give you just the tag name if the current commit is tagged (useful for version tags), otherwise TAG-N-COMMIT, where N is the number of commits since TAG, and finally append -dev if the tree is dirty.


Almost what I've been doing but I didn't know about the "dirty" option so I had a script I wrote to check if there are uncommitted changes and append a suffix like you do. Now I can get rid of that. Thanks.


Thanks! Updating all my build scripts now.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: