Skip to content

Commit

Permalink
Remove goaction.Getenv API
Browse files Browse the repository at this point in the history
  • Loading branch information
posener committed May 8, 2020
1 parent 4ef800a commit 4fc3c81
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 89 deletions.
9 changes: 2 additions & 7 deletions .github/tests/test-input/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"flag"
"os"

"github.com/posener/goaction"
"github.com/posener/goaction/log"
)

Expand All @@ -28,13 +27,9 @@ func main() {
errorf(`flag.Int("arg") = %d, want %d`, got, want)
}

if got, want := goaction.Getenv("env", "", ""), "env"; got != want {
errorf(`goaction.Getenv("env") = %s, want %s`, got, want)
}

// Test that the environment variable is not set with standard name:
if got, want := os.Getenv("os-env"), "os-env"; got != want {
errorf(`os.Getenv("os-env") = %s, want %s`, got, want)
if got, want := os.Getenv("env"), "env"; got != want {
errorf(`os.Getenv("env") = %s, want %s`, got, want)
}

if fail {
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/testworkflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,3 @@ jobs:
message: ${{ needs.prepare.outputs.out }}
arg: 42
env: env
os-env: os-env
7 changes: 5 additions & 2 deletions cmd/goaction/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ var (
icon = flag.String("icon", "", "Set branding icon. (See options at https://feathericons.com).")
color = flag.String("color", "", "Set branding color. (white, yellow, blue, green, orange, red, purple or gray-dark).")

email = goaction.Getenv("email", "posener@gmail.com", "Email for commit message.")
githubToken = goaction.Getenv("github-token", "", "Github token for PR comments. Optional.")
//goaction:description Email for commit message.
//goaction:default posener@gmail.com
email = os.Getenv("email")
//goaction:description Github token for PR comments. Optional.
githubToken = os.Getenv("github-token")
)

const (
Expand Down
23 changes: 8 additions & 15 deletions goaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ In order to convert the repository to a Github action, goaction command line sho
Goaction Github action keeps the Github action file updated according to the main Go file
automatically. When a PR is made, goaction will post a review explaining what changes to expect.
When a new commit is pushed, goreadme makes sure that the Github action files are updated if needed.
When a new commit is pushed, Goaction makes sure that the Github action files are updated if needed.
Add the following content to `.github/workflows/goaction.yml`
Expand All @@ -64,8 +64,8 @@ Add the following content to `.github/workflows/goaction.yml`
with:
# Optional: required only for commenting on PRs.
github-token: '${{ secrets.GITHUB_TOKEN }}'
# Optional: now that the script is a Github action, it is possible to run it in the
# workflow.
# Optional: now that the script is a Github action, it is possible to run it in the
# workflow.
- name: Example
uses: ./
Expand Down Expand Up @@ -93,6 +93,10 @@ after slashes). They can only be set on a `var` definition. The following annota
* `//goaction:skip` - skips an input out output definition.
* `//goaction:description <description>` - add description for `os.Getenv`.
* `//goaction:default <value>` - add default value for `os.Getenv`.
Using Goaction
A list of projects which are using Goaction (please send a PR if your project uses goaction and does
Expand Down Expand Up @@ -170,17 +174,6 @@ func init() {
}
}

// Getenv returns an environment variable for the requested name. On top of `os.Getenv` it enables
// defining a default `value` and description for the github action input section.
func Getenv(name string, value string, desc string) string {
v := os.Getenv(name)
// If empty, return default value.
if v == "" {
return value
}
return v
}

// Setenv sets an environment variable that will only be visible for all following Github actions in
// the current workflow, but not in the current action.
// See https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable.
Expand All @@ -194,7 +187,7 @@ func Setenv(name string, value string) {
}

// Export sets an environment variable that will also be visible for all following Github actions in
// the current worflow.
// the current workflow.
func Export(name string, value string) error {
err := os.Setenv(name, value)
if err != nil {
Expand Down
54 changes: 54 additions & 0 deletions internal/comments/comments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package comments

import (
"go/ast"
"go/token"
"regexp"
"strconv"
)

var (
docRequired = regexp.MustCompile("^//goaction:required$")
docSkip = regexp.MustCompile("^//goaction:skip$")
docDefault = regexp.MustCompile("^//goaction:default (.*)$")
docDesc = regexp.MustCompile("^//goaction:description (.*)$")
)

// Comments holds information from doc string.
type Comments struct {
Required Bool
Skip Bool
Default String
Desc String
}

type Bool struct {
token.Pos
Value bool
}

type String struct {
token.Pos
Value string
}

// parseComment searches for a special doc is a comment group.
func (d *Comments) Parse(doc *ast.CommentGroup) {
if doc == nil {
return
}
for _, comment := range doc.List {
txt := comment.Text
pos := comment.Slash
switch {
case docRequired.MatchString(txt):
d.Required = Bool{Value: true, Pos: pos}
case docSkip.MatchString(txt):
d.Skip = Bool{Value: true, Pos: pos}
case docDefault.MatchString(txt):
d.Default = String{Value: docDefault.FindStringSubmatch(txt)[1], Pos: pos}
case docDesc.MatchString(txt):
d.Desc = String{Value: strconv.Quote(docDesc.FindStringSubmatch(txt)[1]), Pos: pos}
}
}
}
91 changes: 42 additions & 49 deletions internal/metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ import (
"strconv"

"github.com/goccy/go-yaml"
"github.com/posener/goaction/internal/comments"
)

const (
inputFlag = "flag"
inputEnv = "env"

docRequired = "//goaction:required"
docSkip = "//goaction:skip"
)

type ErrParse struct {
Expand Down Expand Up @@ -89,7 +87,7 @@ func New(pkg *ast.Package) (Metadata, error) {
panic(e)
}
}()
return m.inspect(n, docStr{})
return m.inspect(n, comments.Comments{})
})
if err != nil {
return m, err
Expand All @@ -115,7 +113,7 @@ func (m *Metadata) AddOutput(name string, out Output) {
}

// Inspect might panic with `parseError` when parsing failed.
func (m *Metadata) inspect(n ast.Node, d docStr) bool {
func (m *Metadata) inspect(n ast.Node, d comments.Comments) bool {
switch v := n.(type) {
case *ast.File:
if v.Doc != nil {
Expand All @@ -137,23 +135,23 @@ func (m *Metadata) inspect(n ast.Node, d docStr) bool {
return true
}

func (m *Metadata) inspectDecl(decl *ast.GenDecl, d docStr) {
func (m *Metadata) inspectDecl(decl *ast.GenDecl, d comments.Comments) {
// Decleration can be IMPORT, CONST, TYPE, VAR. We are only interested in VAR.
if decl.Tok != token.VAR {
return
}
d.parse(decl.Doc)
if d.skip {
d.Parse(decl.Doc)
if d.Skip.Value {
return
}
for _, spec := range decl.Specs {
m.inspect(spec, d)
}
}

func (m *Metadata) inspectValue(value *ast.ValueSpec, d docStr) {
d.parse(value.Doc)
if d.skip {
func (m *Metadata) inspectValue(value *ast.ValueSpec, d comments.Comments) {
d.Parse(value.Doc)
if d.Skip.Value {
return
}
for _, v := range value.Values {
Expand All @@ -165,7 +163,7 @@ func (m *Metadata) inspectValue(value *ast.ValueSpec, d docStr) {
}
}

func (m *Metadata) inspectCall(call *ast.CallExpr, d docStr) {
func (m *Metadata) inspectCall(call *ast.CallExpr, d comments.Comments) {
selector, ok := call.Fun.(*ast.SelectorExpr)
if !ok {
return
Expand All @@ -178,76 +176,83 @@ func (m *Metadata) inspectCall(call *ast.CallExpr, d docStr) {
default:
return
case "flag.String":
checkNotSet(d.Default, "flag.String", "default")
checkNotSet(d.Desc, "flag.String", "description")
m.AddInput(
unqoute(stringValue(call.Args[0])),
Input{
Default: omitEmpty(unqoute(stringValue(call.Args[1]))),
Desc: stringValue(call.Args[2]),
Required: d.required,
Required: d.Required.Value,
tp: inputFlag,
})
case "flag.StringVar":
checkNotSet(d.Default, "flag.StringVar", "default")
checkNotSet(d.Desc, "flag.StringVar", "description")
m.AddInput(
unqoute(stringValue(call.Args[1])),
Input{
Default: omitEmpty(unqoute(stringValue(call.Args[2]))),
Desc: stringValue(call.Args[3]),
Required: d.required,
Required: d.Required.Value,
tp: inputFlag,
})
case "flag.Int":
checkNotSet(d.Default, "flag.Int", "default")
checkNotSet(d.Desc, "flag.Int", "description")
m.AddInput(
unqoute(stringValue(call.Args[0])),
Input{
Default: intValue(call.Args[1]),
Desc: stringValue(call.Args[2]),
Required: d.required,
Required: d.Required.Value,
tp: inputFlag,
})
case "flag.IntVar":
checkNotSet(d.Default, "flag.IntVar", "default")
checkNotSet(d.Desc, "flag.IntVar", "description")
m.AddInput(
unqoute(stringValue(call.Args[1])),
Input{
Default: intValue(call.Args[2]),
Desc: stringValue(call.Args[3]),
Required: d.required,
Required: d.Required.Value,
tp: inputFlag,
})
case "flag.Bool":
checkNotSet(d.Default, "flag.Bool", "default")
checkNotSet(d.Desc, "flag.Bool", "description")
m.AddInput(
unqoute(stringValue(call.Args[0])),
Input{
Default: boolValue(call.Args[1]),
Desc: stringValue(call.Args[2]),
Required: d.required,
Required: d.Required.Value,
tp: inputFlag,
})
case "flag.BoolVar":
checkNotSet(d.Default, "flag.BoolVar", "default")
checkNotSet(d.Desc, "flag.BoolVar", "description")
m.AddInput(
unqoute(stringValue(call.Args[1])),
Input{
Default: boolValue(call.Args[2]),
Desc: stringValue(call.Args[3]),
Required: d.required,
Required: d.Required.Value,
tp: inputFlag,
})
case "os.Getenv":
m.AddInput(
unqoute(stringValue(call.Args[0])),
Input{
Required: d.required,
tp: inputEnv,
})
case "goaction.Getenv":
m.AddInput(
unqoute(stringValue(call.Args[0])),
Input{
Default: omitEmpty(unqoute(stringValue(call.Args[1]))),
Desc: stringValue(call.Args[2]),
Required: d.required,
Default: omitEmpty(d.Default.Value),
Desc: d.Desc.Value,
Required: d.Required.Value,
tp: inputEnv,
})
case "goaction.Output":
checkNotSet(d.Default, "goaction.Output", "default")
checkNotSet(d.Desc, "goaction.Output", "description")
m.AddOutput(
unqoute(stringValue(call.Args[0])),
Output{
Expand Down Expand Up @@ -312,27 +317,6 @@ func boolValue(e ast.Expr) bool {
return v
}

// doc holds information from doc string.
type docStr struct {
required bool
skip bool
}

// parseComment searches for a special doc is a comment group.
func (d *docStr) parse(doc *ast.CommentGroup) {
if doc == nil {
return
}
for _, comment := range doc.List {
switch comment.Text {
case docRequired:
d.required = true
case docSkip:
d.skip = true
}
}
}

func name(e ast.Expr) string {
id, ok := e.(*ast.Ident)
if !ok {
Expand All @@ -355,3 +339,12 @@ func omitEmpty(s string) interface{} {
}
return s
}

func checkNotSet(s comments.String, fnName, commentName string) {
if s.Value != "" {
panic(ErrParse{
Pos: s.Pos,
error: fmt.Errorf("%s can't have %s annotation", fnName, commentName),
})
}
}
Loading

0 comments on commit 4fc3c81

Please sign in to comment.