Skip to content

Commit

Permalink
Merge pull request #41 from LiteLDev/develop
Browse files Browse the repository at this point in the history
Allow installing system programs
  • Loading branch information
futrime authored Feb 1, 2023
2 parents 96ea985 + 45dea0d commit c603052
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 77 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.7.0] - 2023-02-01

### Added

- Support for installing anything to any path.
- Prompt for confirmation when installing to a path that is not in working directory.

## [0.6.0] - 2023-01-31

### Added
Expand Down Expand Up @@ -108,7 +115,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Basic functions: cache, install, list, show, tooth init, and uninstall.

[unreleased]: https://github.com/LiteLDev/Lip/compare/v0.6.0...HEAD
[unreleased]: https://github.com/LiteLDev/Lip/compare/v0.7.0...HEAD
[0.7.0]: https://github.com/LiteLDev/Lip/releases/tag/v0.6.0...v0.7.0
[0.6.0]: https://github.com/LiteLDev/Lip/releases/tag/v0.5.1...v0.6.0
[0.5.1]: https://github.com/LiteLDev/Lip/releases/tag/v0.4.0...v0.5.1
[0.5.0]: https://github.com/LiteLDev/Lip/releases/tag/v0.4.0...v0.5.0
Expand Down
4 changes: 4 additions & 0 deletions docs/en/commands/lip_install.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ You can install any pre-release versions by specifying the version. And teeth ca

Reinstall the tooth even if they are already up-to-date. When reinstalling, Lip will first uninstall the tooth and then install it. If version specified, Lip will install the version, otherwise the newest version.

- `-y, --yes`

Assume yes to all prompts and run non-interactively.

## Examples

Install from tooth repositories:
Expand Down
19 changes: 5 additions & 14 deletions docs/en/tooth_json_file_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,12 +271,10 @@ Indicates how should Lip handle file placement. When installing, files from "sou

### Syntax

Each placement rule should contain a source field and a destination field. Lip will extract files from the path relative to the root of the tooth specified by source and place them to the path relative to the root of BDS specified by destination.
Each placement rule should contain a source field and a destination field. Lip will extract files from the path relative to the root of the tooth specified by source and place them to the path specified by destination.

If both the source and the destination ends with "*", the placement will be regarded as a wildcard. Lip will recursively place all files under the source directory to the destination directory.

Here we make a strict rule that the source and destination can only contain letters, digits, hyphens, underscores, dots, slashes and asterisks (for the last letter treated as wildcard) [a-zA-Z0-9-_\.\/\*]. If you want to place files to the root of BDS, you should specify every file in the source field. The first letter should not be a slash or a dot. The last letter should not be a slash.

You can also specify GOOS and GOARCH to optionally place files for specific platforms. For example, you can specify "windows" and "amd64" to place files only for Windows 64-bit. If you want to place files for all platforms, you can omit the GOOS and GOARCH fields. However, if you have specified GOARCH, you must also specify GOOS.

