• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hmerritt / reactenv / 21967501412

12 Feb 2026 10:45PM UTC coverage: 78.109% (+37.2%) from 40.958%
21967501412

Pull #6

github

hmerritt
test: improve integration tests for run
Pull Request #6: Major refactor

194 of 290 new or added lines in 3 files covered. (66.9%)

1 existing line in 1 file now uncovered.

446 of 571 relevant lines covered (78.11%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.08
/command/run.go
1
package command
2

3
import (
4
        "fmt"
5
        "os"
6
        "strings"
7

8
        "github.com/hmerritt/reactenv/reactenv"
9
        "github.com/hmerritt/reactenv/ui"
10
        "github.com/spf13/cobra"
11
)
12

13
type RunCommand struct {
14
        FileMatchPattern string
15
}
16

17
const defaultFileMatchPattern = `.*\.js$`
18

NEW
19
func (c *RunCommand) Synopsis() string {
×
NEW
20
        return "Inject environment variables into a bundled react app"
×
NEW
21
}
×
22

NEW
23
func (c *RunCommand) Help() string {
×
NEW
24
        jsInfo := Ui.Colorize(".js", Ui.InfoColor)
×
NEW
25
        helpText := fmt.Sprintf(`
×
NEW
26
Usage: reactenv run [options] PATH
×
NEW
27

×
NEW
28
Inject environment variables into a built react app
×
NEW
29

×
NEW
30
Usage:
×
NEW
31
  reactenv run PATH [flags]
×
NEW
32

×
NEW
33
Flags:
×
NEW
34
  -h, --help           help for run
×
NEW
35
      --match string   File match pattern (regex or glob) (default ".*\\.js$")
×
NEW
36

×
NEW
37
Examples:
×
NEW
38
  $ reactenv run --match "glob:**/*.mjs" ./dist
×
NEW
39
  $ reactenv run --match "regex:^assets/.*\\.js$" ./dist
×
NEW
40
  $ reactenv run ./dist
×
NEW
41

×
NEW
42
    dist/
×
NEW
43
    ├── login/
×
NEW
44
    │   ├── login.css
×
NEW
45
    │   └── login.lazy-b839zm%s
×
NEW
46
    ├── user/
×
NEW
47
    │   ├── user.css
×
NEW
48
    │   └── user.lazy-c7942lh%s <- Runs on all %s files in PATH (recursively)
×
NEW
49
    ├── index.html
×
NEW
50
    ├── index-csxw0qbp%s
×
NEW
51
    ├── robots.txt
×
NEW
52
    └── sitemap.xml
×
NEW
53
`, jsInfo, jsInfo, jsInfo, jsInfo)
×
NEW
54

×
NEW
55
        return strings.TrimSpace(helpText)
×
NEW
56
}
×
57

NEW
58
func NewCommandRun() *cobra.Command {
×
NEW
59
        run := &RunCommand{}
×
NEW
60

×
NEW
61
        cmd := &cobra.Command{
×
NEW
62
                Use:   "run PATH",
×
NEW
63
                Short: run.Synopsis(),
×
NEW
64
                Args:  cobra.ArbitraryArgs,
×
NEW
65
                Run: func(cmd *cobra.Command, args []string) {
×
NEW
66
                        run.Run(args)
×
NEW
67
                },
×
68
        }
69

NEW
70
        cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
×
NEW
71
                Ui.Output(run.Help())
×
NEW
72
        })
×
73

NEW
74
        cmd.Flags().StringVar(&run.FileMatchPattern, "match", defaultFileMatchPattern, "File match pattern (regex or glob)")
×
NEW
75

×
NEW
76
        return cmd
×
77
}
78

