Skip to content

Instantly share code, notes, and snippets.

@rubencaro
Last active April 28, 2024 21:52
Show Gist options
  • Star 59 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save rubencaro/5ce32fb30bbfa70e7db6be14cf42a35c to your computer and use it in GitHub Desktop.
Save rubencaro/5ce32fb30bbfa70e7db6be14cf42a35c to your computer and use it in GitHub Desktop.
Golang installation guide

Setting up a polite Golang project

By polite I mean not interfering in any other projects (even Golang ones), not polluting or forcing your global workspace (no matter how it is organized), and being reproducible on any other machine. For reasons out of the scope of this document, that's not an easy task to accomplish when working with Golang.

These are my notes, not a generic solution. They are not meant to work anywhere outside my machines.

Installing everything needed the first time

Install asdf and its golang plugin, then install golang

asdf lives in https://github.com/asdf-vm/asdf

Follow its installation instructions, which at the moment of writing were:

cd
git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.4.3

# For Ubuntu or other linux distros
echo '. $HOME/.asdf/asdf.sh' >> ~/.bashrc
echo '. $HOME/.asdf/completions/asdf.bash' >> ~/.bashrc

On a new terminal, install Golang plugin:

asdf plugin-add golang https://github.com/kennyp/asdf-golang

Then install Golang:

asdf install golang 1.10.1

Set some Golang verion as global. You will need it to setup your environment afterwards:

asdf global golang 1.10.1

Setup your .bashrc

These are a couple of functions that are handy to manage golang workspace. Add them to your .bashrc, then put the ingopath call in the prompt as well:

######## golang stuff

# This goes up from current folder looking for a folder
# that looks like a golang workspace root (has a 'src' subfolder, by now).
# Then echoes its path and returns 0.
# Returns -1 if does not find such a folder.
function detect_go_workspace_root {
  path="$PWD"
  while [[ $path != / ]];
  do
    src="$path/src"
    if [ -d "$src" ]; then
      echo "$path" && return 0
    fi
    path="$(realpath -s "$path"/..)"  # ignoring symlinks
  done
  return -1
}

# This will setup the Go workspace to the detected root path.
# It will complain if not detected.
# Then it will cd into folder pointed by $GOPATH/.letsgo_srcpath
function letsgo {
  root=$(detect_go_workspace_root)
  if [ $? -ne 0 ]; then
    echo "Could not find Go Workspace Root for $PWD" && return -1
  fi
  export GOPATH=$root
  export GOBIN=$root/bin
  export PATH=$GOBIN:$PATH
  export GOROOT=$(go env GOROOT)
  srcpath="$root/.letsgo_srcpath"
  [ -L "$srcpath" ] && cd "$(readlink $srcpath)"
}

# This will echo some funny symbol if we are inside current GOPATH.
function ingopath {
  [[ $PWD == "$(go env GOPATH)"* ]] && echo "(👀)"
}


## then in the prompt
export PS1='bla bla \[\033[00;36m\]$(ingopath)\[\033[00m\] bla bla $ '

Actually create the new project

Create the workspace folder

Create a folder for your workspace, then put a src folder inside it, and cd into it. Then run letsgo.

~ $ mkdir -p my_project/src
~ $ cd my_project/
~/my_project $ letsgo 
~/my_project(👀) $    # <------ see the eyes, we're inside the GOPATH

