diff --git a/blog/_data/embeddings_cache.pkl b/blog/_data/embeddings_cache.pkl index e935a3585..600912201 100644 Binary files a/blog/_data/embeddings_cache.pkl and b/blog/_data/embeddings_cache.pkl differ diff --git a/blog/_data/related_articles.yml b/blog/_data/related_articles.yml index b6942acb8..4905b3a5d 100644 --- a/blog/_data/related_articles.yml +++ b/blog/_data/related_articles.yml @@ -5431,7 +5431,7 @@ future-is-rusty: - python-c-extension - cargo-chef - data-engineering-analysis - - cargo-workspace-crates + - golang-dlv docker-init-in-go: @@ -5508,8 +5508,8 @@ single-expression-functions: - python-c-extension - rust-sccache - cargo-workspace-crates + - golang-dlv - poetry-publish - - rust-monorepo docker-init-in-python: @@ -5636,10 +5636,10 @@ showboaters: - future-is-rusty - python-c-extension - incremental-rust-builds + - golang-dlv - gradle-monorepo - cargo-workspace-crates - understanding-monorepos - - poetry-publish rust-sccache: @@ -5717,7 +5717,7 @@ readability: - rust-sccache - rust-monorepo - understanding-monorepos - - platform-engineering-tools + - golang-dlv cargo-chef: @@ -5928,3 +5928,16 @@ monorepo-java: - docker-init-rust +golang-dlv: + - docker-init-in-go + - docker-init-in-python + - docker-buildx + - yarn-vite-monorepo + - future-is-rusty + - incremental-rust-builds + - konfig + - docker-init-quickly + - docker-init-rust + - docker-scout + + diff --git a/blog/_data/short_title_cache.pkl b/blog/_data/short_title_cache.pkl index 057dbf89b..e3b26fb89 100644 Binary files a/blog/_data/short_title_cache.pkl and b/blog/_data/short_title_cache.pkl differ diff --git a/blog/_data/short_titles.yml b/blog/_data/short_titles.yml index a29d0bf5f..cf00f0e0b 100644 --- a/blog/_data/short_titles.yml +++ b/blog/_data/short_titles.yml @@ -119,6 +119,7 @@ netlify-cloud-functions: 'Netlify Cloud Functions' incremental-rust-builds: 'Rust Builds in CI' deploy-kubernetes-cri-o-container-runtime: 'Deploy Kubernetes with CRI-O' private-docker-registry: 'Private Docker Registry Setup' +golang-dlv: 'Golang Debugger' lima: 'Lima VM' setup-typescript-monorepo: 'TypeScript Monorepo Setup' pants-python-monorepo: 'Managing Python Monorepo with Pants' diff --git a/blog/_posts/2024-04-18-golang-dlv.md b/blog/_posts/2024-04-18-golang-dlv.md new file mode 100644 index 000000000..0d6960195 --- /dev/null +++ b/blog/_posts/2024-04-18-golang-dlv.md @@ -0,0 +1,367 @@ +--- +title: "go delve - The Golang Debugger " +categories: + - Tutorials +toc: true +author: Adam + +internal-links: + - just an example +--- +Delve (`dlv`) is a CLI-based debugger for Go, tailored to the language's concurrency model and runtime. It allows you to set breakpoints, inspect goroutines, and evaluate and manipulate variables in real-time. Delve supports remote debugging and seamlessly integrates with major IDEs, including Visual Studio Code. Let me walk you through using it, but first, some background. + +## Background + +In my recent foray into Python, one thing that has become endlessly useful is the ability to add a `breakpoint()` to a line of code without an IDE or any setup at all, ending up in a console-based debugger at that point. + +In the past, I had always associated debuggers with IDEs like Visual Studio or IntelliJ. Those are great. However, I find myself in VS Code most of the time now, and I've never fully understood its debugging support. Instead, I resort to using prints, which usually suffices. But there are times when I need to step through some code and require an easy-to-use debugger. This is where Delve shines, making it a breeze to debug a go executable, a go test, and even, in theory, a go docker container running on a remote host. + +## Installing Delve + +To install just run `go install` with the package path: + +~~~ +$ go install github.com/go-delve/delve/cmd/dlv@latest + +go: downloading github.com/go-delve/delve v1.22.1 +go: downloading github.com/hashicorp/golang-lru v1.0.2 +go: downloading github.com/cosiner/argv v0.1.0 +go: downloading github.com/derekparker/trie v0.0.0-20230829180723-39f4de51ef7d +go: downloading github.com/go-delve/liner v1.2.3-0.20231231155935-4726ab1d7f62 +go: downloading golang.org/x/arch v0.6.0 +go: downloading github.com/google/go-dap v0.11.0 +go: downloading go.starlark.net v0.0.0-20231101134539-556fd59b42f6 +go: downloading github.com/mattn/go-runewidth v0.0.13 +go: downloading github.com/rivo/uniseg v0.2.0 + +~~~ + +That will install it into your GOPATH: + +~~~ +$ go env GOPATH +/Users/adam/go +~~~ + +Make sure you have GOPATH in your path ( I didn't): + +~~~ +echo 'export PATH=$PATH:$(go env GOPATH)/bin' >> ~/.zshrc && source ~/.zshrc +~~~ + +Then you can run it: + +~~~ +$ dlv version +Delve Debugger +Version: 1.22.1 +Build: $Id: 0c3470054da6feac5f6dcf3e3e5144a64f7a9a48 $ +~~~ + +### Debugging `dlv debug` + +Now before `dlv`, I was using `go run` with a lot of parameters to run Earthly: + +~~~ +go run cmd/earthly/main.go -P -i --buildkit-image earthly/buildkitd:prerelease ./tests/raw-output+gha +~~~ + +To debug with delve I just change `go run` to `dlv debug` and introduce a `--` to delimit where my apps params start. + +~~~ +dlv debug cmd/earthly/main.go -- -P -i --buildkit-image earthly/buildkitd:prerelease ./tests/raw-output+gha +~~~ + +Now, I want to debug this header printing code in Earthly: + +~~~{.go caption="conslogging/conslogging.go"} +func (cl ConsoleLogger) PrintPhaseHeader(phase string, disabled bool, special string) { + w := new(bytes.Buffer) + cl.mu.Lock() + defer func() { + _, _ = w.WriteTo(cl.errW) + cl.mu.Unlock() + }() + msg := phase + c := cl.color(phaseColor) + if disabled { + c = cl.color(disabledPhaseColor) + msg = fmt.Sprintf("%s (disabled)", msg) + } else if special != "" { + c = cl.color(specialPhaseColor) + msg = fmt.Sprintf("%s (%s)", msg, special) + } + underlineLength := utf8.RuneCountInString(msg) + 2 + if underlineLength < barWidth { + underlineLength = barWidth + } + c.Fprintf(w, " %s", msg) + fmt.Fprintf(w, "\n") + c.Fprintf(w, "%s", strings.Repeat("—", underlineLength)) + fmt.Fprintf(w, "\n\n") +} +~~~ + +I want to understand more about the underlineLength calculation at run time. To do so, I set a breakpoint and continue execution until that point is hit: + +~~~ +(dlv) break conslogging/conslogging.go:230 +Breakpoint 1 set at 0x1050c4e44 for github.com/earthly/earthly/conslogging.ConsoleLogger.PrintPhaseHeader() ./conslogging/conslogging.go:230 +(dlv) continue + +~~~ + +`dlv` returns this, when I hit a break point: + +~~~ + 225: } + 226: underlineLength := utf8.RuneCountInString(msg) + 2 + 227: if underlineLength < barWidth { + 228: underlineLength = barWidth + 229: } +=> 230: c.Fprintf(w, " %s", msg) + 231: fmt.Fprintf(w, "\n") + 232: c.Fprintf(w, "%s", strings.Repeat("—", underlineLength)) + 233: fmt.Fprintf(w, "\n\n") + 234: } + 235: +~~~ + +To check the value of underline length I can use print (p) or list all locals with `locals` + +~~~ +(dlv) p underlineLength +80 +~~~ + +~~~ +(dlv) locals +w = (*bytes.Buffer)(0x1400041c420) +msg = "Init 🚀" +c = ("*github.com/fatih/color.Color")(0x140001e49e0) +underlineLength = 80 +(dlv) p underlineLength +~~~ + +I can also use `args` to get the arguments passed into my function: + +~~~ +(dlv) args +cl = github.com/earthly/earthly/conslogging.ConsoleLogger {prefix: "", metadataMode: false, isLocal: false,...+14 more} +phase = "Init 🚀" +disabled = false +special = "" +~~~ + +From this point, I can do many things, including saving a checkpoint that lets me return to this point of execution later, but for now, I just want to modify `underlineLength` and see how that changes the execution. I can do this using `set` and print `p`. + +~~~ +(dlv) set underlineLength = 5 +(dlv) p underlineLength +5 +~~~ + +I can then use next (`n`) to step through the code and continue (`c`) to continue execution and see my new shortened underline. `c` works just like you'd expect continue to work, it continues the execution until another break point is hit. + +~~~ +(dlv) c +Init 🚀 +----- +~~~ + +That `----` is my much shortened underscore line, but the problem is this function gets called in a loop and so I quickly end up back at my breakpoint again: + +~~~ + 225: } + 226: underlineLength := utf8.RuneCountInString(msg) + 2 + 227: if underlineLength < barWidth { + 228: underlineLength = barWidth + 229: } +=> 230: c.Fprintf(w, " %s", msg) + 231: fmt.Fprintf(w, "\n") + 232: c.Fprintf(w, "%s", strings.Repeat("—", underlineLength)) + 233: fmt.Fprintf(w, "\n\n") + 234: } + 235: +~~~ + +I can keep mashing `c` until I get past this, or clear my breakpoint with `clear`: + +~~~ +breakpoints +Breakpoint runtime-fatal-throw (enabled) at 0x1048551ac,0x10483e3c0,0x10483e480 for (multiple functions)() :0 (0) +Breakpoint unrecoverable-panic (enabled) at 0x10483e750 for runtime.fatalpanic() /opt/homebrew/Cellar/go/1.21.1/libexec/src/runtime/panic.go:1188 (0) + print runtime.curg._panic.arg +Breakpoint 1 (enabled) at 0x1050c4e44 for github.com/earthly/earthly/conslogging.ConsoleLogger.PrintPhaseHeader() ./conslogging/conslogging.go:230 (1) +~~~ + +My breakpoint is breakpoint 1, so I can clear it and continue: + +~~~ +(dlv) clear 1 +(dlv) continue + +~~~ + +**Side Note:** Breakpoints `runtime-fatal-throw` and `unrecoverable-panic` do exactly what they sound like. Delve includes breakpoints on these critical failure points to debug a fatal runtime throw or an unrecoverable panic easily. + +With the breakpoint cleared, Earthly now runs to completion. + +~~~ +Process 91365 has exited with status 0 +Process 91365 has exited with status 0 +(dlv) +~~~ + +From that point, I can restart to rerun the same process with the same arguments or quit. And that is quick tutorial on `dlv`. + +## Quick Tips + +For all the in and outs of `dlv` checkout [the docs](https://github.com/go-delve/delve), but here is some quick tips. + +| **Task** | **Go Command** | **Delve Command** | +|--------------------------|-------------------------|----------------------------| +| **Run Program** | `go run main.go` | `dlv debug` | +| **Run Tests** | `go test` | `dlv test` | +| **Compile and Run Executable** | `go build -o myapp && ./myapp` | `dlv exec ./myapp` | +| **Run Specific Test** | `go test -run TestName` | `dlv test -- -test.run TestName` | + +### Keyboard Shortcuts + +| **Command Name** | **Shortcut** | **Description** | +|------------------|--------------|------------------------------------------------| +| `break` | `b` | Sets a breakpoint at a specific source location. | +| `continue` | `c` | Resumes program execution until a breakpoint. | +| `step` | `s` | Executes the current line, entering functions. | +| `next` | `n` | Executes the current line, skipping functions. | +| `print` | `p` | Prints the value of a variable or expression. | +| `list` | `l` | Displays source around the current execution point. | +| `clear` | | Removes a breakpoint. | +| `set` | | Modifies the value of a variable during debugging. | + +## Headless for remote or Containerize Debugging + +Delve can be run headless, `--headless` , and then connected to remotely. This can be useful in a docker container where the trick is to install `dlv` and then run it and open the correct port: + +~~~{.dockerfile caption="Dockerfile"} +FROM golang:1.18 + +RUN go install github.com/go-delve/delve/cmd/dlv@latest + +COPY myapp . +EXPOSE 40000 + +# Command to run Delve server +CMD ["dlv", "--listen=:40000", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "./myapp"] +~~~ + +Your app needs to be built with debug capabilities, and of course, you can use Earthy to do this building and containerizing. Here is an Earthfile for building the app with debug symbols and building the debug container: + +~~~{.dockerfile caption="Earthfile"} +VERSION 0.8 +FROM golang:1.22 + +build-debug: + WORKDIR /myapp + COPY ./src . + RUN go build -gcflags="all=-N -l" -o /myapp/myapp . + SAVE ARTIFACT /myapp/myapp myapp + +containerize-debug: + FROM golang:1.18 + # Install Delve + RUN go install github.com/go-delve/delve/cmd/dlv@latest + # Copy the built app from the previous stage + COPY +build-debug/myapp /myapp/myapp + WORKDIR /myapp + EXPOSE 40000 + CMD ["dlv", "--listen=:40000", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "/myapp/myapp"] + SAVE IMAGE myapp-debug +~~~ + +Then you can debug like this: + +~~~ +earthly +containerize-debug +docker run -d -p 40000:40000 myapp-debug +dlv connect localhost:40000 +~~~ + +You could even run delve from right within Earthly: + +~~~{.dockerfile caption="Earthfile"} +interactive-debug: + FROM golang:1.18 + RUN go install github.com/go-delve/delve/cmd/dlv@latest + COPY +build-debug/myapp /myapp/myapp + + # Did you know earthly also allows you to run commands that require a tty? + RUN --interactive dlv exec /myapp/myapp +~~~ + +## VS Code and GoLand + +Delve is what powers the debugger you see in various go IDEs. If you want to debug go code in VS Code, then the official go extension will probably just work once you configure a launch.js. + +For me, I had to add this under `Run`-> `Add Configuration`: + +~~~{.yaml caption="launch.js"} +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug Earthly", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "./cmd/earthly/main.go", + "args": [ + "./examples/c+deps" + ], + "env": { + "FORCE_COLOR": "1" + }, + "cwd": "${workspaceFolder}" + } + + ] +} +~~~ + +This is the equivalent of `dlv debug`. To Debug an executable in VS Code, build with debug symbols `-gcflags="all=-N -l"` and then use `mode=exec`, which corresponds to `dlv exec`: + +~~~{.yaml caption="launch.js"} + { + "name": "Debug Earthly Binary", + "type": "go", + "request": "launch", + "mode": "exec", + "env": { + "FORCE_COLOR": "1" + }, + "program": "./build/darwin/amd64/earthly", + "args": [ + "./examples/c+deps" + ], + "cwd": "${workspaceFolder}" + }, +~~~ + +You can make similar changes to `mode` to run tests with `dlv test` and presumably to connect to remote or headless debug sessions. + +### GoLand and IntelliJ + +Delve works out of the box with GoLand, and with the IntelliJ Go Plugin. It works just like IntelliJ users would expect a debugger to work. + +To setup remote debugging a bit more configuration is required but the folks at JetBrains kindly provide a GUI with instructions embedded in it. + +
+{% picture content-wide-nocrop {{site.pimages}}{{page.slug}}/7180.png --alt {{ GoLand Remote Go Debugging }} %} +
GoLand Remote Go Debugging ( find this under Run&Debug Configurations -> Add Configuration -> Go Remote)
+
+ +## That's a Wrap + +Ok, that's a wrap. `dlv` can integrate with neovim, vim, neovim, Sublime and probably a number of other editors as well, but setting those up is left as an exercise for the reader. Check out [github](https://github.com/go-delve/delve) for more information on `dlv`, including trace points and checkpoints which are valuable tools that deserve their own write up. + +{% include_html cta/bottom-cta.html %} diff --git a/blog/_posts/2024-04-23-earthly-functions.md b/blog/_posts/2024-04-23-earthly-functions.md new file mode 100644 index 000000000..4a56ed635 --- /dev/null +++ b/blog/_posts/2024-04-23-earthly-functions.md @@ -0,0 +1,78 @@ +--- +title: "Introducing Earthly Functions: Reusable Code for More Modular, Consistent Build Files" +categories: + - News +toc: true +author: Gavin +topcta: false +internal-links: + - introducing earthly functions + - introduction to earthly functions + - reusable code with earthly functions + - consistent build files with earthly functions +excerpt: | + Introducing Earthly Functions: Reusable code blocks can enhance modularity and consistency in your Earthfiles. +--- + +The concept of functions runs deep in software development. Pretty much every programming language has functions or something similar that delivers the same capabilities: a block of code that performs a specific task that you can call from anywhere. Functions are a fundamental part of C. Methods, functions associated with specific classes, are part of almost every object-oriented programming language. Even SQL has functions. + +Every programming language has functions because they are incredibly valuable. They make it easier to make programs modular and code reusable. These same benefits of modularity and reusability are valuable in your builds too. That's why we want to introduce you to Earthly Functions. Functions are exactly what you'd expect, reusable sets of instructions that can be imported into build targets or other functions in your Earthfiles. They are designed to make it easier to make your Earthfiles more modular and your build code less redundant. + +## How to Use Functions + +![how]({{site.images}}{{page.slug}}/how.png)\ + +Functions are defined similarly to build targets except that the function name must be in ALL_UPPERCASE_SNAKE_CASE and they must start with `FUNCTION`. For example: + +~~~{.dockerfile caption="Earthfile"} +MY_COPY: + FUNCTION + ARG src + ARG dest=./ + ARG recursive=false + RUN cp $(if $recursive = "true"; then printf -- -r; fi) "$src" "$dest" +~~~ + +You invoke a function using the `DO` command. For example: + +~~~{.dockerfile caption="Earthfile"} +build: + FROM alpine:3.18 + WORKDIR /function-example + RUN echo "hello" >./foo + DO +MY_COPY --src=./foo --dest=./bar + RUN cat ./bar # prints "hello" +~~~ + +Functions look and are used in a very similar way to build targets. There are a few differences though. Functions inherit the build context and the build environment from the caller. So any local `COPY` operations in a function will use the directory where the calling Earthfile exists; any files, directories, and dependencies created by previous steps of the caller are available to the function to operate on; and any file changes resulting from execution of the function are passed back to the caller as part of the build environment. + +_[Visit our docs from more information and details about using functions](https://docs.earthly.dev/docs/guides/functions)_ + +You can use functions that are defined in other Earthfiles in your repo or even other repositories. To do this you need to use `IMPORT` just like you would if you were importing build targets from another directory or repository. For an example, I'll be using Earthly [lib](https://github.com/earthly/lib). Earthly lib is a collection of reusable functions that we maintain to be used for common operations in Earthfiles. Here's how to easily mount and cache Gradle's cache using our [Gradle functions from Earthly lib](https://github.com/earthly/lib/tree/main/gradle). + +~~~{.dockerfile caption="Earthfile"} +VERSION 0.8 +IMPORT github.com/earthly/lib/gradle:3.0.2 AS gradle +FROM gradle:8.7.0-jdk21 + +deps: + COPY settings.gradle.kts build.gradle.kts ./ + COPY src src + # Sets $EARTHLY_GRADLE_USER_HOME_CACHE and $EARTHLY_GRADLE_PROJECT_CACHE + DO gradle+GRADLE_GET_MOUNT_CACHE + +build: + FROM +deps + RUN --mount=$EARTHLY_GRADLE_USER_HOME_CACHE --mount=$EARTHLY_GRADLE_PROJECT_CACHE gradle --no-daemon build + SAVE ARTIFACT ./build AS LOCAL ./build +~~~ + +_[Visit our Earthly lib repo for more pre-built functions and details on how to use them](https://github.com/earthly/lib/tree/main)_ + +## Sign Up for Earthly Cloud and Start Using Functions Today + +![Cloud]({{site.images}}{{page.slug}}/cloud.png)\ + +You can use Functions with open source Earthly. The easiest way to get Earthly is to [sign up for Earthly Cloud](https://cloud.earthly.dev/login). It walks you through the process of downloading and getting started with Earthly. Earthly Cloud also gives you 6,000 build minutes per month free on Earthly Satellites. Try Functions out, and let us know how they work for you. + +{% include_html cta/bottom-cta.html %} diff --git a/blog/assets/images/earthly-functions/cloud.png b/blog/assets/images/earthly-functions/cloud.png new file mode 100644 index 000000000..ec53622d0 Binary files /dev/null and b/blog/assets/images/earthly-functions/cloud.png differ diff --git a/blog/assets/images/earthly-functions/header.jpg b/blog/assets/images/earthly-functions/header.jpg new file mode 100644 index 000000000..cd3a20a4d Binary files /dev/null and b/blog/assets/images/earthly-functions/header.jpg differ diff --git a/blog/assets/images/earthly-functions/how.png b/blog/assets/images/earthly-functions/how.png new file mode 100644 index 000000000..9706ec9b4 Binary files /dev/null and b/blog/assets/images/earthly-functions/how.png differ diff --git a/blog/assets/images/golang-dlv/7180.png b/blog/assets/images/golang-dlv/7180.png new file mode 100644 index 000000000..1b59b73d8 Binary files /dev/null and b/blog/assets/images/golang-dlv/7180.png differ diff --git a/blog/assets/images/golang-dlv/header.jpg b/blog/assets/images/golang-dlv/header.jpg new file mode 100644 index 000000000..05c3e5eac Binary files /dev/null and b/blog/assets/images/golang-dlv/header.jpg differ