Compare commits
No commits in common. "5f061f92eb14dbfbb2129fe53da6061787f333d6" and "282d0c66302b49d67c62bac30199aec9789c1754" have entirely different histories.
5f061f92eb
...
282d0c6630
@ -1,4 +1,4 @@
|
|||||||
!include "MUI2.nsh"
|
!include "MUI2.nsh"
|
||||||
!include "LogicLib.nsh"
|
!include "LogicLib.nsh"
|
||||||
!include "installer.nsh"
|
!include "installer.nsh"
|
||||||
|
|
||||||
@ -10,10 +10,6 @@
|
|||||||
!define VERSION "0.1.0"
|
!define VERSION "0.1.0"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
!ifndef BUILD_NUMBER
|
|
||||||
!define BUILD_NUMBER "dev"
|
|
||||||
!endif
|
|
||||||
|
|
||||||
!ifndef APP_SOURCE_DIR
|
!ifndef APP_SOURCE_DIR
|
||||||
!define APP_SOURCE_DIR "app"
|
!define APP_SOURCE_DIR "app"
|
||||||
!endif
|
!endif
|
||||||
@ -25,7 +21,6 @@ InstallDirRegKey HKLM "Software\Tom Butcher\Farm Control" "InstallDir"
|
|||||||
RequestExecutionLevel admin
|
RequestExecutionLevel admin
|
||||||
|
|
||||||
!define MUI_ABORTWARNING
|
!define MUI_ABORTWARNING
|
||||||
!define MUI_BRANDINGTEXT "Farm Control v${VERSION}-b${BUILD_NUMBER} Installer"
|
|
||||||
|
|
||||||
!ifndef INSTALLER_ICON
|
!ifndef INSTALLER_ICON
|
||||||
!define INSTALLER_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
|
!define INSTALLER_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
|
||||||
|
|||||||
@ -8,8 +8,6 @@ param(
|
|||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$Version,
|
[string]$Version,
|
||||||
|
|
||||||
[string]$BuildNumber = "dev",
|
|
||||||
|
|
||||||
[long]$MinInstallerBytes = 10485760
|
[long]$MinInstallerBytes = 10485760
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -99,7 +97,6 @@ $makensisArgs = @(
|
|||||||
"/NOCD"
|
"/NOCD"
|
||||||
"/DOUTFILE=$localInstallerName"
|
"/DOUTFILE=$localInstallerName"
|
||||||
"/DVERSION=$Version"
|
"/DVERSION=$Version"
|
||||||
"/DBUILD_NUMBER=$BuildNumber"
|
|
||||||
"/DAPP_SOURCE_DIR=app"
|
"/DAPP_SOURCE_DIR=app"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -447,15 +447,6 @@ function buildWindowsNsis(appDir, arch) {
|
|||||||
artifactDir,
|
artifactDir,
|
||||||
getReleaseArtifactName(version, arch, 'exe')
|
getReleaseArtifactName(version, arch, 'exe')
|
||||||
)
|
)
|
||||||
const buildInfoPath = path.join(rootDir, 'src/buildInfo.json')
|
|
||||||
const buildInfo = existsSync(buildInfoPath)
|
|
||||||
? JSON.parse(readFileSync(buildInfoPath, 'utf8'))
|
|
||||||
: {}
|
|
||||||
const buildNumber =
|
|
||||||
process.env.BUILD_NUMBER ||
|
|
||||||
process.env.VITE_BUILD_NUMBER ||
|
|
||||||
buildInfo.buildNumber ||
|
|
||||||
'dev'
|
|
||||||
const powershell = process.env.SystemRoot
|
const powershell = process.env.SystemRoot
|
||||||
? path.join(
|
? path.join(
|
||||||
process.env.SystemRoot,
|
process.env.SystemRoot,
|
||||||
@ -479,9 +470,7 @@ function buildWindowsNsis(appDir, arch) {
|
|||||||
'-OutputExe',
|
'-OutputExe',
|
||||||
exePath,
|
exePath,
|
||||||
'-Version',
|
'-Version',
|
||||||
version,
|
version
|
||||||
'-BuildNumber',
|
|
||||||
buildNumber
|
|
||||||
],
|
],
|
||||||
{ stdio: 'inherit' }
|
{ stdio: 'inherit' }
|
||||||
)
|
)
|
||||||
|
|||||||
@ -11,10 +11,7 @@ import { join } from 'node:path'
|
|||||||
export const PROTOCOL_PREFIX = 'farmcontrol://'
|
export const PROTOCOL_PREFIX = 'farmcontrol://'
|
||||||
export const WINDOWS_PIPE_NAME = '\\\\.\\pipe\\com.tombutcher.farmcontrol.instance'
|
export const WINDOWS_PIPE_NAME = '\\\\.\\pipe\\com.tombutcher.farmcontrol.instance'
|
||||||
export const SIGNAL_FILE = 'instance-signal.json'
|
export const SIGNAL_FILE = 'instance-signal.json'
|
||||||
export const LOCK_FILE = 'primary.lock'
|
|
||||||
const FORWARD_TIMEOUT_MS = 750
|
const FORWARD_TIMEOUT_MS = 750
|
||||||
const PIPE_RETRY_ATTEMPTS = 3
|
|
||||||
const PIPE_RETRY_DELAY_MS = 100
|
|
||||||
|
|
||||||
export function getInstanceDir() {
|
export function getInstanceDir() {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
@ -31,37 +28,6 @@ export function getSignalPath() {
|
|||||||
return join(getInstanceDir(), SIGNAL_FILE)
|
return join(getInstanceDir(), SIGNAL_FILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLockPath() {
|
|
||||||
return join(getInstanceDir(), LOCK_FILE)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isProcessAlive(pid) {
|
|
||||||
if (!Number.isInteger(pid) || pid <= 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
process.kill(pid, 0)
|
|
||||||
return true
|
|
||||||
} catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isPrimaryInstanceRunning() {
|
|
||||||
const lockPath = getLockPath()
|
|
||||||
if (!existsSync(lockPath)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const existingPid = Number.parseInt(readFileSync(lockPath, 'utf8').trim(), 10)
|
|
||||||
return isProcessAlive(existingPid)
|
|
||||||
} catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findProtocolUrl(args) {
|
export function findProtocolUrl(args) {
|
||||||
const directMatch = args.find(
|
const directMatch = args.find(
|
||||||
(arg) => typeof arg === 'string' && arg.startsWith(PROTOCOL_PREFIX)
|
(arg) => typeof arg === 'string' && arg.startsWith(PROTOCOL_PREFIX)
|
||||||
@ -151,7 +117,7 @@ export function writeDeeplinkSignal(payload) {
|
|||||||
unlinkSync(tempPath)
|
unlinkSync(tempPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryForwardViaPipe(payload) {
|
export function forwardDeeplinkToRunningInstance(payload) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
let settled = false
|
let settled = false
|
||||||
const finish = (forwarded) => {
|
const finish = (forwarded) => {
|
||||||
@ -160,7 +126,7 @@ function tryForwardViaPipe(payload) {
|
|||||||
resolve(forwarded)
|
resolve(forwarded)
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = net.connect({ path: WINDOWS_PIPE_NAME })
|
const client = net.connect(WINDOWS_PIPE_NAME)
|
||||||
const message = JSON.stringify(payload)
|
const message = JSON.stringify(payload)
|
||||||
|
|
||||||
client.on('connect', () => {
|
client.on('connect', () => {
|
||||||
@ -179,38 +145,3 @@ function tryForwardViaPipe(payload) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function delay(ms) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
setTimeout(resolve, ms)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function tryForwardViaPipeWithRetries(payload) {
|
|
||||||
for (let attempt = 0; attempt < PIPE_RETRY_ATTEMPTS; attempt += 1) {
|
|
||||||
if (await tryForwardViaPipe(payload)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attempt < PIPE_RETRY_ATTEMPTS - 1) {
|
|
||||||
await delay(PIPE_RETRY_DELAY_MS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function forwardDeeplinkToRunningInstance(payload) {
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
if (await tryForwardViaPipeWithRetries(payload)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPrimaryInstanceRunning()) {
|
|
||||||
writeDeeplinkSignal(payload)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
@ -9,19 +9,20 @@ import {
|
|||||||
writeFileSync
|
writeFileSync
|
||||||
} from 'node:fs'
|
} from 'node:fs'
|
||||||
import net from 'node:net'
|
import net from 'node:net'
|
||||||
|
import { join } from 'node:path'
|
||||||
import {
|
import {
|
||||||
buildDeeplinkPayload,
|
buildDeeplinkPayload,
|
||||||
findProtocolUrl,
|
findProtocolUrl,
|
||||||
forwardDeeplinkToRunningInstance,
|
forwardDeeplinkToRunningInstance,
|
||||||
getInstanceDir,
|
getInstanceDir,
|
||||||
getLockPath,
|
|
||||||
getSignalPath,
|
getSignalPath,
|
||||||
isProcessAlive,
|
|
||||||
SIGNAL_FILE,
|
SIGNAL_FILE,
|
||||||
WINDOWS_PIPE_NAME,
|
WINDOWS_PIPE_NAME,
|
||||||
writeDeeplinkSignal
|
writeDeeplinkSignal
|
||||||
} from './deeplink-ipc.js'
|
} from './deeplink-ipc.js'
|
||||||
|
|
||||||
|
const LOCK_FILE = 'primary.lock'
|
||||||
|
|
||||||
let lockFd = null
|
let lockFd = null
|
||||||
let pollInterval = null
|
let pollInterval = null
|
||||||
let fsWatcher = null
|
let fsWatcher = null
|
||||||
@ -32,6 +33,23 @@ let handlers = {
|
|||||||
}
|
}
|
||||||
const pendingMessages = []
|
const pendingMessages = []
|
||||||
|
|
||||||
|
function getLockPath() {
|
||||||
|
return join(getInstanceDir(), LOCK_FILE)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isProcessAlive(pid) {
|
||||||
|
if (!Number.isInteger(pid) || pid <= 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
process.kill(pid, 0)
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function tryAcquirePrimaryLock() {
|
function tryAcquirePrimaryLock() {
|
||||||
mkdirSync(getInstanceDir(), { recursive: true })
|
mkdirSync(getInstanceDir(), { recursive: true })
|
||||||
const lockPath = getLockPath()
|
const lockPath = getLockPath()
|
||||||
@ -229,7 +247,7 @@ function startWindowsPipeServer() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
server.listen({ path: WINDOWS_PIPE_NAME })
|
server.listen(WINDOWS_PIPE_NAME)
|
||||||
pipeServer = server
|
pipeServer = server
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,7 +274,6 @@ export async function ensureSingleInstanceLock({ launchUrl } = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
startWindowsPipeServer()
|
startWindowsPipeServer()
|
||||||
startInstanceSignalWatcher()
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user