diff --git a/Dockerfile b/Dockerfile index 4d4dffc..3b82ea7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -77,6 +77,9 @@ RUN go install golang.org/x/tools/cmd/goimports@latest && \ go install golang.org/x/tools/gopls@latest # Clone from main, build&install gonb binary, and then install it as a kernel in Jupyter. +# - First introduce the cache-busting argument. This number can be bumped whenever we only want +# to rebuild the gonb part. +ARG CACHEBUST=2 WORKDIR ${HOME} RUN git clone 'https://github.com/janpfeifer/gonb.git' WORKDIR ${HOME}/gonb diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 196cb22..ccacdad 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,11 @@ # GoNB Changelog +## v0.10.11, 2025/02/02 + +* New --version and -V flags to print version; Improved `%version` output. (#158) +* Clean up, refactorings and fixed context leakage. +* Added `version.txt` with target version for build. + ## v0.10.10, 2025/01/28 * Reverted `replace` directive: contrary to what the AI suggested, it doesn't work when running from outside a cloned repository. diff --git a/docs/coverage.txt b/docs/coverage.txt index 420852f..9397b04 100644 --- a/docs/coverage.txt +++ b/docs/coverage.txt @@ -1,11 +1,15 @@ github.com/janpfeifer/gonb/main.go init 100.0% -github.com/janpfeifer/gonb/main.go main 60.0% -github.com/janpfeifer/gonb/main.go SetUpLogging 66.7% +github.com/janpfeifer/gonb/main.go main 63.2% +github.com/janpfeifer/gonb/main.go install 66.7% +github.com/janpfeifer/gonb/main.go printVersion 42.9% +github.com/janpfeifer/gonb/main.go setUpExtraLog 15.4% +github.com/janpfeifer/gonb/main.go setUpLogging 66.7% github.com/janpfeifer/gonb/main.go prepend 100.0% github.com/janpfeifer/gonb/main.go UniqueIDFilter.Filter 100.0% github.com/janpfeifer/gonb/main.go UniqueIDFilter.FilterF 100.0% github.com/janpfeifer/gonb/main.go UniqueIDFilter.FilterS 0.0% -github.com/janpfeifer/gonb/main.go SetUpKlog 66.7% +github.com/janpfeifer/gonb/main.go setUpKlog 83.3% +github.com/janpfeifer/gonb/main.go runKernel 78.3% github.com/janpfeifer/gonb/version.go must 50.0% github.com/janpfeifer/gonb/version.go init 100.0% github.com/janpfeifer/gonb/cache/cache.go New 75.0% @@ -289,8 +293,8 @@ github.com/janpfeifer/gonb/internal/goexec/wasm.go *State.ExportWasmConstants github.com/janpfeifer/gonb/internal/goexec/wasm.go *State.RemoveWasmConstants 0.0% github.com/janpfeifer/gonb/internal/goexec/goplsclient/conn.go *Client.ConnClose 0.0% github.com/janpfeifer/gonb/internal/goexec/goplsclient/conn.go *Client.connCloseLocked 100.0% -github.com/janpfeifer/gonb/internal/goexec/goplsclient/conn.go minTimeout 100.0% -github.com/janpfeifer/gonb/internal/goexec/goplsclient/conn.go *Client.Connect 63.4% +github.com/janpfeifer/gonb/internal/goexec/goplsclient/conn.go minTimeout 83.3% +github.com/janpfeifer/gonb/internal/goexec/goplsclient/conn.go *Client.Connect 64.3% github.com/janpfeifer/gonb/internal/goexec/goplsclient/conn.go *Client.NotifyDidOpenOrChange 0.0% github.com/janpfeifer/gonb/internal/goexec/goplsclient/conn.go *Client.notifyDidOpenOrChangeLocked 0.0% github.com/janpfeifer/gonb/internal/goexec/goplsclient/conn.go *Client.CallDefinition 0.0% @@ -415,11 +419,16 @@ github.com/janpfeifer/gonb/internal/specialcmd/definitions.go removeDefinitionI github.com/janpfeifer/gonb/internal/specialcmd/definitions.go removeDefinitions 91.7% github.com/janpfeifer/gonb/internal/specialcmd/specialcmd.go Parse 84.6% github.com/janpfeifer/gonb/internal/specialcmd/specialcmd.go joinLine 87.5% -github.com/janpfeifer/gonb/internal/specialcmd/specialcmd.go execSpecialConfig 55.1% +github.com/janpfeifer/gonb/internal/specialcmd/specialcmd.go execSpecialConfig 56.9% github.com/janpfeifer/gonb/internal/specialcmd/specialcmd.go execShell 100.0% github.com/janpfeifer/gonb/internal/specialcmd/specialcmd.go splitCmd 97.0% github.com/janpfeifer/gonb/internal/specialcmd/track.go execTrack 27.3% github.com/janpfeifer/gonb/internal/specialcmd/track.go execUntrack 54.5% github.com/janpfeifer/gonb/internal/specialcmd/track.go showTrackedList 91.7% +github.com/janpfeifer/gonb/internal/version/version.go AppVersion 66.7% +github.com/janpfeifer/gonb/internal/version/version.go *VersionInfo.GetInfo 100.0% +github.com/janpfeifer/gonb/internal/version/version.go *VersionInfo.String 100.0% +github.com/janpfeifer/gonb/internal/version/version.go *VersionInfo.Print 100.0% +github.com/janpfeifer/gonb/internal/version/version.go *VersionInfo.Markdown 0.0% github.com/janpfeifer/gonb/internal/websocket/websocket.go Javascript 83.3% -total (statements) 64.8% +total (statements) 64.7% diff --git a/examples/tests/bash_script.ipynb b/examples/tests/bash_script.ipynb index 80546a4..27654d2 100644 --- a/examples/tests/bash_script.ipynb +++ b/examples/tests/bash_script.ipynb @@ -34,7 +34,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "/tmp/gonb_dec188e3\n" + "/tmp/gonb_359acad8\n" ] } ], @@ -59,7 +59,7 @@ "output_type": "stream", "text": [ "/home/janpf/Projects/gonb/examples/tests\n", - "/tmp/gonb_dec188e3\n", + "/tmp/gonb_359acad8\n", "/home/janpf/Projects/gonb\n", "/home/janpf/Projects/gonb\n" ] diff --git a/examples/tests/dom.ipynb b/examples/tests/dom.ipynb index e1a1a21..145acda 100644 --- a/examples/tests/dom.ipynb +++ b/examples/tests/dom.ipynb @@ -88,7 +88,7 @@ { "data": { "text/html": [ - "
" + "
" ] }, "metadata": {}, @@ -152,7 +152,7 @@ { "data": { "text/html": [ - "
" + "
" ] }, "metadata": {}, diff --git a/examples/tests/goflags.ipynb b/examples/tests/goflags.ipynb index 8d3bbdc..b47449b 100644 --- a/examples/tests/goflags.ipynb +++ b/examples/tests/goflags.ipynb @@ -153,9 +153,9 @@ "name": "stdout", "output_type": "stream", "text": [ - "gonb_6d78d1c9/main.go:8:\tA\t\t100.0%\n", - "gonb_6d78d1c9/main.go:12:\tB\t\t0.0%\n", - "gonb_6d78d1c9/main.go:17:\tmain\t\t100.0%\n", + "gonb_334e1de4/main.go:8:\tA\t\t100.0%\n", + "gonb_334e1de4/main.go:12:\tB\t\t0.0%\n", + "gonb_334e1de4/main.go:17:\tmain\t\t100.0%\n", "total\t\t\t\t(statements)\t75.0%\n" ] } @@ -238,7 +238,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "# gonb_6d78d1c9\n", + "# gonb_334e1de4\n", "./main.go:10:6: can inline (*Point).ManhattanLen\n", "./main.go:16:12: inlining call to flag.Parse\n", "./main.go:18:27: inlining call to (*Point).ManhattanLen\n", diff --git a/examples/tests/gonbui.ipynb b/examples/tests/gonbui.ipynb index dde9b71..4d3b40b 100644 --- a/examples/tests/gonbui.ipynb +++ b/examples/tests/gonbui.ipynb @@ -22,7 +22,7 @@ "output_type": "stream", "text": [ "%goflags=[\"--cover\" \"--covermode=set\"]\n", - "GOCOVERDIR=/tmp/gonb_test_coverage.MljL9OTplr\n" + "GOCOVERDIR=/tmp/gonb_test_coverage.SYm2UwcmTW\n" ] } ], diff --git a/examples/tests/gotest.ipynb b/examples/tests/gotest.ipynb index a7c3d01..15d0efd 100644 --- a/examples/tests/gotest.ipynb +++ b/examples/tests/gotest.ipynb @@ -287,12 +287,12 @@ "text": [ "goos: linux\n", "goarch: amd64\n", - "pkg: gonb_57055275\n", + "pkg: gonb_b37069f2\n", "cpu: 12th Gen Intel(R) Core(TM) i9-12900K\n", "BenchmarkFibonacciA32\n", - "BenchmarkFibonacciA32-24 \t 172\t 6559009 ns/op\n", + "BenchmarkFibonacciA32-24 \t 180\t 6594959 ns/op\n", "BenchmarkFibonacciB32\n", - "BenchmarkFibonacciB32-24 \t350945394\t 3.942 ns/op\n", + "BenchmarkFibonacciB32-24 \t350331340\t 3.738 ns/op\n", "PASS\n", "coverage: [no statements]\n" ] @@ -334,10 +334,10 @@ "text": [ "goos: linux\n", "goarch: amd64\n", - "pkg: gonb_57055275\n", + "pkg: gonb_b37069f2\n", "cpu: 12th Gen Intel(R) Core(TM) i9-12900K\n", - "BenchmarkFibonacciA32-24 \t 172\t 6583831 ns/op\n", - "BenchmarkFibonacciB32-24 \t309453978\t 3.894 ns/op\n", + "BenchmarkFibonacciA32-24 \t 176\t 6570623 ns/op\n", + "BenchmarkFibonacciB32-24 \t305383951\t 3.899 ns/op\n", "PASS\n", "coverage: [no statements]\n" ] diff --git a/examples/tests/gowork.ipynb b/examples/tests/gowork.ipynb index aa1138e..814f084 100644 --- a/examples/tests/gowork.ipynb +++ b/examples/tests/gowork.ipynb @@ -36,7 +36,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "/tmp/gonb_tests_gowork_Xocdresp" + "/tmp/gonb_tests_gowork_pVaoudJA" ] } ], @@ -64,7 +64,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Temporary test package: /tmp/gonb_tests_gowork_Xocdresp\n" + "Temporary test package: /tmp/gonb_tests_gowork_pVaoudJA\n" ] } ], @@ -136,7 +136,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "\t- Added replace rule for module \"a.com/a/pkg\" to local directory \"/tmp/gonb_tests_gowork_Xocdresp\".\n" + "\t- Added replace rule for module \"a.com/a/pkg\" to local directory \"/tmp/gonb_tests_gowork_pVaoudJA\".\n" ] } ], @@ -155,7 +155,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "module gonb_bedb8330\n", + "module gonb_5dc14c2a\n", "\n", "go 1.23.5\n", "\n", @@ -186,7 +186,7 @@ "text/html": [ "List of files/directories being tracked:\n", "\n" ] }, @@ -208,7 +208,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "module gonb_bedb8330\n", + "module gonb_5dc14c2a\n", "\n", "go 1.23.5\n" ] diff --git a/examples/tests/input_boxes.ipynb b/examples/tests/input_boxes.ipynb index 9f2bf24..edcf89b 100644 --- a/examples/tests/input_boxes.ipynb +++ b/examples/tests/input_boxes.ipynb @@ -22,7 +22,7 @@ "output_type": "stream", "text": [ "%goflags=[\"--cover\" \"--covermode=set\"]\n", - "GOCOVERDIR=/tmp/gonb_test_coverage.MljL9OTplr\n" + "GOCOVERDIR=/tmp/gonb_test_coverage.SYm2UwcmTW\n" ] } ], diff --git a/examples/tests/vartuple.ipynb b/examples/tests/vartuple.ipynb index 83e399d..6c1c4e1 100644 --- a/examples/tests/vartuple.ipynb +++ b/examples/tests/vartuple.ipynb @@ -45,7 +45,7 @@ "text/html": [ "

