Compare commits

..

10 Commits

Author SHA1 Message Date
Michael Stapelberg
9a9519186b integration: add 'gok update' test
Some checks failed
gokrazy CI / CI (macos-latest) (push) Has been cancelled
gokrazy CI / CI (ubuntu-latest) (push) Has been cancelled
gokrazy CI / CI (windows-latest) (push) Has been cancelled
2025-10-03 19:12:17 +02:00
Michael Stapelberg
5675dbb2eb packer: set Accept header (request), not Content-Type (response) 2025-10-03 19:11:55 +02:00
Michael Stapelberg
ab29b81133 tests: use t.Chdir now that we are on Go 1.24 2025-10-03 18:32:43 +02:00
Michael Stapelberg
19d09ffb32 switch from breml/rootcerts to x/crypto fallback bundle
related to https://github.com/golang/go/issues/69898
2025-09-12 09:34:42 +02:00
dependabot[bot]
8143336564 build(deps): bump golang.org/x/oauth2 from 0.23.0 to 0.27.0 (#93)
---
updated-dependencies:
- dependency-name: golang.org/x/oauth2
  dependency-version: 0.27.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-19 10:26:58 +02:00
Michael Stapelberg
2979dc9b26 pull in latest gokrazy/updater 2025-07-05 15:59:45 +02:00
Michael Stapelberg
ab76ef531d update to latest gokrazy/updater (now needs a context) 2025-06-01 08:57:36 +02:00
Michael Stapelberg
2e390edb12 gok vm run: print a tip for how to enable the serial console
This should make it a little easier for users.
2025-05-30 19:00:00 +02:00
Michael Stapelberg
d145a5b689 go.mod: update to latest versions of dependencies 2025-05-30 16:58:56 +02:00
Michael Stapelberg
a0558bc6a0 GitHub Actions: verify gok builds on Windows and macOS, too
related to https://github.com/gokrazy/tools/issues/89
2025-05-29 17:03:18 +02:00
15 changed files with 311 additions and 81 deletions

View File

@@ -7,8 +7,14 @@ on:
jobs: jobs:
build: build:
name: build-and-test name: CI
runs-on: ubuntu-latest strategy:
matrix:
os:
- macos-latest
- ubuntu-latest
- windows-latest
runs-on: ${{ matrix.os }}
steps: steps:
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
@@ -21,14 +27,19 @@ jobs:
id: go id: go
- name: Ensure all files were formatted as per gofmt - name: Ensure all files were formatted as per gofmt
if: matrix.os == 'ubuntu-latest'
run: | run: |
[ "$(gofmt -l $(find . -name '*.go') 2>&1)" = "" ] [ "$(gofmt -l $(find . -name '*.go') 2>&1)" = "" ]
- name: Build and run tests - name: Build
run: |
go install -mod=mod ./cmd/...
- name: Run tests
if: matrix.os == 'ubuntu-latest'
# TestRelativeParentDir verifies breakglass.authorized_keys # TestRelativeParentDir verifies breakglass.authorized_keys
# is correctly included, and the gok CLI only creates that # is correctly included, and the gok CLI only creates that
# file when it finds SSH keys. # file when it finds SSH keys.
run: | run: |
mkdir -p ~/.ssh && echo dummy > ~/.ssh/id_ed25519.pub mkdir -p ~/.ssh && echo dummy > ~/.ssh/id_ed25519.pub
go install -mod=mod ./cmd/...
go test -mod=mod -v ./... go test -mod=mod -v ./...

22
go.mod
View File

@@ -1,24 +1,26 @@
module github.com/gokrazy/tools module github.com/gokrazy/tools
go 1.24 go 1.24.0
toolchain go1.24.6
require ( require (
github.com/breml/rootcerts v0.2.20
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0
github.com/gokrazy/gokapi v0.0.0-20250222080418-e140e9c461d8 github.com/gokrazy/gokapi v0.0.0-20250222080418-e140e9c461d8
github.com/gokrazy/internal v0.0.0-20250520205945-c2e4e2b4f611 github.com/gokrazy/internal v0.0.0-20250526201501-559979153369
github.com/gokrazy/updater v0.0.0-20230215172637-813ccc7f21e2 github.com/gokrazy/updater v0.0.0-20250705135802-db129c40879c
github.com/google/renameio/v2 v2.0.0 github.com/google/renameio/v2 v2.0.0
github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-isatty v0.0.20
github.com/spf13/cobra v1.6.1 github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.6
golang.org/x/mod v0.23.0 golang.org/x/crypto/x509roots/fallback v0.0.0-20250911151450-96dc232fbd79
golang.org/x/sync v0.1.0 golang.org/x/mod v0.24.0
golang.org/x/sys v0.28.0 golang.org/x/sync v0.14.0
golang.org/x/sys v0.33.0
) )
require ( require (
github.com/antihax/optional v1.0.0 // indirect github.com/antihax/optional v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect
) )

39
go.sum
View File

@@ -1,38 +1,37 @@
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/breml/rootcerts v0.2.20 h1:koth1lShwiiDp3VOX6/4qKEZ87S7HgDKsnDr47XEIq0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/breml/rootcerts v0.2.20/go.mod h1:S/PKh+4d1HUn4HQovEB8hPJZO6pUZYrIhmXBhsegfXw=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 h1:C7t6eeMaEQVy6e8CarIhscYQlNmw5e3G36y7l7Y21Ao= github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 h1:C7t6eeMaEQVy6e8CarIhscYQlNmw5e3G36y7l7Y21Ao=
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0/go.mod h1:56wL82FO0bfMU5RvfXoIwSOP2ggqqxT+tAfNEIyxuHw= github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0/go.mod h1:56wL82FO0bfMU5RvfXoIwSOP2ggqqxT+tAfNEIyxuHw=
github.com/gokrazy/gokapi v0.0.0-20250222080418-e140e9c461d8 h1:BvyzTtbpz1GCGD35Z3G/ZR0nK0j3Fh+dRCCso+w3RKE= github.com/gokrazy/gokapi v0.0.0-20250222080418-e140e9c461d8 h1:BvyzTtbpz1GCGD35Z3G/ZR0nK0j3Fh+dRCCso+w3RKE=
github.com/gokrazy/gokapi v0.0.0-20250222080418-e140e9c461d8/go.mod h1:rVItujrJo0NpYZhFR5dYdzLDqMoMCtjEZkdxoCRDo+o= github.com/gokrazy/gokapi v0.0.0-20250222080418-e140e9c461d8/go.mod h1:rVItujrJo0NpYZhFR5dYdzLDqMoMCtjEZkdxoCRDo+o=
github.com/gokrazy/internal v0.0.0-20250520205945-c2e4e2b4f611 h1:BcmhkIKeIsw5xGheRGOCj97zjevG+VImWiP2/XGF2Gg= github.com/gokrazy/internal v0.0.0-20250526201501-559979153369 h1:aNni2iPwJbowfHW1SFapKLfY+ZPUIcBfFrJvYPAh3p4=
github.com/gokrazy/internal v0.0.0-20250520205945-c2e4e2b4f611/go.mod h1:dQY4EMkD4L5ZjYJ0SPtpgYbV7MIUMCxNIXiOfnZ6jP4= github.com/gokrazy/internal v0.0.0-20250526201501-559979153369/go.mod h1:dQY4EMkD4L5ZjYJ0SPtpgYbV7MIUMCxNIXiOfnZ6jP4=
github.com/gokrazy/updater v0.0.0-20230215172637-813ccc7f21e2 h1:kBY5R1tSf+EYZ+QaSrofLaVJtBqYsVNVBWkdMq3Smcg= github.com/gokrazy/updater v0.0.0-20250705135802-db129c40879c h1:j4/v9FR/cOy6nog5rmXUtauBsOU3mm+rTPn5IENUbmg=
github.com/gokrazy/updater v0.0.0-20230215172637-813ccc7f21e2/go.mod h1:PYOvzGOL4nlBmuxu7IyKQTFLaxr61+WPRNRzVtuYOHw= github.com/gokrazy/updater v0.0.0-20250705135802-db129c40879c/go.mod h1:EtAn+BPibqnAHnYGj3FW5e284xNsiOOMOL2dJiwu7H4=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= golang.org/x/crypto/x509roots/fallback v0.0.0-20250911151450-96dc232fbd79 h1:WZWglxfb13JCTbJyKY1pk0V94spHxJzMAQW29INytRQ=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/crypto/x509roots/fallback v0.0.0-20250911151450-96dc232fbd79/go.mod h1:MEIPiCnxvQEjA4astfaKItNwEVZA5Ki+3+nyGbJ5N18=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,197 @@
package gokupdate_test
import (
"context"
"crypto/sha256"
"encoding/json"
"fmt"
"hash"
"hash/crc32"
"io"
"log"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
"github.com/gokrazy/internal/config"
"github.com/gokrazy/tools/gok"
"github.com/gokrazy/tools/internal/packer"
)
type gokrazyTestInstance struct {
configDir string
}
func (inst *gokrazyTestInstance) writeConfig(t *testing.T, basename, content string) {
t.Helper()
fn := filepath.Join(inst.configDir, basename)
if err := os.WriteFile(fn, []byte(content), 0600); err != nil {
t.Fatal(err)
}
}
func writeGokrazyInstance(t *testing.T) *gokrazyTestInstance {
t.Helper()
// Redirect os.UserConfigDir() to a temporary directory under our
// control. gokrazy always uses a path under os.UserConfigDir().
var configDir string
switch runtime.GOOS {
case "linux":
configHomeDir := t.TempDir()
os.Setenv("XDG_CONFIG_HOME", configHomeDir)
// where linux looks:
configDir = filepath.Join(configHomeDir, "gokrazy")
case "darwin":
homeDir := t.TempDir()
os.Setenv("HOME", homeDir)
// where darwin looks:
configDir = filepath.Join(homeDir, "Library", "Application Support", "gokrazy")
default:
t.Fatalf("GOOS=%s unsupported", runtime.GOOS)
}
if err := os.MkdirAll(configDir, 0755); err != nil {
t.Fatal(err)
}
return &gokrazyTestInstance{
configDir: configDir,
}
}
func TestGokUpdate(t *testing.T) {
// Run this whole test in a throw-away temporary directory to not litter the
// gokrazy/tools repository working copy.
t.Chdir(t.TempDir())
_ = writeGokrazyInstance(t)
// TODO: run the gokrazy instance in a VM instead of providing a fake
// implementation of the update protocol.
mux := http.NewServeMux()
mux.HandleFunc("/update/features", func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "not found", http.StatusNotFound)
})
mux.HandleFunc("/update/", func(w http.ResponseWriter, r *http.Request) {
// accept whatever for now.
var hash hash.Hash
switch r.Header.Get("X-Gokrazy-Update-Hash") {
case "crc32":
hash = crc32.NewIEEE()
default:
hash = sha256.New()
}
if _, err := io.Copy(hash, r.Body); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "%x", hash.Sum(nil))
})
mux.HandleFunc("/reboot", func(w http.ResponseWriter, r *http.Request) {
// you got it, boss!
})
mux.HandleFunc("/uploadtemp/", func(w http.ResponseWriter, r *http.Request) {
log.Printf("[HTTP] uploadtemp: %s", r.URL.Path)
})
mux.HandleFunc("/divert", func(w http.ResponseWriter, r *http.Request) {
log.Printf("[HTTP] divert: %s to %s",
r.FormValue("path"),
r.FormValue("diversion"))
})
mux.HandleFunc("/log", func(w http.ResponseWriter, r *http.Request) {
log.Printf("[HTTP] log: %s", r.FormValue("path"))
w.Header().Set("Content-type", "text/event-stream")
if r.FormValue("stream") == "stdout" {
const text = "Hello Sun"
line := fmt.Sprintf("data: %s\n", text)
if _, err := fmt.Fprintln(w, line); err != nil {
return
}
}
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
select {}
})
fakeBuildTimestamp := "fake-" + time.Now().Format(time.RFC3339)
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(strings.ToLower(r.Header.Get("Accept")), "application/json") {
status := struct {
BuildTimestamp string `json:"BuildTimestamp"`
}{
BuildTimestamp: fakeBuildTimestamp,
}
b, err := json.Marshal(&status)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(b)
return
}
http.Error(w, "handler not implemented", http.StatusNotImplemented)
})
srv := httptest.NewServer(mux)
u, err := url.Parse(srv.URL)
if err != nil {
t.Fatal(err)
}
// create a new instance
c := gok.Context{
Args: []string{
"--parent_dir", "gokrazy",
"-i", "hello",
"new",
},
}
t.Logf("running %q", append([]string{"<gok>"}, c.Args...))
if err := c.Execute(context.Background()); err != nil {
t.Fatalf("%v: %v", c.Args, err)
}
// update the instance config to speak to the test server
const configPath = "gokrazy/hello/config.json"
b, err := os.ReadFile(configPath)
if err != nil {
t.Fatal(err)
}
var cfg config.Struct
if err := json.Unmarshal(b, &cfg); err != nil {
t.Fatal(err)
}
cfg.Update.Hostname = "localhost"
cfg.Update.HTTPPort = u.Port()
t.Logf("Updated cfg.Update = %+v", cfg.Update)
b, err = cfg.FormatForFile()
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(configPath, b, 0644); err != nil {
t.Fatal(err)
}
// verify overwrite works (i.e. locates extrafiles)
ctx := context.WithValue(context.Background(), packer.BuildTimestampOverride, fakeBuildTimestamp)
c = gok.Context{
Args: []string{
"--parent_dir", "gokrazy",
"-i", "hello",
"update",
},
}
t.Logf("running %q", append([]string{"<gok>"}, c.Args...))
if err := c.Execute(ctx); err != nil {
t.Fatalf("%v: %v", c.Args, err)
}
}