### Examples
Expand Down Expand Up @@ -305,17 +303,13 @@ Extract from specific folders and place to specific folders:
}
```

### Notes

Do not add any prefix like "/", "./" or "../". Otherwise, Lip will refused to install the tooth.

## possession

Declares the which folders are in the possession of the tooth. When uninstalling, files in the declared folders will be removed. However, when upgrading or reinstalling, Lip will keep files in both the possession of the previous version and the version to install (but those dedicated in placement will still be removed).

### Syntax

Each item of the list should be a valid directory path relative to the root of BDS ending with "/".
Each item of the list should be a valid directory path ending with "/".

### Examples

Expand Down Expand Up @@ -439,12 +433,10 @@ This is a JSON schema of tooth.json, describing the syntax of tooth.json.
],
"properties": {
"source": {
"type": "string",
"pattern": "^[a-zA-Z0-9-_]([a-zA-Z0-9-_\\.\/]*([a-zA-Z0-9-_]|\\/\\*))?$"
"type": "string"
},
"destination": {
"type": "string",
"pattern": "^[a-zA-Z0-9-_]([a-zA-Z0-9-_\\.\/]*([a-zA-Z0-9-_]|\\/\\*))?$"
"type": "string"
},
"GOOS": {
"type": "string"
Expand All @@ -459,8 +451,7 @@ This is a JSON schema of tooth.json, describing the syntax of tooth.json.
"type": "array",
"additionalItems": false,
"items": {
"type": "string",
"pattern": "^[a-zA-Z0-9-_][a-zA-Z0-9-_\\.\/]*\\/$"
"type": "string"
}
},
"commands": {
Expand Down
4 changes: 4 additions & 0 deletions docs/zh-Hans/commands/lip_install.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ Lip在安装依赖之前,是按照 "拓扑顺序 "安装依赖。当遇到依

重新安装tooth包,即使它们已经是最新的了。重新安装时,Lip会先卸载已安装的tooth包,然后再安装它。如果指定了版本,Lip将安装该版本,否则就是最新的版本。

- `-y, --yes`

Assume yes to all prompts and run non-interactively.

## 样例

从tooth存储库安装。
Expand Down
19 changes: 5 additions & 14 deletions docs/zh-Hans/tooth_json_file_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,12 +272,10 @@ Lip提供了一些版本匹配规则:

### 语法

每个放置规则应该包含一个源字段和一个目标字段。Lip将从源字段指定的tooth包中的相对路径中提取文件,并将它们放置到目标地指定的BDS根目录的相对路径中
每个放置规则应该包含一个源字段和一个目标字段。Lip将从源字段指定的tooth包中的相对路径中提取文件,并将它们放置到目标地指定的路径中

如果源目录和目标目录都以 "*"结尾,则该位置将被视为通配符。Lip将递归地把源目录下的所有文件放置到目标目录。

在这里我们做了一个严格的规定,源和目的地只能包含字母、数字、连字符、下划线、点、斜线和星号(对于最后一个字母作为通配符处理)[a-zA-Z0-9-_\.\/\*]。如果你想把文件放到BDS的根部,你应该在`placement`字段中指定每个文件。第一个字母不应该是斜线或点。最后一个字母不应该是斜线。

You can also specify GOOS and GOARCH to optionally place files for specific platforms. For example, you can specify "windows" and "amd64" to place files only for Windows 64-bit. If you want to place files for all platforms, you can omit the GOOS and GOARCH fields. However, if you have specified GOARCH, you must also specify GOOS.

### 样例
Expand Down Expand Up @@ -306,17 +304,13 @@ You can also specify GOOS and GOARCH to optionally place files for specific plat
}
```

### 注意

不要添加任何前缀,如 "/", "./" 或 "../".否则,Lip将拒绝安装这一tooth包。

## `possession`

声明哪些文件夹是由tooth包拥有的。卸载时,声明的文件夹中的文件将被删除。升级或重新安装时,在新旧两个版本的possession中都制定了的文件夹中的文件不会被移除(placement指定的除外)。

### 语法

列表中的每一项都应该是相对于BDS根的有效目录路径,以"/"结尾。
列表中的每一项都应该是有效目录路径,以"/"结尾。

### 样例

