2023-06-24 20:39:55 +08:00
// SiYuan - Refactor your thinking
2022-05-26 15:18:53 +08:00
// Copyright (c) 2020-present, b3log.org
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package util
import (
2023-01-12 21:58:15 +08:00
"errors"
2022-05-26 15:18:53 +08:00
"flag"
2023-01-12 21:58:15 +08:00
"fmt"
2022-05-26 15:18:53 +08:00
"math/rand"
"mime"
"os"
"path/filepath"
2023-08-03 11:26:54 +08:00
"runtime"
2022-05-26 15:18:53 +08:00
"strconv"
"strings"
"sync"
"time"
"github.com/88250/gulu"
figure "github.com/common-nighthawk/go-figure"
2022-12-07 20:45:11 +08:00
"github.com/gofrs/flock"
2023-03-23 19:32:11 +08:00
"github.com/siyuan-note/filelock"
2022-06-23 01:22:28 +08:00
"github.com/siyuan-note/httpclient"
2022-07-17 12:22:32 +08:00
"github.com/siyuan-note/logging"
2022-05-26 15:18:53 +08:00
)
2022-08-21 13:01:54 +08:00
// var Mode = "dev"
2022-05-31 17:58:24 +08:00
var Mode = "prod"
2022-05-26 15:18:53 +08:00
const (
2023-11-21 08:34:17 +08:00
Ver = "2.10.16"
2023-08-02 23:33:10 +08:00
IsInsider = false
2022-05-26 15:18:53 +08:00
)
var (
bootProgress float64 // 启动进度,从 0 到 100
bootDetails string // 启动细节描述
HttpServing = false // 是否 HTTP 伺服已经可用
)
func Boot ( ) {
2023-06-22 21:40:19 +08:00
IncBootProgress ( 3 , "Booting kernel..." )
2022-05-26 15:18:53 +08:00
rand . Seed ( time . Now ( ) . UTC ( ) . UnixNano ( ) )
initMime ( )
2022-12-06 00:37:46 +08:00
initHttpClient ( )
2022-05-26 15:18:53 +08:00
2023-06-24 11:06:42 +08:00
workspacePath := flag . String ( "workspace" , "" , "dir path of the workspace, default to ~/SiYuan/" )
2022-05-26 15:18:53 +08:00
wdPath := flag . String ( "wd" , WorkingDir , "working directory of SiYuan" )
2022-10-25 10:06:15 +08:00
port := flag . String ( "port" , "0" , "port of the HTTP server" )
2022-10-25 11:06:50 +08:00
readOnly := flag . String ( "readonly" , "false" , "read-only mode" )
2022-05-26 15:18:53 +08:00
accessAuthCode := flag . String ( "accessAuthCode" , "" , "access auth code" )
ssl := flag . Bool ( "ssl" , false , "for https and wss" )
2022-07-06 21:58:34 +08:00
lang := flag . String ( "lang" , "" , "zh_CN/zh_CHT/en_US/fr_FR/es_ES" )
2022-06-01 08:56:22 +08:00
mode := flag . String ( "mode" , "prod" , "dev/prod" )
2022-05-26 15:18:53 +08:00
flag . Parse ( )
if "" != * wdPath {
WorkingDir = * wdPath
}
if "" != * lang {
Lang = * lang
}
2022-06-01 08:56:22 +08:00
Mode = * mode
2022-10-25 10:06:15 +08:00
ServerPort = * port
2022-10-25 11:06:50 +08:00
ReadOnly , _ = strconv . ParseBool ( * readOnly )
2022-05-26 15:18:53 +08:00
AccessAuthCode = * accessAuthCode
2022-08-31 12:25:55 +08:00
Container = ContainerStd
2022-05-26 15:18:53 +08:00
if isRunningInDockerContainer ( ) {
2022-08-31 12:25:55 +08:00
Container = ContainerDocker
2023-10-02 22:15:33 +08:00
if "" == AccessAuthCode {
2023-11-22 08:58:59 +08:00
interruptBoot := true
// Set the env `SIYUAN_ACCESS_AUTH_CODE_BYPASS=true` to skip checking access auth code when deploying Docker https://github.com/siyuan-note/siyuan/issues/9709
byPassEnv := os . Getenv ( "SIYUAN_ACCESS_AUTH_CODE_BYPASS" )
bypass , parseErr := strconv . ParseBool ( byPassEnv )
if nil == parseErr && bypass {
interruptBoot = false
2023-11-22 08:59:46 +08:00
fmt . Println ( "bypass access auth code check since the env [SIYUAN_ACCESS_AUTH_CODE_BYPASS] is set to [true]" )
2023-11-22 08:58:59 +08:00
}
if interruptBoot {
// The access authorization code command line parameter must be set when deploying via Docker https://github.com/siyuan-note/siyuan/issues/9328
2023-11-22 08:59:46 +08:00
fmt . Printf ( "the access authorization code command line parameter (--accessAuthCode) must be set when deploying via Docker" )
2023-11-22 08:58:59 +08:00
os . Exit ( 1 )
}
2023-10-02 22:15:33 +08:00
}
2022-05-26 15:18:53 +08:00
}
2023-01-06 10:03:31 +08:00
if ContainerStd != Container {
2022-10-25 12:03:14 +08:00
ServerPort = FixedPort
}
2022-05-26 15:18:53 +08:00
2022-09-08 10:29:27 +08:00
msStoreFilePath := filepath . Join ( WorkingDir , "ms-store" )
ISMicrosoftStore = gulu . File . IsExist ( msStoreFilePath )
2023-08-03 11:26:54 +08:00
UserAgent = UserAgent + " " + Container + "/" + runtime . GOOS
2022-07-20 09:32:10 +08:00
httpclient . SetUserAgent ( UserAgent )
2022-05-26 15:18:53 +08:00
initWorkspaceDir ( * workspacePath )
SSL = * ssl
LogPath = filepath . Join ( TempDir , "siyuan.log" )
2022-07-17 12:22:32 +08:00
logging . SetLogPath ( LogPath )
2022-12-07 20:45:11 +08:00
// 工作空间仅允许被一个内核进程伺服
tryLockWorkspace ( )
2022-05-26 15:18:53 +08:00
AppearancePath = filepath . Join ( ConfDir , "appearance" )
if "dev" == Mode {
ThemesPath = filepath . Join ( WorkingDir , "appearance" , "themes" )
IconsPath = filepath . Join ( WorkingDir , "appearance" , "icons" )
} else {
ThemesPath = filepath . Join ( AppearancePath , "themes" )
IconsPath = filepath . Join ( AppearancePath , "icons" )
}
initPathDir ( )
bootBanner := figure . NewColorFigure ( "SiYuan" , "isometric3" , "green" , true )
2022-07-17 12:22:32 +08:00
logging . LogInfof ( "\n" + bootBanner . String ( ) )
2022-05-26 15:18:53 +08:00
logBootInfo ( )
}
2022-08-01 09:54:10 +08:00
func setBootDetails ( details string ) {
bootDetails = "v" + Ver + " " + details
}
2022-05-26 15:18:53 +08:00
func SetBootDetails ( details string ) {
if 100 <= bootProgress {
return
}
2022-08-01 09:54:10 +08:00
setBootDetails ( details )
2022-05-26 15:18:53 +08:00
}
func IncBootProgress ( progress float64 , details string ) {
if 100 <= bootProgress {
return
}
bootProgress += progress
2022-08-01 09:54:10 +08:00
setBootDetails ( details )
2022-05-26 15:18:53 +08:00
}
func IsBooted ( ) bool {
return 100 <= bootProgress
}
func GetBootProgressDetails ( ) ( float64 , string ) {
return bootProgress , bootDetails
}
func GetBootProgress ( ) float64 {
return bootProgress
}
func SetBooted ( ) {
2022-08-01 09:54:10 +08:00
setBootDetails ( "Finishing boot..." )
2022-05-26 15:18:53 +08:00
bootProgress = 100
2022-07-17 12:22:32 +08:00
logging . LogInfof ( "kernel booted" )
2022-05-26 15:18:53 +08:00
}
var (
HomeDir , _ = gulu . OS . Home ( )
WorkingDir , _ = os . Getwd ( )
2023-08-04 12:05:29 +08:00
WorkspaceDir string // 工作空间目录路径
WorkspaceLock * flock . Flock // 工作空间锁
ConfDir string // 配置目录路径
DataDir string // 数据目录路径
RepoDir string // 仓库目录路径
HistoryDir string // 数据历史目录路径
TempDir string // 临时目录路径
LogPath string // 配置目录下的日志文件 siyuan.log 路径
DBName = "siyuan.db" // SQLite 数据库文件名
DBPath string // SQLite 数据库文件路径
HistoryDBPath string // SQLite 历史数据库文件路径
AssetContentDBPath string // SQLite 资源文件内容数据库文件路径
BlockTreePath string // 区块树文件路径
AppearancePath string // 配置目录下的外观目录 appearance/ 路径
ThemesPath string // 配置目录下的外观目录下的 themes/ 路径
IconsPath string // 配置目录下的外观目录下的 icons/ 路径
SnippetsPath string // 数据目录下的 snippets/ 路径
2022-05-26 15:18:53 +08:00
UIProcessIDs = sync . Map { } // UI 进程 ID
)
func initWorkspaceDir ( workspaceArg string ) {
userHomeConfDir := filepath . Join ( HomeDir , ".config" , "siyuan" )
workspaceConf := filepath . Join ( userHomeConfDir , "workspace.json" )
2023-03-27 11:47:38 +08:00
logging . SetLogPath ( filepath . Join ( userHomeConfDir , "kernel.log" ) )
2022-05-26 15:18:53 +08:00
if ! gulu . File . IsExist ( workspaceConf ) {
if err := os . MkdirAll ( userHomeConfDir , 0755 ) ; nil != err && ! os . IsExist ( err ) {
2023-03-27 11:47:38 +08:00
logging . LogErrorf ( "create user home conf folder [%s] failed: %s" , userHomeConfDir , err )
os . Exit ( logging . ExitCodeInitWorkspaceErr )
2022-05-26 15:18:53 +08:00
}
}
2023-06-24 10:42:01 +08:00
defaultWorkspaceDir := filepath . Join ( HomeDir , "SiYuan" )
2022-08-12 11:41:03 +08:00
if gulu . OS . IsWindows ( ) {
// 改进 Windows 端默认工作空间路径 https://github.com/siyuan-note/siyuan/issues/5622
if userProfile := os . Getenv ( "USERPROFILE" ) ; "" != userProfile {
2023-06-24 10:42:01 +08:00
defaultWorkspaceDir = filepath . Join ( userProfile , "SiYuan" )
2022-08-12 11:41:03 +08:00
}
}
2022-05-26 15:18:53 +08:00
var workspacePaths [ ] string
if ! gulu . File . IsExist ( workspaceConf ) {
WorkspaceDir = defaultWorkspaceDir
} else {
2023-01-12 21:58:15 +08:00
workspacePaths , _ = ReadWorkspacePaths ( )
2022-05-26 15:18:53 +08:00
if 0 < len ( workspacePaths ) {
2023-01-14 11:26:24 +08:00
// 取最后一个(也就是最近打开的)工作空间
2022-05-26 15:18:53 +08:00
WorkspaceDir = workspacePaths [ len ( workspacePaths ) - 1 ]
} else {
WorkspaceDir = defaultWorkspaceDir
2023-01-14 11:26:24 +08:00
}
2023-06-17 11:18:58 +08:00
}
2023-01-14 11:26:24 +08:00
2023-06-17 11:18:58 +08:00
if "" != workspaceArg {
WorkspaceDir = workspaceArg
2022-05-26 15:18:53 +08:00
}
2023-01-14 11:26:24 +08:00
if ! gulu . File . IsDir ( WorkspaceDir ) {
2023-10-23 22:28:35 +08:00
logging . LogWarnf ( "use the default workspace [%s] since the specified workspace [%s] is not a dir" , defaultWorkspaceDir , WorkspaceDir )
2023-06-17 11:18:58 +08:00
if err := os . MkdirAll ( defaultWorkspaceDir , 0755 ) ; nil != err && ! os . IsExist ( err ) {
logging . LogErrorf ( "create default workspace folder [%s] failed: %s" , defaultWorkspaceDir , err )
os . Exit ( logging . ExitCodeInitWorkspaceErr )
}
2023-01-14 11:26:24 +08:00
WorkspaceDir = defaultWorkspaceDir
}
workspacePaths = append ( workspacePaths , WorkspaceDir )
2023-01-12 21:58:15 +08:00
if err := WriteWorkspacePaths ( workspacePaths ) ; nil != err {
2023-03-27 11:47:38 +08:00
logging . LogErrorf ( "write workspace conf [%s] failed: %s" , workspaceConf , err )
os . Exit ( logging . ExitCodeInitWorkspaceErr )
2022-05-26 15:18:53 +08:00
}
ConfDir = filepath . Join ( WorkspaceDir , "conf" )
DataDir = filepath . Join ( WorkspaceDir , "data" )
2022-06-13 15:27:37 +08:00
RepoDir = filepath . Join ( WorkspaceDir , "repo" )
2022-07-03 17:11:00 +08:00
HistoryDir = filepath . Join ( WorkspaceDir , "history" )
2022-05-26 15:18:53 +08:00
TempDir = filepath . Join ( WorkspaceDir , "temp" )
2022-06-01 17:19:31 +08:00
osTmpDir := filepath . Join ( TempDir , "os" )
os . RemoveAll ( osTmpDir )
if err := os . MkdirAll ( osTmpDir , 0755 ) ; nil != err {
2023-03-27 11:47:38 +08:00
logging . LogErrorf ( "create os tmp dir [%s] failed: %s" , osTmpDir , err )
os . Exit ( logging . ExitCodeInitWorkspaceErr )
2022-06-01 17:19:31 +08:00
}
2022-08-21 13:01:54 +08:00
os . RemoveAll ( filepath . Join ( TempDir , "repo" ) )
2022-06-01 17:19:31 +08:00
os . Setenv ( "TMPDIR" , osTmpDir )
os . Setenv ( "TEMP" , osTmpDir )
os . Setenv ( "TMP" , osTmpDir )
2022-05-26 15:18:53 +08:00
DBPath = filepath . Join ( TempDir , DBName )
2022-08-23 10:06:58 +08:00
HistoryDBPath = filepath . Join ( TempDir , "history.db" )
2023-08-04 12:05:29 +08:00
AssetContentDBPath = filepath . Join ( TempDir , "asset_content.db" )
2023-01-26 17:12:38 +08:00
BlockTreePath = filepath . Join ( TempDir , "blocktree" )
2022-10-28 08:29:41 +08:00
SnippetsPath = filepath . Join ( DataDir , "snippets" )
2022-05-26 15:18:53 +08:00
}
2023-01-12 21:58:15 +08:00
func ReadWorkspacePaths ( ) ( ret [ ] string , err error ) {
ret = [ ] string { }
workspaceConf := filepath . Join ( HomeDir , ".config" , "siyuan" , "workspace.json" )
data , err := os . ReadFile ( workspaceConf )
if nil != err {
msg := fmt . Sprintf ( "read workspace conf [%s] failed: %s" , workspaceConf , err )
logging . LogErrorf ( msg )
err = errors . New ( msg )
return
}
if err = gulu . JSON . UnmarshalJSON ( data , & ret ) ; nil != err {
msg := fmt . Sprintf ( "unmarshal workspace conf [%s] failed: %s" , workspaceConf , err )
logging . LogErrorf ( msg )
err = errors . New ( msg )
return
}
var tmp [ ] string
for _ , d := range ret {
d = strings . TrimRight ( d , " \t\n" ) // 去掉工作空间路径尾部空格 https://github.com/siyuan-note/siyuan/issues/6353
if gulu . File . IsDir ( d ) {
tmp = append ( tmp , d )
}
}
ret = tmp
2023-01-14 11:26:24 +08:00
ret = gulu . Str . RemoveDuplicatedElem ( ret )
2023-01-12 21:58:15 +08:00
return
}
func WriteWorkspacePaths ( workspacePaths [ ] string ) ( err error ) {
workspacePaths = gulu . Str . RemoveDuplicatedElem ( workspacePaths )
workspaceConf := filepath . Join ( HomeDir , ".config" , "siyuan" , "workspace.json" )
data , err := gulu . JSON . MarshalJSON ( workspacePaths )
if nil != err {
msg := fmt . Sprintf ( "marshal workspace conf [%s] failed: %s" , workspaceConf , err )
logging . LogErrorf ( msg )
err = errors . New ( msg )
return
}
2023-03-23 19:32:11 +08:00
if err = filelock . WriteFile ( workspaceConf , data ) ; nil != err {
2023-01-12 21:58:15 +08:00
msg := fmt . Sprintf ( "write workspace conf [%s] failed: %s" , workspaceConf , err )
logging . LogErrorf ( msg )
err = errors . New ( msg )
return
}
return
}
2022-05-26 15:18:53 +08:00
var (
2022-10-25 10:06:15 +08:00
ServerPort = "0" // HTTP/WebSocket 端口, 0 为使用随机端口
2022-05-26 15:18:53 +08:00
ReadOnly bool
AccessAuthCode string
2022-07-06 21:58:34 +08:00
Lang = ""
2022-05-26 15:18:53 +08:00
2022-09-08 10:29:27 +08:00
Container string // docker, android, ios, std
ISMicrosoftStore bool // 桌面端是否是微软商店版
2022-05-26 15:18:53 +08:00
)
2022-08-31 12:25:55 +08:00
const (
ContainerStd = "std" // 桌面端
ContainerDocker = "docker" // Docker 容器端
ContainerAndroid = "android" // Android 端
ContainerIOS = "ios" // iOS 端
2022-10-25 11:55:42 +08:00
2022-10-29 21:47:10 +08:00
LocalHost = "127.0.0.1" // 伺服地址
2022-10-28 09:19:57 +08:00
FixedPort = "6806" // 固定端口
2022-08-31 12:25:55 +08:00
)
2022-05-26 15:18:53 +08:00
func initPathDir ( ) {
if err := os . MkdirAll ( ConfDir , 0755 ) ; nil != err && ! os . IsExist ( err ) {
2023-03-27 11:47:38 +08:00
logging . LogFatalf ( logging . ExitCodeInitWorkspaceErr , "create conf folder [%s] failed: %s" , ConfDir , err )
2022-05-26 15:18:53 +08:00
}
if err := os . MkdirAll ( DataDir , 0755 ) ; nil != err && ! os . IsExist ( err ) {
2023-03-27 11:47:38 +08:00
logging . LogFatalf ( logging . ExitCodeInitWorkspaceErr , "create data folder [%s] failed: %s" , DataDir , err )
2022-05-26 15:18:53 +08:00
}
if err := os . MkdirAll ( TempDir , 0755 ) ; nil != err && ! os . IsExist ( err ) {
2023-03-27 11:47:38 +08:00
logging . LogFatalf ( logging . ExitCodeInitWorkspaceErr , "create temp folder [%s] failed: %s" , TempDir , err )
2022-05-26 15:18:53 +08:00
}
assets := filepath . Join ( DataDir , "assets" )
if err := os . MkdirAll ( assets , 0755 ) ; nil != err && ! os . IsExist ( err ) {
2023-03-27 11:47:38 +08:00
logging . LogFatalf ( logging . ExitCodeInitWorkspaceErr , "create data assets folder [%s] failed: %s" , assets , err )
2022-05-26 15:18:53 +08:00
}
templates := filepath . Join ( DataDir , "templates" )
if err := os . MkdirAll ( templates , 0755 ) ; nil != err && ! os . IsExist ( err ) {
2023-03-27 11:47:38 +08:00
logging . LogFatalf ( logging . ExitCodeInitWorkspaceErr , "create data templates folder [%s] failed: %s" , templates , err )
2022-05-26 15:18:53 +08:00
}
widgets := filepath . Join ( DataDir , "widgets" )
if err := os . MkdirAll ( widgets , 0755 ) ; nil != err && ! os . IsExist ( err ) {
2023-03-27 11:47:38 +08:00
logging . LogFatalf ( logging . ExitCodeInitWorkspaceErr , "create data widgets folder [%s] failed: %s" , widgets , err )
2022-05-26 15:18:53 +08:00
}
2023-05-05 15:21:21 +08:00
plugins := filepath . Join ( DataDir , "plugins" )
if err := os . MkdirAll ( plugins , 0755 ) ; nil != err && ! os . IsExist ( err ) {
logging . LogFatalf ( logging . ExitCodeInitWorkspaceErr , "create data plugins folder [%s] failed: %s" , widgets , err )
}
2022-05-26 15:18:53 +08:00
emojis := filepath . Join ( DataDir , "emojis" )
if err := os . MkdirAll ( emojis , 0755 ) ; nil != err && ! os . IsExist ( err ) {
2023-03-27 11:47:38 +08:00
logging . LogFatalf ( logging . ExitCodeInitWorkspaceErr , "create data emojis folder [%s] failed: %s" , widgets , err )
2022-05-26 15:18:53 +08:00
}
2023-06-22 16:30:04 +08:00
// Support directly access `data/public/*` contents via URL link https://github.com/siyuan-note/siyuan/issues/8593
public := filepath . Join ( DataDir , "public" )
if err := os . MkdirAll ( public , 0755 ) ; nil != err && ! os . IsExist ( err ) {
logging . LogFatalf ( logging . ExitCodeInitWorkspaceErr , "create data public folder [%s] failed: %s" , widgets , err )
}
2022-05-26 15:18:53 +08:00
}
func initMime ( ) {
// 在某版本的 Windows 10 操作系统上界面样式异常问题
// https://github.com/siyuan-note/siyuan/issues/247
// https://github.com/siyuan-note/siyuan/issues/3813
mime . AddExtensionType ( ".css" , "text/css" )
mime . AddExtensionType ( ".js" , "application/x-javascript" )
mime . AddExtensionType ( ".json" , "application/json" )
mime . AddExtensionType ( ".html" , "text/html" )
2022-10-25 12:20:10 +08:00
2023-10-12 16:37:09 +08:00
// 某些系统上下载资源文件后打开是 zip https://github.com/siyuan-note/siyuan/issues/6347
2022-10-25 12:20:10 +08:00
mime . AddExtensionType ( ".doc" , "application/msword" )
mime . AddExtensionType ( ".docx" , "application/vnd.openxmlformats-officedocument.wordprocessingml.document" )
mime . AddExtensionType ( ".xls" , "application/vnd.ms-excel" )
mime . AddExtensionType ( ".xlsx" , "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" )
mime . AddExtensionType ( ".dwg" , "image/x-dwg" )
mime . AddExtensionType ( ".dxf" , "image/x-dxf" )
mime . AddExtensionType ( ".dwf" , "drawing/x-dwf" )
mime . AddExtensionType ( ".pdf" , "application/pdf" )
2023-03-30 19:39:59 +08:00
2023-10-12 16:37:09 +08:00
// 某些系统上无法显示 SVG 图片 SVG images cannot be displayed on some systems https://github.com/siyuan-note/siyuan/issues/9413
mime . AddExtensionType ( ".svg" , "image/svg+xml" )
2023-03-30 19:39:59 +08:00
// 文档数据文件
mime . AddExtensionType ( ".sy" , "application/json" )
2022-05-26 15:18:53 +08:00
}
2022-10-16 14:15:42 +08:00
func GetDataAssetsAbsPath ( ) ( ret string ) {
ret = filepath . Join ( DataDir , "assets" )
2023-09-13 08:58:25 +08:00
if IsSymlinkPath ( ret ) {
2022-10-16 14:15:42 +08:00
// 跟随符号链接 https://github.com/siyuan-note/siyuan/issues/5480
2023-09-13 08:58:25 +08:00
var err error
ret , err = filepath . EvalSymlinks ( ret )
2022-10-16 14:15:42 +08:00
if nil != err {
logging . LogErrorf ( "read assets link failed: %s" , err )
}
}
return
}
2022-12-07 20:45:11 +08:00
func tryLockWorkspace ( ) {
WorkspaceLock = flock . New ( filepath . Join ( WorkspaceDir , ".lock" ) )
2022-12-20 19:40:01 +08:00
ok , err := WorkspaceLock . TryLock ( )
if ok {
return
}
if nil != err {
2022-12-07 20:45:11 +08:00
logging . LogErrorf ( "lock workspace [%s] failed: %s" , WorkspaceDir , err )
2022-12-20 19:40:01 +08:00
} else {
logging . LogErrorf ( "lock workspace [%s] failed" , WorkspaceDir )
2022-12-07 20:45:11 +08:00
}
2023-03-18 18:10:06 +08:00
os . Exit ( logging . ExitCodeWorkspaceLocked )
2022-12-07 20:45:11 +08:00
}
2023-01-13 16:26:24 +08:00
func IsWorkspaceLocked ( workspacePath string ) bool {
if ! gulu . File . IsDir ( workspacePath ) {
return false
}
lockFilePath := filepath . Join ( workspacePath , ".lock" )
if ! gulu . File . IsExist ( lockFilePath ) {
return false
}
f := flock . New ( lockFilePath )
defer f . Unlock ( )
ok , _ := f . TryLock ( )
if ok {
return false
}
return true
}
2022-12-07 20:45:11 +08:00
func UnlockWorkspace ( ) {
if nil == WorkspaceLock {
return
}
if err := WorkspaceLock . Unlock ( ) ; nil != err {
logging . LogErrorf ( "unlock workspace [%s] failed: %s" , WorkspaceDir , err )
return
}
if err := os . Remove ( filepath . Join ( WorkspaceDir , ".lock" ) ) ; nil != err {
logging . LogErrorf ( "remove workspace lock failed: %s" , err )
return
}
}