Skip to content

Commit

Permalink
feat: support workspaces for go
Browse files Browse the repository at this point in the history
Signed-off-by: Sergio Schvezov <[email protected]>
  • Loading branch information
sergiusens committed Oct 3, 2024
1 parent 19ec551 commit 1996329
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 4 deletions.
16 changes: 15 additions & 1 deletion craft_parts/plugins/go_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class GoPluginProperties(PluginProperties, frozen=True):

go_buildtags: list[str] = []
go_generate: list[str] = []
go_workspace_use: list[str] = []

# part properties required by the plugin
source: str # pyright: ignore[reportGeneralTypeIssues]
Expand Down Expand Up @@ -93,6 +94,10 @@ class GoPlugin(Plugin):
(list of strings)
Parameters to pass to `go generate` before building. Each item on the list
will be a separate `go generate` call. Default is not to call `go generate`.
- ``go-workspace-use``
(list of strings)
Parameters for `go work use` statements to setup a workspace to consume
go modules from.
"""

properties_class = GoPluginProperties
Expand Down Expand Up @@ -120,12 +125,21 @@ def get_build_commands(self) -> list[str]:
"""Return a list of commands to run during the build step."""
options = cast(GoPluginProperties, self._options)

init_cmds: list[str] = []
if options.go_workspace_use:
# Only initialize if go.work is not there.
init_cmds.append("[ -f go.work ] || go work init")
init_cmds.append("go work use .")
init_cmds.extend([f"go work use {use}" for use in options.go_workspace_use])
else:
init_cmds.append("go mod download all")

tags = f"-tags={','.join(options.go_buildtags)}" if options.go_buildtags else ""

generate_cmds: list[str] = [f"go generate {cmd}" for cmd in options.go_generate]

return [
"go mod download all",
*init_cmds,
*generate_cmds,
f'go install -p "{self._part_info.parallel_build_count}" {tags} ./...',
]
24 changes: 21 additions & 3 deletions docs/common/craft-parts/reference/plugins/go_plugin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ Parameters to pass to `go generate`_ before building. Each item on the list
will be a separate ``go generate`` call. The default behavior is not to call
``go generate``.

go-workspace-use
~~~~~~~~~~~~~~~~
**Type**: list of strings
**Default** []

Parameters to setup a `go workspace`_ with modules to use for the build. The
current source is added automatically when any path is defined. Paths must be on
the local filesystem (e.g.; within ``$CRAFT/STAGE``).

The use of ``go.work`` omits the downloading of go modules.


Environment variables
---------------------

Expand All @@ -48,7 +60,7 @@ provision it by itself, to allow flexibility in the choice of compiler version.

Common means of providing ``go`` are:

* The ``golang`` Ubuntu package, declared as a ``build-package``.
***** The ``golang`` Ubuntu package, declared as a ``build-package``.
* The ``go`` snap, declared as a ``build-snap`` from the desired channel.

Another alternative is to define another part with the name ``go-deps``, and
Expand All @@ -65,11 +77,17 @@ How it works

During the build step the plugin performs the following actions:

* Call ``go mod download all`` to find and download all necessary modules;
* If ``go-workspace-use`` is defined:
* Call ``go work init`` if ``go.work`` is not found;
* Call ``go work use .`` to add the source for the part;
* Call ``go work use <item>`` for each item in ``go-workspace-use``;
* If ``go-workspace-use`` is not defined, call ``go mod download all`` to find
and download all necessary modules;
* Call ``go generate <item>`` for each item in ``go-generate``;
* Call ``go install ./...``, passing the items in ``go-buildtags`` through the
``--tags`` parameter.

If
Examples
--------

Expand All @@ -94,4 +112,4 @@ The following snippet declares a part using the ``go`` plugin. It uses the stabl
.. _Build tags: https://pkg.go.dev/cmd/go#hdr-Build_constraints
.. _Go: https://go.dev/
.. _go generate: https://go.dev/blog/generate

.. _go workspace: https://go.dev/blog/get-familiar-with-workspaces
50 changes: 50 additions & 0 deletions tests/integration/plugins/test_go.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,53 @@ def test_go_generate(new_dir, partitions):
output = subprocess.check_output([str(binary)], text=True)
# This is the expected output that "gen/generator.go" sets in "main.go"
assert output == "This is a generated line\n"


def test_go_workspace_use(new_dir, partitions):
"""Test code generation via "go generate" in parts using the go plugin
The go code in the "test_go" dir uses "gen/generator.go" to create, at build time,
the "main.go" file that produces the final binary.
"""
source_location = Path(__file__).parent / "test_go_workspace"

parts_yaml = textwrap.dedent(
f"""
parts:
go-flags:
source: https://github.com/jessevdk/go-flags.git
plugin: dump
organize:
"*": modules/go-flags/
prime:
- -modules
foo:
after:
- go-flags
plugin: go
source: {source_location}
go-workspace-use:
- $CRAFT_STAGE/modules/go-flags
build-environment:
- GO111MODULE: "on"
"""
)
parts = yaml.safe_load(parts_yaml)
lf = LifecycleManager(
parts,
application_name="test_go",
cache_dir=new_dir,
work_dir=new_dir,
partitions=partitions,
)
actions = lf.plan(Step.PRIME)

with lf.action_executor() as ctx:
ctx.execute(actions)

binary = Path(lf.project_info.prime_dir, "bin", "workspace")
assert binary.is_file()

output = subprocess.check_output([str(binary), "--say=hello"], text=True)
# This is the expected output that "gen/generator.go" sets in "main.go"
assert output == "hello\n"
8 changes: 8 additions & 0 deletions tests/integration/plugins/test_go_workspace/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module example/workspace

go 1.23.2

require (
github.com/jessevdk/go-flags v1.6.1 // indirect
golang.org/x/sys v0.25.0 // indirect
)
6 changes: 6 additions & 0 deletions tests/integration/plugins/test_go_workspace/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
30 changes: 30 additions & 0 deletions tests/integration/plugins/test_go_workspace/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package main

import (
"fmt"
"github.com/jessevdk/go-flags"
"os"
)

type Options struct {
Say string `long:"say" description:"What to say" optional:"yes" default:"Hello world"`
}

var options Options

var parser = flags.NewParser(&options, flags.Default)

func main() {
if _, err := parser.Parse(); err != nil {
switch flagsErr := err.(type) {
case flags.ErrorType:
if flagsErr == flags.ErrHelp {
os.Exit(0)
}
os.Exit(1)
default:
os.Exit(1)
}
}
fmt.Printf("%s\n", options.Say)
}
15 changes: 15 additions & 0 deletions tests/unit/plugins/test_go_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,18 @@ def test_get_build_commands_go_generate(part_info):
"go generate -x b",
'go install -p "1" ./...',
]


def test_get_build_commands_go_workspace_use(part_info):
properties = GoPlugin.properties_class.unmarshal(
{"source": ".", "go-workspace-use": ["go-flags", "go-cmp"]}
)
plugin = GoPlugin(properties=properties, part_info=part_info)

assert plugin.get_build_commands() == [
"[ -f go.work ] || go work init",
"go work use .",
"go work use go-flags",
"go work use go-cmp",
'go install -p "1" ./...',
]

0 comments on commit 1996329

Please sign in to comment.