Golang compile environment variable into binary - go

If I compile this program
package main
import (
"fmt"
"os"
)
var version = os.Getenv("VERSION")
func main() {
fmt.Println(version)
}
It prints the env var when I run it
VERSION="0.123" ./example
> 0.123
Is there a way to compile the env var into the binary, for example:
VERSION="0.123" go build example.go
Then get the same output when I run
./example

Go 1.5 and above edit:
As of now, the syntax has changed.
On Unix use:
go build -ldflags "-X main.Version=$VERSION"
On Windows use:
go build -ldflags "-X main.Version=%VERSION%"
This is what -X linker flag is for. In your case, you would do
go build -ldflags "-X main.Version $VERSION"
Edit: on Windows this would be
go build -ldflags "-X main.Version %VERSION%"
More info in the linker docs.

Ainer-G's answer led me to the right place, but it wasn't a full code sample answering the question. A couple of changes were required:
Declare the version as var version string
Compile with -X main.version=$VERSION
Here's the full code:
package main
import (
"fmt"
)
var version string
func main() {
fmt.Println(version)
}
Now compile with
go build -ldflags "-X main.version=$VERSION"

There is an os.Setenv() function which you can use to set environment variables.
So you can start your application by setting the environment variable like this:
func init() {
os.Setenv("VERSION", "0.123")
}
If you don't want to do this by "hand", you could create a tool for this which would read the current value of the VERSION environment variable and generate a .go source file in your package containing exactly like the code above (except using the current value of the VERSION environment variable). You can run such code generation by issuing the go generate command.
Read the Generating code blog post for more details about go generate.

Related

golang reproducable binary - changing buildId

Currently trying to work out why building twice from same source creates different buildid and more specifically different actionID part.
I have vendored the dependencies with the following main.go
package main
import (
"context"
"fmt"
awsConfig "github.com/aws/aws-sdk-go-v2/config"
)
func main() {
awsCfg, err := awsConfig.LoadDefaultConfig(context.Background())
if err != nil {
fmt.Println(err)
}
_ = awsCfg
fmt.Println("hello world")
}
go build -o bootstrap -mod=vendor -trimpath -buildvcs=false cmd/main.go
go clean --cache
go build -o bootstrap2 -mod=vendor -trimpath -buildvcs=false cmd/main.go
will produce the following difference respectively:
4PSJ2COQu-WEdQUCy9GF/bEj2KquhCFU77YWE3s8m/M-Eq01RmuZNuSlF4NvKh/Sw9yCoyvM-OHIpXpdNG5
1B4wdixPXI9YqxSglbgJ/bEj2KquhCFU77YWE3s8m/M-Eq01RmuZNuSlF4NvKh/Sw9yCoyvM-OHIpXpdNG5
This is causing updates to a lambda function has the produced hash from aws-cdk differs between builds on circleci even if nothing within the actual function has changed. I understand we could pass ldflags=-buildid= as a build argument (??) but want to understand what could be causing this change in actionId
This change is not consistent and sometimes produces the same buildId
go version go1.19 darwin/amd64
edit:
The following issue 33772 states this was resolved when using -trimpath

Negative build tags not working as expected

I have 3 files. One main and two additional ones normal gokrazy which ~are~ should be alternatively be built depending on build constraint. Both update a global var from main during init(). normal is
// +build !gokrazy
package main
import "fmt"
func init() {
foo = "normal"
fmt.Println("init:", foo)
}
the other gokrazy is:
// +build gokrazy
package main
import "fmt"
func init() {
foo = "gokrazy"
fmt.Println("init:", foo)
}
go vet *.go is happy. When I run go run *.go I see that BOTH init functions are called:
init: gokrazy
init: normal
I expected only one to be called. go list confirms that the expected files are selected, however I still see both init functions. Golang Build Constraints Random explains that a forced compile might be necessary, however that does not change the result:
❯ go list -f '{{.GoFiles}}' -tags=gokrazy -a && go run -tags=gokrazy -a *.go
[gokrazy.go main.go]
init: gokrazy
init: normal
Why does the negative !gokrazy build tag not take effect?
Go does actually run all the files if you use go run *.go. Instead use:
go run .
For example if you have a test file (main_test.go) in the folder and use go run *.go, it will complain:
go run: cannot run *_test.go files (main_test.go)
Build tags modify what is built (as the name implies).
Build tags are basically ignored for go run which you should not use anyway (it is just too complicated to get it right except go run .)
Use go build.

Struct from the same parent folder as main is not visible