79
func (c *RunCommand) Run(args []string) int {
1✔
80
        duration := ui.InitDuration(Ui)
1✔
81

1✔
82
        if len(args) == 0 {
2✔
83
                Ui.Error("No asset PATH entered.")
1✔
84
                c.exitWithHelp()
1✔
85
        }
1✔
86

87
        pathToAssets := args[0]
1✔
88

1✔
89
        fileMatchPattern := c.FileMatchPattern
1✔
90

1✔
91
        if _, err := os.Stat(pathToAssets); os.IsNotExist(err) {
1✔
NEW
92
                Ui.Error(fmt.Sprintf("File PATH '%s' does not exist.", pathToAssets))
×
NEW
93
                c.exitWithHelp()
×
NEW
94
        }
×
95

96
        renv := reactenv.NewReactenv(Ui)
1✔
97

1✔
98
        err := renv.FindFiles(pathToAssets, fileMatchPattern)
1✔
99

1✔
100
        if err != nil {
2✔
101
                if matchErr, ok := err.(*reactenv.FileMatchError); ok {
2✔
102
                        Ui.Error(fmt.Sprintf("File match pattern '%s' is not valid.", matchErr.Pattern))
1✔
103
                        if matchErr.AutoRegexErr != nil {
2✔
104
                                Ui.Error(fmt.Sprintf("Regex error: %v", matchErr.AutoRegexErr))
1✔
105
                                Ui.Error(fmt.Sprintf("Glob error: %v", matchErr.Err))
1✔
106
                        } else {
1✔
NEW
107
                                Ui.Error(fmt.Sprintf("%v", matchErr.Err))
×
NEW
108
                        }
×
109
                        c.exitWithHelp()
1✔
110
                }
NEW
111
                Ui.Error(fmt.Sprintf("Error reading files in PATH '%s'.\n", pathToAssets))
×
NEW
112
                Ui.Error(fmt.Sprintf("%v", err))
×
NEW
113
                os.Exit(1)
×
114
        }
115

116
        if len(renv.Files) == 0 {
2✔
117
                Ui.Error(fmt.Sprintf("No files found in path '%s' using matcher '%s'", pathToAssets, fileMatchPattern))
1✔
118
                os.Exit(1)
1✔
119
        }
1✔
120

121
        err = renv.FindOccurrences()
1✔
122

1✔
123
        if err != nil {
1✔
NEW
124
                Ui.Error(fmt.Sprintf("There was an error while searching for __reactenv variables in the %d '%s' files within '%s', therefore nothing was injected.\n", renv.FilesMatchTotal, fileMatchPattern, pathToAssets))
×
NEW
125
                Ui.Error(fmt.Sprintf("%v", err))
×
NEW
126
                os.Exit(1)
×
NEW
127
        }
×
128

129
        if renv.OccurrencesTotal == 0 {
2✔
130
                Ui.Warn(ui.WrapAtLength(fmt.Sprintf("No reactenv environment variables were found in any of the %d '%s' files within '%s', therefore nothing was injected.\n", renv.FilesMatchTotal, fileMatchPattern, pathToAssets), 0))
1✔
131
                Ui.Warn(ui.WrapAtLength("Possible causes:", 4))
1✔
132
                Ui.Warn(ui.WrapAtLength("  - reactenv has already ran on these files", 4))
1✔
133
                Ui.Warn(ui.WrapAtLength("  - Environment variables were not replaced with `__reactenv.<name>` during build", 4))
1✔
134
                Ui.Warn("")
1✔
135
                duration.In(Ui.WarnColor, "")
1✔
136
                return 1
1✔
137
        }
1✔
138

139
        Ui.Output(
1✔
140
                fmt.Sprintf(
1✔
141
                        "Found %d reactenv environment %s in %d/%d matching files:",
1✔
142
                        renv.OccurrencesTotal,
1✔
143
                        ui.Pluralize("variable", renv.OccurrencesTotal),
1✔
144
                        len(renv.Files),
1✔
145
                        renv.FilesMatchTotal,
1✔
146
                ),
1✔
147
        )
1✔
148
        for fileIndex, fileOccurrencesTotal := range renv.OccurrencesByFile {
2✔
149
                Ui.Output(
1✔
150
                        fmt.Sprintf(
1✔
151
                                "  - %4dx in %s",
1✔
152
                                len(fileOccurrencesTotal.Occurrences),
1✔
153
                                renv.FileRelPaths[fileIndex],
1✔
154
                        ),
1✔
155
                )
1✔
156
        }
1✔
157
        Ui.Output("")
1✔
158

1✔
159
        Ui.Output(fmt.Sprintf("Environment %s checklist (ticked if value has been set):", ui.Pluralize("variable", renv.OccurrencesTotal)))
1✔
160
        envValuesMissing := 0
1✔
161
        for occurrenceKey := range renv.OccurrenceKeys {
2✔
162
                check := "✅"
1✔
163
                if _, ok := renv.OccurrenceKeysReplacement[occurrenceKey]; !ok {
2✔
164
                        check = "❌"
1✔
165
                        envValuesMissing++
1✔
166
                }
1✔
167
                Ui.Output(fmt.Sprintf("  - %4s %s", check, occurrenceKey))
1✔
168
        }
169
        Ui.Output("")
1✔
170

1✔
171
        if envValuesMissing > 0 {
2✔
172
                Ui.Error(fmt.Sprintf("Environment %s not set. See above checklist for missing values.", ui.Pluralize("variable", envValuesMissing)))
1✔
173
                os.Exit(1)
1✔
174
        }
1✔
175

176
        renv.ReplaceOccurrences()
1✔
177

1✔
178
        duration.In(Ui.SuccessColor, fmt.Sprintf("Injected all environment variables"))
1✔
179
        return 0
1✔
180
}
181

182
func (c *RunCommand) exitWithHelp() {
1✔
183
        Ui.Output("\nSee 'reactenv run --help'.")
1✔
184
        os.Exit(1)
1✔
185
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc