Skip to content

Commit

Permalink
add msi installer workflows
Browse files Browse the repository at this point in the history
  • Loading branch information
sonroyaalmerol committed Nov 3, 2024
1 parent 4a0e855 commit 4a78ec1
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 117 deletions.
36 changes: 35 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


release-windows-amd64-agent:
name: release agent windows/amd64
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: wangyoucao577/go-release-action@v1
id: go_build
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: windows
Expand All @@ -63,3 +63,37 @@ jobs:
executable_compression: upx
binary_name: pbs-d2d-agent
project_path: ./cmd/pbs_windows_agent
- uses: actions/upload-artifact@v4
with:
name: windows-binary
path: ${{steps.go_build.outputs.release_asset_dir}}/pbs-d2d-agent.exe

release-windows-amd64-agent-installer:
name: release agent installer windows/amd64
runs-on: windows-latest
needs: release-windows-amd64-agent
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: windows-binary
path: $GITHUB_WORKSPACE/build/package/windows/
- uses: actions/setup-go@v4
with:
go-version: '1.23' # You may change this to the latest Go version if needed
- env:
MSI_NAME: "${{ github.event.repository.name }}-${{ github.event.release.tag_name }}-windows.msi"
VERSION: ${{ github.event.release.tag_name[1:] }}
shell: pwsh
run: |
go install github.com/wix-go/go-msi/cmd/go-msi@latest
cd ./build/package/windows
go-msi make --msi "$MSI_NAME" --version "$VERSION"
- name: Publish Release Assets
uses: softprops/action-gh-release@v1
with:
tag: ${{ github.event.release.tag_name }}
files: ./build/package/windows/*.msi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

62 changes: 62 additions & 0 deletions .github/workflows/set-wix-guids.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
on:
push:
branches:
- main
pull_request:
branches:
- main

permissions:
contents: write

jobs:
check-and-set-guid:
runs-on: windows-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Go environment
uses: actions/setup-go@v4
with:
go-version: '1.23' # You may change this to the latest Go version if needed

- name: Check for GUIDs in wix.json
id: check_guid
shell: pwsh
run: |
$jsonFile = "./build/package/windows/wix.json"
$wixJson = Get-Content $jsonFile | ConvertFrom-Json
# Check if any GUID is empty
$needsGuid = $false
if ([string]::IsNullOrEmpty($wixJson.'upgrade-code') -or
[string]::IsNullOrEmpty($wixJson.files.guid) -or
[string]::IsNullOrEmpty($wixJson.env.guid)) {
$needsGuid = $true
}
if ($needsGuid) {
echo "GUIDs are missing. Setting GUIDs."
echo "needs_guid=true" | Out-File -FilePath $env:GITHUB_ENV -Append
} else {
echo "All GUIDs are set."
echo "needs_guid=false" | Out-File -FilePath $env:GITHUB_ENV -Append
}
- name: Install go-msi and set GUIDs
if: env.needs_guid == 'true'
run: |
go install github.com/wix-go/go-msi/cmd/go-msi@latest
go-msi set-guid -p ./build/package/windows/wix.json
- name: Commit and push changes
if: env.needs_guid == 'true'
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git add ./build/package/windows/wix.json
git commit -m "Set GUIDs in wix.json"
git push origin main
shell: pwsh
37 changes: 37 additions & 0 deletions build/package/windows/wix.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"product": "pbs-d2d-backup",
"company": "snry",
"license": "LICENSE",
"upgrade-code": "",
"files": {
"guid": "",
"items": [
"pbs-d2d-backup.exe"
]
},
"directories": [],
"env": {
"guid": "",
"vars": [
{
"name": "PATH",
"value": "[INSTALLDIR]",
"permanent": "no",
"system": "no",
"action": "set",
"part": "last"
}
]
},
"hooks": [
{"when": "install", "command": "sc.exe create PBSAgent binPath=\"[INSTALLDIR]pbs-d2d-backup.exe\" type=share start=auto DisplayName=\"PBS Agent\""},
{"when": "install", "command": "sc.exe start PBSAgent"},
{"when": "uninstall", "command": "sc.exe delete PBSAgent"}
],
"choco": {
"description": "Orchestrating backups with Proxmox Backup Server",
"project-url": "https://github.com/sonroyaalmerol/pbs-d2d-backup",
"tags": "backup agent",
"license-url": "https://github.com/sonroyaalmerol/pbs-d2d-backup/LICENSE"
}
}
150 changes: 83 additions & 67 deletions cmd/pbs_windows_agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package main
import (
"context"
_ "embed"
"flag"
"fmt"
"net/url"
"os"
Expand All @@ -17,6 +16,7 @@ import (
"golang.org/x/sys/windows"

"github.com/getlantern/systray"
"github.com/kardianos/service"
"github.com/sonroyaalmerol/pbs-d2d-backup/internal/agent/sftp"
"github.com/sonroyaalmerol/pbs-d2d-backup/internal/agent/snapshots"
"github.com/sonroyaalmerol/pbs-d2d-backup/internal/utils"
Expand All @@ -25,90 +25,107 @@ import (
//go:embed icon/logo.png
var icon []byte

var logger service.Logger

type AgentProgram struct {
Exec func()
}

func (p *AgentProgram) Start(s service.Service) error {
go p.Exec()
return nil
}
func (p *AgentProgram) Stop(s service.Service) error {
return nil
}

var svc service.Service

func main() {
if !isAdmin() {
fmt.Println("This program needs to be run as administrator.")
runAsAdmin()
os.Exit(0)
svcConfig := &service.Config{
Name: "PBSAgent",
DisplayName: "Proxmox Backup Agent",
Description: "Orchestrating backups with Proxmox Backup Server",
}

serverUrl := flag.String("server", "", "Server URL (e.g. https://192.168.1.1:8008)")
flag.Parse()
serverUrl, ok := os.LookupEnv("PBS_AGENT_SERVER")
if !ok {
for {
serverUrl = promptInput("PBS Agent", "Server URL")

_, err := url.ParseRequestURI(serverUrl)
if err == nil {
serverUrl = strings.TrimSuffix(serverUrl, "/")
break
}
}

if *serverUrl == "" {
promptInput := promptInput("Proxmox Backup Agent", "Server URL")
serverUrl = &promptInput
utils.SetEnvironment("PBS_AGENT_SERVER", serverUrl)
}

_, err := url.ParseRequestURI(*serverUrl)
var err error
svc, err = service.New(
&AgentProgram{Exec: run(serverUrl)},
svcConfig,
)
if err != nil {
showMessageBox("Error", fmt.Sprintf("Invalid server URL: %s", err))
showMessageBox("Error", err.Error())
os.Exit(1)
}

// Reserve port 33450-33476
drives := utils.GetLocalDrives()
ctx := context.Background()
logger, err = svc.Logger(nil)
if err != nil {
showMessageBox("Error", err.Error())
os.Exit(1)
}

var wg sync.WaitGroup
for _, driveLetter := range drives {
rune := []rune(driveLetter)[0]
svc.Run()
}

sftpConfig, err := sftp.InitializeSFTPConfig(*serverUrl, driveLetter)
func run(serverUrl string) func() {
return func() {
_, err := url.ParseRequestURI(serverUrl)
if err != nil {
showMessageBox("Error", fmt.Sprintf("Unable to initialize SFTP: %s", err))
showMessageBox("Error", fmt.Sprintf("Invalid server URL: %s", err))
os.Exit(1)
}

port, err := utils.DriveLetterPort(rune)
if err != nil {
showMessageBox("Error", fmt.Sprintf("Unable to map letter to port: %s", err))
os.Exit(1)
// Reserve port 33450-33476
drives := utils.GetLocalDrives()
ctx := context.Background()

var wg sync.WaitGroup
for _, driveLetter := range drives {
rune := []rune(driveLetter)[0]

sftpConfig, err := sftp.InitializeSFTPConfig(serverUrl, driveLetter)
if err != nil {
showMessageBox("Error", fmt.Sprintf("Unable to initialize SFTP: %s", err))
os.Exit(1)
}

err = sftpConfig.PopulateKeys()
if err != nil {
showMessageBox("Error", fmt.Sprintf("Unable to populate SFTP keys: %s", err))
os.Exit(1)
}

port, err := utils.DriveLetterPort(rune)
if err != nil {
showMessageBox("Error", fmt.Sprintf("Unable to map letter to port: %s", err))
os.Exit(1)
}

wg.Add(1)
go sftp.Serve(ctx, &wg, sftpConfig, "0.0.0.0", port, fmt.Sprintf("%s:\\", driveLetter))
}

wg.Add(1)
go sftp.Serve(ctx, &wg, sftpConfig, "0.0.0.0", port, fmt.Sprintf("%s:\\", driveLetter))
}
defer snapshots.CloseAllSnapshots()

defer snapshots.CloseAllSnapshots()
systray.Run(onReady(serverUrl), onExit)
defer systray.Quit()

systray.Run(onReady(*serverUrl), onExit)
defer systray.Quit()

wg.Wait()
}

func isAdmin() bool {
processHandle := windows.CurrentProcess()

var token windows.Token
err := windows.OpenProcessToken(processHandle, windows.TOKEN_QUERY, &token)
if err != nil {
return false
}
defer token.Close()

sid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
if err != nil {
return false
}

isMember, err := token.IsMember(sid)
return err == nil && isMember
}

func runAsAdmin() {
exe, err := os.Executable()
if err != nil {
showMessageBox("Error", fmt.Sprintf("Failed to get executable path: %s", err))
return
}

cmd := exec.Command("runas", "/user:Administrator", exe)
cmd.SysProcAttr = &windows.SysProcAttr{HideWindow: true}
err = cmd.Run()
if err != nil {
showMessageBox("Error", fmt.Sprintf("Failed to run as administrator: %s", err))
wg.Wait()
}
}

Expand All @@ -120,7 +137,6 @@ func showMessageBox(title, message string) {
}

func promptInput(title, prompt string) string {
// This command uses Windows Script Host (WSH) to display an input box.
cmd := exec.Command("powershell", "-Command", fmt.Sprintf(`
$wshell = New-Object -ComObject WScript.Shell;
$wshell.Popup("%s",0,"%s",1+64);
Expand All @@ -133,7 +149,6 @@ func promptInput(title, prompt string) string {
return ""
}

// Convert the output to a string and trim any extraneous whitespace or newlines
return strings.TrimSpace(string(output))
}

Expand Down Expand Up @@ -163,4 +178,5 @@ func onReady(serverUrl string) func() {

func onExit() {
snapshots.CloseAllSnapshots()
_ = svc.Stop()
}
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ module github.com/sonroyaalmerol/pbs-d2d-backup
go 1.23.0

require (
github.com/getlantern/systray v1.2.2
github.com/kardianos/service v1.2.2
github.com/mattn/go-sqlite3 v1.14.24
github.com/mxk/go-vss v1.2.0
github.com/pkg/sftp v1.13.7
github.com/robfig/cron v1.2.0
golang.org/x/crypto v0.28.0
golang.org/x/sys v0.26.0
)

require (
Expand All @@ -17,10 +19,8 @@ require (
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect
github.com/getlantern/systray v1.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
golang.org/x/sys v0.26.0 // indirect
)
Loading

0 comments on commit 4a78ec1

Please sign in to comment.