At this point you have a pristine golang workspace to follow mainstream conventions (See https://golang.org/doc/code.html).

The source code folder

  • Not existing repo

    If this is a new project you should create a folder for your source code, creating folders after your repo's URL path:

    mkdir -p src/github.com/rubencaro/my_project
    cd src/github.com/rubencaro/my_project

    That's the folder that gets into version control. You should setup git now. From inside that folder, something like:

    • git init
    • echo "# My Project" > README.md
    • git add .
    • git commit -m'First commit'
    • git remote add <blabblablab>
    • git push -u origin master
  • Already existing repo

    If your project already exists on some repo, you can do:

    go get -d github.com/rubencaro/my_project
    cd src/github.com/rubencaro/my_project

    That will create the folders for you.

Note on SSH access to github remote

If you happen to be using SSH to authenticate with github (I recommend that), then you may want to add this to your .gitconfig:

[url "git@github.com:"]
  insteadOf = https://github.com/

This will prevent go get from using https login when accessing github.

Fix golang version

Fix your golang version running asdf local golang 1.8.3 on your source code folder. That will create a .tool-versions file that should be included into version control. That file sets the golang (among others) version to be run when inside this folder.

Then you link that file to the root of your workspace, so everyone in the workspace uses the same golang version you configure on your repo:

ln -s $PWD/.tool-versions $GOPATH/

If the version you just fixed was different from the current one (see go version), then you should continue from a new terminal and run letsgo again to ensure your go version is the one you chose. You can run go env if you are in doubt of anything misconfigured.

Fix your src path (optional)

cd into your source code folder and run this:

ln -s "$PWD" "$GOPATH/.letsgo_srcpath"

This makes a link in your $GOPATH that will make letsgo get you into this folder after setting all Go environment. This is useful to avoid repeated cd src/github.com/blahblah/blahblah every time.

Install dep

From your source code folder run:

go get -u github.com/golang/dep/cmd/dep

Remember this installs dep only on this workspace. Not on global.

  • A new project, first dep run:

    • You should run dep init to create the Gopkg.toml and Gopkg.lock files. See https://github.com/golang/dep for further info.
    • Ignore the vendor folder from your version control echo "vendor" >> .gitignore. You only need to keep track of both the Gopkg.* files.
    • Now you can commit your changes to .gitignore, Gopkg.toml and Gopkg.lock files.
  • An existing project: Simply run dep ensure to install on the workspace everything specified in the Gopkg.lock file. There should be no change on your git status.

Work routine on an existing project

Always run letsgo first

The first thing to do is always to run letsgo from anywhere inside your workspace folder. That will point your GOPATH to the detected workspace root path, and add $GOPATH/bin to the PATH. Then you can open your favourite editor and compile, do your thing. Everything done from there will work with the project dependencies, golang version, etc. You can see the little eyes (or whatever funny symbol you return from the ingopath function) indicating you are somewhere inside your GOPATH.

If you fail to do that, just forgetting, or opening a new terminal and not running letsgo, then you will be working with the global GOPATH, meaning that you will Go to Hell (pun intended). You will mix up everything from your different golang projects. You may be mixing binaries compiled from different golang versions, or mixing different versions of the same libraries, and so on.

Remember to always run letsgo before start working on your project.

Try a new golang version

Any new version of golang may come out, you simply install it using asdf install golang <newversion>. Then to try it on your project, go to the source folder and run asdf local golang <newversion>. That will change your .tool-versions file, and thus change the golang version used everywhere inside your workspace. Remember to recompile everything using the new golang to see if it all checks out.

Install a new dependency

See https://github.com/golang/dep for details. Taken from there:

  1. import the package in your *.go source code file(s).
  2. Run the following command to update your Gopkg.lock and populate vendor/ with the new dependency.
dep ensure
@Ppang0405
Copy link

How can I upgrade golang version in asdf?

@rubencaro
Copy link
Author

rubencaro commented Feb 16, 2021

@Ppang0405 I don't know your exact use case, and you can do it in many ways.

One way would be just to change the version number by running asdf local golang <version>, then install it asdf install golang <version>. When yo do that then you are running the version you wanted and it's fixed by .tool-versions, so every other dev will use the same version.

You may need to reinstall deps (run setup.sh) since you will be using a different installation of Go.

Copy link

ghost commented Feb 17, 2021

Does anyone have a zsh sample ? Need to convert those functions to zsh.

Copy link

ghost commented Feb 17, 2021

Answering my own question. It seems this is enough for zshrc

function detect_go_workspace_root {
path="${PWD:A}"
while [[ $path != / ]];
do
src="$path/src"
if [ -d "$src" ]; then
echo "$path" && return 0
fi
path="${path:h}" # ignoring symlinks
done
return -1
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment