"Go part" fails with go1.11 if go.mod exists in the top-level directory

We have been unable to build the Hugo snap ever since Hugo 0.48 which requires Go 1.11.

As can be seen in the build log snippet below, “Go toolchain2” fails to build when it apparently tries to pull in third-party Go libraries (used by Hugo) which Go compiler itself shouldn’t need:

'hugo' has prerequisites that need to be staged: go
Preparing to build go 
Building go 
Building Go cmd/dist using /usr/lib/go-1.6.
Building Go toolchain1 using /usr/lib/go-1.6.
Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.
Building Go toolchain2 using go_bootstrap and Go toolchain1.
go: golang.org/x/sync@v0.0.0-20180314180146-1d60e4601c6f: unrecognized import path "golang.org/x/sync" (https fetch: no http in bootstrap go command)
go: finding github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721
go: finding github.com/BurntSushi/toml v0.0.0-20170626110600-a368813c5e64
...
go: finding github.com/bep/debounce v1.1.0
go: error loading module requirements
go tool dist: FAILED: /build/hugo/parts/go/build/pkg/tool/linux_arm64/go_bootstrap install -gcflags=all= -ldflags=all= -i cmd/asm cmd/cgo cmd/compile cmd/link: exit status 1

See https://launchpadlibrarian.net/386640495/buildlog_snap_ubuntu_xenial_arm64_hugo-extended-dev_BUILDING.txt.gz for a full log.

It took me a while to figure out that Hugo’s Go 1.11 module definition file go.mod in the top-level directory is somehow interfering with the building of go in the parts/go/build directory. From https://github.com/golang/go/wiki/Modules#how-to-install-and-activate-module-support, Go 1.11’s module support can be activated in one of two ways:

  • Invoke the go command in a directory outside of the $GOPATH/src tree, with a valid go.mod file in the current directory or any parent of it and the environment variable GO111MODULE unset (or explicitly set to auto).
  • Invoke the go command with GO111MODULE=on environment variable set.

After much head-scratching, I finally came up with the following workaround:

  go:
    # Hugo's go.mod would interfere with the build of Go 1.11 in the
    # parts/go/build/ subdirectory.  Move go.mod out of the way
    # as a workaround.  (Note: 'override-build' does not work here,
    # hence the use of the deprecated 'prepare' keyword.)
    # See https://forum.snapcraft.io/t/go-plugin-go1-11-fails-if-go-mod-exists-in-the-top-level-directory/7546
    prepare: |
      set -x
      ( cd ../../.. && mv -v go.mod go.mod~ )
    source-tag: go1.11

And then go.mod~ is moved back to go.mod in the override-build step of building hugo… No, not a pretty workaround…

Would modifying the Go plugin to set the environment variable GO111MODULE=off when compiling Go help remedy the situation? Or would that cause other failures?

Thanks in advance for a better long-term solution to this issue.

Cheers,
Anthony

2 Likes

I am also affected by this issue (related to Go modules).

Your post summarizes thing well, but if the issue is with compiling the newer version of Go maybe this is the relevant repo which needs to be updated? https://github.com/elopio/go/blob/master/snapcraft.yaml

That is what is used when you use the go part. The Go snapcraft plugin could actually be ok.

1 Like

@eberkund, thank you so much for the information! I think you are right! It is the go part, not the go plugin, where something could be done to resolve the go1.11 build issue when a snap source contains a top-level go.mod file.

@elopio, could you please look into this when you have time? Many thanks!

Issue reported at https://github.com/elopio/go/issues/2

Also it might be worth creating a different Go part which doesn’t compile from source but uses the precompiled zip distributions. I guess the advantage of compiling is you can specify any Git tag/commit to use including unreleased versions but I think the majority of people are not using this and would appreciate the faster build times that would come with using the dump plugin to unpack the zip instead of compiling from source.

1 Like

I actually started work on doing something like this: https://github.com/anonymouse64/better-snap-go-part

The only thing I haven’t finished is adding all of the tags/versions for all the go release versions.

I also haven’t tested my part with go modules.

1 Like

Can you just use a variable like this?

https://dl.google.com/go/go$VERSION.src.tar.gz