Variables

\n", "" diff --git a/examples/tests/widgets.ipynb b/examples/tests/widgets.ipynb index 028ac04..7bcea3c 100644 --- a/examples/tests/widgets.ipynb +++ b/examples/tests/widgets.ipynb @@ -95,7 +95,7 @@ { "data": { "text/html": [ - "
" + "
" ] }, "metadata": {}, @@ -189,7 +189,7 @@ { "data": { "text/html": [ - "
" + "
" ] }, "metadata": {}, @@ -304,7 +304,7 @@ { "data": { "text/html": [ - "
" + "
" ] }, "metadata": {}, diff --git a/examples/tests/writefile.ipynb b/examples/tests/writefile.ipynb index 1c14bd5..c96f7fd 100644 --- a/examples/tests/writefile.ipynb +++ b/examples/tests/writefile.ipynb @@ -10,7 +10,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Cell contents written to \"/tmp/gonb_nbtests_writefile_3076514072/poetry.txt\".\n" + "Cell contents written to \"/tmp/gonb_nbtests_writefile_2429412932/poetry.txt\".\n" ] } ], @@ -32,7 +32,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Cell contents appended to \"/tmp/gonb_nbtests_writefile_3076514072/poetry.txt\".\n" + "Cell contents appended to \"/tmp/gonb_nbtests_writefile_2429412932/poetry.txt\".\n" ] } ], diff --git a/internal/goexec/elementtype_string.go b/internal/goexec/elementtype_string.go index 475215d..84b7a33 100644 --- a/internal/goexec/elementtype_string.go +++ b/internal/goexec/elementtype_string.go @@ -5,7 +5,7 @@ package goexec import "strconv" func _() { - // An "invalid array index" compiler err signifies that the constant values have changed. + // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[Invalid-0] diff --git a/internal/goexec/goplsclient/conn.go b/internal/goexec/goplsclient/conn.go index f098133..eeaa448 100644 --- a/internal/goexec/goplsclient/conn.go +++ b/internal/goexec/goplsclient/conn.go @@ -40,18 +40,22 @@ func (c *Client) connCloseLocked() { // minTimeout extends (or returns a new context with extended Deadline), discarding the // previous one -- not the correct use of context, but will do for now. -func minTimeout(ctx context.Context, timeout time.Duration) context.Context { +func minTimeout(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { + var cancel context.CancelFunc minDeadline := time.Now().Add(timeout) if deadline, ok := ctx.Deadline(); !ok || deadline.After(minDeadline) { - ctx, _ = context.WithDeadline(ctx, minDeadline) + ctx, cancel = context.WithDeadline(ctx, minDeadline) + } else { + cancel = func() {} // No-op. } - return ctx + return ctx, cancel } // Connect to the `gopls` in address given by `c.Address()`. It also starts // a goroutine to monitor receiving requests. func (c *Client) Connect(ctx context.Context) error { - ctx = minTimeout(ctx, ConnectTimeout) + ctx, cancel := minTimeout(ctx, ConnectTimeout) + defer cancel() c.mu.Lock() defer c.mu.Unlock() @@ -129,7 +133,8 @@ func (c *Client) NotifyDidOpenOrChange(ctx context.Context, filePath string) (er // Silently do nothing, if no connection available. return } - ctx = minTimeout(ctx, CommunicationTimeout) + ctx, cancel := minTimeout(ctx, CommunicationTimeout) + defer cancel() c.mu.Lock() defer c.mu.Unlock() if c.conn == nil { @@ -227,7 +232,8 @@ func (c *Client) CallDefinition(ctx context.Context, filePath string, line, col // Silently do nothing, if no connection available. return } - ctx = minTimeout(ctx, CommunicationTimeout) + ctx, cancel := minTimeout(ctx, CommunicationTimeout) + defer cancel() c.mu.Lock() defer c.mu.Unlock() if c.conn == nil { @@ -277,7 +283,8 @@ func (c *Client) CallHover(ctx context.Context, filePath string, line, col int) // Silently do nothing, if no connection available. return } - ctx = minTimeout(ctx, CommunicationTimeout) + ctx, cancel := minTimeout(ctx, CommunicationTimeout) + defer cancel() c.mu.Lock() defer c.mu.Unlock() if c.conn == nil { @@ -319,7 +326,8 @@ func (c *Client) CallComplete(ctx context.Context, filePath string, line, col in // Silently do nothing, if no connection available. return } - ctx = minTimeout(ctx, CommunicationTimeout) + ctx, cancel := minTimeout(ctx, CommunicationTimeout) + defer cancel() c.mu.Lock() defer c.mu.Unlock() if c.conn == nil { diff --git a/internal/goexec/goplsclient/goplsclient.go b/internal/goexec/goplsclient/goplsclient.go index 1ef54b6..c8479dc 100644 --- a/internal/goexec/goplsclient/goplsclient.go +++ b/internal/goexec/goplsclient/goplsclient.go @@ -235,29 +235,32 @@ func (c *Client) FileData(filePath string) (content *FileData, updated bool, err err = nil } + // Trivial case: file doesn't exist (in filesystem and cache), or it exists in both + // and it is up-to-date. if !foundInCache && !foundInFile { // No file in cache or file system. return } - - // Deal with missing and deleted file. if foundInCache && foundInFile && fileInfo.ModTime() == content.ContentTime { // Fine not changed. return } + // Something needs updating. updated = true - if !foundInFile && foundInCache { - // Remove notify removal. + + // File no longer exists in filesystem. + if !foundInFile { + // Remove from cache. delete(c.fileCache, filePath) return } - if foundInFile && foundInCache { - klog.V(2).Infof("File %q: stored date is %s, fileInfo mod time is %s", + // Create or update cache for file. + if foundInCache && klog.V(2).Enabled() { + klog.Infof("File %q: stored date is %s, fileInfo mod time is %s. Cache will be udpated.", filePath, content.ContentTime, fileInfo.ModTime()) } - content = &FileData{ URI: uri.File(filePath), Path: filePath, diff --git a/main.go b/main.go index b0ddf8d..29bab77 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,8 @@ package main import ( "flag" "fmt" + "github.com/janpfeifer/gonb/internal/dispatcher" + "github.com/janpfeifer/gonb/internal/goexec" "io" "log" "os" @@ -10,8 +12,6 @@ import ( "time" "github.com/gofrs/uuid" - "github.com/janpfeifer/gonb/internal/dispatcher" - "github.com/janpfeifer/gonb/internal/goexec" "github.com/janpfeifer/gonb/internal/kernel" "github.com/janpfeifer/gonb/version" klog "k8s.io/klog/v2" @@ -33,7 +33,7 @@ var ( var ( // UniqueID uniquely identifies a kernel execution. Used to create the temporary // directory holding the kernel code, and for logging. - // Set by SetUpLogging. + // Set by setUpLogging. UniqueID string coloredUniqueID string @@ -52,112 +52,74 @@ func init() { func main() { klog.InitFlags(nil) defer klog.Flush() - flag.Parse() + // --version or -V if printVersion() { return } - // Setup logging. - if *flagExtraLog != "" { - logFile, err := os.OpenFile(*flagExtraLog, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) - if err != nil { - klog.Fatalf("Failed to open log file %q for writing: %+v", *flagExtraLog, err) - } - _, _ = fmt.Fprintf(logFile, "\n\nLogging for %q (pid=%d) starting at %s\n\n", os.Args[0], os.Getpid(), time.Now()) - logWriter = logFile - flag.VisitAll(func(f *flag.Flag) { - if f.Name == "logtostderr" || f.Name == "alsologtostderr" { - if f.Value.String() == "true" { - logWriter = io.MultiWriter(logFile, os.Stderr) // Write to STDERR and the newly open logFile. - _ = f.Value.Set("false") - } - } - }) - defer func() { _ = logFile.Close() }() + // Logging. + extraLogFile := setUpExtraLog() // --extra_log. + if extraLogFile != nil { + defer func() { _ = extraLogFile.Close() }() } - SetUpLogging() // "log" package. - SetUpKlog() // "github.com/golang/klog" package + setUpLogging() // "log" package. + setUpKlog() // "k8s.io/klog/v2" package - if *flagInstall { - // Install kernel in Jupyter configuration. - var extraArgs []string - if *flagExtraLog != "" { - extraArgs = []string{"--extra_log", *flagExtraLog} - } - if glogFlag := flag.Lookup("vmodule"); glogFlag != nil && glogFlag.Value.String() != "" { - extraArgs = append(extraArgs, fmt.Sprintf("--vmodule=%s", glogFlag.Value.String())) - } - if glogFlag := flag.Lookup("logtostderr"); glogFlag != nil && glogFlag.Value.String() != "false" { - extraArgs = append(extraArgs, "--logtostderr") - } - if glogFlag := flag.Lookup("alsologtostderr"); glogFlag != nil && glogFlag.Value.String() != "false" { - extraArgs = append(extraArgs, "--alsologtostderr") - } - if glogFlag := flag.Lookup("raw_error"); glogFlag != nil && glogFlag.Value.String() != "false" { - extraArgs = append(extraArgs, "--raw_error") - } - if glogFlag := flag.Lookup("work"); glogFlag != nil && glogFlag.Value.String() != "false" { - extraArgs = append(extraArgs, "--work") - } - if glogFlag := flag.Lookup("comms_log"); glogFlag != nil && glogFlag.Value.String() != "false" { - extraArgs = append(extraArgs, "--comms_log") - } - err := kernel.Install(extraArgs, *flagForceDeps, *flagForceCopy) - if err != nil { - log.Fatalf("Installation failed: %+v\n", err) - } + // One of two tasks: (1) install gonb; (2) run kernel, started by JupyterServer. + if install() { return } - - if *flagKernel == "" { - _, _ = fmt.Fprintf(os.Stderr, "Use either --install to install the kernel, or if started by Jupyter the flag --kernel must be provided.\n") - flag.PrintDefaults() - os.Exit(1) + if runKernel() { + return } - gocoverdir := os.Getenv("GOCOVERDIR") - if gocoverdir != "" { - klog.Infof("GOCOVERDIR=%s", gocoverdir) - } + _, _ = fmt.Fprintf(os.Stderr, "Use either --install to install the kernel, or if started by Jupyter the flag --kernel must be provided.\n") + flag.PrintDefaults() + os.Exit(1) + return +} - _, err := exec.LookPath("go") - if err != nil { - klog.Exitf("Failed to find path for the `go` program: %+v\n\nCurrent PATH=%q", err, os.Getenv("PATH")) +// install GoNB kernel as a JupyterServer configuration. +// Returns true if installed. +// Errors are fatal. +func install() bool { + if !*flagInstall { + return false } - // Create a kernel. - k, err := kernel.New(*flagKernel) - klog.Infof("kernel created\n") - if err != nil { - log.Fatalf("Failed to start kernel: %+v", err) + // Install kernel in Jupyter configuration. + var extraArgs []string + if *flagExtraLog != "" { + extraArgs = []string{"--extra_log", *flagExtraLog} } - k.HandleInterrupt() // Handle Jupyter interruptions and Control+C. - - // Create a Go executor. - goExec, err := goexec.New(k, UniqueID, *flagWork, *flagRawError) - if err != nil { - log.Fatalf("Failed to create go executor: %+v", err) + if glogFlag := flag.Lookup("vmodule"); glogFlag != nil && glogFlag.Value.String() != "" { + extraArgs = append(extraArgs, fmt.Sprintf("--vmodule=%s", glogFlag.Value.String())) } - goExec.Comms.LogWebSocket = *flagCommsLog - - // Orchestrate dispatching of messages. - dispatcher.RunKernel(k, goExec) - klog.V(1).Infof("Dispatcher exited.") - - // Stop gopls. - err = goExec.Stop() + if glogFlag := flag.Lookup("logtostderr"); glogFlag != nil && glogFlag.Value.String() != "false" { + extraArgs = append(extraArgs, "--logtostderr") + } + if glogFlag := flag.Lookup("alsologtostderr"); glogFlag != nil && glogFlag.Value.String() != "false" { + extraArgs = append(extraArgs, "--alsologtostderr") + } + if glogFlag := flag.Lookup("raw_error"); glogFlag != nil && glogFlag.Value.String() != "false" { + extraArgs = append(extraArgs, "--raw_error") + } + if glogFlag := flag.Lookup("work"); glogFlag != nil && glogFlag.Value.String() != "false" { + extraArgs = append(extraArgs, "--work") + } + if glogFlag := flag.Lookup("comms_log"); glogFlag != nil && glogFlag.Value.String() != "false" { + extraArgs = append(extraArgs, "--comms_log") + } + err := kernel.Install(extraArgs, *flagForceDeps, *flagForceCopy) if err != nil { - klog.Warningf("Error during shutdown: %+v", err) + log.Fatalf("Installation failed: %+v\n", err) } - klog.V(1).Infof("goExec stopped.") - - // Wait for all polling goroutines. - k.ExitWait() - klog.Infof("Exiting...") + return true } +// printVersion returns whether version printing was requested. func printVersion() bool { if *flagShortVersion { fmt.Println(version.AppVersion.String()) @@ -175,9 +137,32 @@ var ( ColorBgYellow = "\033[7;39;32m" ) -// SetUpLogging creates a UniqueID, uses it as a prefix for logging, and sets up --extra_log if +// setUpExtraLog +func setUpExtraLog() *os.File { + if *flagExtraLog == "" { + return nil + } + + logFile, err := os.OpenFile(*flagExtraLog, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) + if err != nil { + klog.Fatalf("Failed to open log file %q for writing: %+v", *flagExtraLog, err) + } + _, _ = fmt.Fprintf(logFile, "\n\nLogging for %q (pid=%d) starting at %s\n\n", os.Args[0], os.Getpid(), time.Now()) + logWriter = logFile + flag.VisitAll(func(f *flag.Flag) { + if f.Name == "logtostderr" || f.Name == "alsologtostderr" { + if f.Value.String() == "true" { + logWriter = io.MultiWriter(logFile, os.Stderr) // Write to STDERR and the newly open logFile. + _ = f.Value.Set("false") + } + } + }) + return logFile +} + +// setUpLogging creates a UniqueID, uses it as a prefix for logging, and sets up --extra_log if // requested. -func SetUpLogging() { +func setUpLogging() { log.SetPrefix(coloredUniqueID) if logWriter != nil { log.SetOutput(logWriter) @@ -187,14 +172,12 @@ func SetUpLogging() { // UniqueIDFilter prepends the UniqueID for every log line. type UniqueIDFilter struct{} -// prepend value to slice: it makes a copy of the slice. -func prepend[T any](slice []T, value T) []T { - newSlice := make([]T, len(slice)+1) - if len(slice) > 0 { - copy(newSlice[1:], slice) - } - newSlice[0] = value - return newSlice +// prepend value to slice. +func prepend[T any](slice []T, element T) []T { + slice = append(slice, element) // It will be overwritten. + copy(slice[1:], slice) // Shift to the "right" + slice[0] = element + return slice } // Filter implements klog.LogFilter interface. @@ -212,10 +195,60 @@ func (UniqueIDFilter) FilterS(msg string, keysAndValues []interface{}) (string, return coloredUniqueID + msg, keysAndValues } -// SetUpKlog to include prefix with kernel's UniqueID. -func SetUpKlog() { +// setUpKlog to include prefix with kernel's UniqueID. +func setUpKlog() { if logWriter != nil { klog.SetOutput(logWriter) } klog.SetLogFilter(UniqueIDFilter{}) + + // Report about profiling test coverage. + gocoverdir := os.Getenv("GOCOVERDIR") + if gocoverdir != "" { + klog.Infof("GOCOVERDIR=%s", gocoverdir) + } +} + +// runKernel if --kernel is set. Returns whether the kernel was run. +// Errors are fatal. +func runKernel() bool { + if *flagKernel == "" { + return false + } + + _, err := exec.LookPath("go") + if err != nil { + klog.Exitf("Failed to find path for the `go` program: %+v\n\nCurrent PATH=%q", err, os.Getenv("PATH")) + } + + // Create a kernel. + k, err := kernel.New(*flagKernel) + klog.Infof("kernel created\n") + if err != nil { + klog.Fatalf("Failed to start kernel: %+v", err) + } + k.HandleInterrupt() // Handle Jupyter interruptions and Control+C. + + // Create a Go executor. + goExec, err := goexec.New(k, UniqueID, *flagWork, *flagRawError) + if err != nil { + klog.Fatalf("Failed to create go executor: %+v", err) + } + goExec.Comms.LogWebSocket = *flagCommsLog + + // Orchestrate dispatching of messages. + dispatcher.RunKernel(k, goExec) + klog.V(1).Infof("Dispatcher exited.") + + // Stop gopls. + err = goExec.Stop() + if err != nil { + klog.Warningf("Error during shutdown: %+v", err) + } + klog.V(1).Infof("goExec stopped.") + + // Wait for all polling goroutines. + k.ExitWait() + klog.Infof("Exiting...") + return true } diff --git a/version.go b/version.go index d624a42..8e74620 100644 --- a/version.go +++ b/version.go @@ -7,8 +7,8 @@ import ( "github.com/janpfeifer/gonb/version" ) -//go:generate bash -c "printf 'package main\nvar GitTag = \"%s\"\n' \"$(git describe --tags --abbrev=0)\" > version/versiontag.go" -//go:generate bash -c "printf 'package main\nvar GitCommitHash = \"%s\"\n' \"$(git rev-parse HEAD)\" > version/versionhash.go" +//go:generate bash -c "printf 'package version\nvar GitTag = \"%s\"\n' \"$(cat version.txt)\" > version/versiontag.go" +//go:generate bash -c "printf 'package version\nvar GitCommitHash = \"%s\"\n' \"$(git rev-parse HEAD)\" > version/versionhash.go" func must(err error) { if err != nil { diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..8b684d8 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +v0.10.11 \ No newline at end of file diff --git a/version/versionhash.go b/version/versionhash.go index 9e2fccb..5b23c9e 100644 --- a/version/versionhash.go +++ b/version/versionhash.go @@ -1,3 +1,2 @@ package version - -var GitCommitHash = "05c5124f6ed019954e5ed195f7e2674a8713beca" +var GitCommitHash = "60ff94b91e0125e8dce3e83e08ada48effdfd8a0" diff --git a/version/versiontag.go b/version/versiontag.go index 5a8f382..7dd2d6a 100644 --- a/version/versiontag.go +++ b/version/versiontag.go @@ -1,3 +1,2 @@ package version - -var GitTag = "v0.10.10" +var GitTag = "v0.10.11"