I have a small go demo project in Gogland with the structure:
awsomeProject
->src
->awsomeProject
->configuration.go
->main.go
Configuration file has a simple structure just for demo:
configuration.go:
package main
type Config struct {
Data int
}
Main file just uses the Config struct:
main.go
package main
import "fmt"
func main(){
var cfg Config
cfg.Data = 1
fmt.Println("lalala")
}
The error that I have is:
/usr/local/go/bin/go run /Users/lapetre/Work/awsomeProject/src/awsomeProject/main.go
command-line-arguments
src/awsomeProject/main.go:6: undefined: Config
Process finished with exit code 2
Any idea why the Config is not seen in main?
Thanks
When you build reusable pieces of code, you will develop a package as a shared library. But when you develop executable programs, you will use the package “main” for making the package as an executable program. The package “main” tells the Go compiler that the package should compile as an executable program instead of a shared library. The main function in the package “main” will be the entry point of our executable program.
That's why you should use the following structure:
awsomeProject
->src
->awsomeProject
->configuration.go
->main.go
with main.go
package main
import "fmt"
func main(){
var cfg awsomeProject.Config
cfg.Data = 1
fmt.Println("lalala")
}
and configuration.go
package awsomeProject
type Config struct {
Data int
}
For more details:
https://golang.org/doc/code.html
https://thenewstack.io/understanding-golang-packages
How are you calling go run? If you're calling it like
go run main.go
then that's the problem.
Go run only runs the file(s) you tell it to. So you need to tell it to also run configuration.go, or if you have several go files to run you can use
go run *.go
as eXMoor suggested.
There are some limits/drawbacks to "go run *.go" however, so the better alternative is to use go build instead of go run.
go build
Will compile everything. Then, to run the executable:
./awesomeProject
To combine all of this into one command that will compile and run whatever app you're working on, you can use:
go build && ./${PWD##*/}
I have it aliased to
gorunapp
just to make it easier.
This may not actually be the answer to the problem you're having, but hopefully you'll find it useful information regardless.
Try to run your application with command:
go run *.go

In a Golang application, how to embed a version in a other package than main?

Inspired by this SO question I wanted to use the same mechanism to embed a version number in my golang application. However I'm using the Cobra command line parser and want to have a version sub-command. This leads to the following directory and package structure:
.
|-- cmd
`-- version.go
|-- main.go
Up to now I have tried the following:
go run -ldflags "-X cmd/version.versionString=0.1.0" main.go version
-
go run -ldflags "-X version.versionString=0.1.0" main.go version
-
go run -ldflags "-X version.VersionString=0.1.0" main.go version
With version.go containing a variable declaration like:
var versionString string
and
var VersionString string
respectively.
I've also tried to put the variable declarations in main.go but then I'm not clear how to reference the variables in version.go for this option I tried:
import "github.com/basbossink/psiw"
....
fmt.Println(psiw.VersionString)
-
import "github.com/basbossink/psiw/main"
...
fmt.Println(main.VersionString)
In both cases the compiler complains that psiw and main respectively are unknown.
Note that using the VersionString in main gives the expected result.
I would prefer a solution where the link flags point to the variable in the version package since they don't require a back-pointer. But any suggestions are welcome.
Looking at the Hugo source I've discovered the answer to my question. The trick is to use:
go run -ldflags "-X github.com/basbossink/psiw/cmd.versionString=0.1.0" main.go version
So I made two mistakes:
I mixed up the notion of package and source file, the version.go file had package cmd as it's package statement.
The way to reference the variable is to use the fully qualified name.

getting make file variables in a go package?

I have a variable VERSION in a make file that sets the version for binary at compile time using -ldflags
VERSION = $(strip $(TIMESTAMP))
LDFLAGS = -ldflags "-X main.buildTime $(BUILD_TIME) -X main.buildNumber $(VERSION)"
Now I want to get the VERSION in a package which is not main and print it. I tried bunch of options, but not able to make it work.
My question is how can I get it in the package and then print it to client at run time, such as you are connected to app version 2.0..??
Directory structure:
- main.go
- test/
- test.go
test.go
package test
var Version = ""
main.go
package main
import (
"fmt"
"test"
)
func main() {
fmt.Println(test.Version)
}
Finally, run:
go run -ldflags="-X test.Version 2.0.0" main.go
Outputs:
> 2.0.0
Since we can specify import path, we can set the value of a string everywhere, not only in main.
From go 1.5 up, syntax is changed to importpath.name=string.

Resources