I'm an engineer at Google. Previously I worked at HashiCorp, Chef Software, CustomInk, and some Pittsburgh-based startups.
- ๐ฌ Ask me about: Go, Ruby
- ๐ Pronouns: he/him
- ๐ Website
Go SDK for GitHub Actions - easily author GitHub Actions in Go
License: Apache License 2.0
I'm an engineer at Google. Previously I worked at HashiCorp, Chef Software, CustomInk, and some Pittsburgh-based startups.
Your article was a great resource for helping get started with GitHub actions using Go! Thanks!
At the moment this package mainly supports the features that @actions/core has.
I was wondering if you had plans on supporting the features that @actions/github provides? From what I read, the context information this package provides can mainly be found in the events.json file (The path of it you can get with in the environment variable GITHUB_EVENT_PATH
)
I would be happy to take a stab at it and make a pull request, but thought I'd ask first to see what your plans were. The way that GitHub has the toolkit repository structured is having multiple "packages" within one repo, so wasn't sure if that is what you were envisioning in copying as well. Also probably using another name then "context" to describe the events payload might be a good idea to prevent overloading the meaning of that word.
Thanks!
Firstly, thank you very much for starting this project. Your blog post inspired me to try and take the idea further:
https://blog.myitcv.io/2020/02/04/portable-ci-cd-with-pure-go-github-actions.html
I wonder, however, whether in these v0 days we should consider a different module path and package name:
github.com/sethvargo/go-githubactions
has the disadvantage that:
githubactions
, is rather longOptions we have:
github.com/sethvargo/actions
sethvargo.com/actions
Thoughts?
If we're writing actions in Go, it'll be great to get this into this library as well. APIs are not documented, though:
Hi,
I'm barely new to Github action and try to make your awesome project work. I tried to make it as simple as possible.
First, I have a repo with my Go code which contains a Go project, simple main.go (and go.mod / go.sum). Here is my main, pretty straightforward, similar as your project's README. It takes an input and set a time value as output.
package main
import (
"fmt"
"time"
"github.com/sethvargo/go-githubactions"
)
func main() {
user := githubactions.GetInput("who")
fmt.Printf("User : %s\n", user)
time := time.Now()
githubactions.SetOutput("time", time.String())
fmt.Printf("Time : %s", time)
}
In the same repo I have an action.yml file which describe how to run it :
name: "Say hello"
description: "Github Action to say hello"
inputs:
who:
description: 'User to say hello'
required: true
outputs:
time:
description: 'The time we said hello'
runs:
using: composite
steps:
- name: Build
working-directory: src/
run: go build -o ../bin/main
shell: bash
- name: Run
working-directory: bin/
run: ./main
shell: bash
In another repo I try to make it works with a workflow file like this :
name: Go
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Set up Go 1.19
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Checkout code action
uses: actions/checkout@v2
with:
repository: me/myrepo
- name: Go example
uses: me/myrepo@main
with:
who: 'John Doe'
All steps run fine, but "who" input can't be retrieved.
Here are the logs for Go example build step :
Run me/repo@main
Run go build -o ../bin/main
go: downloading github.com/sethvargo/go-githubactions v1.1.0
go: downloading github.com/sethvargo/go-envconfig v0.8.0
Run ./main
User :
Time : 2023-01-27 08:15:35.300536781 +0000 UTC m=+0.000578302
Output is nicely set but I can't figure out why user is empty.
My original motivation was to have a method like
action.GitHubContext().Labels()
and have this method check GITHUB_EVENT_NAME == "pull_request"
and then (if equal) would parse GITHUB_EVENT_PATH
and return the equivalent of ${{ github.event.pull_request.labels }}
.
In general for an action it may be nice to have access to things like GITHUB_REF
to provide programmatic defaults vs. hardcoded values in action.yml
.
I captured an example create
event (also a push
event) and we have environment variables:
GITHUB_ACTION=run1
GITHUB_ACTIONS=true # Not in `${{ github }}` context
GITHUB_ACTION_REF=
GITHUB_ACTION_REPOSITORY=
GITHUB_ACTOR=dhermes
GITHUB_API_URL=https://api.github.com
GITHUB_BASE_REF=
GITHUB_ENV=/home/runner/work/_temp/_runner_file_commands/set_env_735e47f8-a348-42e3-ba30-cafcd54f1c9b
GITHUB_EVENT_NAME=create
GITHUB_EVENT_PATH=/home/runner/work/_temp/_github_workflow/event.json
GITHUB_GRAPHQL_URL=https://api.github.com/graphql
GITHUB_HEAD_REF=
GITHUB_JOB=ci
GITHUB_PATH=/home/runner/work/_temp/_runner_file_commands/add_path_735e47f8-a348-42e3-ba30-cafcd54f1c9b
GITHUB_REF=refs/heads/trigger-many
GITHUB_REPOSITORY=dhermes/bezier
GITHUB_REPOSITORY_OWNER=dhermes
GITHUB_RETENTION_DAYS=90
GITHUB_RUN_ID=818468044
GITHUB_RUN_NUMBER=2
GITHUB_SERVER_URL=https://github.com
GITHUB_SHA=be377e49cf9c2bb751374cc6ee770fee98df3293
GITHUB_WORKFLOW=Trigger Many
GITHUB_WORKSPACE=/home/runner/work/bezier/bezier
where the equivalent ${{ github }}
context (keys re-ordered alphabetically) is
{
"action": "run2",
"action_ref": "",
"action_repository": "",
"actor": "dhermes",
"api_url": "https://api.github.com",
"base_ref": "",
"env": "/home/runner/work/_temp/_runner_file_commands/set_env_735e47f8-a348-42e3-ba30-cafcd54f1c9b",
"event": {...},
"event_name": "create",
"event_path": "/home/runner/work/_temp/_github_workflow/event.json",
"graphql_url": "https://api.github.com/graphql",
"head_ref": "",
"job": "ci",
"path": "/home/runner/work/_temp/_runner_file_commands/add_path_735e47f8-a348-42e3-ba30-cafcd54f1c9b",
"ref": "refs/heads/trigger-many",
"repository": "dhermes/bezier",
"repositoryUrl": "git://github.com/dhermes/bezier.git", // Not in GITHUB_* env vars
"repository_owner": "dhermes",
"retention_days": "90",
"run_id": "818468044",
"run_number": "2",
"server_url": "https://github.com",
"sha": "be377e49cf9c2bb751374cc6ee770fee98df3293",
"token": "***", // Not in GITHUB_* env vars
"workflow": "Trigger Many",
"workspace": "/home/runner/work/bezier/bezier"
}
We can try to leverage something like https://github.com/google/go-github to parse the contents of the file in GITHUB_EVENT_PATH
.
I'm not seeing an option for Noticef
in https://github.com/sethvargo/go-githubactions/blob/main/actions_root.go
I am using action.GetInput("foo") == ""
right now, but there may be a desired for something like this as well:
// LookupInput checks for an input by the given name.
func (c *Action) LookupInput(i string) (string, bool) {
e := strings.ReplaceAll(i, " ", "_")
e = strings.ToUpper(e)
e = "INPUT_" + e
value, exists := os.LookupEnv(e) // c.lookupEnv?
return strings.TrimSpace(value), exists
}
This library is really great, as I much prefer to have pure Go implementations! However, one step that I'm still using in JavaScript, and related to the githubactions api, is persisting workflow data using artifacts using the actions/upload-artifact@v2 action.
I found that the upload-artifact action source code uses the @actions/artifact library.
I was curious if this is something that you might consider adding to this library.
I'm looking for a way to get the commit message. Is this possible?
Currently I do the following, but it's verbose in yaml
- run: echo "COMMIT_MESSAGE=${{ github.event.workflow_run.head_commit.message }}" >> $GITHUB_ENV
shell: bash
if: github.event_name == 'workflow_run'
- run: echo "COMMIT_MESSAGE=${{ github.head_ref }}" >> $GITHUB_ENV
shell: bash
if: github.event_name == 'pull_request'
- run: echo "COMMIT_MESSAGE=${{ github.event.commits[0].message }}" >> $GITHUB_ENV
shell: bash
if: github.ref == 'refs/heads/main'
README says:
By default, GitHub Actions expects actions to be written in Node.js. For other languages like Go, you need to provide a Dockerfile
There is a third option now โ composite actions. Given that all GitHub runners have Go preinstalled, that provides an easier alternative to building and publishing Docker images or using nodejs shims.
What would be the correct Multistage Docker build for Go 1.16 with Go modules?
https://github.com/sethvargo/go-githubactions#publishing
Github Actions API have endpoints to list, get and delete artifacts -
https://docs.github.com/en/free-pro-team@latest/rest/reference/actions#get-an-artifact
Is it possible to implement them in Golang SDK for GH Actions?
I'm writing GitHub actions which make use of this library and want to include the SetOutput
calls within the tests, so that the key/value being outputted can be asserted on. Do you know if there's any libraries that can parse the GitHub environment file which include <<
? Or if a helper function can be added to the library to parse out a GitHub environment file?
To include #9
Currently, is not possible to access github.event
object using this library. I propose building a similar interface as the official actions toolkit.
action.Infof("foo %s", "bar")
prints foo %s [bar]
I'm guessing
Line 279 in ca29fef
Fprintf
instead of Fprintln
See https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/
Probably need to write to these new files: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#environment-files
Can probably port what was done in the @actions/core
package:
I am happy to do the work here, I just wanted sign off before sending a PR. I'm also happy to conform to your preferences, some possible design choices:
Action.GetEnv
an exported field OR make Action.getEnv
unexportedNew()
so that GetEnv
can be optionally set OR provide a NewWithGetEnv()
function similar to NewWithWriter()
OR provide a Action.WithGetEnv()
method for setting the fieldAction.GetEnv
is nil
before usage and fallback to os.Getenv
(would require a wrapper method) OR trust that Action.GetEnv: os.Getenv
will be the default set in New()
and leave it at thatMotivation: This makes it somewhat easier to write unit tests for GitHub actions.
Just now I am testing an "input parsing" function and ended up defining
type getInputer interface {
GetInput(string) string
}
func NewFromInputs(action getInputer) (*Config, error) {
// ...
}
so that in my tests I can use
type GetInputMap struct {
Map map[string]string
}
func (gim GetInputMap) GetInput(i string) string {
return gim.Map[i]
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.