diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..7da4a3e --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: tests + +on: + push: + +permissions: + contents: read + +jobs: + tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: stable + + - run: make verify + - run: make test diff --git a/.golangci.yaml b/.golangci.yaml index cf4cfd3..ad21c62 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -16,9 +16,7 @@ linters: - errchkjson - errname - errorlint - - execinquery - exhaustive - - exportloopref - forcetypeassert - ginkgolinter - gocheckcompilerdirectives diff --git a/Makefile b/Makefile index 0e268e6..fd2b349 100644 --- a/Makefile +++ b/Makefile @@ -8,13 +8,13 @@ help: ## display Makefile's help. .PHONY: build build: ## build qubesome to the path set on TARGET_BIN. - go build -trimpath -tags 'netgo,osusergo,static_build' -ldflags '-extldflags -static -s -w' -o $(TARGET_BIN) main.go + go build -trimpath -tags 'netgo,osusergo,static_build' -ldflags '-extldflags -static -s -w' -o $(TARGET_BIN) cmd/qubesome/main.go .PHONY: test test: ## run golang tests. - go test -race ./... + go test -race -parallel 10 ./... -validate: validate-lint validate-dirty ## Run validation checks. +verify: verify-lint verify-dirty ## Run verification checks. -validate-lint: $(GOLANGCI) +verify-lint: $(GOLANGCI) $(GOLANGCI) run diff --git a/README.md b/README.md index 68b49e3..54b1cae 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ storage, etc). #### Install ##### Using Go ``` -go install github.com/qubesome/cli@latest +go install github.com/qubesome/cli/cmd/qubesome@latest ``` ##### For Tumbleweed users @@ -73,7 +73,7 @@ For more information on each command, run `qubesome --help`. #### Minimum -Qubesome requires `docker` and `xrandr` installed on a machine +Qubesome requires `docker` (or `podman`) and `xrandr` installed on a machine running Xorg. To install them using zypper: ``` sudo zypper install -y docker xrandr diff --git a/cmd/console_test.go b/cmd/console_test.go index bef9f08..ed88b55 100644 --- a/cmd/console_test.go +++ b/cmd/console_test.go @@ -2,6 +2,7 @@ package cmd import ( "embed" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -18,6 +19,10 @@ func TestCommandWiring(t *testing.T) { c := newConsole() for _, entry := range entries { + if strings.EqualFold(entry.Name(), "qubesome") { + continue + } + if !entry.IsDir() { continue } diff --git a/main.go b/cmd/qubesome/main.go similarity index 100% rename from main.go rename to cmd/qubesome/main.go diff --git a/cmd/root.go b/cmd/root.go index 3e7efd9..83bbd37 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -5,6 +5,7 @@ import ( "log/slog" "os" "runtime" + "strings" "github.com/qubesome/cli/internal/command" "github.com/qubesome/cli/internal/log" @@ -17,6 +18,13 @@ var ( ConsoleApp command.App = newConsole() ) +func init() { //nolint + val := os.Getenv("QS_DEBUG") + if strings.EqualFold(val, "true") { + DefaultLogLevel = "DEBUG" + } +} + func Exec(args []string) { if runtime.GOOS != "linux" { fmt.Println("unsupported OS:", runtime.GOOS) diff --git a/go.mod b/go.mod index e18d84c..7f6bb11 100644 --- a/go.mod +++ b/go.mod @@ -1,35 +1,35 @@ module github.com/qubesome/cli -go 1.22.4 +go 1.23.3 require ( - github.com/cyphar/filepath-securejoin v0.3.0 - github.com/go-git/go-git/v5 v5.12.0 + github.com/cyphar/filepath-securejoin v0.3.4 + github.com/go-git/go-git/v5 v5.12.1-0.20241115094014-70dd9f8347eb github.com/google/uuid v1.6.0 github.com/stretchr/testify v1.9.0 - golang.org/x/sys v0.22.0 + golang.org/x/sys v0.27.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - dario.cat/mergo v1.0.0 // indirect + dario.cat/mergo v1.0.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProtonMail/go-crypto v1.0.0 // indirect - github.com/cloudflare/circl v1.3.9 // indirect + github.com/ProtonMail/go-crypto v1.1.2 // indirect + github.com/cloudflare/circl v1.5.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.5.0 // indirect + github.com/go-git/go-billy/v5 v5.6.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect - github.com/skeema/knownhosts v1.2.2 // indirect + github.com/skeema/knownhosts v1.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - golang.org/x/crypto v0.25.0 // indirect - golang.org/x/net v0.27.0 // indirect + golang.org/x/crypto v0.29.0 // indirect + golang.org/x/net v0.31.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index dd3fae0..b1ef886 100644 --- a/go.sum +++ b/go.sum @@ -1,37 +1,35 @@ -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= -github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v1.1.2 h1:A7JbD57ThNqh7XjmHE+PXpQ3Dqt3BrSAC0AL0Go3KS0= +github.com/ProtonMail/go-crypto v1.1.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= -github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= -github.com/cyphar/filepath-securejoin v0.3.0 h1:tXpmbiaeBrS/K2US8nhgwdKYnfAOnVfkcLPKFgFHeA0= -github.com/cyphar/filepath-securejoin v0.3.0/go.mod h1:F7i41x/9cBF7lzCrVsYs9fuzwRZm4NQsGTBdpp6mETc= +github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys= +github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8= +github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= -github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy v0.0.0-20240618083138-03be62527ccb h1:2SoxRauy2IqekRMggrQk3yNI5X6omSnk6ugVbFywwXs= +github.com/elazarl/goproxy v0.0.0-20240618083138-03be62527ccb/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= -github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= +github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8= +github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= -github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= +github.com/go-git/go-git/v5 v5.12.1-0.20241115094014-70dd9f8347eb h1:TEo1aHmTS/QU1IIYczLGhqUEp8m03a7bk3ZdRHoUonk= +github.com/go-git/go-git/v5 v5.12.1-0.20241115094014-70dd9f8347eb/go.mod h1:KECzDiPamjQz6lBAKQI+cIhdDfUcb64jyErZarVFKIE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -49,8 +47,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -62,8 +60,8 @@ github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUz github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= -github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= +github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= @@ -73,64 +71,29 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= +golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/hack/base.mk b/hack/base.mk index df5561d..fb96e4a 100644 --- a/hack/base.mk +++ b/hack/base.mk @@ -1,4 +1,4 @@ -GOLANGCI_VERSION ?= v1.57.2 +GOLANGCI_VERSION ?= v1.62.0 TOOLS_BIN := $(shell mkdir -p build/tools && realpath build/tools) GOLANGCI = $(TOOLS_BIN)/golangci-lint-$(GOLANGCI_VERSION) @@ -16,7 +16,7 @@ GOBIN=$(TOOLS_BIN) go install $(2) ;\ } endef -validate-dirty: +verify-dirty: ifneq ($(shell git status --porcelain --untracked-files=no),) @echo worktree is dirty @git --no-pager status diff --git a/internal/deps/deps.go b/internal/deps/deps.go index cfb917d..da2b222 100644 --- a/internal/deps/deps.go +++ b/internal/deps/deps.go @@ -14,16 +14,16 @@ var deps map[string][]string = map[string][]string{ files.ShBinary, }, "run": { - files.DockerBinary, + files.ContainerRunnerBinary, }, "xdg-open": { - files.DockerBinary, + files.ContainerRunnerBinary, }, "images": { - files.DockerBinary, + files.ContainerRunnerBinary, }, "profiles": { - files.DockerBinary, + files.ContainerRunnerBinary, files.ShBinary, files.XrandrBinary, }, diff --git a/internal/files/binaries.go b/internal/files/binaries.go index 8a7c16b..b770f16 100644 --- a/internal/files/binaries.go +++ b/internal/files/binaries.go @@ -1,9 +1,30 @@ package files +import ( + "fmt" + "os/exec" +) + +func init() { //nolint + p, err := exec.LookPath("podman") + if err != nil { + p2, err := exec.LookPath("docker") + if err == nil { + fmt.Println("falling back to docker") + ContainerRunnerBinary = p2 + } + } + + ContainerRunnerBinary = p +} + +var ( + ContainerRunnerBinary = "/usr/bin/podman" +) + const ( ShBinary = "/bin/sh" XclipBinary = "/usr/bin/xclip" - DockerBinary = "/usr/bin/docker" FireCrackerBinary = "/usr/bin/firecracker" XrandrBinary = "/usr/bin/xrandr" DbusBinary = "/usr/bin/dbus-send" diff --git a/internal/images/images.go b/internal/images/images.go index 4ee2e3d..e42bf15 100644 --- a/internal/images/images.go +++ b/internal/images/images.go @@ -1,9 +1,7 @@ package images import ( - "errors" "fmt" - "io/fs" "log/slog" "os" "sync" @@ -53,7 +51,7 @@ func pullExpired() (bool, error) { fn := files.ImagesLastCheckedPath() fi, err := os.Stat(fn) if err != nil { - if !errors.Is(err, fs.ErrNotExist) { + if !os.IsNotExist(err) { return false, fmt.Errorf("cannot stat %q: %w", fn, err) } if err := os.WriteFile(fn, []byte{}, files.FileMode); err != nil { @@ -76,6 +74,19 @@ func pullExpired() (bool, error) { return false, nil } +func PreemptWorkloadImages(cfg *types.Config) { + slog.Debug("Check need for the preemptive pull of workload images") + fn := files.ImagesLastCheckedPath() + + _, err := os.Stat(fn) + if err != nil && os.IsNotExist(err) { + fmt.Println("INFO: Preemptively pulling workload images. This only happens on first execution and aims to avoid delays opening apps.") + + _ = PullAll(cfg) + _ = os.WriteFile(fn, []byte{}, files.FileMode) + } +} + func PullAll(cfg *types.Config) error { wf, err := cfg.WorkloadFiles() if err != nil { @@ -123,7 +134,7 @@ func PullAll(cfg *types.Config) error { func PullImage(image string) error { slog.Info("pulling container image", "image", image) - cmd := execabs.Command(files.DockerBinary, "pull", image) //nolint + cmd := execabs.Command(files.ContainerRunnerBinary, "pull", image) //nolint cmd.Stdout = os.Stdout return cmd.Run() @@ -131,7 +142,7 @@ func PullImage(image string) error { func PullImageIfNotPresent(image string) error { slog.Debug("checking if container image is present", "image", image) - cmd := execabs.Command(files.DockerBinary, "images", "-q", image) //nolint + cmd := execabs.Command(files.ContainerRunnerBinary, "images", "-q", image) //nolint out, err := cmd.Output() if len(out) > 0 && err == nil { diff --git a/internal/profiles/profiles.go b/internal/profiles/profiles.go index be75126..5930960 100644 --- a/internal/profiles/profiles.go +++ b/internal/profiles/profiles.go @@ -74,8 +74,15 @@ func validGitDir(path string) bool { func StartFromGit(name, gitURL, path, local string) error { ln := files.ProfileConfig(name) + if _, err := os.Lstat(ln); err == nil { - return fmt.Errorf("profile %q is already started", name) + // Wayland is not cleaning up profile state after closure. + if !strings.EqualFold(os.Getenv("XDG_SESSION_TYPE"), "wayland") { + return fmt.Errorf("profile %q is already started", name) + } + if err = os.Remove(ln); err != nil { + return fmt.Errorf("failed to remove leftover profile symlink: %w", err) + } } dir, err := files.GitDirPath(gitURL) @@ -177,11 +184,18 @@ func Start(profile *types.Profile, cfg *types.Config) (err error) { return err } + fi, err := os.Lstat(files.ContainerRunnerBinary) + if err != nil || !fi.Mode().IsRegular() { + return fmt.Errorf("could not find docker or podman") + } + err = images.PullImageIfNotPresent(profile.Image) if err != nil { return fmt.Errorf("cannot pull profile image: %w", err) } + go images.PreemptWorkloadImages(cfg) + if profile.Gpus != "" { if !gpu.Supported() { profile.Gpus = "" @@ -225,6 +239,7 @@ func Start(profile *types.Profile, cfg *types.Config) (err error) { err = err1 } } + wg.Done() }() @@ -244,36 +259,33 @@ func Start(profile *types.Profile, cfg *types.Config) (err error) { return err } - name := fmt.Sprintf(ContainerNameFormat, profile.Name) + // In Wayland, Xephyr is replaced by xwayland-run, which can + // run the Window Manager directly, without the need of a exec + // into the container to trigger it. + if !strings.EqualFold(os.Getenv("XDG_SESSION_TYPE"), "wayland") { + name := fmt.Sprintf(ContainerNameFormat, profile.Name) - // If xhost access control is enabled, it may block qubesome - // execution. A tail sign is the profile container dying early. - if !containerRunning(name) { - msg := os.ExpandEnv("run xhost +SI:localhost:${USER} and try again") - dbus.NotifyOrLog("qubesome start error", msg) - return fmt.Errorf("failed to start profile: %s", msg) - } + // If xhost access control is enabled, it may block qubesome + // execution. A tail sign is the profile container dying early. + if !containerRunning(name) { + msg := os.ExpandEnv("run xhost +SI:localhost:${USER} and try again") + dbus.NotifyOrLog("qubesome start error", msg) + return fmt.Errorf("failed to start profile: %s", msg) + } - if !profile.Dbus { - err = startDbus(name) + err = startWindowManager(name, strconv.Itoa(int(profile.Display)), profile.WindowManager) if err != nil { return err } } - err = startWindowManager(name, strconv.Itoa(int(profile.Display)), profile.WindowManager) - if err != nil { - return err - } - wg.Wait() return nil } func containerRunning(name string) bool { args := fmt.Sprintf("ps -q -f name=%s", name) - fmt.Println(args) - cmd := execabs.Command(files.DockerBinary, //nolint:gosec + cmd := execabs.Command(files.ContainerRunnerBinary, //nolint:gosec strings.Split(args, " ")...) out, err := cmd.Output() @@ -330,30 +342,8 @@ func createMagicCookie(profile *types.Profile) error { func startWindowManager(name, display, wm string) error { args := []string{"exec", name, files.ShBinary, "-c", fmt.Sprintf("DISPLAY=:%s %s", display, wm)} - slog.Debug(files.DockerBinary+" exec", "container-name", name, "args", args) - cmd := execabs.Command(files.DockerBinary, args...) //nolint - - output, err := cmd.CombinedOutput() - if err != nil { - return fmt.Errorf("%s: %w", output, err) - } - return nil -} - -func startDbus(name string) error { - args := []string{ - "exec", - "--detach", - name, - "/usr/bin/dbus-daemon", - "--session", - "--fork", - "--nopidfile", - "--address=unix:path=/run/user/1000/bus", - } - - slog.Debug(files.DockerBinary+" exec", "container-name", name, "args", args) - cmd := execabs.Command(files.DockerBinary, args...) //nolint + slog.Debug(files.ContainerRunnerBinary+" exec", "container-name", name, "args", args) + cmd := execabs.Command(files.ContainerRunnerBinary, args...) //nolint output, err := cmd.CombinedOutput() if err != nil { @@ -383,6 +373,21 @@ func createNewDisplay(profile *types.Profile, display string) error { cArgs = append(cArgs, strings.Split(profile.XephyrArgs, " ")...) } + if strings.EqualFold(os.Getenv("XDG_SESSION_TYPE"), "wayland") { + command = "xwayland-run" + cArgs = []string{ + "-host-grab", + "-geometry", res, + "-extension", "MIT-SHM", + "-extension", "XTEST", + "-nopn", + "-tst", + "-nolisten", "tcp", + "-auth", "/home/xorg-user/.Xserver", + "--", + strings.TrimPrefix(profile.WindowManager, "exec ")} + } + server, err := files.ServerCookiePath(profile.Name) if err != nil { return err @@ -429,8 +434,8 @@ func createNewDisplay(profile *types.Profile, display string) error { paths = append(paths, "-v=/etc/localtime:/etc/localtime:ro") paths = append(paths, "-v=/tmp/.X11-unix:/tmp/.X11-unix:rw") paths = append(paths, fmt.Sprintf("-v=%s:/tmp/qube.sock:ro", socket)) - paths = append(paths, fmt.Sprintf("-v=%s:/home/xorg-user/.Xserver:ro", server)) - paths = append(paths, fmt.Sprintf("-v=%s:/home/xorg-user/.Xauthority:ro", workload)) + paths = append(paths, fmt.Sprintf("-v=%s:/home/xorg-user/.Xserver", server)) + paths = append(paths, fmt.Sprintf("-v=%s:/home/xorg-user/.Xauthority", workload)) paths = append(paths, fmt.Sprintf("-v=%s:/usr/local/bin/qubesome:ro", binPath)) for _, p := range profile.Paths { @@ -443,9 +448,30 @@ func createNewDisplay(profile *types.Profile, display string) error { "-d", // rely on currently set DISPLAY. "-e", "DISPLAY", + "-e", "XDG_SESSION_TYPE=X11", + "--device", "/dev/dri", "--security-opt=no-new-privileges:true", "--cap-drop=ALL", } + + if strings.HasSuffix(files.ContainerRunnerBinary, "podman") { + dockerArgs = append(dockerArgs, "--userns=keep-id") + } + if strings.EqualFold(os.Getenv("XDG_SESSION_TYPE"), "wayland") { + fmt.Println("WARN: running qubesome in Wayland (experimental)") + xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR") + if xdgRuntimeDir == "" { + uid := os.Getuid() + if uid < 1000 { + return fmt.Errorf("qubesome does not support running under privileged users") + } + xdgRuntimeDir = "/run/user/" + strconv.Itoa(uid) + } + + // TODO: Investigate ways to avoid sharing /run/user/1000 on Wayland. + dockerArgs = append(dockerArgs, "-e XDG_RUNTIME_DIR") + dockerArgs = append(dockerArgs, "-v="+xdgRuntimeDir+":/run/user/1000") + } if profile.HostAccess.Gpus != "" { dockerArgs = append(dockerArgs, "--gpus", profile.HostAccess.Gpus) } @@ -498,8 +524,12 @@ func createNewDisplay(profile *types.Profile, display string) error { dockerArgs = append(dockerArgs, command) dockerArgs = append(dockerArgs, cArgs...) + fmt.Println( + "INFO: For best experience use input grabber shortcuts:", + grabberShortcut()) + slog.Debug("exec: docker", "args", dockerArgs) - cmd := execabs.Command(files.DockerBinary, dockerArgs...) //nolint + cmd := execabs.Command(files.ContainerRunnerBinary, dockerArgs...) //nolint output, err := cmd.CombinedOutput() if err != nil { @@ -508,6 +538,14 @@ func createNewDisplay(profile *types.Profile, display string) error { return nil } +func grabberShortcut() string { + if strings.EqualFold(os.Getenv("XDG_SESSION_TYPE"), "wayland") { + return " + " + } + + return " + " +} + func setupRunUserDir(dir string) error { err := os.MkdirAll(dir, files.DirMode) if err != nil { diff --git a/internal/runners/docker/run.go b/internal/runners/docker/run.go index b81532d..f1a4fe5 100644 --- a/internal/runners/docker/run.go +++ b/internal/runners/docker/run.go @@ -6,6 +6,7 @@ import ( "log/slog" "os" "path/filepath" + "strconv" "strings" "github.com/qubesome/cli/internal/env" @@ -52,7 +53,7 @@ x-scheme-handler/snap=snap-handle-link.desktop; func ContainerID(name string) (string, bool) { args := fmt.Sprintf("ps -a -q -f name=%s", name) - cmd := execabs.Command(files.DockerBinary, //nolint:gosec + cmd := execabs.Command(files.ContainerRunnerBinary, //nolint:gosec strings.Split(args, " ")...) out, err := cmd.Output() @@ -69,8 +70,8 @@ func exec(id string, ew types.EffectiveWorkload) error { args := []string{"exec", "--detach", id, ew.Workload.Command} args = append(args, ew.Workload.Args...) - slog.Debug(files.DockerBinary+" exec", "container-id", id, "cmd", ew.Workload.Command, "args", ew.Workload.Args) - cmd := execabs.Command(files.DockerBinary, args...) //nolint + slog.Debug(files.ContainerRunnerBinary+" exec", "container-id", id, "cmd", ew.Workload.Command, "args", ew.Workload.Args) + cmd := execabs.Command(files.ContainerRunnerBinary, args...) //nolint return cmd.Run() } @@ -116,7 +117,12 @@ func Run(ew types.EffectiveWorkload) error { "--rm", "-d", "--security-opt=seccomp=unconfined", - "--security-opt=no-new-privileges:true", + "--security-opt=no-new-privileges=true", + "--group-add=keep-groups", + } + + if strings.HasSuffix(files.ContainerRunnerBinary, "podman") { + args = append(args, "--userns=keep-id") } if ew.Workload.User != nil { @@ -149,36 +155,55 @@ func Run(ew types.EffectiveWorkload) error { args = append(args, cameraParams()...) } - if wl.HostAccess.Dbus || wl.HostAccess.Bluetooth || wl.HostAccess.VarRunUser { - args = append(args, "-v=/run/user/1000:/run/user/1000") - } + display := ew.Profile.Display + if strings.EqualFold(os.Getenv("XDG_SESSION_TYPE"), "wayland") { //nolint + fmt.Println("WARN: running qubesome in Wayland (experimental)") + display = 0 + + xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR") + if xdgRuntimeDir == "" { + uid := os.Getuid() + if uid < 1000 { + return fmt.Errorf("qubesome does not support running under privileged users") + } + xdgRuntimeDir = "/run/user/" + strconv.Itoa(uid) + } - userDir, err := files.IsolatedRunUserPath(ew.Profile.Name) - if err != nil { - return fmt.Errorf("failed to get isolated /user path: %w", err) - } - paths = append(paths, fmt.Sprintf("-v=%s:/dev/shm", filepath.Join(userDir, "shm"))) - if wl.HostAccess.Dbus || wl.HostAccess.Bluetooth || wl.HostAccess.VarRunUser { - args = append(args, hostDbusParams()...) + // TODO: Investigate ways to avoid sharing /run/user/1000 on Wayland. + args = append(args, "-e XDG_RUNTIME_DIR") + args = append(args, "-v="+xdgRuntimeDir+":/run/user/1000") } else { - paths = append(paths, fmt.Sprintf("-v=%s:/run/user/1000", userDir)) + if wl.HostAccess.Dbus || wl.HostAccess.Bluetooth || wl.HostAccess.VarRunUser { + args = append(args, "-v=/run/user/1000:/run/user/1000:z") + } - machineIDPath := filepath.Join(files.ProfileDir(ew.Profile.Name), "machine-id") - paths = append(paths, fmt.Sprintf("-v=%s:/etc/machine-id:ro", machineIDPath)) + userDir, err := files.IsolatedRunUserPath(ew.Profile.Name) + if err != nil { + return fmt.Errorf("failed to get isolated /user path: %w", err) + } + paths = append(paths, fmt.Sprintf("-v=%s:/dev/shm", filepath.Join(userDir, "shm"))) + if wl.HostAccess.Dbus || wl.HostAccess.Bluetooth || wl.HostAccess.VarRunUser { + args = append(args, hostDbusParams()...) + } else { + paths = append(paths, fmt.Sprintf("-v=%s:/run/user/1000:z", userDir)) + + machineIDPath := filepath.Join(files.ProfileDir(ew.Profile.Name), "machine-id") + paths = append(paths, fmt.Sprintf("-v=%s:/etc/machine-id:ro", machineIDPath)) + } } args = append(args, paths...) args = append(args, "--device=/dev/dri") // Display is used for all qubesome applications. - args = append(args, fmt.Sprintf("-e=DISPLAY=:%d", ew.Profile.Display)) + args = append(args, fmt.Sprintf("-e=DISPLAY=:%d", display)) pp, err := files.ClientCookiePath(ew.Profile.Name) if err != nil { return err } args = append(args, fmt.Sprintf("-v=%s:/tmp/.Xauthority:ro", pp)) args = append(args, "-e=XAUTHORITY=/tmp/.Xauthority") - args = append(args, fmt.Sprintf("-v=/tmp/.X11-unix/X%[1]d:/tmp/.X11-unix/X%[1]d", ew.Profile.Display)) + args = append(args, fmt.Sprintf("-v=/tmp/.X11-unix/X%[1]d:/tmp/.X11-unix/X%[1]d", display)) args = append(args, fmt.Sprintf("-e=QUBESOME_PROFILE=%s", ew.Profile.Name)) if ew.Profile.Timezone != "" { @@ -272,15 +297,15 @@ func Run(ew types.EffectiveWorkload) error { } dst := ps[1] - args = append(args, fmt.Sprintf("-v=%s:%s", src, dst)) + args = append(args, fmt.Sprintf("-v=%s:%s:z", src, dst)) } args = append(args, wl.Image) args = append(args, wl.Command) args = append(args, wl.Args...) - slog.Debug(fmt.Sprintf("exec: %s", files.DockerBinary), "args", args) - cmd := execabs.Command(files.DockerBinary, args...) //nolint + slog.Debug(fmt.Sprintf("exec: %s", files.ContainerRunnerBinary), "args", args) + cmd := execabs.Command(files.ContainerRunnerBinary, args...) //nolint cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin @@ -292,8 +317,8 @@ func Run(ew types.EffectiveWorkload) error { func getHomeDir(image string) (string, error) { args := []string{"run", "--rm", image, "ls", "/home"} - slog.Debug(files.DockerBinary + " " + strings.Join(args, " ")) - cmd := execabs.Command(files.DockerBinary, args...) //nolint + slog.Debug(files.ContainerRunnerBinary + " " + strings.Join(args, " ")) + cmd := execabs.Command(files.ContainerRunnerBinary, args...) //nolint out, err := cmd.Output() if err != nil { @@ -305,9 +330,9 @@ func getHomeDir(image string) (string, error) { func hostDbusParams() []string { return []string{ - "-v=/run/dbus/system_bus_socket:/run/dbus/system_bus_socket", - "-v=/var/lib/dbus:/var/lib/dbus", - "-v=/usr/share/dbus-1:/usr/share/dbus-1", + "-v=/run/dbus/system_bus_socket:/run/dbus/system_bus_socket:z", + "-v=/var/lib/dbus:/var/lib/dbus:z", + "-v=/usr/share/dbus-1:/usr/share/dbus-1:z", // At the moment we are mapping /run/user/1000 when // the host Dbus is being used. Therefore, there is no // point in mounting descending dirs. @@ -322,7 +347,7 @@ func hostDbusParams() []string { func cameraParams() []string { params := []string{ - "--group-add=video", + // "--group-add=video", } vds, _ := filepath.Glob("/dev/video*") @@ -336,8 +361,8 @@ func cameraParams() []string { func audioParams() []string { return []string{ // TODO: For Bluetooth (Apple AirPods) you may require /run/user/1000 shared via VarRunUser - "-v=/run/user/1000/pipewire-0:/run/user/1000/pipewire-0", + "-v=/run/user/1000/pipewire-0:/run/user/1000/pipewire-0:z", "--device=/dev/snd", - "--group-add=audio", + // "--group-add=audio", } } diff --git a/internal/runners/firecracker/deps.go b/internal/runners/firecracker/deps.go index e6486ac..0fef097 100644 --- a/internal/runners/firecracker/deps.go +++ b/internal/runners/firecracker/deps.go @@ -74,7 +74,7 @@ func ensureDependencies(img string) error { func setupTaps(img string) error { slog.Info("setting up taps") - cmd := execabs.Command(files.DockerBinary, //nolint + cmd := execabs.Command(files.ContainerRunnerBinary, //nolint "run", "--rm", "--privileged", "--network", "host", img, diff --git a/internal/runners/firecracker/run.go b/internal/runners/firecracker/run.go index b8caf44..f75653f 100644 --- a/internal/runners/firecracker/run.go +++ b/internal/runners/firecracker/run.go @@ -23,7 +23,7 @@ type configParams struct { func createRootFs(dir, img string) (string, error) { slog.Info("creating root fs") rootfs := filepath.Join(dir, "roofs.ext4") - cmd := execabs.Command(files.DockerBinary, //nolint + cmd := execabs.Command(files.ContainerRunnerBinary, //nolint "run", "--rm", "--privileged", "-v", "/tmp/:/tmp/", img, diff --git a/internal/types/config.go b/internal/types/config.go index f005c40..6e1f7fa 100644 --- a/internal/types/config.go +++ b/internal/types/config.go @@ -187,6 +187,8 @@ func LoadConfig(path string) (*Config, error) { return nil, fmt.Errorf("cannot unmarshal qubesome config %q: %w", path, err) } + cfg.RootDir = filepath.Dir(path) + // To avoid names being defined twice on the profiles, the name // is only defined when referring to a profile which results // on the .name field of Profiles not being populated. diff --git a/internal/util/dbus/dbus.go b/internal/util/dbus/dbus.go index 0f31e31..5f977f2 100644 --- a/internal/util/dbus/dbus.go +++ b/internal/util/dbus/dbus.go @@ -4,6 +4,7 @@ import ( "fmt" "log/slog" "os" + "strings" "github.com/qubesome/cli/internal/files" "golang.org/x/sys/execabs" @@ -57,6 +58,10 @@ func Notify(title, body string) error { } func NotifyOrLog(title, body string) { + if strings.EqualFold(os.Getenv("XDG_SESSION_TYPE"), "wayland") { + slog.Error("logging notification (sending not supported on wayland)", "title", title, "body", body) + } + err := Notify(title, body) if err != nil { slog.Error("cannot send notification", "error", err, "notification", body)