Well you still want to be able to do something in snapcraft.yaml to specify the version of go to use with the source-tag or source-branch in the part definition, so I still need those tags/branches to exist in that repo.

@popey @Wimpress, can the snapcrafters please adopt the go part? I am testing it constantly with the IPFS snap, but I won’t have the time to try other features like this mod thing.

2 Likes

@ijohnson If I understand your comment and code correctly you want it to download the precompiled .zip and copy it unless the version or platform specified is not available in which case it will attempt to compile from source (like the current plugin does). That sounds like a good solution to me which will give the speed benefits without losing the ability to run any version.

Also if you decide to do this in your repo, you would need to create a new wiki entry to make it available, see: https://docs.snapcraft.io/build-snaps/remote-parts#update-parts-wiki

Another option would be to use the snapped Go binaries. It won’t give you a choice down to the minor release level, but you can pick one of the available tracks. For example, putting the following in your snapcraft.yaml:

build-snaps:
 - go/1.11/stable

… will ask Snapcraft to install the Go snap from the stable channel of the 1.11 track. This can either go at the top level, or in the scope of a part. If you don’t care about the Go version, you can use:

build-snaps:
 - go/latest/stable
2 Likes

@jamesh I like that suggestion, however it is not working for me. I get an error

requests.exceptions.ConnectionError: ('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))

with

parts:
  mypart:
    source: .
    plugin: go
    go-importpath: mypart
    build-snaps:
      - go/latest/stable

There is no such channel in the store for go called latest/stable. You either want go/1.11/stable or go/stable.

parts:
  mypart:
    source: .
    plugin: go
    go-importpath: mypart
    build-snaps:
      - go/1.11/stable

Same result

Sorry, Snapcraft ran into an error when trying to running through its
lifecycle that generated the following traceback:
Traceback (most recent call last):
  File "/snap/snapcraft/current/lib/python3.5/site-packages/requests/packages/urllib3/connectionpool.py", line 559, in urlopen
    body=body, headers=headers)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/requests/packages/urllib3/connectionpool.py", line 353, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/snap/snapcraft/current/usr/lib/python3.5/http/client.py", line 1106, in request
    self._send_request(method, url, body, headers)
  File "/snap/snapcraft/current/usr/lib/python3.5/http/client.py", line 1151, in _send_request
    self.endheaders(body)
  File "/snap/snapcraft/current/usr/lib/python3.5/http/client.py", line 1102, in endheaders
    self._send_output(message_body)
  File "/snap/snapcraft/current/usr/lib/python3.5/http/client.py", line 934, in _send_output
    self.send(msg)
  File "/snap/snapcraft/current/usr/lib/python3.5/http/client.py", line 877, in send
    self.connect()
  File "/snap/snapcraft/current/lib/python3.5/site-packages/requests_unixsocket/adapters.py", line 32, in connect
    sock.connect(socket_path)
FileNotFoundError: [Errno 2] No such file or directory

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/snap/snapcraft/current/lib/python3.5/site-packages/requests/adapters.py", line 376, in send
    timeout=timeout
  File "/snap/snapcraft/current/lib/python3.5/site-packages/requests/packages/urllib3/connectionpool.py", line 609, in urlopen
    _stacktrace=sys.exc_info()[2])
  File "/snap/snapcraft/current/lib/python3.5/site-packages/requests/packages/urllib3/util/retry.py", line 247, in increment
    raise six.reraise(type(error), error, _stacktrace)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/requests/packages/urllib3/packages/six.py", line 309, in reraise
    raise value.with_traceback(tb)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/requests/packages/urllib3/connectionpool.py", line 559, in urlopen
    body=body, headers=headers)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/requests/packages/urllib3/connectionpool.py", line 353, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/snap/snapcraft/current/usr/lib/python3.5/http/client.py", line 1106, in request
    self._send_request(method, url, body, headers)
  File "/snap/snapcraft/current/usr/lib/python3.5/http/client.py", line 1151, in _send_request
    self.endheaders(body)
  File "/snap/snapcraft/current/usr/lib/python3.5/http/client.py", line 1102, in endheaders
    self._send_output(message_body)
  File "/snap/snapcraft/current/usr/lib/python3.5/http/client.py", line 934, in _send_output
    self.send(msg)
  File "/snap/snapcraft/current/usr/lib/python3.5/http/client.py", line 877, in send
    self.connect()
  File "/snap/snapcraft/current/lib/python3.5/site-packages/requests_unixsocket/adapters.py", line 32, in connect
    sock.connect(socket_path)