View File

@@ -15,9 +15,10 @@ import (
func TestNonModuleFiles(t *testing.T) { func TestNonModuleFiles(t *testing.T) {
// Run this whole test in a throw-away temporary directory to not litter the // Run this whole test in a throw-away temporary directory to not litter the
// gokrazy/tools repository working copy. // gokrazy/tools repository working copy.
parent := t.TempDir() t.Chdir(t.TempDir())
// create a new instance // create a new instance
parent := t.TempDir()
c := gok.Context{ c := gok.Context{
Args: []string{ Args: []string{
"--parent_dir=" + parent, "--parent_dir=" + parent,

View File

@@ -13,22 +13,7 @@ import (
func TestRelativeParentDir(t *testing.T) { func TestRelativeParentDir(t *testing.T) {
// Run this whole test in a throw-away temporary directory to not litter the // Run this whole test in a throw-away temporary directory to not litter the
// gokrazy/tools repository working copy. // gokrazy/tools repository working copy.
t.Chdir(t.TempDir())
// TODO(go1.24): use t.Chdir()
oldwd, err := os.Open(".")
if err != nil {
t.Fatal(err)
}
if err := os.Chdir(t.TempDir()); err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
err := oldwd.Chdir()
oldwd.Close()
if err != nil {
t.Fatal(err)
}
})
// create a new instance // create a new instance
c := gok.Context{ c := gok.Context{

View File

@@ -151,7 +151,7 @@ func (r *overwriteImplConfig) run(ctx context.Context, args []string, stdout, st
Output: &output, Output: &output,
} }
pack.Main("gokrazy gok") pack.Main(ctx, "gokrazy gok")
return nil return nil
} }

View File

@@ -127,7 +127,7 @@ func (r *runImplConfig) run(ctx context.Context, args []string, stdout, stderr i
return err return err
} }
target, err := updater.NewTarget(updateBaseUrl.String(), httpClient) target, err := updater.NewTarget(ctx, updateBaseUrl.String(), httpClient)
if err != nil { if err != nil {
return fmt.Errorf("checking target partuuid support: %v", err) return fmt.Errorf("checking target partuuid support: %v", err)
} }
@@ -153,7 +153,7 @@ func (r *runImplConfig) run(ctx context.Context, args []string, stdout, stderr i
{ {
start := time.Now() start := time.Now()
err := target.Put("uploadtemp/gok-run/"+basename, io.TeeReader(f, &progress.Writer{})) err := target.Put(ctx, "uploadtemp/gok-run/"+basename, io.TeeReader(f, &progress.Writer{}))
if err != nil { if err != nil {
return fmt.Errorf("uploading temporary binary: %v", err) return fmt.Errorf("uploading temporary binary: %v", err)
} }
@@ -171,6 +171,7 @@ func (r *runImplConfig) run(ctx context.Context, args []string, stdout, stderr i
// /user/<basename>. Includes an automatic service restart. // /user/<basename>. Includes an automatic service restart.
{ {
err := target.Divert( err := target.Divert(
ctx,
"/user/"+basename, "/user/"+basename,
"gok-run/"+basename, "gok-run/"+basename,
cfg.PackageConfig[importPath].CommandLineFlags, cfg.PackageConfig[importPath].CommandLineFlags,

View File

@@ -80,7 +80,7 @@ func (r *sbomConfig) run(ctx context.Context, args []string, stdout, stderr io.W
Cfg: cfg, Cfg: cfg,
} }
sbomMarshaled, sbomWithHash, err := pack.GenerateSBOM() sbomMarshaled, sbomWithHash, err := pack.GenerateSBOM(ctx)
if os.IsNotExist(err) { if os.IsNotExist(err) {
// Common case, handle with a good error message // Common case, handle with a good error message
os.Stderr.WriteString("\n") os.Stderr.WriteString("\n")

View File

@@ -90,7 +90,7 @@ func (r *updateImplConfig) run(ctx context.Context, args []string, stdout, stder
Cfg: cfg, Cfg: cfg,
} }
pack.Main("gokrazy gok") pack.Main(ctx, "gokrazy gok")
return nil return nil
} }

View File

@@ -76,11 +76,7 @@ func init() {
instanceflag.RegisterPflags(vmRunCmd.Flags()) instanceflag.RegisterPflags(vmRunCmd.Flags())
} }
func (r *vmRunConfig) buildFullDiskImage(ctx context.Context, dest string) error { func (r *vmRunConfig) buildFullDiskImage(ctx context.Context, dest string, fileCfg *config.Struct) error {
fileCfg, err := config.ApplyInstanceFlag()
if err != nil {
return err
}
if r.arch != "" { if r.arch != "" {
os.Setenv("GOARCH", r.arch) os.Setenv("GOARCH", r.arch)
@@ -137,7 +133,7 @@ func (r *vmRunConfig) buildFullDiskImage(ctx context.Context, dest string) error
Output: &output, Output: &output,
} }
pack.Main("gokrazy gok") pack.Main(ctx, "gokrazy gok")
return nil return nil
} }
@@ -231,6 +227,22 @@ func (r *vmRunConfig) runQEMU(ctx context.Context, fullDiskImage string, extraAr
} }
func (r *vmRunConfig) run(ctx context.Context, args []string, stdout, stderr io.Writer) error { func (r *vmRunConfig) run(ctx context.Context, args []string, stdout, stderr io.Writer) error {
fileCfg, err := config.ApplyInstanceFlag()
if err != nil {
return err
}
if fileCfg.SerialConsole == "disabled" {
// The serial console is disabled by default:
// https://gokrazy.org/userguide/instance-config/#serialconsole
// 'gok vm run' currently launches QEMU such that
// there is a serial0 monitor available, but no HDMI.
// Hence, print a tip for how to get the serial console to work.
log.Printf("")
log.Printf(` Tip: Your config.json disables the serial console. Set "SerialConsole": "ttyAMA0,115200", then select View -> serial0 in QEMU to access an interactive shell for debugging.`)
log.Printf("")
}
f, err := os.CreateTemp("", "gokrazy-vm") f, err := os.CreateTemp("", "gokrazy-vm")
if err != nil { if err != nil {
return err return err
@@ -241,7 +253,7 @@ func (r *vmRunConfig) run(ctx context.Context, args []string, stdout, stderr io.
fdi := f.Name() fdi := f.Name()
log.Printf("building disk image") log.Printf("building disk image")
if !r.dry { if !r.dry {
if err := r.buildFullDiskImage(ctx, fdi); err != nil { if err := r.buildFullDiskImage(ctx, fdi, fileCfg); err != nil {
return err return err
} }
} }

View File

@@ -1,10 +1,11 @@
package packer package packer
import ( import (
"encoding/pem"
"os" "os"
"path/filepath" "path/filepath"
"github.com/breml/rootcerts/embedded" "golang.org/x/crypto/x509roots/fallback/bundle"
) )
func (pack *Pack) findSystemCertsPEM() (string, error) { func (pack *Pack) findSystemCertsPEM() (string, error) {
@@ -14,6 +15,7 @@ func (pack *Pack) findSystemCertsPEM() (string, error) {
defer func() { defer func() {
log.Printf("Loading system CA certificates from %s", source) log.Printf("Loading system CA certificates from %s", source)
}() }()
// On Linux, we can copy the operating systems certificate store. // On Linux, we can copy the operating systems certificate store.
// certFiles is defined in cacerts_linux.go (or defined as empty in // certFiles is defined in cacerts_linux.go (or defined as empty in
// cacertsstub.go on non-Linux): // cacertsstub.go on non-Linux):
@@ -37,7 +39,18 @@ func (pack *Pack) findSystemCertsPEM() (string, error) {
return string(b), nil return string(b), nil
} }
// Fall back to github.com/breml/rootcerts, i.e. the bundled Mozilla CA list: // Fall back to the x/crypto fallback bundle root certificates:
source = "bundled Mozilla CA list" source = "bundled x509roots/fallback/bundle"
return embedded.MozillaCACertificatesPEM(), nil return xrf(), nil
}
func xrf() string {
var certs []byte
for c := range bundle.Roots() {
certs = append(certs, pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: c.Certificate,
})...)
}
return string(certs)
} }

View File

@@ -37,6 +37,10 @@ import (
"github.com/gokrazy/updater" "github.com/gokrazy/updater"
) )
type contextKey int
var BuildTimestampOverride contextKey
const MB = 1024 * 1024 const MB = 1024 * 1024
type filePathAndModTime struct { type filePathAndModTime struct {
@@ -1099,7 +1103,7 @@ func filterGoEnv(env []string) []string {
return relevant return relevant
} }
func (pack *Pack) logicPrepare(programName string, sbomHook func(marshaled []byte, withHash SBOMWithHash)) error { func (pack *Pack) logicPrepare(ctx context.Context, programName string, sbomHook func(marshaled []byte, withHash SBOMWithHash)) error {
log := pack.Env.Logger() log := pack.Env.Logger()
cfg := pack.Cfg cfg := pack.Cfg
updateflag.SetUpdate(cfg.InternalCompatibilityFlags.Update) updateflag.SetUpdate(cfg.InternalCompatibilityFlags.Update)
@@ -1178,6 +1182,9 @@ func (pack *Pack) logicPrepare(programName string, sbomHook func(marshaled []byt
log.Printf("Build target: %s", strings.Join(filterGoEnv(packer.Env()), " ")) log.Printf("Build target: %s", strings.Join(filterGoEnv(packer.Env()), " "))
pack.buildTimestamp = time.Now().Format(time.RFC3339) pack.buildTimestamp = time.Now().Format(time.RFC3339)
if ts, ok := ctx.Value(BuildTimestampOverride).(string); ok {
pack.buildTimestamp = ts
}
log.Printf("Build timestamp: %s", pack.buildTimestamp) log.Printf("Build timestamp: %s", pack.buildTimestamp)
systemCertsPEM, err := pack.findSystemCertsPEM() systemCertsPEM, err := pack.findSystemCertsPEM()
@@ -1630,7 +1637,7 @@ func (pack *Pack) logicBuild(programName string, sbomHook func(marshaled []byte,
return nil return nil
} }
func (pack *Pack) logic(programName string, sbomHook func(marshaled []byte, withHash SBOMWithHash)) error { func (pack *Pack) logic(ctx context.Context, programName string, sbomHook func(marshaled []byte, withHash SBOMWithHash)) error {
dnsCheck := make(chan error) dnsCheck := make(chan error)
go func() { go func() {
defer close(dnsCheck) defer close(dnsCheck)
@@ -1646,7 +1653,7 @@ func (pack *Pack) logic(programName string, sbomHook func(marshaled []byte, with
dnsCheck <- nil dnsCheck <- nil
}() }()
if err := pack.logicPrepare(programName, sbomHook); err != nil { if err := pack.logicPrepare(ctx, programName, sbomHook); err != nil {
return err return err
} }
@@ -1669,6 +1676,7 @@ func (pack *Pack) logic(programName string, sbomHook func(marshaled []byte, with
} }
func (pack *Pack) logicWrite(programName string, sbomHook func(marshaled []byte, withHash SBOMWithHash), bindir string, dnsCheck chan error) error { func (pack *Pack) logicWrite(programName string, sbomHook func(marshaled []byte, withHash SBOMWithHash), bindir string, dnsCheck chan error) error {
ctx := context.Background()
log := pack.Env.Logger() log := pack.Env.Logger()
var ( var (
@@ -1718,7 +1726,7 @@ func (pack *Pack) logicWrite(programName string, sbomHook func(marshaled []byte,
} }
updateBaseUrl.Path = "/" updateBaseUrl.Path = "/"
target, err = updater.NewTarget(updateBaseUrl.String(), updateHttpClient) target, err = updater.NewTarget(ctx, updateBaseUrl.String(), updateHttpClient)
if err != nil { if err != nil {
return fmt.Errorf("checking target partuuid support: %v", err) return fmt.Errorf("checking target partuuid support: %v", err)
} }
@@ -2035,7 +2043,7 @@ func (pack *Pack) logicWrite(programName string, sbomHook func(marshaled []byte,
return err return err
} }
if err := target.StreamTo("mbr", mbrReader); err != nil { if err := target.StreamTo(ctx, "mbr", mbrReader); err != nil {
if err == updater.ErrUpdateHandlerNotImplemented { if err == updater.ErrUpdateHandlerNotImplemented {
log.Printf("target does not support updating MBR yet, ignoring") log.Printf("target does not support updating MBR yet, ignoring")
} else { } else {
@@ -2044,11 +2052,11 @@ func (pack *Pack) logicWrite(programName string, sbomHook func(marshaled []byte,
} }
if cfg.InternalCompatibilityFlags.Testboot { if cfg.InternalCompatibilityFlags.Testboot {
if err := target.Testboot(); err != nil { if err := target.Testboot(ctx); err != nil {
return fmt.Errorf("enable testboot of non-active partition: %v", err) return fmt.Errorf("enable testboot of non-active partition: %v", err)
} }
} else { } else {
if err := target.Switch(); err != nil { if err := target.Switch(ctx); err != nil {
return fmt.Errorf("switching to non-active partition: %v", err) return fmt.Errorf("switching to non-active partition: %v", err)
} }
} }
@@ -2057,7 +2065,7 @@ func (pack *Pack) logicWrite(programName string, sbomHook func(marshaled []byte,
canc() canc()
log.Printf("Triggering reboot") log.Printf("Triggering reboot")
if err := target.Reboot(); err != nil { if err := target.Reboot(ctx); err != nil {
if errors.Is(err, syscall.ECONNRESET) { if errors.Is(err, syscall.ECONNRESET) {
log.Printf("ignoring reboot error: %v", err) log.Printf("ignoring reboot error: %v", err)
} else { } else {
@@ -2159,6 +2167,7 @@ func (pack *Pack) validateTargetArchMatchesKernel() error {
} }
func (pack *Pack) updateWithProgress(prog *progress.Reporter, reader io.Reader, target *updater.Target, logStr string, stream string) error { func (pack *Pack) updateWithProgress(prog *progress.Reporter, reader io.Reader, target *updater.Target, logStr string, stream string) error {
ctx := context.Background()
log := pack.Env.Logger() log := pack.Env.Logger()
start := time.Now() start := time.Now()
@@ -2170,7 +2179,7 @@ func (pack *Pack) updateWithProgress(prog *progress.Reporter, reader io.Reader,
prog.SetTotal(uint64(st.Size())) prog.SetTotal(uint64(st.Size()))
} }
} }
if err := target.StreamTo(stream, io.TeeReader(reader, &progress.Writer{})); err != nil { if err := target.StreamTo(ctx, stream, io.TeeReader(reader, &progress.Writer{})); err != nil {
return fmt.Errorf("updating %s: %w", logStr, err) return fmt.Errorf("updating %s: %w", logStr, err)
} }
duration := time.Since(start) duration := time.Since(start)
@@ -2184,17 +2193,17 @@ func (pack *Pack) updateWithProgress(prog *progress.Reporter, reader io.Reader,
return nil return nil
} }
func (pack *Pack) Main(programName string) { func (pack *Pack) Main(ctx context.Context, programName string) {
if err := pack.logic(programName, nil); err != nil { if err := pack.logic(ctx, programName, nil); err != nil {
fmt.Fprintf(os.Stderr, "ERROR:\n %s\n", err) fmt.Fprintf(os.Stderr, "ERROR:\n %s\n", err)
os.Exit(1) os.Exit(1)
} }
} }
func (pack *Pack) GenerateSBOM() ([]byte, SBOMWithHash, error) { func (pack *Pack) GenerateSBOM(ctx context.Context) ([]byte, SBOMWithHash, error) {
var sbom []byte var sbom []byte
var sbomWithHash SBOMWithHash var sbomWithHash SBOMWithHash
if err := pack.logic("gokrazy gok", func(b []byte, wh SBOMWithHash) { if err := pack.logic(ctx, "gokrazy gok", func(b []byte, wh SBOMWithHash) {
sbom = b sbom = b
sbomWithHash = wh sbomWithHash = wh
}); err != nil { }); err != nil {

View File

@@ -19,7 +19,7 @@ func pollUpdated1(ctx context.Context, updateHttpClient *http.Client, updateBase
return err return err
} }
req = req.WithContext(ctx) req = req.WithContext(ctx)
req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json")
resp, err := updateHttpClient.Do(req) resp, err := updateHttpClient.Do(req)
if err != nil { if err != nil {
return err return err