@@ -11,6 +11,7 @@ import (
11
11
"os"
12
12
"path"
13
13
"path/filepath"
14
+ "runtime/pprof"
14
15
"strings"
15
16
"time"
16
17
@@ -34,6 +35,7 @@ import (
34
35
"github.com/terramate-io/terramate/hcl/fmt"
35
36
"github.com/terramate-io/terramate/hcl/info"
36
37
"github.com/terramate-io/terramate/modvendor/download"
38
+ "github.com/terramate-io/terramate/runtime"
37
39
"github.com/terramate-io/terramate/versions"
38
40
39
41
"github.com/terramate-io/terramate/stack/trigger"
@@ -109,6 +111,7 @@ type cliSpec struct {
109
111
LogDestination string `optional:"true" default:"stderr" enum:"stderr,stdout" help:"Destination of log messages"`
110
112
Quiet bool `optional:"false" help:"Disable output"`
111
113
Verbose int `short:"v" optional:"true" default:"0" type:"counter" help:"Increase verboseness of output"`
114
+ CPUProfiling bool `optional:"true" default:"false" help:"Create a CPU profile file when running"`
112
115
113
116
DisableCheckGitUntracked bool `optional:"true" default:"false" help:"Disable git check for untracked files"`
114
117
DisableCheckGitUncommitted bool `optional:"true" default:"false" help:"Disable git check for uncommitted files"`
@@ -324,6 +327,19 @@ func newCLI(version string, args []string, stdin io.Reader, stdout, stderr io.Wr
324
327
fatal (err , "parsing cli args %v" , args )
325
328
}
326
329
330
+ if parsedArgs .CPUProfiling {
331
+ stdfmt .Println ("Creating CPU profile..." )
332
+ f , err := os .Create ("terramate.prof" )
333
+ if err != nil {
334
+ fatal (err , "can't create profile output file" )
335
+ }
336
+ err = pprof .StartCPUProfile (f )
337
+ if err != nil {
338
+ fatal (err , "error when starting CPU profiling" )
339
+ }
340
+
341
+ }
342
+
327
343
configureLogging (parsedArgs .LogLevel , parsedArgs .LogFmt ,
328
344
parsedArgs .LogDestination , stdout , stderr )
329
345
// If we don't re-create the logger after configuring we get some
@@ -474,6 +490,8 @@ func newCLI(version string, args []string, stdin io.Reader, stdout, stderr io.Wr
474
490
log .Fatal ().Msg ("flag --changed provided but no git repository found" )
475
491
}
476
492
493
+ globalsResolver := globals .NewResolver (& prj .root )
494
+ prj .globals = globalsResolver
477
495
uimode := HumanMode
478
496
if val := os .Getenv ("CI" ); envVarIsSet (val ) {
479
497
uimode = AutomationMode
@@ -516,6 +534,13 @@ func (c *cli) run() {
516
534
517
535
logger .Debug ().Msg ("Handle command." )
518
536
537
+ // We start the CPU Profiling during the flags parsing, but can't defer
538
+ // the stop there, as the CLI parsing returns far before the program is
539
+ // done running. Therefore we schedule it here.
540
+ if c .parsedArgs .CPUProfiling {
541
+ defer pprof .StopCPUProfile ()
542
+ }
543
+
519
544
switch c .ctx .Command () {
520
545
case "fmt" :
521
546
c .format ()
@@ -812,7 +837,7 @@ func (c *cli) gencodeWithVendor() (generate.Report, download.Report) {
812
837
813
838
log .Debug ().Msg ("generating code" )
814
839
815
- report := generate .Do (c .cfg (), c .vendorDir (), vendorRequestEvents )
840
+ report := generate .Do (c .cfg (), c .globals (), c . vendorDir (), vendorRequestEvents )
816
841
817
842
log .Debug ().Msg ("code generation finished, waiting for vendor requests to be handled" )
818
843
@@ -1496,7 +1521,7 @@ func (c *cli) generateDebug() {
1496
1521
selectedStacks [stack .Dir ()] = struct {}{}
1497
1522
}
1498
1523
1499
- results , err := generate .Load (c .cfg (), c .vendorDir ())
1524
+ results , err := generate .Load (c .cfg (), c .globals (), c . vendorDir ())
1500
1525
if err != nil {
1501
1526
fatal (err , "generate debug: loading generated code" )
1502
1527
}
@@ -1536,16 +1561,25 @@ func (c *cli) printStacksGlobals() {
1536
1561
1537
1562
for _ , stackEntry := range c .filterStacks (report .Stacks ) {
1538
1563
stack := stackEntry .Stack
1539
- report := globals .ForStack (c .cfg (), stack )
1540
- if err := report .AsError (); err != nil {
1564
+ tree := stackEntry .Stack .Tree ()
1565
+ evalctx := eval .New (
1566
+ stack .Dir ,
1567
+ runtime .NewResolver (c .cfg (), stack ),
1568
+ c .globals (),
1569
+ )
1570
+ evalctx .SetFunctions (stdlib .Functions (evalctx , tree .HostDir ()))
1571
+
1572
+ expr , _ := ast .ParseExpression (`global` , `<print-globals>` )
1573
+ globals , err := evalctx .Eval (expr )
1574
+ if err != nil {
1541
1575
logger := log .With ().
1542
1576
Stringer ("stack" , stack .Dir ).
1543
1577
Logger ()
1544
1578
1545
1579
errlog .Fatal (logger , err , "listing stacks globals: loading stack" )
1546
1580
}
1547
1581
1548
- globalsStrRepr := report . Globals . String ( )
1582
+ globalsStrRepr := fmt . FormatAttributes ( globals . AsValueMap () )
1549
1583
if globalsStrRepr == "" {
1550
1584
continue
1551
1585
}
@@ -1676,6 +1710,18 @@ func (c *cli) partialEval() {
1676
1710
}
1677
1711
}
1678
1712
1713
+ func (c * cli ) detectEvalContext (overrideGlobals map [string ]string ) * eval.Context {
1714
+ var st * config.Stack
1715
+ if config .IsStack (c .cfg (), c .wd ()) {
1716
+ var err error
1717
+ st , err = config .LoadStack (c .cfg (), prj .PrjAbsPath (c .rootdir (), c .wd ()))
1718
+ if err != nil {
1719
+ fatal (err , "setup eval context: loading stack config" )
1720
+ }
1721
+ }
1722
+ return c .setupEvalContext (st , overrideGlobals )
1723
+ }
1724
+
1679
1725
func (c * cli ) evalRunArgs (st * config.Stack , cmd []string ) []string {
1680
1726
ctx := c .setupEvalContext (st , map [string ]string {})
1681
1727
var newargs []string
@@ -1749,62 +1795,42 @@ func (c *cli) outputEvalResult(val cty.Value, asJSON bool) {
1749
1795
c .output .MsgStdOut (string (data ))
1750
1796
}
1751
1797
1752
- func (c * cli ) detectEvalContext (overrideGlobals map [string ]string ) * eval.Context {
1753
- var st * config.Stack
1754
- if config .IsStack (c .cfg (), c .wd ()) {
1755
- var err error
1756
- st , err = config .LoadStack (c .cfg (), prj .PrjAbsPath (c .rootdir (), c .wd ()))
1757
- if err != nil {
1758
- fatal (err , "setup eval context: loading stack config" )
1759
- }
1760
- }
1761
- return c .setupEvalContext (st , overrideGlobals )
1762
- }
1763
-
1764
1798
func (c * cli ) setupEvalContext (st * config.Stack , overrideGlobals map [string ]string ) * eval.Context {
1765
- runtime := c .cfg ().Runtime ()
1766
-
1767
- var tdir string
1799
+ var pdir prj.Path
1768
1800
if st != nil {
1769
- tdir = st .HostDir (c .cfg ())
1770
- runtime .Merge (st .RuntimeValues (c .cfg ()))
1801
+ pdir = st .Dir
1771
1802
} else {
1772
- tdir = c .wd ()
1773
- }
1774
-
1775
- ctx := eval .NewContext (stdlib .NoFS (tdir ))
1776
- ctx .SetNamespace ("terramate" , runtime )
1777
-
1778
- wdPath := prj .PrjAbsPath (c .rootdir (), tdir )
1779
- tree , ok := c .cfg ().Lookup (wdPath )
1780
- if ! ok {
1781
- fatal (errors .E ("configuration at %s not found" , wdPath ))
1782
- }
1783
- exprs , err := globals .LoadExprs (tree )
1784
- if err != nil {
1785
- fatal (err , "loading globals expressions" )
1803
+ pdir = prj .PrjAbsPath (c .rootdir (), c .wd ())
1786
1804
}
1787
1805
1806
+ var overrideStmts eval.Stmts
1788
1807
for name , exprStr := range overrideGlobals {
1789
1808
expr , err := ast .ParseExpression (exprStr , "<cmdline>" )
1790
1809
if err != nil {
1791
1810
fatal (errors .E (err , "--global %s=%s is an invalid expresssion" , name , exprStr ))
1792
1811
}
1793
1812
parts := strings .Split (name , "." )
1794
- length := len (parts )
1795
- globalPath := globals .NewGlobalAttrPath (parts [0 :length - 1 ], parts [length - 1 ])
1796
- exprs .SetOverride (
1797
- wdPath ,
1798
- globalPath ,
1799
- expr ,
1800
- info .NewRange (c .rootdir (), hhcl.Range {
1801
- Filename : "<eval argument>" ,
1813
+ ref := eval .NewRef ("global" , parts ... )
1814
+ overrideStmts = append (overrideStmts , eval.Stmt {
1815
+ Origin : ref ,
1816
+ LHS : ref ,
1817
+ Info : eval .NewInfo (pdir , info .NewRange (c .rootdir (), hhcl.Range {
1802
1818
Start : hhcl .InitialPos ,
1803
1819
End : hhcl .InitialPos ,
1804
- }),
1805
- )
1820
+ Filename : `<cmdline>` ,
1821
+ })),
1822
+ RHS : eval .NewExprRHS (expr ),
1823
+ })
1806
1824
}
1807
- _ = exprs .Eval (ctx )
1825
+
1826
+ tree , _ := c .cfg ().Lookup (pdir )
1827
+ ctx := eval .New (
1828
+ tree .Dir (),
1829
+ runtime .NewResolver (c .cfg (), st ),
1830
+ globals .NewResolver (c .cfg (), overrideStmts ... ),
1831
+ )
1832
+
1833
+ ctx .SetFunctions (stdlib .NoFS (ctx , c .wd ()))
1808
1834
return ctx
1809
1835
}
1810
1836
@@ -1821,7 +1847,7 @@ func (c *cli) checkOutdatedGeneratedCode() {
1821
1847
return
1822
1848
}
1823
1849
1824
- outdatedFiles , err := generate .DetectOutdated (c .cfg (), c .vendorDir ())
1850
+ outdatedFiles , err := generate .DetectOutdated (c .cfg (), c .globals (), c . vendorDir ())
1825
1851
if err != nil {
1826
1852
fatal (err , "failed to check outdated code on project" )
1827
1853
}
@@ -1860,11 +1886,12 @@ func (c *cli) gitSafeguardRemoteEnabled() bool {
1860
1886
return true
1861
1887
}
1862
1888
1863
- func (c * cli ) wd () string { return c .prj .wd }
1864
- func (c * cli ) rootdir () string { return c .prj .rootdir }
1865
- func (c * cli ) cfg () * config.Root { return & c .prj .root }
1866
- func (c * cli ) rootNode () hcl.Config { return c .prj .root .Tree ().Node }
1867
- func (c * cli ) cred () credential { return c .cloud .client .Credential .(credential ) }
1889
+ func (c * cli ) wd () string { return c .prj .wd }
1890
+ func (c * cli ) rootdir () string { return c .prj .rootdir }
1891
+ func (c * cli ) cfg () * config.Root { return & c .prj .root }
1892
+ func (c * cli ) globals () * globals.Resolver { return c .prj .globals }
1893
+ func (c * cli ) rootNode () hcl.Config { return c .prj .root .Tree ().Node }
1894
+ func (c * cli ) cred () credential { return c .cloud .client .Credential .(credential ) }
1868
1895
1869
1896
func (c * cli ) friendlyFmtDir (dir string ) (string , bool ) {
1870
1897
return prj .FriendlyFmtDir (c .rootdir (), c .wd (), dir )
0 commit comments