requests.packages.urllib3.exceptions.ProtocolError: ('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/snap/snapcraft/current/bin/snapcraft", line 11, in <module>
    load_entry_point('snapcraft==2.42.1', 'console_scripts', 'snapcraft')()
  File "/snap/snapcraft/current/lib/python3.5/site-packages/click/core.py", line 722, in __call__
    return self.main(*args, **kwargs)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/click/core.py", line 1043, in invoke
    return Command.invoke(self, ctx)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/click/decorators.py", line 17, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/snapcraft/cli/_runner.py", line 79, in run
    ctx.forward(lifecyclecli.commands['snap'])
  File "/snap/snapcraft/current/lib/python3.5/site-packages/click/core.py", line 553, in forward
    return self.invoke(cmd, **kwargs)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/snapcraft/cli/lifecycle.py", line 135, in snap
    project_options, directory=directory, output=output)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/snapcraft/internal/lifecycle/_packer.py", line 46, in snap
    execute('prime', project_options)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/snapcraft/internal/lifecycle/_runner.py", line 67, in execute
    installed_snaps = repo.snaps.install_snaps(config.build_snaps)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/snapcraft/internal/repo/snaps.py", line 198, in install_snaps
    if not snap_pkg.is_valid():
  File "/snap/snapcraft/current/lib/python3.5/site-packages/snapcraft/internal/repo/snaps.py", line 151, in is_valid
    if not self.in_store:
  File "/snap/snapcraft/current/lib/python3.5/site-packages/snapcraft/internal/repo/snaps.py", line 80, in in_store
    self._is_in_store = self.get_store_snap_info() is not None
  File "/snap/snapcraft/current/lib/python3.5/site-packages/snapcraft/internal/repo/snaps.py", line 103, in get_store_snap_info
    self._store_snap_info = _get_store_snap_info(self.name)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/snapcraft/internal/repo/snaps.py", line 275, in _get_store_snap_info
    snap_info = session.get(url)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/requests/sessions.py", line 480, in get
    return self.request('GET', url, **kwargs)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/requests/sessions.py", line 468, in request
    resp = self.send(prep, **send_kwargs)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/requests/sessions.py", line 576, in send
    r = adapter.send(request, **kwargs)
  File "/snap/snapcraft/current/lib/python3.5/site-packages/requests/adapters.py", line 426, in send
    raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))
ERROR: Job failed: exit code 1

latest is the name of the default track: it isn’t a channel. If you see releases in the snap command that only mention a channel and not a track, then they come from the latest channel. So go/stable is equivalent to go/latest/stable.

@eberkund’s problems seem to be with snapcraft not being able to talk to snapd’s API socket at /run/snapd.socket, which is unrelated to tracks and channels.

1 Like

Thanks, actually it works locally with snapcraft cleanbuild it is just GitLab CI builds (which have been a constant pain with snapcraft) that throw that error but I guess that is another topic.

I think the build-snaps method isn’t ideal because it doesn’t work within docker environments which is common for CI systems.

oooooh, thanks for the pointer. I learned something new today :slight_smile: Sorry for the noise, then :man_facepalming:

Hello again, I actually ran into this issue with modules myself and was able to avoid it by modifying the go part to specify GO111MODULE=off when running ./make.bash (and then in the actual build step for your go code specify GO111MODULE=on to use modules when building).

@elopio if other snapcrafters are okay with this, I can maintain the go part for you and modify the wiki to point to my fork of your repo.

As noted by others, build-snaps doesn’t work when building inside docker containers and so isn’t a solution for us and I would like to upstream both this change to disable modules when building go itself as well as the PR I submitted to stop using the install and build stanzas as they are deprecated.

1 Like

Oh, yes, please! I don’t want to maintain this anymore. @popey or @Wimpress please ack.

1 Like