Expand Down Expand Up @@ -440,12 +434,10 @@ windows/arm64
],
"properties": {
"source": {
"type": "string",
"pattern": "^[a-zA-Z0-9-_]([a-zA-Z0-9-_\\.\/]*([a-zA-Z0-9-_]|\\/\\*))?$"
"type": "string"
},
"destination": {
"type": "string",
"pattern": "^[a-zA-Z0-9-_]([a-zA-Z0-9-_\\.\/]*([a-zA-Z0-9-_]|\\/\\*))?$"
"type": "string"
},
"GOOS": {
"type": "string"
Expand All @@ -460,8 +452,7 @@ windows/arm64
"type": "array",
"additionalItems": false,
"items": {
"type": "string",
"pattern": "^[a-zA-Z0-9-_][a-zA-Z0-9-_\\.\/]*\\/$"
"type": "string"
}
},
"commands": {
Expand Down
9 changes: 7 additions & 2 deletions src/cmd/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type FlagDict struct {
helpFlag bool
upgradeFlag bool
forceReinstallFlag bool
yesFlag bool
}

const helpMessage = `
Expand All @@ -35,7 +36,8 @@ Description:
Options:
-h, --help Show help.
--upgrade Upgrade the specified tooth to the newest available version.
--force-reinstall Reinstall the tooth even if they are already up-to-date.`
--force-reinstall Reinstall the tooth even if they are already up-to-date.
-y, --yes Assume yes to all prompts and run non-interactively.`

// Run is the entry point.
func Run() {
Expand All @@ -61,6 +63,9 @@ func Run() {

flagSet.BoolVar(&flagDict.forceReinstallFlag, "force-reinstall", false, "")

flagSet.BoolVar(&flagDict.yesFlag, "yes", false, "")
flagSet.BoolVar(&flagDict.yesFlag, "y", false, "")

flagSet.Parse(os.Args[2:])

// Help flag has the highest priority.
Expand Down Expand Up @@ -302,7 +307,7 @@ func Run() {
// TODO: Check if the tooth file is manually installed.
isManuallyInstalled := false

err = install(toothFile, isManuallyInstalled)
err = install(toothFile, isManuallyInstalled, flagDict.yesFlag)
if err != nil {
logger.Error(err.Error())
return
Expand Down
70 changes: 46 additions & 24 deletions src/cmd/install/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/liteldev/lip/tooth/toothfile"
"github.com/liteldev/lip/tooth/toothrecord"
"github.com/liteldev/lip/utils/download"
"github.com/liteldev/lip/utils/logger"
versionutils "github.com/liteldev/lip/utils/version"
)

Expand Down Expand Up @@ -163,7 +164,7 @@ func FetchVersionList(repoPath string) ([]versionutils.Version, error) {
}

// Install installs the .tth file.
func install(t toothfile.ToothFile, isManuallyInstalled bool) error {
func install(t toothfile.ToothFile, isManuallyInstalled bool, isYes bool) error {
// 1. Check if the tooth is already installed.

recordDir, err := localfile.RecordDir()
Expand All @@ -179,37 +180,20 @@ func install(t toothfile.ToothFile, isManuallyInstalled bool) error {
return errors.New("the tooth is already installed")
}

// 2. Install the record file.
// 2. Place the files to the right place in the workspace.

// Create a record object from the metadata.
record := toothrecord.NewFromMetadata(t.Metadata(), isManuallyInstalled)

// Encode the record object to JSON.
recordJSON, err := record.JSON()
if err != nil {
return err
}

// Write the metadata bytes to the record file.
err = os.WriteFile(recordFilePath, recordJSON, 0755)
// Open the .tth file.
r, err := zip.OpenReader(t.FilePath())
if err != nil {
return errors.New("failed to write record file " + recordFilePath + " " + err.Error())
return errors.New("failed to open tooth file " + t.FilePath())
}

// 3. Place the files to the right place in the workspace.
defer r.Close()

workSpaceDir, err := localfile.WorkSpaceDir()
if err != nil {
return err
}

// Open the .tth file.
r, err := zip.OpenReader(t.FilePath())
if err != nil {
return errors.New("failed to open tooth file " + t.FilePath())
}
defer r.Close()

// Get the file prefix.
filePrefix := toothfile.GetFilePrefix(r)

Expand All @@ -223,7 +207,28 @@ func install(t toothfile.ToothFile, isManuallyInstalled bool) error {
}

source := placement.Source
destination := workSpaceDir + "/" + placement.Destination
destination := placement.Destination

if !isYes {
workSpaceDirAbs, err := filepath.Abs(workSpaceDir)
if err != nil {
return errors.New("failed to get the absolute path of the workspace directory")
}

destinationAbs, err := filepath.Abs(destination)
if err != nil {
return errors.New("failed to get the absolute path of the destination")
}

relPath, err := filepath.Rel(workSpaceDirAbs, destinationAbs)
if err != nil || strings.HasPrefix(relPath, "../") || strings.HasPrefix(relPath, "..\\") {
ans := logger.Prompt("The destination " + destination + " is not in the workspace. Do you want to continue? (y/N)")
if ans != "y" && ans != "Y" {
return errors.New("installation aborted")
}
isYes = true
}
}

// Create the parent directory of the destination.
os.MkdirAll(filepath.Dir(destination), 0755)
Expand Down Expand Up @@ -291,6 +296,23 @@ func install(t toothfile.ToothFile, isManuallyInstalled bool) error {
}
}

// 3. Install the record file.

// Create a record object from the metadata.
record := toothrecord.NewFromMetadata(t.Metadata(), isManuallyInstalled)

// Encode the record object to JSON.
recordJSON, err := record.JSON()
if err != nil {
return err
}

// Write the metadata bytes to the record file.
err = os.WriteFile(recordFilePath, recordJSON, 0755)
if err != nil {
return errors.New("failed to write record file " + recordFilePath + " " + err.Error())
}

return nil
}

Expand Down
23 changes: 7 additions & 16 deletions src/cmd/uninstall/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/liteldev/lip/localfile"
"github.com/liteldev/lip/tooth/toothrecord"
"github.com/liteldev/lip/utils/logger"
)

// Uninstall uninstalls a tooth.
Expand Down Expand Up @@ -82,12 +83,7 @@ func Uninstall(recordFileName string, possessionList []string) error {
continue
}

workspaceDir, err := localfile.WorkSpaceDir()
if err != nil {
return err
}

destination := workspaceDir + "/" + placement.Destination
destination := placement.Destination

// Continue if the destination does not exist.
if _, err := os.Stat(destination); os.IsNotExist(err) {
Expand All @@ -96,7 +92,7 @@ func Uninstall(recordFileName string, possessionList []string) error {

err = os.Remove(destination)
if err != nil {
return errors.New("cannot delete the file " + destination + ": " + err.Error())
logger.Error("cannot delete the file " + destination + ": " + err.Error() + ". Please delete it manually.")
}

// Delete the parent directory if it is empty.
Expand All @@ -110,7 +106,7 @@ func Uninstall(recordFileName string, possessionList []string) error {
if len(files) == 0 {
err = os.Remove(parentDir)
if err != nil {
return errors.New("cannot delete the directory " + parentDir + ": " + err.Error())
logger.Error("cannot delete the directory " + parentDir + ": " + err.Error() + ". Please delete it manually.")
}
}
}
Expand All @@ -131,22 +127,17 @@ func Uninstall(recordFileName string, possessionList []string) error {
continue
}

workspaceDir, err := localfile.WorkSpaceDir()
if err != nil {
return err
}

// Remove the folder.
err = os.RemoveAll(workspaceDir + "/" + possession)
err = os.RemoveAll(possession)
if err != nil {
return errors.New("cannot delete the folder " + workspaceDir + "/" + possession + ": " + err.Error())
logger.Error("cannot delete the folder " + possession + ": " + err.Error() + ". Please delete it manually.")
}
}

// Delete the record file.
err = os.Remove(recordDir + "/" + recordFileName)
if err != nil {
return errors.New("cannot delete the record file " + recordDir + "/" + recordFileName + ": " + err.Error())
logger.Error("cannot delete the record file " + recordDir + "/" + recordFileName + ": " + err.Error() + ". Please delete it manually.")
}

return nil
Expand Down
Loading

0 comments on commit c603052

Please sign in to comment.