Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bundling app into Flatpak #72

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
Draft
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,7 @@ fyne-cross/*
.idea
.idea/*
cryoutilities.log
cryo_utilities
build-dir
repo
.flatpak-builder
8 changes: 8 additions & 0 deletions cmd/cryoutilities/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"log"
"os"
"path/filepath"
"strconv"
"strings"

Expand All @@ -15,6 +16,13 @@ import (
func main() {
// Delete old log file
os.Remove(internal.LogFilePath)

// Create logging directory
err := os.MkdirAll(filepath.Dir(internal.LogFilePath), 0777)
if err != nil {
log.Panic(err)
}

// Create a log file
logFile, err := os.OpenFile(internal.LogFilePath, os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
Expand Down
8 changes: 5 additions & 3 deletions internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import (
"path/filepath"
)

var HomeDir = getUserHomeDir()

DavidDeSimone marked this conversation as resolved.
Show resolved Hide resolved
// CurrentVersionNumber Version number to build with, Fyne can't support build flags just yet.
var CurrentVersionNumber = "2.0.0"

// InstallDirectory Location the program is installed.
var InstallDirectory = "/home/deck/.cryo_utilities"
var InstallDirectory = filepath.Join(HomeDir, ".cryo_utilities")

// LogFilePath Location of the log file
var LogFilePath = filepath.Join(InstallDirectory, "cryoutilities.log")
Expand Down Expand Up @@ -109,13 +111,13 @@ var GigabyteMultiplier = 1024 * 1024 * 1024
////////////////////////

// LibraryVDFLocation The default location of Steam's library VDF
var LibraryVDFLocation = "/home/deck/.steam/steam/steamapps/libraryfolders.vdf"
var LibraryVDFLocation = filepath.Join(HomeDir, ".steam/steam/steamapps/libraryfolders.vdf")

// MountDirectory The folder where all external devices are mounts
var MountDirectory = "/run/media"

// SteamDataRoot The default location where Steam keeps compatdata and shadercache
var SteamDataRoot = "/home/deck/.local/share/Steam"
var SteamDataRoot = filepath.Join(HomeDir, ".local/share/Steam")

// SteamCompatRoot Generates the full path of the compatdata folder, on SSD
var SteamCompatRoot = filepath.Join(SteamDataRoot, "steamapps/compatdata")
Expand Down
13 changes: 6 additions & 7 deletions internal/handler_swap.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ package internal
import (
"fmt"
"os"
"os/exec"
"strconv"
"strings"
)

// Get the current swap and swappiness values
func getSwappinessValue() (int, error) {
cmd, err := exec.Command("sysctl", "vm.swappiness").Output()
cmd, err := createCommand("sysctl", "vm.swappiness").Output()
if err != nil {
return 100, fmt.Errorf("error getting current swappiness")
}
Expand Down Expand Up @@ -70,7 +69,7 @@ func getAvailableSwapSizes() ([]string, error) {
// Disable swapping completely
func disableSwap() error {
CryoUtils.InfoLog.Println("Disabling swap temporarily...")
_, err := exec.Command("sudo", "swapoff", "-a").Output()
_, err := createCommand("sudo", "swapoff", "-a").Output()
if err != nil {
return fmt.Errorf("error disabling swap")
}
Expand All @@ -84,7 +83,7 @@ func resizeSwapFile(size int) error {

CryoUtils.InfoLog.Println("Resizing swap to", size, "GB...")
// Use dd to write zeroes, reevaluate using Go directly in the future
_, err := exec.Command("sudo", "dd", "if=/dev/zero", locationArg, "bs=1G", countArg, "status=progress").Output()
_, err := createCommand("sudo", "dd", "if=/dev/zero", locationArg, "bs=1G", countArg, "status=progress").Output()
if err != nil {
return fmt.Errorf("error resizing %s", CryoUtils.SwapFileLocation)
}
Expand All @@ -94,7 +93,7 @@ func resizeSwapFile(size int) error {
// Set swap permissions to a valid value.
func setSwapPermissions() error {
CryoUtils.InfoLog.Println("Setting permissions on", CryoUtils.SwapFileLocation, "to 0600...")
_, err := exec.Command("sudo", "chmod", "600", CryoUtils.SwapFileLocation).Output()
_, err := createCommand("sudo", "chmod", "600", CryoUtils.SwapFileLocation).Output()
if err != nil {
return fmt.Errorf("error setting permissions on %s", CryoUtils.SwapFileLocation)
}
Expand All @@ -104,11 +103,11 @@ func setSwapPermissions() error {
// Enable swapping on the newly resized file.
func initNewSwapFile() error {
CryoUtils.InfoLog.Println("Enabling swap on", CryoUtils.SwapFileLocation, "...")
_, err := exec.Command("sudo", "mkswap", CryoUtils.SwapFileLocation).Output()
_, err := createCommand("sudo", "mkswap", CryoUtils.SwapFileLocation).Output()
if err != nil {
return fmt.Errorf("error creating swap on %s", DefaultSwapFileLocation)
}
_, err = exec.Command("sudo", "swapon", CryoUtils.SwapFileLocation).Output()
_, err = createCommand("sudo", "swapon", CryoUtils.SwapFileLocation).Output()
if err != nil {
return fmt.Errorf("error enabling swap on %s", DefaultSwapFileLocation)
}
Expand Down
5 changes: 2 additions & 3 deletions internal/ui_authentication.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package internal

import (
"os/exec"
"time"

"fyne.io/fyne/v2/dialog"
Expand All @@ -11,7 +10,7 @@ import (
// Renews sudo auth for GUI mode
func renewSudoAuth() {
// Do a really basic command to renew sudo auth
cmd := exec.Command("sudo", "-S", "--", "echo")
cmd := createPasswordCommand("sudo", "-S", "--", "echo")
//Sudo will exit immediately if it's the correct password, but will hang for a moment if it isn't.
cmd.WaitDelay = 500 * time.Millisecond
stdin, err := cmd.StdinPipe()
Expand Down Expand Up @@ -47,7 +46,7 @@ func testAuth(password string) error {
d.Show()
defer d.Hide()
// Do a really basic command to renew sudo auth
cmd := exec.Command("sudo", "-S", "--", "echo")
cmd := createPasswordCommand("sudo", "-S", "--", "echo")
//Sudo will exit immediately if it's the correct password, but will hang for a moment if it isn't.
cmd.WaitDelay = 500 * time.Millisecond
stdin, err := cmd.StdinPipe()
Expand Down
88 changes: 83 additions & 5 deletions internal/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func writeFile(path string, contents string) error {
}

// Move the completed file to final location.
_, err = exec.Command("sudo", "mv", tempPath, path).Output()
_, err = createCommand("sudo", "mv", tempPath, path).Output()
if err != nil {
return fmt.Errorf("error moving temp file to final location")
}
Expand All @@ -167,7 +167,7 @@ func writeFile(path string, contents string) error {

func removeFile(path string) error {
CryoUtils.InfoLog.Println("Removing", path)
_, err := exec.Command("sudo", "rm", path).Output()
_, err := createCommand("sudo", "rm", path).Output()
if err != nil {
CryoUtils.ErrorLog.Println("Couldn't delete", path, ", likely missing.")
}
Expand Down Expand Up @@ -225,7 +225,7 @@ func removeElementFromStringSlice(str string, slice []string) []string {

func getUnitStatus(param string) (string, error) {
var output string
cmd, err := exec.Command("sudo", "cat", UnitMatrix[param]).Output()
cmd, err := createCommand("sudo", "cat", UnitMatrix[param]).Output()
if err != nil {
CryoUtils.ErrorLog.Println(err)
return "nil", err
Expand Down Expand Up @@ -271,10 +271,14 @@ func removeUnitFile(param string) error {

func setUnitValue(param string, value string) error {
CryoUtils.InfoLog.Println("Writing", value, "for param", param, "to memory.")
if isAppWithinFlatpak() {
return setUnitValueWithinFlatpak(param, value)
}

// This mess is the only way I could find to push directly to unit files, without requiring
// a sudo password on installation to change capabilities.
echoCmd := exec.Command("echo", value)
teeCmd := exec.Command("sudo", "tee", UnitMatrix[param])
echoCmd := createCommand("echo", value)
teeCmd := createCommand("sudo", "tee", UnitMatrix[param])
reader, writer := io.Pipe()
var buf bytes.Buffer
echoCmd.Stdout = writer
Expand All @@ -290,3 +294,77 @@ func setUnitValue(param string, value string) error {

return nil
}

func setUnitValueWithinFlatpak(param string, value string) error {
hostSpawnCmd := "host-spawn"
hostCommandArgs := []string{"sudo", "sh", "-c"}
shellCmd := fmt.Sprintf("echo %v | tee %v", value, UnitMatrix[param])
hostCommandArgs = append(hostCommandArgs, shellCmd)
fmt.Println("Executing command ", hostSpawnCmd, hostCommandArgs)
cmd := exec.Command(hostSpawnCmd, hostCommandArgs...)
stdin, err := cmd.StdinPipe()
if err != nil {
fmt.Println(err)
return err
}
cmd.Start()
_, err = stdin.Write([]byte(CryoUtils.UserPassword + "\n"))
if err != nil {
fmt.Println(err)
return err
}
return cmd.Wait()
}
Comment on lines +342 to +362
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these functions would need to be documented prior to being ready for prime-time.


func isAppWithinFlatpak() bool {
return os.Getenv("container") != ""
}

func createCommand(command string, args ...string) *exec.Cmd {
if !isAppWithinFlatpak() {
return exec.Command(command, args...)
}

hostSpawnCmd := "host-spawn"
hostCommandArgs := []string{command}
hostCommandArgs = append(hostCommandArgs, args...)
fmt.Println("Executing command ", hostSpawnCmd, hostCommandArgs)
cmd := exec.Command(hostSpawnCmd, hostCommandArgs...)

if command == "sudo" {
stdin, err := cmd.StdinPipe()
if err != nil {
CryoUtils.ErrorLog.Println(err)
return nil
}
_, err = stdin.Write([]byte(CryoUtils.UserPassword + "\n"))
if err != nil {
CryoUtils.ErrorLog.Println(err)
return nil
}
}

return cmd
}

func createPasswordCommand(command string, args ...string) *exec.Cmd {
if !isAppWithinFlatpak() {
return exec.Command(command, args...)
}

hostSpawnCmd := "host-spawn"
hostCommandArgs := []string{command}
hostCommandArgs = append(hostCommandArgs, args...)
fmt.Println("Executing command ", hostSpawnCmd, hostCommandArgs)
cmd := exec.Command(hostSpawnCmd, hostCommandArgs...)
return cmd
}

func getUserHomeDir() string {
homedir := os.Getenv("HOME")
if homedir != "" {
return homedir
}

return "/home/deck/"
}
53 changes: 53 additions & 0 deletions io.github.steamdeck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
app-id: io.github.steamdeckutilities
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be a real domain controlled by the project to be listed on flathub. I just made something up for iteration purposes.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I own cryobyte.io, which would be perfect. Does it need to have its own subdomain?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Short answer: From my reading of the docs, the project just needs to control the root domain, so controlling io.cryobyte should be sufficient. The third part of the url will just be the project identifier, so my recommendation would be io.cryobyte.steamdeckutilities

Docs: https://docs.flatpak.org/en/latest/conventions.html#application-ids

runtime: org.gnome.Platform
runtime-version: '43'
sdk: org.gnome.Sdk
command: launcher.sh
finish-args:
- '--share=ipc'
- '--socket=x11'
- '--filesystem=host'
- '--device=dri'
- '--share=network'
- '--talk-name=org.freedesktop.Flatpak'
build-options:
env:
- GOBIN=/app/bin
- GOROOT=/app/lib/sdk/golang/
modules:
- name: golang
buildsystem: simple
sources:
- type: archive
only-arches:
- aarch64
url: https://go.dev/dl/go1.20.1.linux-arm64.tar.gz
sha256: fc0aa29c933cec8d76f5435d859aaf42249aa08c74eb2d154689ae44c08d23b3
- type: archive
only-arches:
- x86_64
url: https://go.dev/dl/go1.20.1.linux-amd64.tar.gz
sha256: 000a5b1fca4f75895f78befeb2eecf10bfff3c428597f3f1e69133b63b911b02
build-commands:
- install -d /app/lib/sdk/golang
- cp -rpv * /app/lib/sdk/golang/
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need go 1.20, but the SDK extension was only providing go 1.19. It may be there was something I was missing.

- name: host-spawn
buildsystem: simple
sources:
- type: file
only-arches:
- x86_64
url: https://github.com/1player/host-spawn/releases/download/1.4.1/host-spawn-x86_64
sha256: d81bb6125ec73a9a2e26266c48787367fbcb665d67c2e7fb42217f0981106cf7
build-commands:
- install -D host-spawn-x86_64 /app/bin/host-spawn
- name: cryo_utilities
buildsystem: simple
build-commands:
- $GOROOT/bin/go build -ldflags="-s -w" -o cryo_utilities ./cmd/cryoutilities
- install -D cryo_utilities /app/bin/cryo_utilities
- install -D launcher.sh /app/bin/launcher.sh
sources:
- type: git
path: .
branch: relative-homdir
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a trick I use for iterating on builds, but isn't the end solution. If we were to use this "for real", we would want to use a tag/commit hash combination for a release.

2 changes: 1 addition & 1 deletion launcher.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ if [ "$(xrandr | grep ' connected' | wc -l)" -eq 1 ]; then
export FYNE_SCALE=0.25
fi

"$HOME"/.cryo_utilities/cryo_utilities gui
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to think of a better way to rectify launching in the flatpak vs. out of the flatpak. It should be an easy thing to solve.

cryo_utilities gui