<|start_filename|>action/docker.go<|end_filename|>
package action

import (
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/pkg/errors"
)

func (action Action) reifyArgs(stepInputs StepInputs) []string {
	// TODO: make this logic faster and more correct
	args := action.Runs.Args
	for i := 0; i < len(args); i++ {
		for k, v := range stepInputs {
			args[i] = strings.Replace(args[i], "${{ inputs."+k+" }}", v, -1)
		}
	}
	return args
}

func RunDockerAction(action Action, stepInputs StepInputs, dir string) error {
	if action.Runs.Image != "Dockerfile" {
		return errors.Errorf("Can't use non-Dockerfile image %s", action.Runs.Image)
	}
	imageArgs := action.reifyArgs(stepInputs)
	cmd := exec.Command("docker", "build", "-t", "lights-camera-action-image", dir)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	fmt.Println("Building Docker image to run Action...")
	if err := cmd.Run(); err != nil {
		return err
	}
	dockerArgs := append([]string{"run", "lights-camera-action-image"}, imageArgs...)
	cmd = exec.Command("docker", dockerArgs...)
	// TODO: capture "::set-output" et al. in stdout
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	fmt.Println("Running Docker image...")
	if err := cmd.Run(); err != nil {
		return err
	}
	return nil
}


<|start_filename|>action/action.go<|end_filename|>
package action

import (
	"crypto/rand"
	"fmt"
	"io/ioutil"
	"math/big"
	"os"
	"os/exec"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/pkg/errors"
)

type StepInputs map[string]string

type Param struct {
	Description string
	Required    bool
	Default     string
}

type Action struct {
	Name        string
	Description string
	Inputs      map[string]Param
	Outputs     map[string]Param
	Runs        struct {
		Using string
		Main  string
		Image string
		Args  []string
	}
}

func tempDir() string {
	i, err := rand.Int(rand.Reader, big.NewInt(100000000))
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("/tmp/action%d", i)
}

func cloneRepo(actionRef string, dir string) error {
	strs := strings.Split(actionRef, "@")
	if len(strs) != 2 {
		return fmt.Errorf("Expected org/repo@tag, got %s", actionRef)
	}
	repoName := strs[0]
	tag := strs[1]
	cmd := exec.Command("git", "clone", "https://github.com/"+repoName, dir, "--branch", tag, "--depth", "1")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	if err != nil {
		return errors.Wrap(err, "error cloning the action repo: ")
	}
	return nil
}

func parseAction(dir string) (action Action, err error) {
	in, err := os.Open(dir + "/action.yml")
	if err != nil {
		return
	}
	defer in.Close()
	bytes, err := ioutil.ReadAll(in)
	if err != nil {
		return
	}
	yaml.Unmarshal(bytes, &action)
	return
}

func Run(actionRef string, stepInputs StepInputs) error {
	fmt.Println("Running", actionRef, stepInputs)
	dir := tempDir()
	defer func() {
		fmt.Println("Cleaning up", dir)
		os.RemoveAll(dir)
	}()
	if err := cloneRepo(actionRef, dir); err != nil {
		return err
	}
	action, err := parseAction(dir)
	if err != nil {
		return err
	}
	// TODO: resolve required/optional parameters, add defaults for missing fields
	switch action.Runs.Using {
	case "docker":
		RunDockerAction(action, stepInputs, dir)
	case "node12":
		RunNodeAction(action, stepInputs, dir)
	}
	return nil
}


<|start_filename|>action/nodejs.go<|end_filename|>
package action

import (
	"fmt"
	"os"
	"os/exec"
	"strings"
)

func nodeEnvVars(stepInputs StepInputs) []string {
	vars := []string{}
	for k, v := range stepInputs {
		bind := "INPUT_" + strings.ToUpper(strings.Replace(k, " ", "_", -1)) + "=" + v
		vars = append(vars, bind)
	}
	return vars
}

func RunNodeAction(action Action, stepInputs StepInputs, dir string) error {
	cmd := exec.Command("node", action.Runs.Main)
	// TODO: capture "::set-output" et al. in stdout
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Env = append(os.Environ(), nodeEnvVars(stepInputs)...)
	fmt.Println("env:", cmd.Env)
	cmd.Dir = dir
	if err := cmd.Run(); err != nil {
		return err
	}
	return nil
}


<|start_filename|>action/action_test.go<|end_filename|>
package action

import "testing"

func TestDockerAction(t *testing.T) {
	err := Run("actions/hello-world-docker-action@master", map[string]string{
		"who-to-greet": "Mona the Octocat",
	})
	if err != nil {
		t.Error("Error running Dockerfile action:", err)
	}
}

func TestNodeAction(t *testing.T) {
	err := Run("actions/hello-world-javascript-action@master", map[string]string{
		"who-to-greet": "Mona the Octocat",
	})
	if err != nil {
		t.Error("Error running Node action:", err)
	}
}
