actions / runner

The Runner for GitHub Actions :rocket:
https://github.com/features/actions
MIT License
4.93k stars 971 forks source link

Native support for pure Go actions #333

Open myitcv opened 4 years ago

myitcv commented 4 years ago

Following on from discussion in https://github.com/actions/runner/issues/243#issuecomment-585764955

Describe the enhancement

Native support for pure Go actions.

Go is a great choice for actions for a number of reasons, including:

In a recent blog post I experimented with pure Go GitHub actions using a thin NodeJS wrapper. That experiment worked well; GitHub Actions have generous concurrency limits, fast startup times, and solid cross-platform runners.

However:

This issue is therefore a request that GitHub Actions add native support for Go actions, thereby solving all of the above problems (and possibly others).

Code Snippet

At the bottom of the blog post I sketched out what v1 of a pure Go solution might look like, from the user's perspective:

# .github/workflows/test.yml
# ...
    - name: Display a greeting
      uses: github.com/myitcv/myfirstgoaction@v1.0.0
      with:
        name: Helena

In practice this would mean, from the runner's perspective:

A disadvantage of this bare-bones v1 is that the first use of every action in a workflow results in cache miss: module or build cache and build caches start from cold. But that could easily be fixed in v2 with a simple internal GitHub service that cached and served pre-built cross-platform binaries for $package@$version. That would obviate the download and build time, replacing it with a very fast CDN-speed binary download parameterised by GOOS and GOARCH.

Additional information

n/a

cc @bryanmacfarlane @mvdan @rogpeppe

bryanmacfarlane commented 4 years ago

One architectural note is that the runner has a goal of being self-contained and package the runtime it uses to run actions. It can update it itself and there's scenarios around running actions in containers while the runner is in the host etc. For example, in the action yaml, it says node12: scriptPath which makes it clear that node12 is the runtime and there's no compat surprises as things drift. So, the runner packages node (and actually a musl version of node for alpine) as script runtime for actions. We also build n times so not a problem.

I think if we did this, it would mean we package the go binary in the runner and the action would be an actions.yml and *.go files that are run via go run?

We would also need to create an actions/toolkit which is go centric. The toolkit is tiny helpers around the envvar conventions etc. so not a big deal to knock out. Note that right now, dependencies like that are self contained in the action (folks use ncc to webpack it into the action). We want to avoid reliability issues and security issues with deps drifting. So the action is self contained.

All of these are possible to solve. I'm just providing some background here and our scenarios and goals.

bryanmacfarlane commented 4 years ago

Also adding an argument to do this. if we go after broader platform support for the runner, then allowing go actions would expand the platforms of actions at the same time. Absent that, you end up with a degraded experience on the delta platforms (run: works and scripts work, don't use actions).

myitcv commented 4 years ago

@bryanmacfarlane

All of these are possible to solve. I'm just providing some background here and our scenarios and goals.

Thanks very much for providing the background and context, very useful.

One architectural note is that the runner has a goal of being self-contained and package the runtime it uses to run actions

With the Go compatibility promise, we know that we should1 always be able to compile an action using the latest stable Go version. The author can specify an expected language version in their go.mod, but this will always be less than or equal to the latest stable version at any given point in time, and in any case only affects language features available.

In my opinion the ultimate goal here is to have zero runtime requirements for pure Go actions; that is what I was hinting at with my v2 proposal. Each action can be cross-compiled ahead of time (and served by some caching service) into a binary for the runner's operating system and architecture (GOOS and GOARCH in Go terms). Zero runtime requirements for pure Go actions means a very lean runner.

That said, a first cut v1 of native Go action support could use go run as I mention above, adding the requirement that Go be available on the runner (a requirement that would disappear with v2).

Hence I don't think we need/want a Go action author to specify a Go version in the action.yml, because ultimately the concept of a runtime will/should disappear.

We want to avoid reliability issues and security issues with deps drifting

Totally agree. Using modules with proxy.golang.org and sum.golang.org we will achieve exactly that, modulo one proviso I cover below.

We would also need to create an actions/toolkit which is go centric.

There is already a first cut of such a package: github.com/sethvargo/go-githubactions. Clearly there is benefit in GitHub defining and owning such a package. There was also some discussion about package name and import paths in sethvargo/go-githubactions#2 FWIW.

Outstanding questions/details

1 modulo the very limited caveats in the linked doc

gliptak commented 4 years ago

https://github.com/actions/runner/issues/689

AlekSi commented 2 years ago

While not directly related to this issue, composite actions provide a better alternative for actions written in Go to building Docker images or using NodeJS wrapper.

sethvargo commented 2 years ago

... We would also need to create an actions/toolkit which is go centric.

@bryanmacfarlane I'd be happy to donate https://github.com/sethvargo/go-githubactions to GitHub if that ends up being a major blocker to implementation.