Skip to content

Commit

Permalink
Merge branch 'main' into adamb/fix-flaky-TestTerminalCommand_EnvPropa…
Browse files Browse the repository at this point in the history
…gation
  • Loading branch information
sourishkrout authored Jun 4, 2024
2 parents 2fd34aa + 46953cc commit 32eb3c7
Show file tree
Hide file tree
Showing 11 changed files with 162 additions and 68 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ jobs:
with:
fetch-depth: 0
fetch-tags: true
- name: Setup deno
uses: denoland/setup-deno@v1
with:
deno-version: v1.x
- name: Setup go
uses: actions/setup-go@v5
with:
Expand Down Expand Up @@ -125,6 +129,10 @@ jobs:

- name: SonarCloud Scan
uses: SonarSource/[email protected]
# Skip this test if the PR is created from a fork.
# If its created from a fork the PR won't be able to fetch the secrets so
# the step will fail.
if: github.event.pull_request.head.repo.full_name == github.repository
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
72 changes: 35 additions & 37 deletions internal/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/stateful/runme/v3/internal/project"
"github.com/stateful/runme/v3/internal/runner/client"
"github.com/stateful/runme/v3/internal/tui"
"github.com/stateful/runme/v3/pkg/document"
)

type CommandExportExtractMatch struct {
Expand All @@ -41,7 +42,7 @@ func runCmd() *cobra.Command {
parallel bool
replaceScripts []string
serverAddr string
categories []string
cmdCategories []string
getRunnerOpts func() ([]client.RunnerOption, error)
runIndex int
)
Expand All @@ -56,7 +57,7 @@ func runCmd() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
runWithIndex := fFileMode && runIndex >= 0

runMany := runAll || (len(categories) > 0 && len(args) == 0)
runMany := runAll || (len(cmdCategories) > 0 && len(args) == 0)
if !runMany && len(args) == 0 && !runWithIndex {
return errors.New("must provide at least one command to run")
}
Expand Down Expand Up @@ -86,50 +87,31 @@ func runCmd() *cobra.Command {
block := task.CodeBlock

// Check if to run all and if the block should be excluded
if runAll && len(categories) == 0 && block.ExcludeFromRunAll() {
if runAll && len(cmdCategories) == 0 && block.ExcludeFromRunAll() {
// Skip the task if it should be excluded from run all
continue
}

// Check if categories are specified and if the block should be excluded
if len(categories) > 0 && block.ExcludeFromRunAll() {
if len(cmdCategories) > 0 && block.ExcludeFromRunAll() {
// Skip the task if it should be excluded based on categories
continue
}

// Check if the block matches any of the specified categories
if len(categories) > 0 {
bcats := block.Categories()
if len(cmdCategories) > 0 {
blockCategories := block.Categories()
fm, _ := block.Document().Frontmatter()
fmCategories := resolveFrontmatterCategories(fm)
match := false

if fm != nil && fm.Category != "" {
// Check if the frontmatter category matches any block category
for _, bcat := range bcats {
if fm.Category == bcat {
match = true
break
}
}
if !match {
// Check if the frontmatter category matches any specified category
for _, cat := range categories {
if fm.Category == cat {
match = true
break
}
}
}
} else {
// Check if any block category matches any specified category
for _, bcat := range bcats {
for _, cat := range categories {
if bcat == cat {
match = true
break
}
}
if len(fmCategories) > 0 && containsCategories(fmCategories, cmdCategories) {
if len(blockCategories) == 0 {
match = true
} else {
match = containsCategories(fmCategories, blockCategories)
}
} else if containsCategories(blockCategories, cmdCategories) {
match = true
}

if !match {
Expand Down Expand Up @@ -239,7 +221,7 @@ func runCmd() *cobra.Command {
}

if runMany {
err := confirmExecution(cmd, len(runTasks), parallel, categories)
err := confirmExecution(cmd, len(runTasks), parallel, cmdCategories)
if err != nil {
return err
}
Expand Down Expand Up @@ -267,8 +249,8 @@ func runCmd() *cobra.Command {
if runMany && parallel {
scriptRunText = "Running"
blockNames = []string{blockColor.Sprint("all tasks")}
if len(categories) > 0 {
blockNames = []string{blockColor.Sprintf("tasks for categories %s", categories)}
if len(cmdCategories) > 0 {
blockNames = []string{blockColor.Sprintf("tasks for categories %s", cmdCategories)}
}
}

Expand Down Expand Up @@ -347,7 +329,7 @@ func runCmd() *cobra.Command {
cmd.Flags().BoolVarP(&parallel, "parallel", "p", false, "Run tasks in parallel.")
cmd.Flags().BoolVarP(&runAll, "all", "a", false, "Run all commands.")
cmd.Flags().BoolVar(&skipPrompts, "skip-prompts", false, "Skip prompting for variables.")
cmd.Flags().StringArrayVarP(&categories, "category", "c", nil, "Run from a specific category.")
cmd.Flags().StringArrayVarP(&cmdCategories, "category", "c", nil, "Run from a specific category.")
cmd.Flags().IntVarP(&runIndex, "index", "i", -1, "Index of command to run, 0-based. (Ignored in project mode)")
cmd.PreRun = func(cmd *cobra.Command, args []string) {
skipPromptsExplicitly = cmd.Flags().Changed("skip-prompts")
Expand Down Expand Up @@ -652,3 +634,19 @@ func confirmExecution(cmd *cobra.Command, numTasks int, parallel bool, categorie

return nil
}

func resolveFrontmatterCategories(fm *document.Frontmatter) []string {
if fm != nil && fm.Category != "" {
return []string{fm.Category}
}
return []string{}
}

func containsCategories(s1 []string, s2 []string) bool {
for _, element := range s2 {
if strings.Contains(strings.Join(s1, ","), element) {
return true
}
}
return false
}
24 changes: 10 additions & 14 deletions internal/command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,14 @@ func (c *base) lookPath(path string) (string, error) {
}

func (c *base) findDefaultProgram(name string, args []string) (string, []string, error) {
name, normArgs := normalizeProgramName(name)
if isShellLanguage(name) {
globalShell := shellFromShellPath(c.globalShellPath())
res, err := c.lookPath(globalShell)
if err != nil {
return "", nil, errors.Errorf("failed lookup default shell %s", globalShell)
}
return res, args, nil
return res, append(normArgs, args...), nil
}
// Default to "cat" for shebang++
res, err := c.lookPath("cat")
Expand All @@ -140,14 +141,12 @@ func (c *base) findDefaultProgram(name string, args []string) (string, []string,
}

func (c *base) findProgramInPath(name string, args []string) (string, []string, error) {
if name == "" {
return "", nil, errors.New("program name is empty")
}
name, normArgs := normalizeProgramName(name)
res, err := c.lookPath(name)
if err != nil {
return "", nil, errors.Errorf("failed program lookup %q", name)
}
return res, args, nil
return res, append(normArgs, args...), nil
}

func (c *base) findProgramInKnownInterpreters(programName string, args []string) (string, []string, error) {
Expand All @@ -157,7 +156,7 @@ func (c *base) findProgramInKnownInterpreters(programName string, args []string)
}

for _, interpreter := range interpreters {
interProgram, interArgs := parseInterpreter(interpreter)
interProgram, interArgs := normalizeProgramName(interpreter)
if path, err := c.lookPath(interProgram); err == nil {
return path, append(interArgs, args...), nil
}
Expand Down Expand Up @@ -211,20 +210,17 @@ func shellFromShellPath(programPath string) string {
return programFile[:len(programFile)-len(filepath.Ext(programFile))]
}

// parseInterpreter handles cases when the interpreter is, for instance, "deno run".
// normalizeProgramName handles cases when the program is, for instance, "deno run".
// Only the first word is a program name and the rest is arguments.
func parseInterpreter(interpreter string) (program string, args []string) {
parts := strings.SplitN(interpreter, " ", 2)

func normalizeProgramName(name string) (_ string, args []string) {
parts := strings.SplitN(name, " ", 2)
if len(parts) > 0 {
program = parts[0]
name = parts[0]
}

if len(parts) > 1 {
args = strings.Split(parts[1], " ")
}

return
return name, args
}

func inferInterpreterFromLanguage(langID string) []string {
Expand Down
45 changes: 42 additions & 3 deletions internal/command/command_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import (
type fileCommand struct {
internalCommand

logger *zap.Logger

scriptFile *os.File
tempDir string

logger *zap.Logger
}

func (c *fileCommand) Start(ctx context.Context) error {
Expand Down Expand Up @@ -71,11 +71,23 @@ func (c *fileCommand) createTempDir() (err error) {
}

func (c *fileCommand) createScriptFile() (err error) {
c.scriptFile, err = os.CreateTemp(c.tempDir, "runme-script-*")
pattern := "runme-script-*"
if ext := c.scriptFileExt(); ext != "" {
pattern += "." + ext
}
c.scriptFile, err = os.CreateTemp(c.tempDir, pattern)
err = errors.WithMessage(err, "failed to create a temporary file for script execution")
return
}

func (c *fileCommand) scriptFileExt() string {
cfg := c.ProgramConfig()
if ext := cfg.GetFileExtension(); ext != "" {
return ext
}
return inferFileExtension(cfg.GetLanguageId())
}

func (c *fileCommand) removeTempDir() error {
if c.tempDir == "" {
return nil
Expand All @@ -90,3 +102,30 @@ func (c *fileCommand) writeScript(script string) error {
}
return errors.WithMessage(c.scriptFile.Close(), "failed to close the temporary file")
}

var fileExtensionByLanguageID = map[string]string{
"js": "js",
"javascript": "js",
"jsx": "jsx",
"javascriptreact": "jsx",
"tsx": "tsx",
"typescriptreact": "tsx",
"typescript": "ts",
"ts": "ts",
"sh": "sh",
"bash": "sh",
"ksh": "sh",
"zsh": "sh",
"fish": "sh",
"powershell": "ps1",
"cmd": "bat",
"dos": "bat",
"py": "py",
"python": "py",
"ruby": "rb",
"rb": "rb",
}

func inferFileExtension(languageID string) string {
return fileExtensionByLanguageID[languageID]
}
19 changes: 19 additions & 0 deletions internal/command/command_file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,23 @@ func TestFileCommand(t *testing.T) {

testExecuteCommand(t, cfg, nil, "test\n", "")
})

// TypeScript runner requires the file extension to be .ts.
t.Run("TypeScript", func(t *testing.T) {
t.Parallel()

cfg := &ProgramConfig{
LanguageId: "ts",
Source: &runnerv2alpha1.ProgramConfig_Script{
Script: `function print(message: string): void {
console.log(message)
}
print("important message")
`,
},
Mode: runnerv2alpha1.CommandMode_COMMAND_MODE_FILE,
}

testExecuteCommand(t, cfg, nil, "important message\n", "")
})
}
12 changes: 12 additions & 0 deletions internal/command/command_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,18 @@ func TestCommand(t *testing.T) {
expectedStdout: "test\n",
expectedStderr: "test\n",
},
{
// This test demonstrates a behaviour for some languages
// like TypeScript running with "deno run". In the ideal world,
// the caller should handle that and pass "deno" as ProgramName and
// "run" in Arguments.
name: "MultiWordProgramName",
cfg: &runnerv2alpha1.ProgramConfig{
ProgramName: "echo -n test",
Mode: runnerv2alpha1.CommandMode_COMMAND_MODE_INLINE,
},
expectedStdout: "test",
},
}

for _, tc := range testCases {
Expand Down
4 changes: 4 additions & 0 deletions internal/command/command_virtual.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ func (c *virtualCommand) Pid() int {
return c.cmd.Process.Pid
}

func (c *virtualCommand) Stdin() io.Reader {
return c.stdin
}

func (c *virtualCommand) Start(ctx context.Context) (err error) {
c.pty, c.tty, err = pty.Open()
if err != nil {
Expand Down
7 changes: 2 additions & 5 deletions internal/command/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,9 @@ func (f *commandFactory) buildNative(base *base) internalCommand {

func (f *commandFactory) buildVirtual(base *base, opts CommandOptions) internalCommand {
var stdin io.ReadCloser

if !isNil(base.Stdin()) {
stdin = &readCloser{r: base.Stdin(), done: make(chan struct{})}
base.stdin = stdin
if in := base.Stdin(); !isNil(in) {
stdin = &readCloser{r: in, done: make(chan struct{})}
}

return &virtualCommand{
base: base,
isEchoEnabled: opts.EnableEcho,
Expand Down
2 changes: 1 addition & 1 deletion internal/runner/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,7 @@ func readLoop(
func runnerWinsizeToPty(winsize *runnerv1.Winsize) *pty.Winsize {
if winsize == nil {
// sane default
return &pty.Winsize{Cols: 80}
return &pty.Winsize{Rows: 24, Cols: 80}
}

return &pty.Winsize{
Expand Down
4 changes: 4 additions & 0 deletions internal/runner/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"syscall"
"testing"
Expand Down Expand Up @@ -347,6 +348,9 @@ func Test_runnerService(t *testing.T) {
})

t.Run("Input", func(t *testing.T) {
if skip, err := strconv.ParseBool(os.Getenv("SKIP_FLAKY")); err == nil && skip {
t.Skip("skipping flaky test")
}
t.Parallel()

stream, err := client.Execute(context.Background())
Expand Down
Loading

0 comments on commit 32eb3c7

Please sign in to comment.