From eb9c479123fc739efdcdf0fcae23f4008c5503f0 Mon Sep 17 00:00:00 2001 From: Zijian Zhang Date: Mon, 30 Dec 2024 16:13:47 +0800 Subject: [PATCH 01/63] docs: draft v3 schema --- schemas/tooth.v3.schema.json | 147 +++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 schemas/tooth.v3.schema.json diff --git a/schemas/tooth.v3.schema.json b/schemas/tooth.v3.schema.json new file mode 100644 index 0000000..09718d2 --- /dev/null +++ b/schemas/tooth.v3.schema.json @@ -0,0 +1,147 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "format_version": { + "type": "integer", + "const": 3 + }, + "tooth": { + "type": "string" + }, + "version": { + "type": "string" + }, + "info": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9-]+(:[a-z0-9-]+)?$" + } + }, + "avatar_url": { + "type": "string" + } + } + } + }, + "patternProperties": { + "^([a-z0-9]+|\\*)\\/([a-z0-9]+|\\*)$": { + "type": "object", + "properties": { + "commands": { + "type": "object", + "properties": { + "pre_install": { + "type": "array", + "items": { + "type": "string" + } + }, + "post_install": { + "type": "array", + "items": { + "type": "string" + } + }, + "pre_uninstall": { + "type": "array", + "items": { + "type": "string" + } + }, + "post_uninstall": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "dependencies": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "string" + } + } + }, + "prerequisites": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "string" + } + } + }, + "assets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "download_urls": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "string", + "enum": [ + "uncompressed", + "tar", + "tar.gz", + "zip" + ] + }, + "place": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "type": "string" + }, + "dest": { + "type": "string" + } + }, + "required": [ + "src", + "dest" + ] + } + }, + "preserve": { + "type": "array", + "items": { + "type": "string" + } + }, + "remove": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "required": [ + "format_version", + "tooth", + "version" + ] +} From 48708865ccbfdfd9d13bb3231f51682c4b3fcd39 Mon Sep 17 00:00:00 2001 From: Zijian Zhang Date: Mon, 30 Dec 2024 16:19:39 +0800 Subject: [PATCH 02/63] docs: add required field to assets --- schemas/tooth.v3.schema.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/schemas/tooth.v3.schema.json b/schemas/tooth.v3.schema.json index 09718d2..9412422 100644 --- a/schemas/tooth.v3.schema.json +++ b/schemas/tooth.v3.schema.json @@ -133,7 +133,11 @@ "type": "string" } } - } + }, + "required": [ + "download_urls", + "type" + ] } } } From 1ea36a265f6afb8c3642ff97d182b440db63b3df Mon Sep 17 00:00:00 2001 From: Zijian Zhang Date: Fri, 3 Jan 2025 17:07:34 +0800 Subject: [PATCH 03/63] refactor: remove everything for a new start --- .github/ISSUE_TEMPLATE/bug_report.yml | 45 -- .github/ISSUE_TEMPLATE/feature_request.yml | 28 - .github/PULL_REQUEST_TEMPLATE.md | 18 - .github/dependabot.yml | 12 - .github/workflows/build.yml | 63 -- .github/workflows/codeql.yml | 76 -- .github/workflows/release.yml | 140 ---- .gitignore | 29 - CHANGELOG.md | 510 ------------- COPYING | 674 ------------------ README.md | 79 -- cmd/lip/main.go | 81 --- docs/faq.md | 17 - docs/faq.zh.md | 17 - docs/images/favicon.ico | Bin 4286 -> 0 bytes docs/images/logo.webp | Bin 118 -> 0 bytes docs/index.md | 77 -- docs/index.zh.md | 77 -- docs/install.md | 19 - docs/install.zh.md | 19 - docs/quickstart.md | 95 --- docs/quickstart.zh.md | 95 --- docs/reference/lip.md | 35 - docs/reference/lip.zh.md | 29 - docs/reference/lip_cache.md | 17 - docs/reference/lip_cache.zh.md | 17 - docs/reference/lip_cache_purge.md | 17 - docs/reference/lip_cache_purge.zh.md | 17 - docs/reference/lip_config.md | 22 - docs/reference/lip_config.zh.md | 22 - docs/reference/lip_install.md | 102 --- docs/reference/lip_list.md | 25 - docs/reference/lip_list.zh.md | 27 - docs/reference/lip_show.md | 25 - docs/reference/lip_show.zh.md | 25 - docs/reference/lip_tooth.md | 17 - docs/reference/lip_tooth.zh.md | 17 - docs/reference/lip_tooth_init.md | 17 - docs/reference/lip_tooth_init.zh.md | 17 - docs/reference/lip_tooth_pack.md | 17 - docs/reference/lip_uninstall.md | 26 - docs/reference/lip_uninstall.zh.md | 26 - docs/reference/tooth_json_file_reference.md | 428 ----------- .../reference/tooth_json_file_reference.zh.md | 428 ----------- docs/tutorials/create_a_lip_tooth.md | 126 ---- docs/tutorials/create_a_lip_tooth.zh.md | 124 ---- go.mod | 27 - go.sum | 56 -- internal/cmd/cmdlip/cmdlip.go | 139 ---- internal/cmd/cmdlipcache/cmdlipcache.go | 27 - .../cmd/cmdlipcachepurge/cmdlipcachepurge.go | 51 -- internal/cmd/cmdlipconfig/cmdlipconfig.go | 171 ----- internal/cmd/cmdlipfreeze/cmdlipfreeze.go | 55 -- internal/cmd/cmdlipinstall/archive.go | 226 ------ internal/cmd/cmdlipinstall/cmdlipinstall.go | 235 ------ internal/cmd/cmdlipinstall/dependencies.go | 131 ---- internal/cmd/cmdlipinstall/download.go | 176 ----- internal/cmd/cmdlipinstall/prerequisites.go | 63 -- internal/cmd/cmdlipinstall/specifier.go | 89 --- internal/cmd/cmdliplist/cmdliplist.go | 178 ----- internal/cmd/cmdlipshow/cmdlipshow.go | 148 ---- internal/cmd/cmdliptooth/cmdliptooth.go | 28 - .../cmd/cmdliptoothinit/cmdliptoothinit.go | 126 ---- .../cmd/cmdliptoothpack/cmdliptoothpack.go | 235 ------ .../cmd/cmdlipuninstall/cmdlipuninstall.go | 102 --- internal/context/config.go | 7 - internal/context/context.go | 221 ------ internal/install/commands.go | 44 -- internal/install/install.go | 291 -------- internal/install/uninstall.go | 182 ----- internal/must/must.go | 9 - internal/network/github.go | 26 - internal/network/gomoduleproxy.go | 69 -- internal/network/network.go | 84 --- internal/path/path.go | 211 ------ internal/specifier/specifier.go | 164 ----- internal/tooth/archive.go | 245 ------- internal/tooth/jsonshema.go | 231 ------ internal/tooth/metadata.go | 404 ----------- internal/tooth/migration/v1tov2/v1tov2.go | 299 -------- internal/tooth/rawmetadata.go | 54 -- internal/tooth/utils.go | 203 ------ internal/zip/zip.go | 29 - mkdocs.yml | 96 --- requirements.txt | 3 - schemas/tooth.v1.schema.json | 99 --- schemas/tooth.v2.schema.json | 229 ------ schemas/tooth.v3.schema.json | 151 ---- scripts/make_installer.nsi | 152 ---- scripts/setup_nsis.ps1 | 11 - scripts/validate_release.py | 57 -- 91 files changed, 9628 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml delete mode 100644 .github/PULL_REQUEST_TEMPLATE.md delete mode 100644 .github/dependabot.yml delete mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/codeql.yml delete mode 100644 .github/workflows/release.yml delete mode 100644 .gitignore delete mode 100644 CHANGELOG.md delete mode 100644 COPYING delete mode 100644 README.md delete mode 100644 cmd/lip/main.go delete mode 100644 docs/faq.md delete mode 100644 docs/faq.zh.md delete mode 100644 docs/images/favicon.ico delete mode 100644 docs/images/logo.webp delete mode 100644 docs/index.md delete mode 100644 docs/index.zh.md delete mode 100644 docs/install.md delete mode 100644 docs/install.zh.md delete mode 100644 docs/quickstart.md delete mode 100644 docs/quickstart.zh.md delete mode 100644 docs/reference/lip.md delete mode 100644 docs/reference/lip.zh.md delete mode 100644 docs/reference/lip_cache.md delete mode 100644 docs/reference/lip_cache.zh.md delete mode 100644 docs/reference/lip_cache_purge.md delete mode 100644 docs/reference/lip_cache_purge.zh.md delete mode 100644 docs/reference/lip_config.md delete mode 100644 docs/reference/lip_config.zh.md delete mode 100644 docs/reference/lip_install.md delete mode 100644 docs/reference/lip_list.md delete mode 100644 docs/reference/lip_list.zh.md delete mode 100644 docs/reference/lip_show.md delete mode 100644 docs/reference/lip_show.zh.md delete mode 100644 docs/reference/lip_tooth.md delete mode 100644 docs/reference/lip_tooth.zh.md delete mode 100644 docs/reference/lip_tooth_init.md delete mode 100644 docs/reference/lip_tooth_init.zh.md delete mode 100644 docs/reference/lip_tooth_pack.md delete mode 100644 docs/reference/lip_uninstall.md delete mode 100644 docs/reference/lip_uninstall.zh.md delete mode 100644 docs/reference/tooth_json_file_reference.md delete mode 100644 docs/reference/tooth_json_file_reference.zh.md delete mode 100644 docs/tutorials/create_a_lip_tooth.md delete mode 100644 docs/tutorials/create_a_lip_tooth.zh.md delete mode 100644 go.mod delete mode 100644 go.sum delete mode 100644 internal/cmd/cmdlip/cmdlip.go delete mode 100644 internal/cmd/cmdlipcache/cmdlipcache.go delete mode 100644 internal/cmd/cmdlipcachepurge/cmdlipcachepurge.go delete mode 100644 internal/cmd/cmdlipconfig/cmdlipconfig.go delete mode 100644 internal/cmd/cmdlipfreeze/cmdlipfreeze.go delete mode 100644 internal/cmd/cmdlipinstall/archive.go delete mode 100644 internal/cmd/cmdlipinstall/cmdlipinstall.go delete mode 100644 internal/cmd/cmdlipinstall/dependencies.go delete mode 100644 internal/cmd/cmdlipinstall/download.go delete mode 100644 internal/cmd/cmdlipinstall/prerequisites.go delete mode 100644 internal/cmd/cmdlipinstall/specifier.go delete mode 100644 internal/cmd/cmdliplist/cmdliplist.go delete mode 100644 internal/cmd/cmdlipshow/cmdlipshow.go delete mode 100644 internal/cmd/cmdliptooth/cmdliptooth.go delete mode 100644 internal/cmd/cmdliptoothinit/cmdliptoothinit.go delete mode 100644 internal/cmd/cmdliptoothpack/cmdliptoothpack.go delete mode 100644 internal/cmd/cmdlipuninstall/cmdlipuninstall.go delete mode 100644 internal/context/config.go delete mode 100644 internal/context/context.go delete mode 100644 internal/install/commands.go delete mode 100644 internal/install/install.go delete mode 100644 internal/install/uninstall.go delete mode 100644 internal/must/must.go delete mode 100644 internal/network/github.go delete mode 100644 internal/network/gomoduleproxy.go delete mode 100644 internal/network/network.go delete mode 100644 internal/path/path.go delete mode 100644 internal/specifier/specifier.go delete mode 100644 internal/tooth/archive.go delete mode 100644 internal/tooth/jsonshema.go delete mode 100644 internal/tooth/metadata.go delete mode 100644 internal/tooth/migration/v1tov2/v1tov2.go delete mode 100644 internal/tooth/rawmetadata.go delete mode 100644 internal/tooth/utils.go delete mode 100644 internal/zip/zip.go delete mode 100644 mkdocs.yml delete mode 100644 requirements.txt delete mode 100644 schemas/tooth.v1.schema.json delete mode 100644 schemas/tooth.v2.schema.json delete mode 100644 schemas/tooth.v3.schema.json delete mode 100644 scripts/make_installer.nsi delete mode 100644 scripts/setup_nsis.ps1 delete mode 100644 scripts/validate_release.py diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index e0aaadc..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Bug Report -description: Create a report to help us improve -title: "[Bug]: " -labels: ["bug"] -body: - - type: textarea - attributes: - label: Describe the bug - description: A clear and concise description of what the bug is. - validations: - required: true - - - type: textarea - attributes: - label: To Reproduce - description: Steps to reproduce the behavior. - validations: - required: true - - - type: textarea - attributes: - label: Expected behavior - description: A clear and concise description of what you expected to happen. - validations: - required: true - - - type: textarea - attributes: - label: Screenshots - description: If applicable, add screenshots to help explain your problem. - - - type: input - attributes: - label: Platform - description: The platform you are using. (e.g. Windows 10, macOS 10.15, Ubuntu 20.04) - - - type: input - attributes: - label: Version - description: The version of the application you are using. (e.g. 1.0.0) - - - type: textarea - attributes: - label: Additional context - description: Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index b0f4b33..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Feature request -description: Suggest an idea for this project -title: "[Feature]: " -labels: ["enhancement"] -body: - - type: textarea - attributes: - label: Is your feature request related to a problem? Please describe. - description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - validations: - required: true - - - type: textarea - attributes: - label: Describe the solution you'd like - description: A clear and concise description of what you want to happen. - validations: - required: true - - - type: textarea - attributes: - label: Describe alternatives you've considered - description: A clear and concise description of any alternative solutions or features you've considered. - - - type: textarea - attributes: - label: Additional context - description: Add any other context or screenshots about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 164d406..0000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,18 +0,0 @@ -## What does this PR do? - - - -## Which issues does this PR resolve? - - - -## Checklist before merging - -Thank you for your contribution to the repository. -Before submitting this PR, please make sure: - -- [ ] Your code builds clean without any errors or warnings -- [ ] Your code follows [Google Go Style Guide](https://google.github.io/styleguide/go/) -- [ ] You have tested all functions -- [ ] You have not used code without license -- [ ] You have added statement for third-party code diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index b577183..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,12 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: "gomod" # See documentation for possible values - directory: "" # Location of package manifests - schedule: - interval: "daily" - diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 1167af1..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,63 +0,0 @@ -on: - pull_request: - push: - workflow_dispatch: - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - GOOS: - - darwin - - linux - - windows - GOARCH: - - amd64 - - arm64 - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-go@v5 - with: - cache: true - - - name: Build - run: | - export GOOS=${{ matrix.GOOS }} - export GOARCH=${{ matrix.GOARCH }} - go build -ldflags "-s -w" -o bin/ github.com/lippkg/lip/cmd/lip - - - uses: actions/upload-artifact@v4 - with: - name: lip-${{ matrix.GOOS }}-${{ matrix.GOARCH }}-${{ github.sha }} - path: bin - - build-docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Install MkDocs and MkDocs static i18n plugin - run: | - pip install -r requirements.txt - - name: Build - run: | - mkdocs build - - uses: actions/upload-pages-artifact@v3 - with: - path: site/ - - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - if: github.ref == 'refs/heads/main' && github.event_name == 'push' - needs: build-docs - permissions: - id-token: write - pages: write - runs-on: ubuntu-latest - steps: - - id: deployment - uses: actions/deploy-pages@v4 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 61cff61..0000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,76 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ main ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ main ] - schedule: - - cron: '36 13 * * 6' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'go' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Use only 'java' to analyze code written in Java, Kotlin or both - # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - category: "/language:${{matrix.language}}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index b82b98d..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,140 +0,0 @@ -on: - release: - types: - - published - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - GOOS: - - darwin - - linux - - windows - GOARCH: - - amd64 - - arm64 - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-go@v5 - with: - cache: true - - - name: Build - run: | - export GOOS=${{ matrix.GOOS }} - export GOARCH=${{ matrix.GOARCH }} - go build -ldflags "-s -w" -o bin/ github.com/lippkg/lip/cmd/lip - - - uses: actions/upload-artifact@v4 - with: - name: lip-${{ matrix.GOOS }}-${{ matrix.GOARCH }}-${{ github.sha }} - path: bin - - pack: - needs: build - runs-on: windows-latest - strategy: - matrix: - OS: - - windows - ARCH: - - amd64 - - arm64 - steps: - - name: Checkout the repository - uses: actions/checkout@v4 - - - name: Download build - uses: actions/download-artifact@v4 - with: - name: lip-${{ matrix.OS }}-${{ matrix.ARCH }}-${{ github.sha }} - - - name: Set up NSIS - run: .\scripts\setup_nsis.ps1 - - - name: Build setup program - run: | - .\nsis-portable\makensis /DLIP_VERSION=${{ github.ref_name }} /DLIP_OS=${{ matrix.OS }} /DLIP_ARCH=${{ matrix.ARCH }} ./nsis/make_installer.nsi - - - name: Upload build - uses: actions/upload-artifact@v4 - with: - name: lip-${{ matrix.OS }}-${{ matrix.ARCH }}-${{ github.sha }}-setup - path: nsis/lip-${{ matrix.OS }}-${{ matrix.ARCH }}-setup.exe - - update-release-notes: - permissions: - contents: write - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Validate release stuff (tooth.json, CHANGELOG.md, etc.) - run: | - npm i -g keep-a-changelog - python scripts/validate_release.py --tag ${{ github.event.release.tag_name }} - - - id: extract-release-notes - uses: ffurrer2/extract-release-notes@v2 - - - uses: softprops/action-gh-release@v1 - with: - body: ${{ steps.extract-release-notes.outputs.release_notes }} - - upload-to-release: - needs: - - update-release-notes - - pack - permissions: - contents: write - runs-on: ubuntu-latest - strategy: - matrix: - GOOS: [darwin, linux, windows] - GOARCH: [amd64, arm64] - steps: - - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 - with: - name: lip-${{ matrix.GOOS }}-${{ matrix.GOARCH }}-${{ github.sha }} - path: artifact - - - uses: actions/download-artifact@v4 - if: matrix.GOOS == 'windows' - with: - name: lip-${{ matrix.GOOS }}-${{ matrix.GOARCH }}-${{ github.sha }}-setup - - - name: Copy essential files - run: | - cp CHANGELOG.md COPYING README.md artifact/ - - - name: Pack artifact (Windows) - if: matrix.GOOS == 'windows' - run: | - zip -r ../lip-${{ matrix.GOOS }}-${{ matrix.GOARCH }}.zip * - working-directory: artifact - - - name: Pack artifact (Others) - if: matrix.GOOS != 'windows' - run: | - tar -czvf ../lip-${{ matrix.GOOS }}-${{ matrix.GOARCH }}.tar.gz * - working-directory: artifact - - - name: Upload artifact to release (Windows) - if: matrix.GOOS == 'windows' - uses: softprops/action-gh-release@v2 - with: - files: | - lip-${{ matrix.GOOS }}-${{ matrix.GOARCH }}.zip - lip-${{ matrix.GOOS }}-${{ matrix.GOARCH }}-setup.exe - - - name: Upload artifact to release (Others) - if: matrix.GOOS != 'windows' - uses: softprops/action-gh-release@v2 - with: - files: | - lip-${{ matrix.GOOS }}-${{ matrix.GOARCH }}.tar.gz diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 1483838..0000000 --- a/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -## Go.gitignore -# If you prefer the allow list template instead of the deny list, see community template: -# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore -# -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Dependency directories (remove the comment below to include it) -# vendor/ - -# Go workspace file -go.work - - -## Repository specific -.lip/ -.vscode/ -bin/ -site/ diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 264af27..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,510 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [0.24.0] - 2024-10-01 - -### Added - -- `specifiers.txt` support - -### Changed - -- Allow colons in tags -- Parse arguments with `urfave/cli` - -## [0.23.2] - 2024-08-17 - -### Fixed - -- Partially resolved [#157] by adding .tmp extension to incomplete downloads (#158) - -## [0.23.1] - 2024-07-28 - -### Added - -- Add colorlog detector (#146) -- Add a line break to the end of the output of `lip --version` - -## [0.23.0] - 2024-07-12 - -### Added - -- Add version variable support for asset_url [#134] - -### Fixed - -- Fix `.lip` path in linux [#129] -- Fix unable to decompress .tar.gz [#140] -- Delete surplus tabs (#139) - -## [0.22.0] - 2024-03-23 - -### Added - -- `--no-color` flag to disable color output. -- NSIS installer. - -## [0.21.2] - 2024-02-21 - -### Fixed - -- Some ambiguous error messages. -- Not prompting for confirmation to overwrite existing files when installing a tooth. - -## [0.21.1] - 2024-02-17 - -### Fixed - -- Untidy error messages. -- Failed to install any tooth without setting a proxy. - -## [0.21.0] - 2024-02-17 - -### Added - -- Proxy configuration support. -- Support more kinds of GitHub mirrors -- Support `NO_COLOR` environment variable to disable color output. - -### Changed - -- Set `https://github.com` as the default GitHub mirror. - -### Fixed - -- Panic when dependencies and prerequisites have malformed version strings. - -## [0.20.1] - 2024-02-04 - -### Fixed - -- Version string. - -## [0.20.0] - 2024-02-03 - -### Added - -- Support Go module URL as asset URL. - -### Changed - -- Files downloaded from GitHub mirror are now cached in different paths. - -### Removed - -- `source` field in `tooth.json`. - -## [0.19.0] - 2024-01-20 - -### Added - -- Tons of debug logging. -- Show available versions without installing. - -### Changed - -- Optimize error logging. - -### Fixed - -- Wildcard not parsing correctly. -- Wrong error output when "%" is used in messages. -- Wrong version `@0.0.0` when stringify specifiers without version. - -## [0.18.1] - 2024-01-12 - -### Fixed - -- Platform-specific items in tooth.json. -- Go module path escaping. - -## [0.18.0] - 2024-01-12 - -### Added - -- Config support. -- GitHub mirror support. - -### Changed - -- Use `github.com/sirupsen/logrus` for logging. -- Use `github.com/blang/semver/v4` for versioning. -- Refactor most of the code. - -### Removed - -- Remove `lip autoremove` command. -- Percentage progress bar. - -### Fixed - -- Some bugs. - -## [0.17.0] - 2023-12-30 - -### Added - -- Warning for deprecated tooth.json format versions. -- `info.source` field for indicating the source repo of a tooth. - -### Changed - -- Check tooth repo validity with Go official module library. - -### Fixed - -- Failed to uninstall a tooth when some of its files are already removed. - -## [0.16.1] - 2023-10-11 - -### Fixed - -- Unable to specify which version to install. - -## [0.16.0] - 2023-10-09 - -### Added - -- Prerequisite support. - -### Fixed - -- Failing to parse files under root directory of a tooth. - -## [0.15.2] - 2023-09-05 - -### Fixed - -- Inconsistent tooth.json schema with lipIndex. -- Wrong help messages. - -## [0.15.1] - 2023-09-01 - -### Fixed - -- Wrong help message for `--json` flag of `lip show` command. -- Wrong regex for validating versions. - -## [0.15.0] - 2023-06-23 - -### Added - -- Tooth file preservation and removal. - -### Changed - -- Optimize output information format. -- Change tooth.json format to version 2. -- Force to specify output path in `lip tooth pack` command. -- Refactor all code and optimize performance. - -### Removed - -- `lip exec` command. -- Tooth file possession (use "preserve" and "remove" instead). -- Number-only progress bar. -- Registry support. -- Placed files display in `lip show` command. - -## [0.14.2] - 2023-04-24 - -### Fixed - -- Remove misleading message when dependencies are satisfied. -- Packed tooth file not able to be installed. - -## [0.14.1] - 2023-04-20 - -### Fixed - -- Broken GOOS and GOARCH specifiers of placement. - -## [0.14.0] - 2023-03-24 - -### Added - -- `lip tooth pack` command to pack a tooth into a tooth file. -- Questions when initializing a tooth. - -### Fixed - -- Not properly parsing placement sometimes. -- Not aborting when a tooth fails to install. - -## [0.13.0] - 2023-03-05 - -### Added - -- Aliases for subcommands. - -### Changed - -- Tool should be registered in `tooth.json` manually now. - -## [0.12.0] - 2023-02-27 - -### Added - -- Dependency version validation when installing. -- `lip autoremove` command to remove teeth not required by other teeth. - -### Fixed - -- Failing to run tools with arguments. -- Dependencies still being installed when the dependent is not going to be installed. - -## [0.11.45141] - 2023-02-19 - -### Fixed - -- Wrongly showing debug information when redirecting to local lip. - -## [0.11.4514] - 2023-02-17 - -### Added - -- Showing information about installed teeth in `lip list` command. - -## [0.11.0] - 2023-02-17 - -### Added - -- `--available` flag for `lip show` command. -- `--numeric-progress` flag for `lip install` command. -- `--no-dependencies` flag for `lip install` command. -- `confirmation` field in `tooth.json` to show messages and ask for confirmation before installing. -- Check for invalid additional arguments. -- Structured information output. -- Support for multiple GOPROXYs. -- `--keep-possession` flag for `lip uninstall` command. -- Automatic deletion of empty directories when uninstalling a tooth. -- Support for file possession. - -### Fixed - -- Remove wrongly displayed debug information. -- Failing to re-download when a broken tooth file exists in cache. - -## [0.10.0] - 2023-02-12 - -### Added - -- Verbose and quiet mode. -- JSON output support for `lip list` and `lip show`. - -## [0.9.0] - 2023-02-11 - -### Added - -- HTTP Code reporting when failing to make a request. -- `lip list --upgradable` command. -- Topological sorting for dependencies. -- Progress bar for downloading tooth files. - -### Fixed - -- No notice when a tooth file is cached. -- Tooth paths in `dependencies` field of `tooth.json` not converting to lowercase. -- Mistakes in help message of `lip cache purge`. - -## [0.8.3] - 2023-02-09 - -### Fixed - -- Mistakes in path prefix extraction when there is only one file in the tooth. - -## [0.8.2] - 2023-02-09 - -### Fixed - -- Failing to input anything to post-install and pre-uninstall commands. -- Wrong installation order of dependencies. -- Registry not working in `lip show`. -- Unstable versions can be wrongly installed when no specific version is specified. - -## [0.8.1] - 2023-02-07 - -### Fixed - -- Failing to get information from registry with other index than index.json. - -## [0.8.0] - 2023-02-06 - -### Added - -- Registry support. The default registry is . - -### Fixed - -- Failing to uninstall teeth with uppercase letters in provided tooth path. - -## [0.7.1] - 2023-02-05 - -### Fixed - -- Failing to hot update or remove lip in local directory. - -## [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 - -- Support for on-demand installation depending on OS and platform. -- Removal for downloaded tooth files that do not pass the validation. - -## [0.5.1] - 2023-01-30 - -### Fixed - -- Failing to install any tool. - -## [0.5.0] - 2023-01-30 - -### Added - -- Available version list in `lip show` command. -- Redirection to local lip executable when running `lip`. -- Support for pre-uninstall scripts. -- Support for hot update of lip. -- Support for executing tools in `.lip/tools` directory. - -## [0.4.0] - 2023-01-26 - -### Added - -- Post-install script support. -- Tooth path validation. -- Flexible tooth.json parsing. - -## [0.3.4] - 2023-01-25 - -### Changed - -- Bumped github.com/fatih/color from 1.14.0 to 1.14.1. - -### Fixed - -- Misleading error hints. -- Failing to fetch tooth with major version v0 or v1. -- Failing to match dependencies. -- Failing to fetch tooth when uppercase letters exist in tooth path. - -## [0.3.3] - 2023-01-24 - -### Fixed - -- Default to earliest version when no version is specified in tooth.json. -- Panic when tooth.json is invalid. - -## [0.3.2] - 2023-01-23 - -### Added - -- "Add to PATH" option in setup utility. -- Mac OS, Linux and OpenBSD support. -- Arm64 support. - -## [0.3.1] - 2023-01-21 - -### Added - -- Setup utility to install lip. - -## [0.3.0] - 2023-01-20 - -### Added - -- Possession keeping support when force-reinstalling or upgrading. -- `--force-reinstall` flag and `--upgrade` flag support. - -## [0.2.1] - 2023-01-18 - -### Fixed - -- Failing to fetch tooth whose version has suffix `+incompatible`. -- Failing to parse wildcards. - -## [0.2.0] - 2023-01-18 - -### Added - -- Possession field in tooth.json to specify directory to remove when uninstalling a tooth. - -### Changed - -- Change extension name of tooth files to .tth - -### Fixed - -- Fix failing to fetch tooth when the repository does not contain go.mod file. -- Fix failing to parse tooth file when the tooth is downloaded via GOPROXY. -- Fix failing to parse tooth when tooth.json is the only file in the tooth. - -## [0.1.0] - 2023-01-17 - -### Added - -- Basic functions: cache, install, list, show, tooth init, and uninstall. - -[#129]: https://github.com/lippkg/lip/issues/129 -[#134]: https://github.com/lippkg/lip/issues/134 -[#140]: https://github.com/lippkg/lip/issues/140 -[#157]: https://github.com/lippkg/lip/issues/157 - -[0.24.0]: https://github.com/lippkg/lip/compare/v0.23.2...v0.24.0 -[0.23.2]: https://github.com/lippkg/lip/compare/v0.23.1...v0.23.2 -[0.23.1]: https://github.com/lippkg/lip/compare/v0.23.0...v0.23.1 -[0.23.0]: https://github.com/lippkg/lip/compare/v0.22.0...v0.23.0 -[0.22.0]: https://github.com/lippkg/lip/compare/v0.21.2...v0.22.0 -[0.21.2]: https://github.com/lippkg/lip/compare/v0.21.1...v0.21.2 -[0.21.1]: https://github.com/lippkg/lip/compare/v0.21.0...v0.21.1 -[0.21.0]: https://github.com/lippkg/lip/compare/v0.20.1...v0.21.0 -[0.20.1]: https://github.com/lippkg/lip/compare/v0.20.0...v0.20.1 -[0.20.0]: https://github.com/lippkg/lip/compare/v0.19.0...v0.20.0 -[0.19.0]: https://github.com/lippkg/lip/compare/v0.18.1...v0.19.0 -[0.18.1]: https://github.com/lippkg/lip/compare/v0.18.0...v0.18.1 -[0.18.0]: https://github.com/lippkg/lip/compare/v0.17.0...v0.18.0 -[0.17.0]: https://github.com/lippkg/lip/compare/v0.16.1...v0.17.0 -[0.16.1]: https://github.com/lippkg/lip/compare/v0.16.0...v0.16.1 -[0.16.0]: https://github.com/lippkg/lip/compare/v0.15.2...v0.16.0 -[0.15.2]: https://github.com/lippkg/lip/compare/v0.15.1...v0.15.2 -[0.15.1]: https://github.com/lippkg/lip/compare/v0.15.0...v0.15.1 -[0.15.0]: https://github.com/lippkg/lip/compare/v0.14.2...v0.15.0 -[0.14.2]: https://github.com/lippkg/lip/compare/v0.14.1...v0.14.2 -[0.14.1]: https://github.com/lippkg/lip/compare/v0.14.0...v0.14.1 -[0.14.0]: https://github.com/lippkg/lip/compare/v0.13.0...v0.14.0 -[0.13.0]: https://github.com/lippkg/lip/compare/v0.12.0...v0.13.0 -[0.12.0]: https://github.com/lippkg/lip/compare/v0.11.45141...v0.12.0 -[0.11.45141]: https://github.com/lippkg/lip/compare/v0.11.4514...v0.11.45141 -[0.11.4514]: https://github.com/lippkg/lip/compare/v0.11.0...v0.11.4514 -[0.11.0]: https://github.com/lippkg/lip/compare/v0.10.0...v0.11.0 -[0.10.0]: https://github.com/lippkg/lip/compare/v0.9.0...v0.10.0 -[0.9.0]: https://github.com/lippkg/lip/compare/v0.8.3...v0.9.0 -[0.8.3]: https://github.com/lippkg/lip/compare/v0.8.2...v0.8.3 -[0.8.2]: https://github.com/lippkg/lip/compare/v0.8.1...v0.8.2 -[0.8.1]: https://github.com/lippkg/lip/compare/v0.8.0...v0.8.1 -[0.8.0]: https://github.com/lippkg/lip/compare/v0.7.1...v0.8.0 -[0.7.1]: https://github.com/lippkg/lip/compare/v0.7.0...v0.7.1 -[0.7.0]: https://github.com/lippkg/lip/compare/v0.6.0...v0.7.0 -[0.6.0]: https://github.com/lippkg/lip/compare/v0.5.1...v0.6.0 -[0.5.1]: https://github.com/lippkg/lip/compare/v0.5.0...v0.5.1 -[0.5.0]: https://github.com/lippkg/lip/compare/v0.4.0...v0.5.0 -[0.4.0]: https://github.com/lippkg/lip/compare/v0.3.4...v0.4.0 -[0.3.4]: https://github.com/lippkg/lip/compare/v0.3.3...v0.3.4 -[0.3.3]: https://github.com/lippkg/lip/compare/v0.3.2...v0.3.3 -[0.3.2]: https://github.com/lippkg/lip/compare/v0.3.1...v0.3.2 -[0.3.1]: https://github.com/lippkg/lip/compare/v0.3.0...v0.3.1 -[0.3.0]: https://github.com/lippkg/lip/compare/v0.2.1...v0.3.0 -[0.2.1]: https://github.com/lippkg/lip/compare/v0.2.0...v0.2.1 -[0.2.0]: https://github.com/lippkg/lip/compare/v0.1.0...v0.2.0 -[0.1.0]: https://github.com/lippkg/lip/releases/tag/v0.1.0 diff --git a/COPYING b/COPYING deleted file mode 100644 index f288702..0000000 --- a/COPYING +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/README.md b/README.md deleted file mode 100644 index b696500..0000000 --- a/README.md +++ /dev/null @@ -1,79 +0,0 @@ -# lip - -[![Build](https://img.shields.io/github/actions/workflow/status/lippkg/lip/build.yml?style=for-the-badge)](https://github.com/lippkg/lip/actions) -[![Latest Tag](https://img.shields.io/github/v/tag/lippkg/lip?label=LATEST%20TAG&style=for-the-badge)](https://github.com/lippkg/lip/releases/latest) -[![Downloads](https://img.shields.io/github/downloads/lippkg/lip/latest/total?style=for-the-badge)](https://github.com/lippkg/lip/releases/latest) - -A general package installer - -lip is a general package installer. You can use lip to install packages from any Git repository. - -## Security - -This software package manager (hereinafter referred to as "this software") is developed and provided by LiteLDev (hereinafter referred to as "the developer"). This software is designed to help users manage and install various software packages, but is not responsible for any content, quality, functionality, security or legality of any software package. Users should use this software at their own discretion and assume all related risks. - -The developer does not guarantee the stability, reliability, accuracy or completeness of this software. The developer is not liable for any defects, errors, viruses or other harmful components that may exist in this software. The developer is not liable for any direct or indirect damages (including but not limited to data loss, device damage, profit loss etc.) caused by the use of this software. - -The developer reserves the right to modify, update or terminate this software and its related services at any time without prior notice to users. Users should back up important data and check regularly for updates of this software. - -Users should comply with relevant laws and regulations when using this software, respect the intellectual property rights and privacy rights of others, and not use this software for any illegal or infringing activities. If users violate the above provisions and cause any damage to any third party or are claimed by any third party, the developer does not bear any responsibility. - -If you have any questions or comments about this disclaimer, please contact the developer. - -## Install - -First, download latest version of lip from . You may select the version for your platform. - -Then, unzip the content to somewhere you would like to install lip. - -Finally, add the location to PATH environment variable. - -To check if lip is installed successfully, run `lip --version` in your terminal. You should see the version of lip you just installed. - -For more information, please refer to [the documentation](https://docs.lippkg.com). - -## Usage - -To install a online tooth (a package in lip), run `lip install `. Here is an example: - -```bash -lip install github.com/LiteLDev/LeviLamina -``` - -To install a local tooth (typically with `.tth` extension name), run `lip install `. Here is an example: - -```bash -lip install ./example.tth -``` - -To uninstall a tooth, run `lip uninstall `. Here is an example: - -```bash -lip uninstall github.com/LiteLDev/LeviLamina -``` - -To list all installed teeth, run `lip list`. Here is an example: - -To show information of a tooth, run `lip show `. Here is an example: - -```bash -lip show github.com/LiteLDev/LeviLamina -``` - -For more information, please refer to [the documentation](https://docs.lippkg.com). - -## Contributing - -Feel free to dive in! [Open an issue](https://github.com/lippkg/lip/issues/new/choose) or submit PRs. - -lip follows the [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) Code of Conduct. - -### Contributors - -This project exists thanks to all the people who contribute. - -![Contributors](https://contrib.rocks/image?repo=lippkg/lip) - -## License - -GPL-3.0-only © 2021-2024 lippkg diff --git a/cmd/lip/main.go b/cmd/lip/main.go deleted file mode 100644 index ec0ae29..0000000 --- a/cmd/lip/main.go +++ /dev/null @@ -1,81 +0,0 @@ -package main - -import ( - "os" - "reflect" - "runtime" - "strings" - - nested "github.com/antonfisher/nested-logrus-formatter" - "github.com/blang/semver/v4" - "github.com/lippkg/lip/internal/cmd/cmdlip" - "github.com/lippkg/lip/internal/context" - log "github.com/sirupsen/logrus" - "golang.org/x/term" -) - -var defaultConfig context.Config = context.Config{ - GitHubMirrorURL: "https://github.com", - GoModuleProxyURL: "https://goproxy.io", - ProxyURL: "", -} - -var lipVersion semver.Version = semver.MustParse("0.24.0") - -func IsStdoutAndStderrSupportAnsi() bool { - if os.Getenv("NO_COLOR") != "" { - return false - } - if os.Getenv("TERM") != "" && !strings.HasSuffix(os.Getenv("TERM"), "color") { - return false - } - if !term.IsTerminal(int(os.Stdout.Fd())) || !term.IsTerminal(int(os.Stderr.Fd())) { - return false - } - if runtime.GOOS == "windows" { - { - state, err := term.GetState(int(os.Stdout.Fd())) - if err != nil { - return false - } - if reflect.ValueOf(*state).Field(0).Field(0).Uint()&0x4 == 0 { - return false - } - } - { - state, err := term.GetState(int(os.Stderr.Fd())) - if err != nil { - return false - } - if reflect.ValueOf(*state).Field(0).Field(0).Uint()&0x4 == 0 { - return false - } - } - } - return true -} - -func main() { - if !IsStdoutAndStderrSupportAnsi() { - log.SetFormatter(&nested.Formatter{NoColors: true}) - } else { - log.SetFormatter(&nested.Formatter{}) - } - - ctx := context.New(defaultConfig, lipVersion) - - if err := ctx.CreateDirStructure(); err != nil { - log.Errorf("\n\tcannot create directory structure\n\t%v", err.Error()) - return - } - - if err := ctx.LoadOrCreateConfigFile(); err != nil { - log.Errorf("\n\tcannot load or create config file\n\t%v", err.Error()) - return - } - - if err := cmdlip.Run(ctx, os.Args); err != nil { - log.Errorf("\n\t%v", err.Error()) - return - } -} diff --git a/docs/faq.md b/docs/faq.md deleted file mode 100644 index 3bf5477..0000000 --- a/docs/faq.md +++ /dev/null @@ -1,17 +0,0 @@ -# FAQ - -## Where can I find online available teeth? - -Visit [lipWeb](https://www.lippkg.com). - -## It downloads so slowly! What can I do? - -lip downloads teeth via GOPROXY. You can use a faster proxy by running `lip config GoModuleProxyURL `. lip supports GitHub mirror as well. You can use it by running `lip config GitHubMirrorURL `. If you are setting up HTTP proxy, you can simply set the `HTTP_PROXY` and `HTTPS_PROXY` environment variable. - -## It always shows errors when I try to install a tooth! - -Probably the cache is corrupted. Try to purge the cache by running `lip cache purge`. - -## How can I update lip? - -Remove the current lip executable and install the latest version. diff --git a/docs/faq.zh.md b/docs/faq.zh.md deleted file mode 100644 index 8ac0cad..0000000 --- a/docs/faq.zh.md +++ /dev/null @@ -1,17 +0,0 @@ -# 常问问题 - -## 我在哪里可以找到在线可用的tooth? - -访问[lipWeb](https://www.lippkg.com). - -## 它的下载速度太慢了! 我可以做什么呢? - -Lip通过GOPROXY下载依赖。你可以通过运行 `lip config GoModuleProxyURL ` 来使用更快的代理。Lip还支持GitHub镜像,你可以通过运行 `lip config GitHubMirrorURL ` 来使用它。如果您正在设置 HTTP 代理,您只需设置 `HTTP_PROXY` 和 `HTTPS_PROXY` 环境变量。 - -## 当我试图安装一个tooth时,它总是显示错误! - -可能是缓存被破坏了。尝试通过运行 `lip cache purge` 来清除缓存。 - -## 我怎样才能更新lip? - -删除当前的lip可执行文件并安装最新版本。 diff --git a/docs/images/favicon.ico b/docs/images/favicon.ico deleted file mode 100644 index b60da2f7673208d2f7fecc8a09b3eecdcbe6a7eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmeH~?@Gc@7{-qv^g$B?UCrt8PpDuSG!PdCelVP6kOUD43Ee|bP*``1DG9(0~o5v0R0*fd-`O#|wu7=wK2r9#NuvLWzpuY?taL^i|X()rXXE zE`XjgIuoVYr{euH74DwqsACq(mZkfUZuA7u(hbYrZC%ew4{!70@P&5jE%rjX559gB z`l@1HnW({0+qYCg4XzIr_>hASI{HDE*ALK#4;Fmr`aZn6J|t_Wv|T!O&>trBrS-#K z9|q3e*$rj8Bg%A!bJXDZAbDK`K3KM5_Gj6P$GK*WC|NVHF4ox(JymI5n&=7FhxPZv z>U9zJL)Z_?&&jYK!hQ(*f%nY~-7mr%<>x2^`2o4w%i7X%b&wyt9Cg5X5}7tpHVFC? z=<^vvqQ1LtFXhRB!?~KXJ#?k(FaB8IgJmPzPmv#|(3kFipug~;m*MjTpK+kSwsYO0 oj&NS>0AYNMefBXkTOQvLBIZYoUFaP*ng4%*e+AHo4<>K?0zF=r)&Kwi diff --git a/docs/images/logo.webp b/docs/images/logo.webp deleted file mode 100644 index 369ab0cc09c315be28d88f23d72e90987eaab195..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118 zcmV-+0Ez!nNk&F)00012MM6+kP&iCs0000lpTH*&C!ip00~SukpO}VBX<|M~0t7%2 zRpvhc{#mG$peT~mXf0p?3s{JMQyj0nI3xORHfQXwtN(V?LKJ|?. You may select the version for your platform. - -Then, unzip the content to somewhere you would like to install lip. - -Finally, add the location to PATH environment variable. - -To check if lip is installed successfully, run `lip --version` in your terminal. You should see the version of lip you just installed. - -If you are using Windows, you can also download `.exe` installer to install lip. - -## Usage - -To install a online tooth (a package in lip), run `lip install `. Here is an example: - -```bash -lip install github.com/LiteLDev/LeviLamina -``` - -To install a local tooth (typically with `.tth` extension name), run `lip install `. Here is an example: - -```bash -lip install ./example.tth -``` - -To uninstall a tooth, run `lip uninstall `. Here is an example: - -```bash -lip uninstall github.com/LiteLDev/LeviLamina -``` - -To list all installed teeth, run `lip list`. Here is an example: - -To show information of a tooth, run `lip show `. Here is an example: - -```bash -lip show github.com/LiteLDev/LeviLamina -``` - -## Contributing - -Feel free to dive in! [Open an issue](https://github.com/lippkg/lip/issues/new/choose) or submit PRs. - -lip follows the [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) Code of Conduct. - -### Contributors - -This project exists thanks to all the people who contribute. - -![Contributors](https://contrib.rocks/image?repo=lippkg/lip) - -## License - -GPL-3.0-only © 2021-2024 lippkg diff --git a/docs/index.zh.md b/docs/index.zh.md deleted file mode 100644 index c0d3056..0000000 --- a/docs/index.zh.md +++ /dev/null @@ -1,77 +0,0 @@ -# lip - -[![构建](https://img.shields.io/github/actions/workflow/status/lippkg/lip/build.yml?style=for-the-badge)](https://github.com/lippkg/lip/actions) -[![最新标签](https://img.shields.io/github/v/tag/lippkg/lip?label=LATEST%20TAG&style=for-the-badge)](https://github.com/lippkg/lip/releases/latest) -[![下载次数](https://img.shields.io/github/downloads/lippkg/lip/latest/total?style=for-the-badge)](https://github.com/lippkg/lip/releases/latest) - -一个通用的软件包安装程序 - -lip是一个通用的软件包安装程序。您可以使用lip从任何Git存储库安装软件包。 - -## 安全性 - -此软件包管理器(以下简称“本软件”)由lippkg(以下简称“开发者”)开发和提供。本软件旨在帮助用户管理和安装各种软件包,但不对任何软件包的内容、质量、功能、安全性或合法性负责。用户应自行谨慎使用本软件并承担所有相关风险。 - -开发者不保证本软件的稳定性、可靠性、准确性或完整性。开发者对本软件中可能存在的任何缺陷、错误、病毒或其他有害组件不负责任。开发者不对因使用本软件而导致的任何直接或间接损害(包括但不限于数据丢失、设备损坏、利润损失等)负责。 - -开发者保留随时修改、更新或终止本软件及其相关服务的权利,而无需事先通知用户。用户应定期备份重要数据并检查本软件的更新。 - -用户在使用本软件时应遵守相关法律法规,尊重他人的知识产权和隐私权,不得将本软件用于任何非法或侵权活动。如果用户违反上述规定并对任何第三方造成损害或被任何第三方索赔,开发者不承担任何责任。 - -如果您对本免责声明有任何疑问或意见,请联系开发者。 - -## 安装 - -首先,从 下载lip的最新版本。您可以选择适合您平台的版本。 - -然后,解压内容到您想要安装lip的位置。 - -最后,将该位置添加到PATH环境变量中。 - -要检查是否成功安装lip,请在终端中运行 `lip --version`。您应该看到刚刚安装的lip的版本。 - -如果你使用的是Windows系统,你也可以在Assets中下载 `.exe` 后缀的安装程序来安装。 - -## 使用 - -要安装在线tooth(lip中的软件包),运行 `lip install `。以下是一个示例: - -```bash -lip install github.com/LiteLDev/LeviLamina -``` - -要安装本地tooth(通常使用`.tth`扩展名),运行 `lip install `。以下是一个示例: - -```bash -lip install ./example.tth -``` - -要卸载tooth,运行 `lip uninstall `。以下是一个示例: - -```bash -lip uninstall github.com/LiteLDev/LeviLamina -``` - -要列出所有安装的tooth,运行 `lip list`。以下是一个示例: - -要显示tooth的信息,运行 `lip show `。以下是一个示例: - -```bash -lip show github.com/LiteLDev/LeviLamina -``` - -## 贡献 - -随时加入![提出问题](https://github.com/lippkg/lip/issues/new/choose) 或提交PR。 - -lip遵循[贡献者公约](https://www.contributor-covenant.org/version/2/1/code_of_conduct/)行为准则。 - -### 贡献者 - -感谢所有为此项目做出贡献的人。 - -![贡献者](https://contrib.rocks/image?repo=lippkg/lip) - -## 许可证 - -GPL-3.0-only © 2021-2024 lippkg diff --git a/docs/install.md b/docs/install.md deleted file mode 100644 index 4638780..0000000 --- a/docs/install.md +++ /dev/null @@ -1,19 +0,0 @@ -# Installation - -Download the latest version of lip for your operating system and processor architecture from . - -Unzip the content to somewhere you would like to install lip. - -Add the location to PATH environment variable. - -If you are using Windows, you can also download `.exe` installer to install lip. - -## Upgrading lip - -You can just replace the `lip.exe` with the latest version to upgrade it. - -If you installed lip via an installer, you can uninstall the previous version and installer the newer version with the newer installer. - -## Install lipUI - -lipUI is a GUI for lip. You can download it from . diff --git a/docs/install.zh.md b/docs/install.zh.md deleted file mode 100644 index d2f4553..0000000 --- a/docs/install.zh.md +++ /dev/null @@ -1,19 +0,0 @@ -# 安装 - -从 . 下载对应操作系统和处理器架构的lip的最新版。 - -将lip解压到你想安装它的地方。 - -将该位置添加到PATH环境变量中。 - -如果你使用的是Windows系统,你也可以在Assets中下载 `.exe` 后缀的安装程序来安装。 - -## 升级lip - -你可以通过将 `lip.exe` 替换为最新版来升级它。 - -如果您通过安装程序安装了 lip,则可以卸载以前的版本并使用较新的安装程序安装较新的版本。 - -## 安装lipUI - -lipUI是lip的一个GUI。你可以从以下网站下载它 . diff --git a/docs/quickstart.md b/docs/quickstart.md deleted file mode 100644 index 87614fd..0000000 --- a/docs/quickstart.md +++ /dev/null @@ -1,95 +0,0 @@ -# Quickstart - -!!! tip - Not accustomed to using command-line tools? You can use [LipUI](https://github.com/lippkg/LipUI). - -To get started with using lip, you should [have lip installed](install.md) first. If you are not familiar with the command-line interface, you can also use [lipUI](https://github.com/lippkg/LipUI) to manage your teeth. - -## Ensure you have a working lip - -As a first step, you should check that you have a working lip installed. This can be done by running the following commands and making sure that the output looks similar. - -```shell -> lip --version -lip 0.15.0 from C:\Users\ExampleUser\AppData\Local\lip\lip.exe -``` - -## Common Tasks - -### Install a tooth - -```shell -> lip install github.com/tooth-hub/bdsdownloader -[...] -Done. -``` - -By default, lip will fetch teeth via GOPROXY, a proxy of Git repos. - -### Install a tooth from a tooth file - -```shell -> lip install ./bdsdownloader.tth -[...] -Done. -``` - -### Install multiple teeth - -lip suppports installing multiple files at a time. - -```shell -> lip install github.com/liteldev/bdsdownloader github.com/tooth-hub/crashlogger -[...] -Done. -``` - -### Upgrade a tooth - -```shell -> lip install --upgrade github.com/liteldev/bdsdownloader -[...] -Done. -``` - -### Uninstall a tooth - -To uninstall a tooth, you must provide the tooth path of the tooth. - -```shell -> lip uninstall github.com/liteldev/bdsdownloader -[...] -Done. -``` - -### List all teeth - -```shell -> lip list -+------------------------------------+----------------+---------+ -| TOOTH | NAME | VERSION | -+------------------------------------+----------------+---------+ -| github.com/tooth-hub/bdsdownloader | BDS Donwloader | 0.3.1 | -| github.com/tooth-hub/crashlogger | CrashLogger | 1.0.1 | -| github.com/tooth-hub/peeditor | PeEditor | 3.2.0 | -+------------------------------------+----------------+---------+ -``` - -### Show information of a tooth - -```shell -> lip show github.com/liteldev/exampletooth -+-------------+------------------------------------+ -| KEY | VALUE | -+-------------+------------------------------------+ -| Tooth Repo | github.com/tooth-hub/bdsdownloader | -| Name | BDS Donwloader | -| Description | A CLI tool to download BDS | -| Author | Jasonzyt | -| Version | 0.3.1 | -+-------------+------------------------------------+ -``` - -## Next Steps - -You can read pages under Commands directory to get more detailed descriptions of lip commands. If you are a creator and want to publish your tooth, you can read [this tutorial](tutorials/create_a_lip_tooth.md) to learn how to do it. diff --git a/docs/quickstart.zh.md b/docs/quickstart.zh.md deleted file mode 100644 index b462732..0000000 --- a/docs/quickstart.zh.md +++ /dev/null @@ -1,95 +0,0 @@ -# 开始使用 - -!!! tip - 不习惯使用命令行工具?你可以使用[LipUI](https://github.com/lippkg/LipUI)。 - -要开始使用lip你应该先完成[安装](install.md)。如果你对命令行界面不熟悉,你也可以使用[lipUI](https://github.com/lippkg/LipUI)来管理你的tooth。 - -## 确定你的lip正常工作 - -第一步,你需要检查你的lip的安装,执行下面的命令,并确保输出看起来相似。 - -```shell -> lip --version -lip 0.15.0 from C:\Users\ExampleUser\AppData\Local\lip\lip.exe -``` - -## 常见任务 - -### 安装一个tooth - -```shell -> lip install github.com/tooth-hub/bdsdownloader -[...] -Done. -``` - -默认情况下,lip会通过GOPROXY,一个Git仓库的代理来获取tooth。 - -### tooth文件安装tooth - -```shell -> lip install ./bdsdownloader.tth -[...] -Done. -``` - -### 安装多个tooth - -lip 支持一次安装多个tooth - -```shell -> lip install github.com/liteldev/bdsdownloader github.com/tooth-hub/crashlogger -[...] -Done. -``` - -### 升级tooth - -```shell -> lip install --upgrade github.com/liteldev/bdsdownloader -[...] -Done. -``` - -### 卸载tooth - -要卸载一个tooth,你必须提供该tooth的包路径。 - -```shell -> lip uninstall github.com/liteldev/bdsdownloader -[...] -Done. -``` - -### 列出所有tooth - -```shell -> lip list -+------------------------------------+----------------+---------+ -| TOOTH | NAME | VERSION | -+------------------------------------+----------------+---------+ -| github.com/tooth-hub/bdsdownloader | BDS Donwloader | 0.3.1 | -| github.com/tooth-hub/crashlogger | CrashLogger | 1.0.1 | -| github.com/tooth-hub/peeditor | PeEditor | 3.2.0 | -+------------------------------------+----------------+---------+ -``` - -### 查看一个tooth的具体信息 - -```shell -> lip show github.com/liteldev/exampletooth -+-------------+------------------------------------+ -| KEY | VALUE | -+-------------+------------------------------------+ -| Tooth Repo | github.com/tooth-hub/bdsdownloader | -| Name | BDS Donwloader | -| Description | A CLI tool to download BDS | -| Author | Jasonzyt | -| Version | 0.3.1 | -+-------------+------------------------------------+ -``` - -## 下一步 - -你可以阅读 **命令** 目录下的页面,以获得lip命令的更详细描述。如果你想要创建一个tooth,你可以阅读[这个教程](tutorials/create_a_lip_tooth.md)。 diff --git a/docs/reference/lip.md b/docs/reference/lip.md deleted file mode 100644 index 37ab15e..0000000 --- a/docs/reference/lip.md +++ /dev/null @@ -1,35 +0,0 @@ -# lip - -## Usage - -```shell -lip [options] -``` - -## Description - -lip is a general package manager. - -When a lip executable file exists under .lip/tools/lip/, it will be executed instead of the built-in one. - -## Options - -- `-h, --help` - - Show help. - -- `-V, --version` - - Show version and exit. - -- `-v, --verbose` - - Show verbose output. - -- `-q, --quiet` - - Show only errors. - -- `--no-color` - - Disable color output. diff --git a/docs/reference/lip.zh.md b/docs/reference/lip.zh.md deleted file mode 100644 index 44777e8..0000000 --- a/docs/reference/lip.zh.md +++ /dev/null @@ -1,29 +0,0 @@ -# lip - -## 用法 - -```shell -lip [options] -``` - -## 选项 - -- `-h, --help` - - 展示帮助。 - -- `-V, --version` - - 显示版本并退出。 - -- `-v, --verbose` - - 显示详细的输出。 - -- `-q, --quiet` - - 只显示错误。 - -- `--no-color` - - 禁用颜色输出。 diff --git a/docs/reference/lip_cache.md b/docs/reference/lip_cache.md deleted file mode 100644 index 7fbaff5..0000000 --- a/docs/reference/lip_cache.md +++ /dev/null @@ -1,17 +0,0 @@ -# lip cache - -## Usage - -```shell -lip cache [options] -``` - -## Description - -Inspect and manage lip’s tooth cache. - -## Options - -- `-h, --help` - - Show help. diff --git a/docs/reference/lip_cache.zh.md b/docs/reference/lip_cache.zh.md deleted file mode 100644 index 09052a9..0000000 --- a/docs/reference/lip_cache.zh.md +++ /dev/null @@ -1,17 +0,0 @@ -# lip cache - -## 用法 - -```shell -lip cache [options] -``` - -## 功能 - -检查和管理lip的tooth缓存。 - -## 选项 - -- `-h, --help` - - 展示帮助信息。 diff --git a/docs/reference/lip_cache_purge.md b/docs/reference/lip_cache_purge.md deleted file mode 100644 index febbbf7..0000000 --- a/docs/reference/lip_cache_purge.md +++ /dev/null @@ -1,17 +0,0 @@ -# lip cache purge - -## Usage - -```shell -lip cache purge [options] -``` - -## Description - -Remove all items from the cache. - -## Options - -- `-h, --help` - - Show help. \ No newline at end of file diff --git a/docs/reference/lip_cache_purge.zh.md b/docs/reference/lip_cache_purge.zh.md deleted file mode 100644 index f6818d9..0000000 --- a/docs/reference/lip_cache_purge.zh.md +++ /dev/null @@ -1,17 +0,0 @@ -# lip cache purge - -## 用法 - -```shell -lip cache purge [options] -``` - -## 功能 - -移除缓存中的所有项目。 - -## 选项 - -- `-h, --help` - - 展示帮助。 \ No newline at end of file diff --git a/docs/reference/lip_config.md b/docs/reference/lip_config.md deleted file mode 100644 index b5107c8..0000000 --- a/docs/reference/lip_config.md +++ /dev/null @@ -1,22 +0,0 @@ -# lip config - -## Usage - -```shell -lip config [options] -lip config [ []] -``` - -## Description - -Manage configuration. - -- If no arguments are specified, list all configuration. -- If a key is specified, print the value of the key. -- If a key and a value are specified, set the value of the key. - -## Options - -- `-h, --help` - - Show help. diff --git a/docs/reference/lip_config.zh.md b/docs/reference/lip_config.zh.md deleted file mode 100644 index 0d4b671..0000000 --- a/docs/reference/lip_config.zh.md +++ /dev/null @@ -1,22 +0,0 @@ -# lip config - -## 用法 - -```shell -lip config [options] -lip config [ []] -``` - -## 描述 - -管理配置。 - -- 如果未指定任何参数,则列出所有配置。 -- 如果指定了键,则打印键的值。 -- 如果指定了键和值,则设置键的值。 - -## 选项 - -- `-h, --help` - - 显示帮助。 diff --git a/docs/reference/lip_install.md b/docs/reference/lip_install.md deleted file mode 100644 index 7f6bd87..0000000 --- a/docs/reference/lip_install.md +++ /dev/null @@ -1,102 +0,0 @@ -# lip install - -## Usage - -```shell -lip install [options] -lip install [options] -``` - -## Description - -Install a tooth from: - -- tooth repositories via Goproxy. -- local standalone tooth files. - -For the tooth repository, you can specific the version by add suffix like `@1.2.3` or `@1.2.0-beta.3`. However, when another version is installed and you run lip without `--upgrade` or `--force-reinstall` flag, lip will not install the specific version. - -Only letters, numbers, dashes, underlines, dots, slashes [A-Za-z0-9-_./] and one @ are allowed in requirement specifiers. - -If you have set environment variable GOPROXY, lip will access tooth repositories via it. Otherwise, lip will choose the default Goproxy . - -### Overview - -`lip install` has several stages: - -1. Identify the base requirements. The user supplied arguments are processed here. -2. Fetch teeth and resolve dependencies. Dependencies will be resolved as soon as teeth are fetched. -3. Install the teeth (and uninstall anything being upgraded) - -Note that `lip install` prefers to leave the installed version as-is unless `--upgrade` is specified. - -### Argument Handling - -When looking at the items to be installed, lip checks what type of item each is, in the following order: - -1. Local tooth file. -2. Tooth repository, which can be accessed via Goproxy. - -### Satisfying Requirements - -Once lip has the set of requirements to satisfy, it chooses which version of each requirement to install using the simple rule that the latest stable version that satisfies the given constraints will be installed. If no stable version is available, lip will choose the latest pre-release version. - -### Installation Order - -lip installs dependencies before their dependents, i.e. in “topological order”. When encountering a cycle in the dependency graph, lip will refuse to install teeth. All developers should avoid any cycle in the dependency graph. - -This dependency graph will be maintained by lip. When uninstalling some packages, lip will check the graph to ensure that all dependents uninstalled. If not, lip will ask you whether to uninstall them or cancel the procedure. - -### Pre-release Versions - -You can install any pre-release versions by specifying the version. And teeth can declare pre-release versions as their dependencies. However, when teeth use any type of range version match or wildcard, lip will ignore pre-release versions. - -## Options - -- `-h, --help` - - Show help. - -- `--upgrade` - - Upgrade the specified tooth to the newest available version. If a version is specified and it is newer, upgrade to that version. The handling of dependencies depends on the upgrade-strategy used. When upgrading, lip will first uninstall the old version and then install the new version. - -- `--force-reinstall` - - 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. - -- `--no-dependencies` - - Do not install dependencies. Also bypass prerequisite checks. - -## Examples - -Install from tooth repositories: - -```shell -lip install example.com/some_user/some_tooth # Latest version -lip install example.com/some_user/some_tooth@1.0.0 # Specific version -``` - -Upgrade an already installed tooth: - -```shell -lip install --upgrade example.com/some_user/some_tooth -``` - -Force reinstall a tooth: - -```shell -lip install --force-reinstall example.com/some_user/some_tooth -``` - -Install from a local tooth: - -```shell -lip install example.tth -lip install ./example/example.tth -``` diff --git a/docs/reference/lip_list.md b/docs/reference/lip_list.md deleted file mode 100644 index afa3a46..0000000 --- a/docs/reference/lip_list.md +++ /dev/null @@ -1,25 +0,0 @@ -# lip list - -## Usage - -```shell -lip list [options] -``` - -## Description - -List installed teeth. - -## Options - -- `-h, --help` - - Show help. - -- `--upgradable` - - List upgradable teeth. - -- `--json` - - Output in JSON format. diff --git a/docs/reference/lip_list.zh.md b/docs/reference/lip_list.zh.md deleted file mode 100644 index f32c2ab..0000000 --- a/docs/reference/lip_list.zh.md +++ /dev/null @@ -1,27 +0,0 @@ -# lip list - -## 用法 - -```shell -lip list [options] -``` - -## 功能 - -列出已安装的tooth - -tooth以不区分大小写的排序方式列出。 - -## 选项 - -- `-h, --help` - - 展示帮助 - -- `--upgradable` - - 列出所有可升级的tooth - -- `--json` - - 以JSON格式输出 diff --git a/docs/reference/lip_show.md b/docs/reference/lip_show.md deleted file mode 100644 index 98584a2..0000000 --- a/docs/reference/lip_show.md +++ /dev/null @@ -1,25 +0,0 @@ -# lip show - -## Usage - -```shell -lip show [options] -``` - -## Description - -Show information about an installed tooth. - -## Options - -- `-h, --help` - - Show help. - -- `--available` - - Show the full list of available versions. - -- `--json` - - Output in JSON format. diff --git a/docs/reference/lip_show.zh.md b/docs/reference/lip_show.zh.md deleted file mode 100644 index 2080eb9..0000000 --- a/docs/reference/lip_show.zh.md +++ /dev/null @@ -1,25 +0,0 @@ -# lip show - -## Usage - -```shell -lip show [options] -``` - -## 功能 - -展示一个已经安装了的tooth的信息。 - -## 选项 - -- `-h, --help` - - 展示帮助。 - -- `--available` - - 显示可用版本的完整列表。 - -- `--json` - - 以JSON格式输出 diff --git a/docs/reference/lip_tooth.md b/docs/reference/lip_tooth.md deleted file mode 100644 index 9136d22..0000000 --- a/docs/reference/lip_tooth.md +++ /dev/null @@ -1,17 +0,0 @@ -# lip tooth - -## Usage - -```shell -lip tooth [options] -``` - -## Description - -Manage teeth. - -## Options - -- `-h, --help` - - Show help. diff --git a/docs/reference/lip_tooth.zh.md b/docs/reference/lip_tooth.zh.md deleted file mode 100644 index 7907763..0000000 --- a/docs/reference/lip_tooth.zh.md +++ /dev/null @@ -1,17 +0,0 @@ -# lip tooth - -## 用法 - -```shell -lip tooth [options] -``` - -## 功能 - -管理tooth - -## 选项 - -- `-h, --help` - - 展示帮助. \ No newline at end of file diff --git a/docs/reference/lip_tooth_init.md b/docs/reference/lip_tooth_init.md deleted file mode 100644 index 964daa5..0000000 --- a/docs/reference/lip_tooth_init.md +++ /dev/null @@ -1,17 +0,0 @@ -# lip tooth init - -## Usage - -```shell -lip tooth init -``` - -## Description - -Initialize and writes a new tooth.json file in the current directory, in effect creating a new tooth rooted at the current directory. The tooth.json file must not already exist. - -## Options - -- `-h, --help` - - Show help. \ No newline at end of file diff --git a/docs/reference/lip_tooth_init.zh.md b/docs/reference/lip_tooth_init.zh.md deleted file mode 100644 index 8da5782..0000000 --- a/docs/reference/lip_tooth_init.zh.md +++ /dev/null @@ -1,17 +0,0 @@ -# lip tooth init - -## 用法 - -```shell -lip tooth init -``` - -## 功能 - -初始化并在当前目录中写入一个新的 tooth.json 文件。tooth.json文件不能已经存在。 - -## 选项 - -- `-h, --help` - - 展示帮助 \ No newline at end of file diff --git a/docs/reference/lip_tooth_pack.md b/docs/reference/lip_tooth_pack.md deleted file mode 100644 index 6aa7976..0000000 --- a/docs/reference/lip_tooth_pack.md +++ /dev/null @@ -1,17 +0,0 @@ -# lip tooth pack - -## Usage - -```shell -lip tooth pack [options] -``` - -## Description - -Initialize and writes a new tooth.json file in the current directory, in effect creating a new tooth rooted at the current directory. The tooth.json file must not already exist. - -## Options - -- `-h, --help` - - Show help. diff --git a/docs/reference/lip_uninstall.md b/docs/reference/lip_uninstall.md deleted file mode 100644 index dd8935f..0000000 --- a/docs/reference/lip_uninstall.md +++ /dev/null @@ -1,26 +0,0 @@ -# lip uninstall - -## Usage - -```shell -lip uninstall [options] -``` - -## Description - -Uninstall teeth. -This command will remove the files released by the tooth package and the contents of the folder that the tooth author specified the tooth to occupy. - -## Options - -- `-h, --help` - - Show help. - -- `-y, --yes` - - Skip the confirmation prompt. - -- `--keep-possession` - - Keep files that the tooth author specified the tooth to occupy. These files are often configuration files, data files, etc. diff --git a/docs/reference/lip_uninstall.zh.md b/docs/reference/lip_uninstall.zh.md deleted file mode 100644 index 15aeeaa..0000000 --- a/docs/reference/lip_uninstall.zh.md +++ /dev/null @@ -1,26 +0,0 @@ -# lip uninstall - -## 用法 - -```shell -lip uninstall [options] -``` - -## 功能 - -卸载tooth。 -本命令将会移除tooth所释放的文件,以及tooth作者指定该tooth占有的文件夹内容。 - -## 选择 - -- `-h, --help` - - 展示帮助. - -- `-y, --yes` - - 跳过确认提示。 - -- `--keep-possession` - - 保留tooth作者指定的tooth所占用的文件。这些文件通常是配置文件、数据文件等。 diff --git a/docs/reference/tooth_json_file_reference.md b/docs/reference/tooth_json_file_reference.md deleted file mode 100644 index fb3d89a..0000000 --- a/docs/reference/tooth_json_file_reference.md +++ /dev/null @@ -1,428 +0,0 @@ -# tooth.json File Reference - -Each tooth is defined by a tooth.json file that describes the tooth's properties, including its dependencies on other teeth and other information. - -You can generate a tooth.json file by running the lip tooth init command. The following example creates a tooth.json file: - -```shell -lip tooth init -``` - -## Schema - -Refer to . - -## Example - -A tooth.json includes directives as shown in the following example. These are described elsewhere in this topic. - -```json -{ - "format_version": 2, - "tooth": "github.com/tooth-hub/example", - "version": "1.0.0", - "info": { - "name": "Example", - "description": "An example package", - "author": "exmaple", - "tags": [ - "example" - ], - "avatar_url": "avatar.png" - }, - "asset_url": "https://github.com/tooth-hub/example/releases/download/v1.0.0/example-1.0.0.zip", - "commands": { - "pre_install": [ - "echo \"pre_install\"" - ], - "post_install": [ - "echo \"post_install\"" - ], - "pre_uninstall": [ - "echo \"pre_uninstall\"" - ], - "post_uninstall": [ - "echo \"post_uninstall\"" - ] - }, - "dependencies": { - "github.com/tooth-hub/example-deps": ">=1.0.0 <2.0.0 || >=3.0.0 <4.0.0" - }, - "prerequisites": { - "github.com/tooth-hub/example-pre": ">=1.0.0" - }, - "files": { - "place": [ - { - "src": "example.js", - "dest": "dir/example.js" - }, - { - "src": "plug/*", - "dest": "dir/plug/" - } - ], - "preserve": [ - "dir/data.json" - ], - "remove": [ - "dir/temp.txt" - ] - } -} -``` - -## `format_version` (required) - -Indicates the format of the tooth.json file. lip will parse tooth.json according to this field. - -### Examples - -```json -{ - "format_version": 2 -} -``` - -### Notes - -You should set the format_version to 2. - -## `tooth` (required) - -Declares the tooth's tooth repository path, which is the tooth's unique identifier (when combined with the tooth version number). - -### Syntax - -Generally, tooth path should be in the form of a URL without protocol prefix (e.g. github.com/tooth-hub/corepack). - -Only letters, digits, dashes, underlines, dots and slashes [A-Za-z0-9-_./] are allowed. Must be identical to the tooth repository path. - -### Examples - -```json -{ - "tooth": "github.com/tooth-hub/mytooth" -} -``` - -### Notes - -The tooth path must uniquely identify your tooth. For most teeth, the path is a URL where lip can find the code. For teeth that won’t ever be downloaded directly, the tooth path can be just some name you control that will ensure uniqueness. - -Note that the tooth path should not include protocol prefix (e.g. "https://" or "git://"), which already violates the syntax. - -If you would like to publish your tooth, please make the tooth path a real URL. For example, the first character should be a letter or a digit. - -## `version` (required) - -### Syntax - -We adopted [Semantic Versioning 2.0.0](https://semver.org). - -### Examples - -Example of a production release: - -```json -{ - "version": "1.2.3" -} -``` - -Example of a pre-release: - -```json -{ - "version": "1.2.0-beta.3" -} -``` - -Example of a early development release: - -```json -{ - "version": "0.1.2" -} -``` - -### Notes - -When releasing your tooth, you should set the Git tag with prefix "v", e.g. v1.2.3. Otherwise, lip will not correctly parse the tags. - -Since GOPROXY regards versions with prefix "v0.0.0" as psuedo-versions, you should not set the version beginning with "0.0.0" if you would like to publish your tooth. - -## `info` (required) - -Declares necessary information of your tooth. - -### Syntax - -Provide information about your tooth in the form of a JSON object with the following fields: - -- `name`: (required) the name of your tooth. -- `description`: (required) a short description of your tooth. -- `author`: (required) the author of your tooth. -- `tags`: (required) an array of tags of your tooth. Only [a-z0-9-] are allowed. A colon is allowed for splitting two parts. -- `avatar_url`: the URL of the tooth's avatar. If not set, the default avatar will be used. If a relative path is provided, it will be regarded as a path relative to **the source repository path**. - -!!!tip - tags shouldn't contain upper letters - -### Examples - -```json -{ - "info": { - "name": "Example", - "description": "An example package", - "author": "example", - "tags": [ - "example" - ], - "avartar_url": "" - } -} -``` - -### Notes - -Several tags have special meanings: - -Platforms: - -- `bds`: indicates that the tooth should be installed on Minecraft Bedrock Dedicated Server platform. -- `levilamina`: indicates that the tooth should be installed on LeviLamina platform. -- `lse`: indicates that the tooth should be installed on LegacyScriptEngine platform. -- `pnx`: indicates that the tooth should be installed on PowerNukkitX platform. - -Types: - -- `addon`: indicates that the tooth is an addon. -- `library`: indicates that the tooth is a library. -- `plugin`: indicates that the tooth is a plugin. -- `plugin-engine`: indicates that the tooth is a plugin engine. -- `utility`: indicates that the tooth is a utility. -- `world`: indicates that the tooth is a world. - -These tags will be used to filter teeth when searching. - -## `asset_url` (optional) - -Declares the URL of the tooth asset. If this field is set, lip will download the asset and use files in the asset archive instead of files in the tooth repository. This helps when releasing large binary files. - -### Syntax - -The URL should be a direct link to the asset file or Go Module URL. The asset file should be a zip archive file. -In lip 0.23.0 and above, you can use `$(version)` to refer to the `version` field above. - -### Examples - -```json -{ - "asset_url": "https://github.com/tooth-hub/example/releases/download/v1.0.0/example-1.0.0.zip" -} -``` - -### Notes - -For GitHub links, the configured GitHub mirror will be used to download the asset. If the mirror is not configured, the official GitHub will be used. - -## `commands` (optional) - -Declare commands to run before or after installing or uninstalling the tooth. - -### Syntax - -This field contains four sub-fields: - -- `pre-install`: an array of commands to run before installing the tooth. (optional) -- `post-install`: an array of commands to run after installing the tooth. (optional) -- `pre-uninstall`: an array of commands to run before uninstalling the tooth. (optional) -- `post-uninstall`: an array of commands to run after uninstalling the tooth. (optional) - -Each item in the array is a string of the command to run. The command will be run in the workspace. - -### Examples - -```json -{ - "commands": { - "pre-install": [ - "echo Pre-install command" - ], - "post-install": [ - "echo Post-install command" - ], - "pre-uninstall": [ - "echo Pre-uninstall command" - ], - "post-uninstall": [ - "echo Post-uninstall command" - ] - } -} -``` - -## `dependencies` (optional) - -Declare dependencies of your tooth. - -### Syntax - -Refer to [here](https://github.com/blang/semver#ranges) for the syntax of version ranges. - -### Examples - -```json -{ - "dependencies": { - "github.com/tooth-hub/example-deps": ">=1.0.0 <=1.1.0 || 2.0.x" - } -} -``` - -## `prerequisites` (optional) - -Declare prerequisites of your tooth. The syntax follows the `dependencies` field. The key difference is that prerequisites will not be installed by lip automatically. - -### Notes - -Some teeth should not be installed automatically, e.g. bds. Automatically installing these teeth may cause severe imcompatibility issues. - -## `files` (optional) - -Describe how the files in your tooth should be handled. - -### Syntax - -This field contains three sub-fields: - -- `place`: an array to specify how files in the tooth should be place to the workspace. Each item is an object with three sub-fields: (optional) - - `src`: the source path of the file. It can be a file or a directory with suffix "*" (e.g. `plug/*`). (required) - - `dest`: the destination path of the file. It can be a file or a directory. If `src` has suffix "*", `dest` must be a directory. Otherwise, `dest` must be a file. (required) -- `preserve`: an array to specify which files in `place` field should be preserved when uninstalling the tooth. Each item is a string of the path of the file. (optional) -- `remove`: an array to specify which files should be removed when uninstalling the tooth. Each item is a string of the path of the file. (optional) - -### Examples - -```json -{ - "files": { - "place": [ - { - "src": "plug/*", - "dest": "plugins" - }, - { - "src": "config.yml", - "dest": "config.yml" - } - ], - "preserve": [ - "config.yml" - ], - "remove": [ - "plugins/ExamplePlugin.dll" - ] - } -} -``` - -### Notes - -- Files specified in `place` but not in `preserve` will be removed when uninstalling the tooth. Therefore, you don't need to specify them in `remove`. -- `remove` field is prior to `preserve` field. If a file is specified in both fields, it will be removed. -- Only `place` filed support "*" suffix. `preserve` and `remove` fields do not support it. - -## `platforms` (optional) - -Declare platform-specific configurations. - -### Syntax - -This field is an array of platform-specific configurations. Each item is an object with these sub-fields: - -- `asset_url`: same as `asset_url` field. (optional) -- `commands`: same as `commands` field. (optional) -- `dependencies`: same as `dependencies` field. (optional) -- `prerequisites`: same as `prerequisites` field. (optional) -- `files`: same as `files` field. (optional) -- `goos`: the target operating system. For the values, see [here](https://go.dev/doc/install/source#environment). (required) -- `goarch`: the target architecture. For the values, see [here](https://go.dev/doc/install/source#environment). Omitting means match all. (optional) - -If provided and matched, the platform-specific configuration will override the global configuration. - -### Examples - -```json -{ - "platforms": [ - { - "commands": { - "pre-install": [ - "echo Pre-install command for Windows" - ] - }, - "dependencies": { - "github.com/tooth-hub/example-deps": ">=1.0.0 <=1.1.0 || 2.0.x" - }, - "files": { - "place": [ - { - "src": "plug/*", - "dest": "plugins" - }, - { - "src": "config.yml", - "dest": "config.yml" - } - ], - "preserve": [ - "config.yml" - ], - "remove": [ - "plugins/ExamplePlugin.dll" - ] - }, - "goos": "windows" - }, - { - "commands": { - "pre-install": [ - "echo Pre-install command for Linux AMD64" - ] - }, - "dependencies": { - "github.com/tooth-hub/example-deps": ">=1.0.0 <=1.1.0 || 2.0.x" - }, - "files": { - "place": [ - { - "src": "plug/*", - "dest": "plugins" - }, - { - "src": "config.yml", - "dest": "config.yml" - } - ], - "preserve": [ - "config.yml" - ], - "remove": [ - "plugins/ExamplePlugin.dll" - ] - }, - "goos": "linux", - "goarch": "amd64" - } - ] -} -``` - -### Notes - -If multiple platform-specific configurations are matched, the last one will override the previous ones. Therefore, you should put the most specific configuration at the end of the array. - -If a platform-specific configuration is set, `commands`, `dependencies` and `files` in the global configuration will be ignored, no matter whether they are set or not in the platform-specific configuration. Thus, it is highly recommended not to set any of them in the global configuration if you would like to set platform-specific configurations. diff --git a/docs/reference/tooth_json_file_reference.zh.md b/docs/reference/tooth_json_file_reference.zh.md deleted file mode 100644 index b65230b..0000000 --- a/docs/reference/tooth_json_file_reference.zh.md +++ /dev/null @@ -1,428 +0,0 @@ -# tooth.json文件参考 - -每个tooth都由一个tooth.json文件定义,该文件描述了tooth的属性,包括其对其他tooth的依赖关系和其他信息。 - -您可以通过运行lip tooth init命令来生成一个tooth.json文件。下面的例子创建了一个tooth.json文件: - -```shell -lip tooth init -``` - -## 模式 - -请参考。 - -## 示例 - -一个tooth.json包含如下所示的指令。这些指令在本主题的其他地方有描述。 - -```json -{ - "format_version": 2, - "tooth": "github.com/tooth-hub/example", - "version": "1.0.0", - "info": { - "name": "Example", - "description": "An example package", - "author": "exmaple", - "tags": [ - "example" - ], - "avatar_url": "avatar.png" - }, - "asset_url": "https://github.com/tooth-hub/example/releases/download/v1.0.0/example-1.0.0.zip", - "commands": { - "pre_install": [ - "echo \"pre_install\"" - ], - "post_install": [ - "echo \"post_install\"" - ], - "pre_uninstall": [ - "echo \"pre_uninstall\"" - ], - "post_uninstall": [ - "echo \"post_uninstall\"" - ] - }, - "dependencies": { - "github.com/tooth-hub/example-deps": ">=1.0.0 <2.0.0 || >=3.0.0 <4.0.0" - }, - "prerequisites": { - "github.com/tooth-hub/example-pre": ">=1.0.0" - }, - "files": { - "place": [ - { - "src": "example.js", - "dest": "dir/example.js" - }, - { - "src": "plug/*", - "dest": "dir/plug/" - } - ], - "preserve": [ - "dir/data.json" - ], - "remove": [ - "dir/temp.txt" - ] - } -} -``` - -## `format_version`(必需) - -表示tooth.json文件的格式。lip会根据这个字段来解析tooth.json文件。 - -### 示例 - -```json -{ - "format_version": 2 -} -``` - -### 注意 - -您应该将format_version设置为2。 - -## `tooth`(必需) - -声明tooth的tooth仓库路径,这是tooth的唯一标识符(结合tooth版本号)。 - -### 语法 - -通常,tooth路径应该是没有协议前缀的URL的形式(例如github.com/tooth-hub/corepack)。 - -只允许使用字母、数字、破折号、下划线、点和斜杠[A-Za-z0-9-_./]。必须与tooth仓库路径相同。 - -### 示例 - -```json -{ - "tooth": "github.com/tooth-hub/mytooth" -} -``` - -### 注意 - -tooth路径必须唯一地标识您的tooth。对于大多数tooth,路径是一个URL,lip可以在那里找到代码。对于不会直接下载的tooth,tooth路径可以是您控制的一些能确保唯一性的名称。 - -请注意,tooth路径不应该包含协议前缀(例如"https://"或"git://"),这已经违反了语法。 - -如果您想发布您的tooth,请将tooth路径设置为一个真正的URL。例如,第一个字符应该是一个字母或数字。 - -## `version`(必需) - -### 语法 - -我们采用了[语义化版本2.0.0](https://semver.org)。 - -### 示例 - -生产发布的例子: - -```json -{ - "version": "1.2.3" -} -``` - -预发布的例子: - -```json -{ - "version": "1.2.0-beta.3" -} -``` - -早期开发发布的例子: - -```json -{ - "version": "0.1.2" -} -``` - -### 注意 - -当发布您的tooth时,您应该用前缀"v"来设置Git标签,例如v1.2.3。否则,lip将无法正确解析标签。 - -由于GOPROXY将前缀为"v0.0.0"的版本视为伪版本,如果您想发布您的tooth,您不应该将版本设置为以"0.0.0"开头的。 - -## `info`(必需) - -声明您的tooth的必要信息。 - -### 语法 - -以JSON对象的形式提供有关您的tooth的信息,包含以下字段: - -- `name`:(必需)您的tooth的名称。 -- `description`:(必需)您的tooth的简短描述。 -- `author`:(必需)您的tooth的作者。 -- `tags`:(必需)您的tooth的标签数组。只允许使用[a-z0-9-],可以存在一个冒号分隔两个部分。 -- `avatar_url`:tooth的头像的URL。如果没有设置,将使用默认头像。如果提供了相对路径,它将被视为相对于**源仓库路径**的路径。 - -!!!tip - tags不应该包含大写字母 - -### 示例 - -```json -{ - "info": { - "name": "Example", - "description": "An example package", - "author": "example", - "tags": [ - "example" - ], - "avartar_url": "" - } -} -``` - -### 注意 - -有些标签有特殊的含义: - -平台: - -- `bds`:表示tooth应该安装在Minecraft Bedrock Dedicated Server平台上。 -- `levilamina`:表示tooth应该安装在LeviLamina平台上。 -- `lse`:表示tooth应该安装在LegacyScriptEngine平台上。 -- `pnx`:表示tooth应该安装在PowerNukkitX平台上。 - -类型: - -- `addon`:表示tooth是一个附加组件。 -- `library`:表示tooth是一个库。 -- `plugin`:表示tooth是一个插件。 -- `plugin-engine`:表示tooth是一个插件引擎。 -- `utility`:表示tooth是一个实用工具。 -- `world`:表示tooth是一个世界。 - -这些标签将用于在搜索时过滤tooth。 - -## `asset_url`(可选) - -声明tooth资产的URL。如果设置了这个字段,lip将下载资产并使用资产归档中的文件,而不是tooth仓库中的文件。这有助于发布大的二进制文件。 - -### 语法 - -URL应该是指向资产文件的直接链接或Go Module URL。资产文件应该是一个zip归档文件。 -在lip 0.23.0及以上的版本中,你可以使用`$(version)`来引用上文中的`version`字段 - -### 示例 - -```json -{ - "asset_url": "https://github.com/tooth-hub/example/releases/download/v1.0.0/example-1.0.0.zip" -} -``` - -### 注意 - -对于GitHub链接,将使用配置的GitHub镜像来下载资产。如果没有配置镜像,将使用GitHub官方地址。 - -## `commands`(可选) - -声明在安装或卸载tooth之前或之后运行的命令。 - -### 语法 - -这个字段包含四个子字段: - -- `pre-install`:一个在安装tooth之前运行的命令的数组。(可选) -- `post-install`:一个在安装tooth之后运行的命令的数组。(可选) -- `pre-uninstall`:一个在卸载tooth之前运行的命令的数组。(可选) -- `post-uninstall`:一个在卸载tooth之后运行的命令的数组。(可选) - -数组中的每一项都是一个要运行的命令的字符串。命令将在工作空间中运行。 - -### 示例 - -```json -{ - "commands": { - "pre-install": [ - "echo Pre-install command" - ], - "post-install": [ - "echo Post-install command" - ], - "pre-uninstall": [ - "echo Pre-uninstall command" - ], - "post-uninstall": [ - "echo Post-uninstall command" - ] - } -} -``` - -## `dependencies`(可选) - -声明您的 tooth 的依赖项。 - -### 语法 - -有关版本范围的语法,请参阅[此处](https://github.com/blang/semver#ranges)。 - -### 示例 - -```json -{ - "dependencies": { - "github.com/tooth-hub/example-deps": ">=1.0.0 <=1.1.0 || 2.0.x" - } -} -``` - -## `prerequisites`(可选) - -声明您的 tooth 的先决条件。语法与 `dependencies` 字段相同,但先决条件不会被 lip 自动安装。 - -### 注意 - -某些 tooth 不应自动安装,例如 bds。自动安装这些 tooth 可能会导致严重的不兼容性问题。 - -## `files`(可选) - -描述如何处理 tooth 中的文件。 - -### 语法 - -此字段包含三个子字段: - -- `place`:一个数组,用于指定 tooth 中的文件应该放置到工作区的方式。每个项目都是一个对象,具有三个子字段:(可选) - - `src`:文件的源路径。它可以是文件或带有后缀“*”的目录(例如 `plug/*`)。 (必需) - - `dest`:文件的目标路径。它可以是文件或目录。如果 `src` 有后缀“*”,则 `dest` 必须是目录。否则,`dest` 必须是文件。 (必需) -- `preserve`:一个数组,用于指定在卸载 tooth 时应保留 `place` 字段中的哪些文件。每个项目都是文件路径的字符串。 (可选) -- `remove`:一个数组,用于指定在卸载 tooth 时应删除哪些文件。每个项目都是文件路径的字符串。 (可选) - -### 示例 - -```json -{ - "files": { - "place": [ - { - "src": "plug/*", - "dest": "plugins" - }, - { - "src": "config.yml", - "dest": "config.yml" - } - ], - "preserve": [ - "config.yml" - ], - "remove": [ - "plugins/ExamplePlugin.dll" - ] - } -} -``` - -### 注意 - -- 在 `place` 中指定但不在 `preserve` 中的文件将在卸载 tooth 时被删除。因此,您无需在 `remove` 中指定它们。 -- `remove` 字段优先于 `preserve` 字段。如果一个文件在两个字段中都有指定,它将被删除。 -- 只有 `place` 字段支持“*”后缀。`preserve` 和 `remove` 字段不支持它。 - -## `platforms`(可选) - -声明特定于平台的配置。 - -### 语法 - -此字段是一个特定于平台的配置数组。每个项目都是一个带有以下子字段的对象: - -- `asset_url`:与`asset_url`字段相同。(可选) -- `commands`:与`commands`字段相同。(可选) -- `dependencies`:与`dependencies`字段相同。(可选) -- `prerequisites`:与`prerequisites`字段相同。(可选) -- `files`:与`files`字段相同。(可选) -- `goos`:目标操作系统。有关值,请参见[此处](https://go.dev/doc/install/source#environment)。(必填) -- `goarch`:目标架构。有关值,请参见[此处](https://go.dev/doc/install/source#environment)。省略表示匹配所有。(可选) - -如果提供并匹配,特定于平台的配置将覆盖全局配置。 - -### 示例 - -```json -{ - "platforms": [ - { - "commands": { - "pre-install": [ - "echo Pre-install command for Windows" - ] - }, - "dependencies": { - "github.com/tooth-hub/example-deps": ">=1.0.0 <=1.1.0 || 2.0.x" - }, - "files": { - "place": [ - { - "src": "plug/*", - "dest": "plugins" - }, - { - "src": "config.yml", - "dest": "config.yml" - } - ], - "preserve": [ - "config.yml" - ], - "remove": [ - "plugins/ExamplePlugin.dll" - ] - }, - "goos": "windows" - }, - { - "commands": { - "pre-install": [ - "echo Pre-install command for Linux AMD64" - ] - }, - "dependencies": { - "github.com/tooth-hub/example-deps": ">=1.0.0 <=1.1.0 || 2.0.x" - }, - "files": { - "place": [ - { - "src": "plug/*", - "dest": "plugins" - }, - { - "src": "config.yml", - "dest": "config.yml" - } - ], - "preserve": [ - "config.yml" - ], - "remove": [ - "plugins/ExamplePlugin.dll" - ] - }, - "goos": "linux", - "goarch": "amd64" - } - ] -} -``` - -### 注意事项 - -如果匹配了多个特定于平台的配置,最后一个将覆盖前面的配置。因此,您应该将最具体的配置放在数组的末尾。 - -如果设置了特定于平台的配置,则全局配置中的`commands`、`dependencies`和`files`将被忽略,无论它们在特定于平台的配置中是否设置。因此,如果您想设置特定于平台的配置,强烈建议不要在全局配置中设置它们。 diff --git a/docs/tutorials/create_a_lip_tooth.md b/docs/tutorials/create_a_lip_tooth.md deleted file mode 100644 index 466bb81..0000000 --- a/docs/tutorials/create_a_lip_tooth.md +++ /dev/null @@ -1,126 +0,0 @@ -# Create a lip Tooth - -This is the first part of a tutorial that introduces a few fundamental features of lip. If you're just getting started with lip, be sure to take a look at [Getting Started](../quickstart.md), which introduces the basic commands of lip. - -In this tutorial you'll create a tooth containing a plugin of LeviLamina. - -## Prerequisites - -- **Some project management experience.** You ought to learn the basic usage of Git, and the basic syntax of JSON in advance. - -- **A tool to edit tooth.json** Any text editor you have will work fine. The most popular are VSCode and Vim. - -- **A command terminal** lip works well with both PowerShell and cmd in Windows. - -- **lip command-line tool** You should install lip in advance. For more information, refer to [Installation](../installation.md) - -## Prepare plugin distributions - -lip fetches all content of a version of a Git repository for installing. Therefore, you should get all files to be installed ready under the management of Git. - -If you just work with text (e.g. script plugins, addons), you could just use the repository for development to create a tooth. - -However, if you are working with binaries (e.g. native plugins, worlds), you might have to create another repository to store the content. Otherwise, the binaries may make your repository too large to manage. - -In this example, we assume the repository structure as listed below: - -```text -exampleplugin.dll -exampleplugin/ - config.json - libexample.dll - data/ -``` - -## Initialize the tooth - -Open a command prompt and cd to the repository root. If you are using Windows, you can just press *shift* and right click in the file explorer, then click "Open PowerShell window here". - -Run the command below to initialize the tooth. The command will create a tooth.json under the root of the repository. - -```shell -lip tooth init -``` - -Edit tooth.json. Fill in the content enclosed in pointed brackets ("<" and ">"). - -- The tooth field indicates the tooth path of the tooth. If you would like to publish the tooth, it must be the tooth repository URL without protocol prefix (e.g. https:// or http://). - -- The placement filed indicates how will lip copy files from the tooth to the BDS. The source path bases on the root of the tooth (or the repository in this example and most cases), while the destination path bases on the root of BDS, in which "bedrock_server.exe" locates. - -- The possession field indicates the private directory of this tooth. It will be removed when uninstalling the tooth but will be kept when reinstalling or upgrading the tooth. Note that the path indicated in the possession field bases on the root of BDS. And every item should ends with "/". - -## Test the tooth - -Before publishing the tooth, you should test it to make sure it works as expected. - -Zip all files in the repository root, and rename the zip file to "exampleplugin.tth". - -Copy the zip file to a certain directory, and then run the command below to install the tooth. - -```shell -lip install exampleplugin.tth -``` - -Run the command below to uninstall the tooth. - -```shell -lip uninstall exampleplugin.tth -``` - -Run the command below to install the tooth again. - -```shell -lip install exampleplugin.tth -``` - -Check if the tooth works as expected. - -## Publish your tooth - -- Stash and commit the changes, and then push them to the public Git service. - -- Add a tag and publish a release with the version name. The tag name should be the version name added with prefix "v", e.g. "v1.0.0". - -## Another example: make a Minecraft world a tooth - -Generally, a Minecraft world has the following file structure: - -```text -Bedrock level/ - level.dat - level.dat_old - levelname.txt - db/ -``` - -You can create a tooth.json like this: - -```json -{ - "format_version": 2, - "tooth": "example.com/exampleuser/exampleworld", - "version": "1.0.0", - "dependencies": {}, - "information": { - "name": "Example World", - "description": "An example world", - "author": "Example User", - "tags": [ - "ll", "llbds", "bds" - ] - }, - "files": { - "place": [ - { - "src": "Bedrock level/*", - "dest": "worlds/Bedrock level/" - } - ] - } -} -``` - -## Next Steps - -You can read [tooth.json File Reference](../reference/tooth_json_file_reference.md) for further reference. diff --git a/docs/tutorials/create_a_lip_tooth.zh.md b/docs/tutorials/create_a_lip_tooth.zh.md deleted file mode 100644 index aa8e4f5..0000000 --- a/docs/tutorials/create_a_lip_tooth.zh.md +++ /dev/null @@ -1,124 +0,0 @@ -# 创建一个lip齿包 - -这是一个教程的第一部分,介绍了lip的一些基本特性。如果你刚开始使用lip,一定要看看[入门指南](../quickstart.md),它介绍了lip的基本命令。在这个教程中,你将创建一个包含LeviLamina插件的齿包。 - -## 先决条件 - -- **一些项目管理经验。**你应该提前学习Git的基本用法,以及JSON的基本语法。 - -- **一个用来编辑tooth.json的工具** 你有的任何文本编辑器都可以。最流行的是VSCode和Vim。 - -- **一个命令终端** lip在Windows的PowerShell和cmd中都可以很好地工作。 - -- **lip命令行工具** 你应该提前安装lip。更多信息,请参考[安装](../installation.md) - -## 准备插件分发 - -lip在安装时会获取Git仓库的一个版本的所有内容。因此,你应该准备好所有要安装的文件,并放在Git的管理下。 - -如果你只是使用文本工作(例如脚本插件,附加组件),你可以直接使用开发的仓库来创建一个齿包。但是,如果你使用二进制文件(例如本地插件,世界),你可能需要创建另一个仓库来存储内容。 - -否则,二进制文件可能会使你的仓库过大,难以管理。 - -在这个例子中,我们假设仓库的结构如下: - -```text -exampleplugin.dll -exampleplugin/ - config.json - libexample.dll - data/ -``` - -## 初始化齿包 - -打开一个命令提示符,然后cd到仓库的根目录。如果你使用Windows,你可以在文件资源管理器中按*shift*并右键单击,然后单击“在此处打开PowerShell窗口”。 - -运行下面的命令来初始化齿包。该命令将在仓库的根目录下创建一个tooth.json文件。 - -```shell -lip tooth init -``` - -编辑tooth.json。填写尖括号(“<”和“>”)中的内容。 - -- tooth字段表示齿包的路径。如果你想发布齿包,它必须是没有协议前缀(例如https://或http://)的齿包仓库URL。 - -- placement字段表示lip将如何从齿包复制文件到BDS。源路径基于齿包的根目录(或在这个例子和大多数情况下的仓库),而目标路径基于BDS的根目录,其中“bedrock_server.exe”位于。 - -- possession字段表示这个齿包的私有目录。它在卸载齿包时会被删除,但在重新安装或升级齿包时会保留。注意,possession字段中指示的路径基于BDS的根目录。每个项目都应以“/”结尾。 - -## 测试齿包 - -在发布齿包之前,你应该测试它,以确保它按预期工作。 - -将仓库根目录下的所有文件压缩,并将zip文件重命名为“exampleplugin.tth”。 - -将zip文件复制到某个目录,然后运行下面的命令来安装齿包。 - -```shell -lip install exampleplugin.tth -``` - -运行下面的命令来卸载齿包。 - -```shell -lip uninstall exampleplugin.tth -``` - -运行下面的命令来再次安装齿包。 - -```shell -lip install exampleplugin.tth -``` - -检查齿包是否按预期工作。 - -## 发布你的齿包 - -- 存储并提交更改,然后将它们推送到公共Git服务。 - -- 添加一个标签,并使用版本名称发布一个发布。标签名称应该是版本名称加上前缀“v”,例如“v1.0.0”。 - -## 另一个示例:将Minecraft世界制作成一个齿包 - -通常,Minecraft世界具有以下文件结构: - -```text -Bedrock level/ - level.dat - level.dat_old - levelname.txt - db/ -``` - -您可以创建一个名为`tooth.json`的文件,内容如下: - -```json -{ - "format_version": 2, - "tooth": "example.com/exampleuser/exampleworld", - "version": "1.0.0", - "dependencies": {}, - "information": { - "name": "Example World", - "description": "An example world", - "author": "Example User", - "tags": [ - "ll", "llbds", "bds" - ] - }, - "files": { - "place": [ - { - "src": "Bedrock level/*", - "dest": "worlds/Bedrock level/" - } - ] - } -} -``` - -## 下一步 - -你可以阅读[tooth.json文件参考](../reference/tooth_json_file_reference.md)以获得更多参考。 diff --git a/go.mod b/go.mod deleted file mode 100644 index 8203405..0000000 --- a/go.mod +++ /dev/null @@ -1,27 +0,0 @@ -module github.com/lippkg/lip - -go 1.19 - -require ( - github.com/antonfisher/nested-logrus-formatter v1.3.1 - github.com/blang/semver/v4 v4.0.0 - github.com/olekukonko/tablewriter v0.0.5 - github.com/schollz/progressbar/v3 v3.14.6 - github.com/sirupsen/logrus v1.9.3 - github.com/urfave/cli/v2 v2.27.5 - github.com/xeipuuv/gojsonschema v1.2.0 - golang.org/x/mod v0.20.0 - golang.org/x/term v0.27.0 -) - -require ( - github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect - github.com/rivo/uniseg v0.4.7 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect - github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect - golang.org/x/sys v0.28.0 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 874993f..0000000 --- a/go.sum +++ /dev/null @@ -1,56 +0,0 @@ -github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ= -github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA= -github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= -github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= -github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/schollz/progressbar/v3 v3.14.6 h1:GyjwcWBAf+GFDMLziwerKvpuS7ZF+mNTAXIB2aspiZs= -github.com/schollz/progressbar/v3 v3.14.6/go.mod h1:Nrzpuw3Nl0srLY0VlTvC4V6RL50pcEymjy6qyJAaLa0= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= -github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/cmd/cmdlip/cmdlip.go b/internal/cmd/cmdlip/cmdlip.go deleted file mode 100644 index 0d6dbc0..0000000 --- a/internal/cmd/cmdlip/cmdlip.go +++ /dev/null @@ -1,139 +0,0 @@ -package cmdlip - -import ( - "fmt" - "os" - - nested "github.com/antonfisher/nested-logrus-formatter" - "github.com/lippkg/lip/internal/cmd/cmdlipcache" - "github.com/lippkg/lip/internal/cmd/cmdlipconfig" - "github.com/lippkg/lip/internal/cmd/cmdlipfreeze" - "github.com/lippkg/lip/internal/cmd/cmdlipinstall" - "github.com/lippkg/lip/internal/cmd/cmdliplist" - "github.com/lippkg/lip/internal/cmd/cmdlipshow" - "github.com/lippkg/lip/internal/cmd/cmdliptooth" - "github.com/lippkg/lip/internal/cmd/cmdlipuninstall" - "github.com/lippkg/lip/internal/context" - "github.com/urfave/cli/v2" - - log "github.com/sirupsen/logrus" -) - -func Run(ctx *context.Context, args []string) error { - cli.AppHelpTemplate = ` - {{.Name}} - {{.Usage}}{{if .Version}} - {{.Version}} {{end}} - -Usage: - {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} - {{if len .Authors}} -Author: - {{range .Authors}}{{ . }}{{end}} - {{end}}{{if .Commands}} -Commands: -{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}} -Global Options: - {{range .VisibleFlags}}{{.}} - {{end}}{{end}}{{if .Copyright }} -Copyright: - {{.Copyright}} - {{end}} -` - cli.CommandHelpTemplate = ` -{{.Usage}} - -Usage: - {{template "usageTemplate" .}}{{if .Category}} - -Catrogy: - {{.Category}}{{end}}{{if .Description}} - -Description: - {{template "descriptionTemplate" .}}{{end}}{{if .VisibleFlagCategories}} - -Options:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} - -Options:{{template "visibleFlagTemplate" .}}{{end}} -` - cli.SubcommandHelpTemplate = ` -{{.Usage}} - -Usage: - {{template "usageTemplate" .}}{{if .Category}} - -Catrogy: - {{.Category}}{{end}}{{if .Description}} - -Description: - {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}} - -Commands:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}} - -Options:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} - -Options:{{template "visibleFlagTemplate" .}}{{end}} -` - cli.VersionFlag = &cli.BoolFlag{ - Name: "verison", - Aliases: []string{"V"}, - Usage: "print the version", - DisableDefaultText: true, - } - cli.VersionPrinter = func(cCtx *cli.Context) { - fmt.Printf("lip %v from %v\n", ctx.LipVersion().String(), os.Args[0]) - } - return (&cli.App{ - Name: "lip", - Usage: "A general package installer", - Version: ctx.LipVersion().String(), - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "verbose", - Aliases: []string{"v"}, - Usage: "show verbose output", - DisableDefaultText: true, - }, - &cli.BoolFlag{ - Name: "quiet", - Aliases: []string{"q"}, - Usage: "show only errors", - DisableDefaultText: true, - }, - &cli.BoolFlag{ - Name: "no-color", - Usage: "disable color output", - DisableDefaultText: true, - }, - }, - Before: func(cCtx *cli.Context) error { - if cCtx.Bool("no-color") { - log.SetFormatter(&nested.Formatter{NoColors: true}) - } - if cCtx.Bool("verbose") { - log.SetLevel(log.DebugLevel) - } else if cCtx.Bool("quiet") { - log.SetLevel(log.ErrorLevel) - } else { - log.SetLevel(log.InfoLevel) - } - - if cCtx.Bool("verbose") && cCtx.Bool("quiet") { - return fmt.Errorf("verbose and quiet flags are mutually exclusive") - } - return nil - }, - Commands: []*cli.Command{ - cmdlipcache.Command(ctx), - cmdlipconfig.Command(ctx), - cmdlipinstall.Command(ctx), - cmdlipuninstall.Command(ctx), - cmdliplist.Command(ctx), - cmdlipshow.Command(ctx), - cmdlipfreeze.Command(ctx), - cmdliptooth.Command(ctx), - }, - CommandNotFound: func(cCtx *cli.Context, command string) { - log.Errorf("unknown command: lip %v", command) - }, - }).Run(args) - -} diff --git a/internal/cmd/cmdlipcache/cmdlipcache.go b/internal/cmd/cmdlipcache/cmdlipcache.go deleted file mode 100644 index fa89669..0000000 --- a/internal/cmd/cmdlipcache/cmdlipcache.go +++ /dev/null @@ -1,27 +0,0 @@ -package cmdlipcache - -import ( - "fmt" - - "github.com/lippkg/lip/internal/cmd/cmdlipcachepurge" - "github.com/lippkg/lip/internal/context" - - "github.com/urfave/cli/v2" -) - -func Command(ctx *context.Context) *cli.Command { - return &cli.Command{ - Name: "cache", - Usage: "inspect and manage lip's cache", - Subcommands: []*cli.Command{ - cmdlipcachepurge.Command(ctx), - }, - Action: func(cCtx *cli.Context) error { - if cCtx.NArg() >= 1 { - return fmt.Errorf("unknown command: lip %v %v", cCtx.Command.Name, cCtx.Args().First()) - } - return fmt.Errorf( - "no command specified. See 'lip %v --help' for more information", cCtx.Command.Name) - }, - } -} diff --git a/internal/cmd/cmdlipcachepurge/cmdlipcachepurge.go b/internal/cmd/cmdlipcachepurge/cmdlipcachepurge.go deleted file mode 100644 index 2407107..0000000 --- a/internal/cmd/cmdlipcachepurge/cmdlipcachepurge.go +++ /dev/null @@ -1,51 +0,0 @@ -package cmdlipcachepurge - -import ( - "fmt" - "os" - - "github.com/lippkg/lip/internal/context" - - "github.com/urfave/cli/v2" -) - -func Command(ctx *context.Context) *cli.Command { - return &cli.Command{ - Name: "purge", - Usage: "clear the cache", - Description: "Remove all items from the cache.", - Action: func(cCtx *cli.Context) error { - if cCtx.NArg() != 0 { - return fmt.Errorf("unexpected arguments: %v", cCtx.Args()) - } - - if err := purgeCache(ctx); err != nil { - return fmt.Errorf("failed to purge the cache\n\t%w", err) - } - - return nil - }, - } -} - -// --------------------------------------------------------------------- - -// purgeCache removes all items from the cache. -func purgeCache(ctx *context.Context) error { - cacheDir, err := ctx.CacheDir() - if err != nil { - return fmt.Errorf("failed to get the cache directory\n\t%w", err) - } - - // Remove the cache directory. - if err := os.RemoveAll(cacheDir.LocalString()); err != nil { - return fmt.Errorf("failed to remove the cache directory\n\t%w", err) - } - - // Recreate the cache directory. - if err := os.MkdirAll(cacheDir.LocalString(), 0755); err != nil { - return fmt.Errorf("failed to recreate the cache directory\n\t%w", err) - } - - return nil -} diff --git a/internal/cmd/cmdlipconfig/cmdlipconfig.go b/internal/cmd/cmdlipconfig/cmdlipconfig.go deleted file mode 100644 index c973cc8..0000000 --- a/internal/cmd/cmdlipconfig/cmdlipconfig.go +++ /dev/null @@ -1,171 +0,0 @@ -package cmdlipconfig - -import ( - "fmt" - "reflect" - "strconv" - "strings" - - "github.com/lippkg/lip/internal/context" - "github.com/olekukonko/tablewriter" - "github.com/urfave/cli/v2" -) - -const descriptionText = `Manage configuration. - - - If no arguments are specified, list all configuration. - - If a key is specified, print the value of the key. - - If a key and a value are specified, set the value of the key.` - -func Command(ctx *context.Context) *cli.Command { - return &cli.Command{ - Name: "config", - Usage: "manage configuration", - Description: descriptionText, - ArgsUsage: "[ []]", - Action: func(cCtx *cli.Context) error { - switch cCtx.NArg() { - case 0: - showAllConfig(ctx) - - case 1: - if err := showConfig(ctx, cCtx.Args().Get(0)); err != nil { - return fmt.Errorf("failed to show config\n\t%w", err) - } - - case 2: - if err := setConfig(ctx, cCtx.Args().Get(0), cCtx.Args().Get(1)); err != nil { - return fmt.Errorf("failed to set config\n\t%w", err) - } - - default: - return fmt.Errorf("too many arguments") - } - - return nil - }, - } -} - -func convertConfigToMap(config context.Config) map[string]interface{} { - value := reflect.ValueOf(config) - typeOfValue := value.Type() - - configMap := make(map[string]interface{}) - for i := 0; i < value.NumField(); i++ { - configMap[typeOfValue.Field(i).Name] = value.Field(i).Interface() - } - - return configMap -} - -func convertStringToType(str string, targetType reflect.Type) (interface{}, error) { - switch targetType.Kind() { - case reflect.String: - return str, nil - - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - intValue, err := strconv.ParseInt(str, 10, targetType.Bits()) - if err != nil { - return nil, err - } - value := reflect.New(targetType).Elem() - value.SetInt(intValue) - return value.Interface(), nil - - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - uintValue, err := strconv.ParseUint(str, 10, targetType.Bits()) - if err != nil { - return nil, err - } - value := reflect.New(targetType).Elem() - value.SetUint(uintValue) - return value.Interface(), nil - - case reflect.Float32, reflect.Float64: - floatValue, err := strconv.ParseFloat(str, targetType.Bits()) - if err != nil { - return nil, err - } - value := reflect.New(targetType).Elem() - value.SetFloat(floatValue) - return value.Interface(), nil - - case reflect.Bool: - boolValue, err := strconv.ParseBool(str) - if err != nil { - return nil, err - } - value := reflect.New(targetType).Elem() - value.SetBool(boolValue) - return value.Interface(), nil - - default: - return nil, fmt.Errorf("unsupported type: %v", targetType) - } -} - -func setConfig(ctx *context.Context, key string, value string) error { - field := reflect.ValueOf(ctx.Config()).Elem().FieldByName(key) - if !field.IsValid() { - return fmt.Errorf("no such key: %v", key) - } - - if !field.CanSet() { - return fmt.Errorf("cannot set key: %v", key) - } - - fieldType := field.Type() - structValueInterface, err := convertStringToType(value, fieldType) - if err != nil { - return fmt.Errorf("failed to convert value to type\n\t%w", err) - } - structValue := reflect.ValueOf(structValueInterface) - - if structValue.Type().ConvertibleTo(fieldType) { - field.Set(structValue.Convert(fieldType)) - - } else { - return fmt.Errorf("cannot convert value to type: %v", fieldType) - } - - if err := ctx.SaveConfigFile(); err != nil { - return fmt.Errorf("failed to save config file\n\t%w", err) - } - - return nil -} - -func showAllConfig(ctx *context.Context) { - tableData := make([][]string, 0) - for key, value := range convertConfigToMap(*ctx.Config()) { - tableData = append(tableData, []string{key, fmt.Sprintf("%v", value)}) - } - - tableString := &strings.Builder{} - table := tablewriter.NewWriter(tableString) - table.SetHeader([]string{ - "Key", "Value", - }) - - for _, row := range tableData { - table.Append(row) - } - - table.Render() - - fmt.Print(tableString.String()) -} - -func showConfig(ctx *context.Context, key string) error { - configMap := convertConfigToMap(*ctx.Config()) - - if value, ok := configMap[key]; ok { - fmt.Printf("%v\n", value) - - } else { - return fmt.Errorf("no such key: %v", key) - } - - return nil -} diff --git a/internal/cmd/cmdlipfreeze/cmdlipfreeze.go b/internal/cmd/cmdlipfreeze/cmdlipfreeze.go deleted file mode 100644 index 4d20c0a..0000000 --- a/internal/cmd/cmdlipfreeze/cmdlipfreeze.go +++ /dev/null @@ -1,55 +0,0 @@ -package cmdlipfreeze - -import ( - "fmt" - "os" - - "github.com/lippkg/lip/internal/context" - "github.com/lippkg/lip/internal/path" - log "github.com/sirupsen/logrus" - "github.com/urfave/cli/v2" - - "github.com/lippkg/lip/internal/tooth" -) - -func Command(ctx *context.Context) *cli.Command { - return &cli.Command{ - Name: "freeze", - Usage: "generate specifiers.txt", - Description: "Generate a specifiers.txt that can be used for batch installation.", - ArgsUsage: "[output path]", - Action: func(cCtx *cli.Context) error { - - var outputPath = path.MustParse("./specifiers.txt") - - if cCtx.NArg() >= 1 { - return fmt.Errorf("expected zero or one argument") - } - - if cCtx.NArg() == 1 { - if input, err := path.Parse(cCtx.Args().Get(0)); err == nil { - outputPath = input - } else { - return fmt.Errorf("failed to perse the path to specifiers.txt\n\t%w", err) - } - } - - var specifiers string - - metadataList, err := tooth.GetAllMetadata(ctx) - if err != nil { - return fmt.Errorf("failed to get all installed teeth\n\t%w", err) - } - for _, i := range metadataList { - specifiers += fmt.Sprintf("%v@%v\n", i.ToothRepoPath(), i.Version().String()) - } - - if err := os.WriteFile(outputPath.LocalString(), []byte(specifiers), 0666); err != nil { - return fmt.Errorf("failed to create specifiers.txt\n\t%w", err) - } - - log.Info("specifiers.txt generated successful.") - return nil - }, - } -} diff --git a/internal/cmd/cmdlipinstall/archive.go b/internal/cmd/cmdlipinstall/archive.go deleted file mode 100644 index 092fd05..0000000 --- a/internal/cmd/cmdlipinstall/archive.go +++ /dev/null @@ -1,226 +0,0 @@ -package cmdlipinstall - -import ( - "fmt" - - "github.com/blang/semver/v4" - "github.com/lippkg/lip/internal/context" - "github.com/lippkg/lip/internal/install" - "github.com/lippkg/lip/internal/network" - "github.com/lippkg/lip/internal/path" - "github.com/lippkg/lip/internal/tooth" - log "github.com/sirupsen/logrus" -) - -func filterInstalledToothArchives(ctx *context.Context, archives []tooth.Archive, upgradeFlag bool, - forceReinstallFlag bool) ([]tooth.Archive, error) { - - if forceReinstallFlag { - return archives, nil - } - - filteredArchives := make([]tooth.Archive, 0) - for _, archive := range archives { - isInstalled, err := tooth.IsInstalled(ctx, archive.Metadata().ToothRepoPath()) - if err != nil { - return nil, fmt.Errorf("failed to check if tooth is installed\n\t%w", err) - } - - if !isInstalled { - filteredArchives = append(filteredArchives, archive) - } else if upgradeFlag { - currentMetadata, err := tooth.GetMetadata(ctx, archive.Metadata().ToothRepoPath()) - if err != nil { - return nil, fmt.Errorf("failed to find installed tooth metadata\n\t%w", err) - } - - if archive.Metadata().Version().GT(currentMetadata.Version()) { - filteredArchives = append(filteredArchives, archive) - } else { - log.Infof("Tooth %v is already up-to-date", archive.Metadata().ToothRepoPath()) - } - } else { - log.Infof("Tooth %v is already installed", archive.Metadata().ToothRepoPath()) - } - } - - return filteredArchives, nil -} - -// installToothArchive installs the tooth archive. -func installToothArchive(ctx *context.Context, archive tooth.Archive, forceReinstall bool, upgrade bool, yes bool) error { - debugLogger := log.WithFields(log.Fields{ - "package": "cmdlipinstall", - "method": "installToothArchive", - }) - - isInstalled, err := tooth.IsInstalled(ctx, archive.Metadata().ToothRepoPath()) - if err != nil { - return fmt.Errorf("failed to check if tooth is installed\n\t%w", err) - } - - shouldInstall := false - shouldUninstall := false - - if isInstalled && forceReinstall { - log.Infof("Reinstalling tooth %v", archive.Metadata().ToothRepoPath()) - - shouldInstall = true - shouldUninstall = true - - } else if isInstalled && upgrade { - currentMetadata, err := tooth.GetMetadata(ctx, - archive.Metadata().ToothRepoPath()) - if err != nil { - return fmt.Errorf("failed to find installed tooth metadata\n\t%w", err) - } - - if archive.Metadata().Version().GT(currentMetadata.Version()) { - log.Infof("Upgrading tooth %v", archive.Metadata().ToothRepoPath()) - - shouldInstall = true - shouldUninstall = true - } else { - log.Infof("Tooth %v is already up-to-date", archive.Metadata().ToothRepoPath()) - - shouldInstall = false - shouldUninstall = false - } - - } else if isInstalled { - log.Infof("Tooth %v is already installed", archive.Metadata().ToothRepoPath()) - - shouldInstall = false - shouldUninstall = false - - } else { - log.Infof("Installing tooth %v", archive.Metadata().ToothRepoPath()) - - shouldInstall = true - shouldUninstall = false - } - - if shouldUninstall { - err := install.Uninstall(ctx, archive.Metadata().ToothRepoPath()) - if err != nil { - return fmt.Errorf("failed to uninstall tooth\n\t%w", err) - } - debugLogger.Debugf("Uninstalled tooth %v", archive.Metadata().ToothRepoPath()) - } - - if shouldInstall { - assetURL, err := archive.Metadata().AssetURL() - if err != nil { - return fmt.Errorf("failed to get asset URL\n\t%w", err) - } - - assetArchiveFilePath := path.MakeEmpty() - if assetURL.String() != "" { - gitHubMirrorURL, err := ctx.GitHubMirrorURL() - if err != nil { - return fmt.Errorf("failed to get GitHub mirror URL\n\t%w", err) - } - - mirroredURL := assetURL - if network.IsGitHubDirectDownloadURL(assetURL) { - // Rewrite GitHub URL to GitHub mirror URL if it is set. - mirroredURL, err = network.GenerateGitHubMirrorURL(assetURL, gitHubMirrorURL) - if err != nil { - return fmt.Errorf("failed to generate GitHub mirror URL\n\t%w", err) - } - } - - cachePath, err := getCachePath(ctx, mirroredURL) - if err != nil { - return fmt.Errorf("failed to get cache path of asset URL %v\n\t%w", assetURL, err) - } - - assetArchiveFilePath = cachePath - } - - archiveWithAssets, err := archive.ToAssetArchiveAttached(assetArchiveFilePath) - if err != nil { - return fmt.Errorf("failed to attach asset archive %v\n\t%w", assetArchiveFilePath.LocalString(), err) - } - - if err := install.Install(ctx, archiveWithAssets, yes); err != nil { - return fmt.Errorf("failed to install tooth archive %v\n\t%w", archiveWithAssets.FilePath().LocalString(), err) - } - debugLogger.Debugf("Installed tooth archive %v", archiveWithAssets.FilePath().LocalString()) - } - - return nil -} - -// topoSortToothArchives sorts tooth archives by dependence with topological sort. -func topoSortToothArchives(archiveList []tooth.Archive) ([]tooth.Archive, error) { - // Make a map from tooth path to tooth archive. - archiveMap := make(map[string]tooth.Archive) - for _, archive := range archiveList { - archiveMap[archive.Metadata().ToothRepoPath()] = archive - } - - preVisited := make(map[string]bool) - visited := make(map[string]bool) - sorted := make([]tooth.Archive, 0) - for _, toothArchive := range archiveList { - err := topoSortVisit(toothArchive, archiveMap, preVisited, visited, &sorted) - - if err != nil { - return nil, err - } - } - - return sorted, nil -} - -// topoSortVisit visits a tooth archive and its dependencies. -func topoSortVisit(archive tooth.Archive, archiveMap map[string]tooth.Archive, - preVisited map[string]bool, visited map[string]bool, sorted *[]tooth.Archive) error { - - if visited[archive.Metadata().ToothRepoPath()] { - return nil - } - - if preVisited[archive.Metadata().ToothRepoPath()] && !visited[archive.Metadata().ToothRepoPath()] { - return fmt.Errorf("tooth %s has a circular dependency", archive.Metadata().ToothRepoPath()) - } - - preVisited[archive.Metadata().ToothRepoPath()] = true - dependencies, err := archive.Metadata().Dependencies() - if err != nil { - return fmt.Errorf("failed to get dependencies of tooth %s\n\t%w", archive.Metadata().ToothRepoPath(), err) - } - - for depToothPath := range dependencies { - // Find the tooth archive of the dependency. - dep, ok := archiveMap[depToothPath] - if !ok { - // Ignore the dependency if it is not in the tooth archive list. - // sortToothArchives only sorts the tooth archives in the tooth archive list. - continue - } - - err := topoSortVisit(dep, archiveMap, preVisited, visited, sorted) - - if err != nil { - return err - } - } - *sorted = append(*sorted, archive) - visited[archive.Metadata().ToothRepoPath()] = true - return nil -} - -// validateToothArchive validates the archive. -func validateToothArchive(archive tooth.Archive, toothRepoPath string, version semver.Version) error { - if archive.Metadata().ToothRepoPath() != toothRepoPath { - return fmt.Errorf("tooth name mismatch: %v != %v", archive.Metadata().ToothRepoPath(), toothRepoPath) - } - - if archive.Metadata().Version().NE(version) { - return fmt.Errorf("tooth version mismatch: %v != %v", archive.Metadata().Version(), version) - } - - return nil -} diff --git a/internal/cmd/cmdlipinstall/cmdlipinstall.go b/internal/cmd/cmdlipinstall/cmdlipinstall.go deleted file mode 100644 index 0378627..0000000 --- a/internal/cmd/cmdlipinstall/cmdlipinstall.go +++ /dev/null @@ -1,235 +0,0 @@ -package cmdlipinstall - -import ( - "fmt" - "os" - "strings" - - "github.com/lippkg/lip/internal/context" - "github.com/lippkg/lip/internal/path" - "github.com/lippkg/lip/internal/specifier" - "github.com/urfave/cli/v2" - - "github.com/lippkg/lip/internal/tooth" - log "github.com/sirupsen/logrus" -) - -const descriptionText = ` -Install teeth from: - -- tooth repositories. (e.g. "github.com/tooth-hub/llbds3@3.1.0") -- local tooth archives. (e.g. "./foo.tth") -` - -func Command(ctx *context.Context) *cli.Command { - return &cli.Command{ - Name: "install", - Usage: "install a tooth", - Description: descriptionText, - ArgsUsage: " [...]", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "yes", - Aliases: []string{"y"}, - Usage: "skip confirmation", - DisableDefaultText: true, - }, - &cli.BoolFlag{ - Name: "upgrade", - Usage: "upgrade the specified tooth to the newest available version", - DisableDefaultText: true, - }, - &cli.BoolFlag{ - Name: "force-reinstall", - Usage: "reinstall the tooth even if they are already up-to-date", - DisableDefaultText: true, - }, - &cli.BoolFlag{ - Name: "no-dependencies", - Usage: "do not install dependencies. Also bypass prerequisite checks", - DisableDefaultText: true, - }, - &cli.BoolFlag{ - Name: "specifiers", - Aliases: []string{"s"}, - Usage: "install form specifiers.txt", - DisableDefaultText: true, - }, - }, - Action: func(cCtx *cli.Context) error { - debugLogger := log.WithFields(log.Fields{ - "package": "cmdlipinstall", - "method": "Action", - }) - - // At least one specifier is required. - if cCtx.NArg() == 0 { - return fmt.Errorf("at least one specifier is required") - } - - log.Info("Downloading teeth and resolving dependencies...") - - // Parse specifiers. - specifiers := make([]specifier.Specifier, 0) - - if cCtx.Bool("specifiers") { - if cCtx.NArg() != 1 { - return fmt.Errorf("must give one specifiers.txt") - } - - inputPath, err := path.Parse(cCtx.Args().Get(0)) - if err != nil { - return fmt.Errorf("failed to perse the path to specifiers.txt\n\t%w", err) - } - - if _, err := os.Stat(inputPath.LocalString()); os.IsNotExist(err) { - return fmt.Errorf("the file %v not exist", inputPath.LocalString()) - } else if err != nil { - return fmt.Errorf("failed to check if file exists\n\t%w", err) - } - - bytes, err := os.ReadFile(inputPath.LocalString()) - - if err != nil { - return fmt.Errorf("cannot read the specifiers file at %v", inputPath.LocalString()) - } - - for _, specifierString := range strings.Split(string(bytes), "\n") { - if specifierString == "" { - continue - } - debugLogger.Debug(strings.Split(string(bytes), "\n")) - specifier, err := specifier.Parse(specifierString) - if err != nil { - return fmt.Errorf("failed to parse specifier\n\t%w", err) - } - - specifiers = append(specifiers, specifier) - } - } else { - for _, specifierString := range cCtx.Args().Slice() { - specifier, err := specifier.Parse(specifierString) - if err != nil { - return fmt.Errorf("failed to parse specifier\n\t%w", err) - } - - specifiers = append(specifiers, specifier) - } - } - - debugLogger.Debug("Got specifiers from arguments:") - for _, specifier := range specifiers { - debugLogger.Debugf(" %v", specifier) - } - - // Download remote tooth archives. Then open all specified tooth archives. - - specifiedArchives, err := resolveSpecifiers(ctx, specifiers) - if err != nil { - return fmt.Errorf("failed to parse and download specifier string list\n\t%w", err) - } - - debugLogger.Debug("Got tooth archives from specifiers:") - for _, archive := range specifiedArchives { - debugLogger.Debugf(" %v@%v: %v", archive.Metadata().ToothRepoPath(), archive.Metadata().Version(), archive.FilePath().LocalString()) - } - - // Resolve dependencies and check prerequisites. - - archivesToInstall := specifiedArchives - if !cCtx.Bool("no-dependencies") { - archives, err := resolveDependencies(ctx, specifiedArchives, cCtx.Bool("upgrade"), - cCtx.Bool("force-reinstall")) - if err != nil { - return fmt.Errorf("failed to resolve dependencies\n\t%w", err) - } - - archivesToInstall = archives - - debugLogger.Debug("After resolving dependencies, got tooth archives to install:") - for _, archive := range archivesToInstall { - debugLogger.Debugf(" %v@%v: %v", archive.Metadata().ToothRepoPath(), archive.Metadata().Version(), archive.FilePath().LocalString()) - } - - _, missingPrerequisites, err := getMissingPrerequisites(ctx, archivesToInstall) - if err != nil { - return fmt.Errorf("failed to find missing prerequisites\n\t%w", err) - } - - if len(missingPrerequisites) != 0 { - message := "Missing prerequisites:\n" - for prerequisite, versionRangeString := range missingPrerequisites { - message += fmt.Sprintf(" %v: %v\n", prerequisite, versionRangeString) - } - return fmt.Errorf(message) - } - } - - // Filter installed teeth. - - filteredArchives, err := filterInstalledToothArchives(ctx, archivesToInstall, cCtx.Bool("upgrade"), - cCtx.Bool("force-reinstall")) - if err != nil { - return fmt.Errorf("failed to filter installed teeth\n\t%w", err) - } - - debugLogger.Debug("After filtering installed teeth, got archives to install:") - for _, archive := range filteredArchives { - debugLogger.Debugf(" %v@%v: %v", archive.Metadata().ToothRepoPath(), archive.Metadata().Version(), archive.FilePath().LocalString()) - } - - // Download tooth assets if necessary. - - for _, archive := range filteredArchives { - if err := downloadToothAssetArchiveIfNotCached(ctx, archive); err != nil { - return fmt.Errorf("failed to download tooth assets\n\t%w", err) - } - } - - // Ask for confirmation. - - if !cCtx.Bool("yes") { - err := askForConfirmation(ctx, filteredArchives) - if err != nil { - return err - } - } - - // Install teeth. - - log.Info("Installing teeth...") - - for _, archive := range filteredArchives { - if err := installToothArchive(ctx, archive, cCtx.Bool("force-reinstall"), cCtx.Bool("upgrade"), cCtx.Bool("yes")); err != nil { - return fmt.Errorf("failed to install tooth archive %v\n\t%w", archive.FilePath().LocalString(), err) - } - } - - log.Info("Done.") - - return nil - }, - } -} - -// askForConfirmation asks for confirmation before installing the tooth. -func askForConfirmation(ctx *context.Context, - archiveList []tooth.Archive) error { - - // Print the list of teeth to be installed. - log.Info("The following teeth will be installed:") - for _, archive := range archiveList { - log.Infof(" %v@%v: %v", archive.Metadata().ToothRepoPath(), archive.Metadata().Version(), - archive.Metadata().Info().Name) - } - - // Ask for confirmation. - log.Info("Do you want to continue? [y/N]") - var ans string - fmt.Scanln(&ans) - if ans != "y" && ans != "Y" { - return fmt.Errorf("aborted") - } - - return nil -} diff --git a/internal/cmd/cmdlipinstall/dependencies.go b/internal/cmd/cmdlipinstall/dependencies.go deleted file mode 100644 index 4f32bc3..0000000 --- a/internal/cmd/cmdlipinstall/dependencies.go +++ /dev/null @@ -1,131 +0,0 @@ -package cmdlipinstall - -import ( - "container/list" - "fmt" - - "github.com/blang/semver/v4" - "github.com/lippkg/lip/internal/context" - "github.com/lippkg/lip/internal/tooth" - log "github.com/sirupsen/logrus" -) - -func getFixedToothAndVersionMap(ctx *context.Context, specifiedArchives []tooth.Archive, upgradeFlag bool, - forceReinstallFlag bool) (map[string]semver.Version, error) { - - fixedTeethAndVersions := make(map[string]semver.Version) - - installedToothMetadataList, err := tooth.GetAllMetadata(ctx) - if err != nil { - return nil, fmt.Errorf("failed to get all installed tooth metadata\n\t%w", err) - } - - for _, installedToothMetadata := range installedToothMetadataList { - fixedTeethAndVersions[installedToothMetadata.ToothRepoPath()] = installedToothMetadata.Version() - } - - for _, archive := range specifiedArchives { - if fixedVersion, ok := fixedTeethAndVersions[archive.Metadata().ToothRepoPath()]; !ok { - // If not installed, fix it. - fixedTeethAndVersions[archive.Metadata().ToothRepoPath()] = archive.Metadata().Version() - - } else if forceReinstallFlag { - // If to force reinstall, fix it. - fixedTeethAndVersions[archive.Metadata().ToothRepoPath()] = archive.Metadata().Version() - - } else if upgradeFlag && - archive.Metadata().Version().GT(fixedTeethAndVersions[archive.Metadata().ToothRepoPath()]) { - // If to upgrade and the version is newer, fix it. - fixedTeethAndVersions[archive.Metadata().ToothRepoPath()] = archive.Metadata().Version() - - } else if fixedVersion.NE(archive.Metadata().Version()) { - return nil, fmt.Errorf( - "trying to fix tooth %v with version %v, but found version %v fixed", - archive.Metadata().ToothRepoPath(), archive.Metadata().Version(), fixedVersion) - } - } - - return fixedTeethAndVersions, nil -} - -// resolveDependencies resolves the dependencies of the tooth specified by the -// specifier and returns the paths to the downloaded teeth. rootArchiveList -// contains the root tooth archives to resolve dependencies. -// The first return value indicates whether the dependencies are resolved. -func resolveDependencies(ctx *context.Context, rootArchiveList []tooth.Archive, - upgradeFlag bool, forceReinstallFlag bool) ([]tooth.Archive, error) { - debugLogger := log.WithFields(log.Fields{ - "package": "cmdlipinstall", - "method": "resolveDependencies", - }) - - fixedToothAndVersionMap, err := getFixedToothAndVersionMap(ctx, rootArchiveList, upgradeFlag, - forceReinstallFlag) - if err != nil { - return nil, fmt.Errorf("failed to get fixed tooth and version map\n\t%w", err) - } - - notResolvedArchiveQueue := list.New() - for _, rootArchive := range rootArchiveList { - notResolvedArchiveQueue.PushBack(rootArchive) - } - - resolvedArchiveList := make([]tooth.Archive, 0) - - for notResolvedArchiveQueue.Len() > 0 { - archive := notResolvedArchiveQueue.Front().Value.(tooth.Archive) - notResolvedArchiveQueue.Remove(notResolvedArchiveQueue.Front()) - - depMap, err := archive.Metadata().Dependencies() - if err != nil { - return nil, fmt.Errorf("failed to get dependencies of %v\n\t%w", archive.FilePath().LocalString(), err) - } - - depStrMap := archive.Metadata().DependenciesAsStrings() - - for dep, versionRange := range depMap { - if fixedVersion, ok := fixedToothAndVersionMap[dep]; ok { - if !versionRange(fixedToothAndVersionMap[dep]) { - return nil, fmt.Errorf("fixed tooth %v of version %v does not satisfy the version range %v", - dep, fixedVersion.String(), depStrMap[dep]) - } - - // Avoid downloading the same tooth multiple times. - debugLogger.Debugf("Dependency %v@%v is already fixed, skip", dep, fixedVersion) - continue - } - - targetVersion, err := tooth.GetLatestVersionInVersionRange(ctx, dep, versionRange) - if err != nil { - return nil, fmt.Errorf("no available version in %v found for dependency %v", depStrMap[dep], dep) - } - - debugLogger.Debugf("Dependency %v of range %v is resolved to version %v", dep, depStrMap[dep], targetVersion) - - currentArchive, err := downloadToothArchiveIfNotCached(ctx, dep, targetVersion) - if err != nil { - return nil, fmt.Errorf("failed to download tooth\n\t%w", err) - } - - debugLogger.Debugf("Downloaded tooth archive %v", currentArchive.FilePath().LocalString()) - - notResolvedArchiveQueue.PushBack(currentArchive) - - fixedToothAndVersionMap[dep] = targetVersion - } - - resolvedArchiveList = append(resolvedArchiveList, archive) - } - - sortedArchives, err := topoSortToothArchives(resolvedArchiveList) - if err != nil { - return nil, fmt.Errorf("failed to sort teeth\n\t%w", err) - } - - debugLogger.Debug("Topologically sorted teeth:") - for _, archive := range sortedArchives { - debugLogger.Debugf(" %v@%v: %v", archive.Metadata().ToothRepoPath(), archive.Metadata().Version(), archive.FilePath().LocalString()) - } - - return sortedArchives, nil -} diff --git a/internal/cmd/cmdlipinstall/download.go b/internal/cmd/cmdlipinstall/download.go deleted file mode 100644 index 41cddc7..0000000 --- a/internal/cmd/cmdlipinstall/download.go +++ /dev/null @@ -1,176 +0,0 @@ -package cmdlipinstall - -import ( - "fmt" - "net/url" - "os" - - "github.com/blang/semver/v4" - "github.com/lippkg/lip/internal/context" - "github.com/lippkg/lip/internal/network" - "github.com/lippkg/lip/internal/path" - "github.com/lippkg/lip/internal/tooth" - log "github.com/sirupsen/logrus" - "golang.org/x/mod/module" -) - -func downloadFileIfNotCached(ctx *context.Context, downloadURL *url.URL) (path.Path, error) { - debugLogger := log.WithFields(log.Fields{ - "package": "cmdlipinstall", - "method": "downloadFileIfNotCached", - }) - - cachePath, err := getCachePath(ctx, downloadURL) - if err != nil { - return path.Path{}, fmt.Errorf("failed to get cache path of %v\n\t%w", downloadURL, err) - } - - // Skip downloading if the file is already in the cache. - if _, err := os.Stat(cachePath.LocalString()); os.IsNotExist(err) { - log.Infof("Downloading %v", downloadURL) - - var enableProgressBar bool - if log.GetLevel() == log.PanicLevel || log.GetLevel() == log.FatalLevel || - log.GetLevel() == log.ErrorLevel || log.GetLevel() == log.WarnLevel { - enableProgressBar = false - } else { - enableProgressBar = true - } - - proxyURL, err := ctx.ProxyURL() - if err != nil { - return path.Path{}, fmt.Errorf("failed to get proxy URL\n\t%w", err) - } - - if err := network.DownloadFile(downloadURL, proxyURL, cachePath, enableProgressBar); err != nil { - return path.Path{}, fmt.Errorf("failed to download file\n\t%w", err) - } - - } else if err != nil { - return path.Path{}, fmt.Errorf("failed to check if file exists\n\t%w", err) - } else { - debugLogger.Debugf("File %v already exists in the cache, skip downloading", cachePath.LocalString()) - } - - return cachePath, nil -} - -// downloadToothArchiveIfNotCached downloads the tooth archive from the Go module proxy -// if it is not cached, and returns the path to the downloaded tooth archive. -func downloadToothArchiveIfNotCached(ctx *context.Context, toothRepoPath string, - toothVersion semver.Version) (tooth.Archive, error) { - debugLogger := log.WithFields(log.Fields{ - "package": "cmdlipinstall", - "method": "downloadToothArchiveIfNotCached", - }) - - goModuleProxyURL, err := ctx.GoModuleProxyURL() - if err != nil { - return tooth.Archive{}, fmt.Errorf("failed to get Go module proxy URL\n\t%w", err) - } - - downloadURL, err := network.GenerateGoModuleZipFileURL(toothRepoPath, toothVersion, goModuleProxyURL) - if err != nil { - return tooth.Archive{}, fmt.Errorf("failed to generate Go module zip file URL\n\t%w", err) - } - - cachePath, err := downloadFileIfNotCached(ctx, downloadURL) - if err != nil { - return tooth.Archive{}, fmt.Errorf("failed to download file\n\t%w", err) - } - - debugLogger.Debugf("Downloaded tooth archive from %v to %v", downloadURL, cachePath.LocalString()) - - archive, err := tooth.MakeArchive(cachePath) - if err != nil { - return tooth.Archive{}, fmt.Errorf("failed to open archive %v\n\t%w", cachePath.LocalString(), err) - } - - if err := validateToothArchive(archive, toothRepoPath, toothVersion); err != nil { - return tooth.Archive{}, fmt.Errorf("failed to validate archive\n\t%w", err) - } - - debugLogger.Debugf("Downloaded tooth archive %v", cachePath.LocalString()) - - return archive, nil -} - -func downloadToothAssetArchiveIfNotCached(ctx *context.Context, archive tooth.Archive) error { - metadata := archive.Metadata() - assetURL, err := metadata.AssetURL() - if err != nil { - return fmt.Errorf("failed to get asset URL\n\t%w", err) - } - - if assetURL.String() == "" { - return nil - } - - // Rewrite GitHub URL to GitHub mirror URL if it is set. - - gitHubMirrorURL, err := ctx.GitHubMirrorURL() - if err != nil { - return fmt.Errorf("failed to get GitHub mirror URL\n\t%w", err) - } - - if network.IsGitHubDirectDownloadURL(assetURL) { - // HTTP or HTTPS URL from GitHub. - - mirroredURL, err := network.GenerateGitHubMirrorURL(assetURL, gitHubMirrorURL) - if err != nil { - return fmt.Errorf("failed to generate GitHub mirror URL\n\t%w", err) - } - - if _, err := downloadFileIfNotCached(ctx, mirroredURL); err != nil { - return fmt.Errorf("failed to download file\n\t%w", err) - } - - } else if assetURL.Scheme == "http" || assetURL.Scheme == "https" { - // Other HTTP or HTTPS URL. - - if _, err := downloadFileIfNotCached(ctx, assetURL); err != nil { - return fmt.Errorf("failed to download file\n\t%w", err) - } - - } else if err := module.CheckPath(assetURL.String()); err == nil { - // Go module path. - - goModuleProxyURL, err := ctx.GoModuleProxyURL() - if err != nil { - return fmt.Errorf("failed to get Go module proxy URL\n\t%w", err) - } - - downloadURL, err := network.GenerateGoModuleZipFileURL(assetURL.String(), archive.Metadata().Version(), goModuleProxyURL) - if err != nil { - return fmt.Errorf("failed to generate Go module zip file URL\n\t%w", err) - } - - if _, err := downloadFileIfNotCached(ctx, downloadURL); err != nil { - return fmt.Errorf("failed to download file\n\t%w", err) - } - - } else { - return fmt.Errorf("unsupported asset URL: %v", assetURL) - } - - return nil -} - -func getCachePath(ctx *context.Context, u *url.URL) (path.Path, error) { - debugLogger := log.WithFields(log.Fields{ - "package": "cmdlipinstall", - "method": "getCachePath", - }) - - cacheDir, err := ctx.CacheDir() - if err != nil { - return path.Path{}, fmt.Errorf("failed to get cache directory\n\t%w", err) - } - - cacheFileName := url.QueryEscape(u.String()) - cachePath := cacheDir.Join(path.MustParse(cacheFileName)) - - debugLogger.Debugf("Cache path of %v is %v", u, cachePath.LocalString()) - - return cachePath, nil -} diff --git a/internal/cmd/cmdlipinstall/prerequisites.go b/internal/cmd/cmdlipinstall/prerequisites.go deleted file mode 100644 index 2e1a379..0000000 --- a/internal/cmd/cmdlipinstall/prerequisites.go +++ /dev/null @@ -1,63 +0,0 @@ -package cmdlipinstall - -import ( - "fmt" - - "github.com/blang/semver/v4" - "github.com/lippkg/lip/internal/context" - "github.com/lippkg/lip/internal/tooth" -) - -// getMissingPrerequisites finds missing prerequisites of the tooth specified -// by the specifier and returns the map of missing prerequisites. -func getMissingPrerequisites(ctx *context.Context, - archiveList []tooth.Archive) (map[string]semver.Range, map[string]string, error) { - missingPrerequisiteMap := make(map[string]semver.Range) - missingPrerequisitesAsStrings := make(map[string]string) - - for _, archive := range archiveList { - prerequisites, err := archive.Metadata().Prerequisites() - if err != nil { - return nil, nil, fmt.Errorf("failed to get prerequisites\n\t%w", err) - } - - prerequisitesAsStrings := archive.Metadata().PrerequisitesAsStrings() - - for prerequisite, versionRange := range prerequisites { - isInstalled, err := tooth.IsInstalled(ctx, prerequisite) - if err != nil { - return nil, nil, fmt.Errorf("failed to check if tooth is installed\n\t%w", err) - } - - if isInstalled { - currentMetadata, err := tooth.GetMetadata(ctx, prerequisite) - if err != nil { - return nil, nil, fmt.Errorf("failed to find installed tooth metadata\n\t%w", err) - } - - if !versionRange(currentMetadata.Version()) { - missingPrerequisiteMap[prerequisite] = versionRange - missingPrerequisitesAsStrings[prerequisite] = prerequisitesAsStrings[prerequisite] - } - - break - } else { - // Check if the tooth is in the archive list. - isInArchiveList := false - for _, archive := range archiveList { - if archive.Metadata().ToothRepoPath() == prerequisite && versionRange(archive.Metadata().Version()) { - isInArchiveList = true - break - } - } - - if !isInArchiveList { - missingPrerequisiteMap[prerequisite] = versionRange - missingPrerequisitesAsStrings[prerequisite] = prerequisitesAsStrings[prerequisite] - } - } - } - } - - return missingPrerequisiteMap, missingPrerequisitesAsStrings, nil -} diff --git a/internal/cmd/cmdlipinstall/specifier.go b/internal/cmd/cmdlipinstall/specifier.go deleted file mode 100644 index 3869733..0000000 --- a/internal/cmd/cmdlipinstall/specifier.go +++ /dev/null @@ -1,89 +0,0 @@ -package cmdlipinstall - -import ( - "fmt" - - "github.com/blang/semver/v4" - "github.com/lippkg/lip/internal/context" - "github.com/lippkg/lip/internal/must" - specifierpkg "github.com/lippkg/lip/internal/specifier" - "github.com/lippkg/lip/internal/tooth" -) - -// downloadToothRepoSpecifier downloads the tooth specified by the specifier and returns -// the path to the downloaded tooth. -func downloadToothRepoSpecifier(ctx *context.Context, - specifier specifierpkg.Specifier) (tooth.Archive, error) { - if specifier.Kind() != specifierpkg.ToothRepoKind { - return tooth.Archive{}, fmt.Errorf("invalid specifier kind %v", specifier.Kind()) - } - - toothRepoPath := must.Must(specifier.ToothRepoPath()) - - // Parse or get the tooth version. - - var toothVersion semver.Version - isToothVersionSpecified, err := specifier.IsToothVersionSpecified() - if err != nil { - return tooth.Archive{}, fmt.Errorf("failed to get is tooth version specified\n\t%w", err) - } - - if isToothVersionSpecified { - toothVersion = must.Must(specifier.ToothVersion()) - - } else { - latestVersion, err := tooth.GetLatestVersion(ctx, toothRepoPath) - if err != nil { - return tooth.Archive{}, fmt.Errorf("failed to look up tooth version\n\t%w", err) - } - - toothVersion = latestVersion - } - - archive, err := downloadToothArchiveIfNotCached(ctx, toothRepoPath, toothVersion) - if err != nil { - return tooth.Archive{}, fmt.Errorf("failed to download archive of %v@%v\n\t%w", toothRepoPath, - toothVersion, err) - } - - return archive, nil -} - -// resolveSpecifiers parses the specifier string list and -// downloads the tooth specified by the specifier, and returns the list of -// downloaded tooth archives. -func resolveSpecifiers(ctx *context.Context, - specifiers []specifierpkg.Specifier) ([]tooth.Archive, error) { - - archiveList := make([]tooth.Archive, 0) - - for _, specifier := range specifiers { - var archive tooth.Archive - - switch specifier.Kind() { - case specifierpkg.ToothArchiveKind: - archivePath := must.Must(specifier.ToothArchivePath()) - localArchive, err := tooth.MakeArchive(archivePath) - if err != nil { - return nil, fmt.Errorf("failed to open archive %v\n\t%w", archivePath.LocalString(), err) - } - - archive = localArchive - - case specifierpkg.ToothRepoKind: - downloadedArchive, err := downloadToothRepoSpecifier(ctx, specifier) - if err != nil { - return nil, fmt.Errorf("failed to download specifier %v\n\t%w", specifier, err) - } - - archive = downloadedArchive - - default: - panic("unreachable") - } - - archiveList = append(archiveList, archive) - } - - return archiveList, nil -} diff --git a/internal/cmd/cmdliplist/cmdliplist.go b/internal/cmd/cmdliplist/cmdliplist.go deleted file mode 100644 index 67cd890..0000000 --- a/internal/cmd/cmdliplist/cmdliplist.go +++ /dev/null @@ -1,178 +0,0 @@ -package cmdliplist - -import ( - "encoding/json" - "fmt" - "strings" - - "github.com/lippkg/lip/internal/context" - log "github.com/sirupsen/logrus" - "github.com/urfave/cli/v2" - - "github.com/lippkg/lip/internal/tooth" - "github.com/olekukonko/tablewriter" -) - -func Command(ctx *context.Context) *cli.Command { - return &cli.Command{ - Name: "list", - Usage: "list installed teeth", - Description: "List installed teeth.", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "json", - Usage: "output in JSON format", - DisableDefaultText: true, - }, - &cli.BoolFlag{ - Name: "upgradable", - Usage: "list upgradable teeth", - DisableDefaultText: true, - }, - }, - Action: func(cCtx *cli.Context) error { - // Check if there are unexpected arguments. - if cCtx.NArg() != 0 { - return fmt.Errorf("unexpected arguments: %v", cCtx.Args()) - } - - if cCtx.Bool("upgradable") { - err := listUpgradable(ctx, cCtx.Bool("json")) - if err != nil { - return fmt.Errorf("failed to list upgradable teeth\n\t%w", err) - } - - return nil - } else { - err := listAll(ctx, cCtx.Bool("json")) - if err != nil { - return fmt.Errorf("failed to list all teeth\n\t%w", err) - } - - return nil - } - }, - } -} - -// --------------------------------------------------------------------- - -// listAll lists all installed teeth. -func listAll(ctx *context.Context, jsonFlag bool) error { - - metadataList, err := tooth.GetAllMetadata(ctx) - if err != nil { - return fmt.Errorf("failed to list all installed teeth\n\t%w", err) - } - - if jsonFlag { - // Marshal the data. - jsonBytes, err := json.Marshal(metadataList) - if err != nil { - return fmt.Errorf("failed to marshal JSON\n\t%w", err) - } - - jsonString := string(jsonBytes) - fmt.Print(jsonString) - } else { - tableData := make([][]string, 0) - for _, metadata := range metadataList { - tableData = append(tableData, []string{ - metadata.ToothRepoPath(), - metadata.Info().Name, - metadata.Version().String(), - }) - } - - tableString := &strings.Builder{} - table := tablewriter.NewWriter(tableString) - table.SetHeader([]string{ - "Tooth", "Name", "Version", - }) - - for _, row := range tableData { - table.Append(row) - } - - table.Render() - - fmt.Print(tableString.String()) - } - - return nil -} - -// listUpgradable lists upgradable teeth. -func listUpgradable(ctx *context.Context, jsonFlag bool) error { - - metadataList, err := tooth.GetAllMetadata(ctx) - if err != nil { - return fmt.Errorf("failed to list all installed teeth\n\t%w", err) - } - - if jsonFlag { - dataList := make([]tooth.Metadata, 0) - - for _, metadata := range metadataList { - currentVersion := metadata.Version() - latestVersion, err := tooth.GetLatestVersion(ctx, - metadata.ToothRepoPath()) - if err != nil { - log.Errorf( - "\n\tfailed to look up latest version for %v\n\t%v", metadata.ToothRepoPath(), err.Error()) - continue - } - - if latestVersion.GT(currentVersion) { - dataList = append(dataList, metadata) - } - } - - // Marshal the data. - jsonBytes, err := json.Marshal(dataList) - if err != nil { - return fmt.Errorf("failed to marshal JSON\n\t%w", err) - } - - jsonString := string(jsonBytes) - fmt.Print(jsonString) - - } else { - tableData := make([][]string, 0) - for _, metadata := range metadataList { - currentVersion := metadata.Version() - latestVersion, err := tooth.GetLatestVersion(ctx, - metadata.ToothRepoPath()) - if err != nil { - log.Errorf( - "\n\tfailed to look up latest version for %v\n\t%v", metadata.ToothRepoPath(), err.Error()) - continue - } - - if latestVersion.GT(currentVersion) { - tableData = append(tableData, []string{ - metadata.ToothRepoPath(), - metadata.Info().Name, - metadata.Version().String(), - latestVersion.String(), - }) - } - } - - tableStringBuilder := &strings.Builder{} - table := tablewriter.NewWriter(tableStringBuilder) - table.SetHeader([]string{ - "Tooth", "Name", "Version", "Latest", - }) - - for _, row := range tableData { - table.Append(row) - } - - table.Render() - - fmt.Print(tableStringBuilder.String()) - } - - return nil -} diff --git a/internal/cmd/cmdlipshow/cmdlipshow.go b/internal/cmd/cmdlipshow/cmdlipshow.go deleted file mode 100644 index 7ffd8d8..0000000 --- a/internal/cmd/cmdlipshow/cmdlipshow.go +++ /dev/null @@ -1,148 +0,0 @@ -package cmdlipshow - -import ( - "encoding/json" - "fmt" - "strings" - - "github.com/lippkg/lip/internal/context" - "github.com/urfave/cli/v2" - - "github.com/lippkg/lip/internal/tooth" - "github.com/olekukonko/tablewriter" -) - -func Command(ctx *context.Context) *cli.Command { - return &cli.Command{ - Name: "show", - Usage: "show information about installed teeth", - Description: "Show information about an installed tooth.", - ArgsUsage: "", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "available", - Usage: "show the full list of available versions", - DisableDefaultText: true, - }, - &cli.BoolFlag{ - Name: "json", - Usage: "output in JSON format", - DisableDefaultText: true, - }, - }, - Action: func(cCtx *cli.Context) error { - if cCtx.NArg() != 1 { - return fmt.Errorf("invalid number of arguments") - } - - toothRepoPath := cCtx.Args().Get(0) - - if err := show(ctx, toothRepoPath, cCtx.Bool("available"), cCtx.Bool("json")); err != nil { - return fmt.Errorf("failed to show JSON\n\t%w", err) - } - - return nil - }, - } -} - -// checkIsInstalledAndGetMetadata checks if the tooth is installed and returns -// its metadata. -func checkIsInstalledAndGetMetadata(ctx *context.Context, - toothRepoPath string) (bool, tooth.Metadata, error) { - - isInstalled, err := tooth.IsInstalled(ctx, toothRepoPath) - if err != nil { - return false, tooth.Metadata{}, - fmt.Errorf("failed to check if tooth is installed\n\t%w", err) - } - - if isInstalled { - metadata, err := tooth.GetMetadata(ctx, toothRepoPath) - if err != nil { - return false, tooth.Metadata{}, - fmt.Errorf("failed to find installed tooth metadata\n\t%w", err) - } - - return true, metadata, nil - } else { - return false, tooth.Metadata{}, nil - } -} - -func show(ctx *context.Context, toothRepoPath string, - availableFlag bool, jsonFlag bool) error { - - isInstalled, metadata, err := checkIsInstalledAndGetMetadata(ctx, toothRepoPath) - if err != nil { - return err - } - - availableVersions := make([]string, 0) - if availableFlag { - versionList, err := tooth.GetAvailableVersions(ctx, toothRepoPath) - if err != nil { - return fmt.Errorf("failed to get tooth version list\n\t%w", err) - } - - for _, v := range versionList { - availableVersions = append(availableVersions, v.String()) - } - } - - if !isInstalled && !availableFlag { - return fmt.Errorf("tooth is not installed") - } - - if jsonFlag { - info := make(map[string]interface{}) - - if isInstalled { - info["metadata"] = metadata - } - - if availableFlag { - info["available_versions"] = availableVersions - } - - jsonBytes, err := json.Marshal(info) - if err != nil { - return fmt.Errorf("failed to marshal JSON\n\t%w", err) - } - - fmt.Print(string(jsonBytes)) - - } else { - tableData := make([][]string, 0) - - if isInstalled { - tableData = append(tableData, [][]string{ - {"Tooth Repo", metadata.ToothRepoPath()}, - {"Name", metadata.Info().Name}, - {"Description", metadata.Info().Description}, - {"Author", metadata.Info().Author}, - {"Tags", strings.Join(metadata.Info().Tags, ", ")}, - {"Version", metadata.Version().String()}, - }...) - } - - if availableFlag { - tableData = append(tableData, []string{"Available Versions", - strings.Join(availableVersions, ", ")}) - } - - tableString := &strings.Builder{} - table := tablewriter.NewWriter(tableString) - table.SetHeader([]string{"Key", "Value"}) - - for _, v := range tableData { - table.Append(v) - } - - table.Render() - - fmt.Print(tableString.String()) - } - - return nil -} diff --git a/internal/cmd/cmdliptooth/cmdliptooth.go b/internal/cmd/cmdliptooth/cmdliptooth.go deleted file mode 100644 index 1f52716..0000000 --- a/internal/cmd/cmdliptooth/cmdliptooth.go +++ /dev/null @@ -1,28 +0,0 @@ -package cmdliptooth - -import ( - "fmt" - - "github.com/lippkg/lip/internal/cmd/cmdliptoothinit" - "github.com/lippkg/lip/internal/cmd/cmdliptoothpack" - "github.com/lippkg/lip/internal/context" - "github.com/urfave/cli/v2" -) - -func Command(ctx *context.Context) *cli.Command { - return &cli.Command{ - Name: "tooth", - Usage: "maintain a tooth", - Subcommands: []*cli.Command{ - cmdliptoothinit.Command(ctx), - cmdliptoothpack.Command(ctx), - }, - Action: func(cCtx *cli.Context) error { - if cCtx.NArg() >= 1 { - return fmt.Errorf("unknown command: lip %v %v", cCtx.Command.Name, cCtx.Args().First()) - } - return fmt.Errorf( - "no command specified. See 'lip %v --help' for more information", cCtx.Command.Name) - }, - } -} diff --git a/internal/cmd/cmdliptoothinit/cmdliptoothinit.go b/internal/cmd/cmdliptoothinit/cmdliptoothinit.go deleted file mode 100644 index 7e18035..0000000 --- a/internal/cmd/cmdliptoothinit/cmdliptoothinit.go +++ /dev/null @@ -1,126 +0,0 @@ -package cmdliptoothinit - -import ( - "bufio" - "fmt" - "os" - - "github.com/lippkg/lip/internal/context" - "github.com/lippkg/lip/internal/path" - log "github.com/sirupsen/logrus" - "github.com/urfave/cli/v2" - - "github.com/lippkg/lip/internal/tooth" -) - -var metadataTemplate = tooth.RawMetadata{ - FormatVersion: 2, - Tooth: "", - Version: "0.0.0", - Info: tooth.RawMetadataInfo{ - Name: "", - Description: "", - Author: "", - Tags: []string{}, - }, -} - -func Command(ctx *context.Context) *cli.Command { - return &cli.Command{ - Name: "init", - Usage: "initialize and writes a new tooth.json file in the current directory", - Description: "Initialize and writes a new tooth.json file in the current directory, in effect creating a new tooth rooted at the current directory.", - Action: func(cCtx *cli.Context) error { - - // Check if there are unexpected arguments. - if cCtx.NArg() != 0 { - return fmt.Errorf("unexpected arguments: %v", cCtx.Args()) - } - - if err := initTooth(ctx); err != nil { - return fmt.Errorf("failed to initialize the tooth\n\t%w", err) - } - - return nil - }, - } -} - -// --------------------------------------------------------------------- - -// initTooth initializes a new tooth in the current directory. -func initTooth(ctx *context.Context) error { - - // Check if tooth.json already exists. - _, err := os.Stat("tooth.json") - if err == nil { - return fmt.Errorf("tooth.json already exists") - } - - rawMetadata := metadataTemplate - - // Ask for information. - var ans string - scanner := bufio.NewScanner(os.Stdin) - - log.Info("What is the tooth repo path? (e.g. github.com/tooth-hub/llbds3)") - scanner.Scan() - ans = scanner.Text() - - if !tooth.IsValidToothRepoPath(ans) { - return fmt.Errorf("invalid tooth repo path %v\n\t%w", ans, err) - } - - rawMetadata.Tooth = ans - - log.Info("What is the name?") - scanner.Scan() - ans = scanner.Text() - rawMetadata.Info.Name = ans - - log.Info("What is the description?") - scanner.Scan() - ans = scanner.Text() - rawMetadata.Info.Description = ans - - log.Info("What is the author? Please input your GitHub username.") - scanner.Scan() - ans = scanner.Text() - rawMetadata.Info.Author = ans - - metadata, err := tooth.MakeMetadataFromRaw(rawMetadata) - if err != nil { - return fmt.Errorf("failed to make metadata\n\t%w", err) - } - - jsonBytes, err := metadata.MarshalJSON() - if err != nil { - return fmt.Errorf("failed to marshal metadata\n\t%w", err) - } - - // Create tooth.json. - workspaceDirStr, err := os.Getwd() - if err != nil { - return fmt.Errorf("failed to get workspace directory\n\t%w", err) - } - - workspaceDir, err := path.Parse(workspaceDirStr) - if err != nil { - return fmt.Errorf("failed to parse workspace directory\n\t%w", err) - } - - file, err := os.Create(workspaceDir.Join(path.MustParse("tooth.json")).LocalString()) - if err != nil { - return fmt.Errorf("failed to create tooth.json\n\t%w", err) - } - defer file.Close() - - // Write default tooth.json content. - if _, err := file.Write(jsonBytes); err != nil { - return fmt.Errorf("failed to write tooth.json\n\t%w", err) - } - - log.Info("Successfully initialized a new tooth.") - - return nil -} diff --git a/internal/cmd/cmdliptoothpack/cmdliptoothpack.go b/internal/cmd/cmdliptoothpack/cmdliptoothpack.go deleted file mode 100644 index 28ee5c8..0000000 --- a/internal/cmd/cmdliptoothpack/cmdliptoothpack.go +++ /dev/null @@ -1,235 +0,0 @@ -package cmdliptoothpack - -import ( - "archive/zip" - "compress/flate" - "fmt" - "io" - "os" - "path/filepath" - - "github.com/lippkg/lip/internal/context" - "github.com/lippkg/lip/internal/path" - log "github.com/sirupsen/logrus" - "github.com/urfave/cli/v2" - - "github.com/lippkg/lip/internal/tooth" -) - -func Command(ctx *context.Context) *cli.Command { - return &cli.Command{ - Name: "pack", - Usage: "pack the current directory into a tooth file", - ArgsUsage: "", - Description: "Pack the tooth into a tooth archive.", - Action: func(cCtx *cli.Context) error { - - // Exactly one argument is required. - if cCtx.NArg() != 1 { - return fmt.Errorf("expected exactly one argument") - } - - // Validate tooth.json. - if err := validateToothJSON(ctx); err != nil { - return fmt.Errorf("failed to validate tooth.json\n\t%w", err) - } - - // Pack the tooth. - outputPath, err := path.Parse(cCtx.Args().Get(0)) - if err != nil { - return fmt.Errorf("failed to parse output path %v\n\t%w", cCtx.Args().Get(0), err) - } - - if err := packTooth(ctx, outputPath); err != nil { - return fmt.Errorf("failed to pack tooth\n\t%w", err) - } - - return nil - }, - } -} - -// --------------------------------------------------------------------- - -// copyFile copies a file from sourcePath to destinationPath. -func copyFile(sourcePath, destinationPath path.Path) error { - source, err := os.Open(sourcePath.LocalString()) - if err != nil { - return err - } - defer source.Close() - - destination, err := os.Create(destinationPath.LocalString()) - if err != nil { - return err - } - defer destination.Close() - - buf := make([]byte, 1024*1024) - for { - n, err := source.Read(buf) - if err != nil && err != io.EOF { - return err - } - if n == 0 { - break - } - - if _, err := destination.Write(buf[:n]); err != nil { - return err - } - } - - return nil -} - -// packFilesToTemp packs files to a temporary zip file. -func packFilesToTemp(fileList []path.Path) (path.Path, error) { - zipFile, err := os.CreateTemp("", "*") - if err != nil { - return path.Path{}, fmt.Errorf("failed to create a temporary zip file\n\t%w", err) - } - defer zipFile.Close() - - zipFilePath, err := path.Parse(zipFile.Name()) - if err != nil { - return path.Path{}, fmt.Errorf("failed to parse the temporary zip file path %v\n\t%w", zipFile.Name(), err) - } - - zipWriter := zip.NewWriter(zipFile) - defer zipWriter.Close() - - // Set compression level. - zipWriter.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) { - return flate.NewWriter(out, flate.BestCompression) - }) - - // Write files to the zip file. - for _, file := range fileList { - log.Infof("Packing %v...", file.LocalString()) - - writer, err := zipWriter.Create(file.String()) - if err != nil { - return path.Path{}, fmt.Errorf("failed to create %v in zip file\n\t%w", file.String(), err) - } - - reader, err := os.Open(file.LocalString()) - if err != nil { - return path.Path{}, fmt.Errorf("failed to open %v\n\t%w", file.LocalString(), err) - } - - if _, err := io.Copy(writer, reader); err != nil { - return path.Path{}, fmt.Errorf("failed to copy %v\n\t%w", file.LocalString(), err) - } - - if err := reader.Close(); err != nil { - return path.Path{}, fmt.Errorf("failed to close %v\n\t%w", file.LocalString(), err) - } - } - - return zipFilePath, nil -} - -// packTooth packs the tooth into a tooth archive. -func packTooth(ctx *context.Context, outputPath path.Path) error { - _, err := os.Stat(outputPath.LocalString()) - if err == nil { - return fmt.Errorf("output path %v already exists", outputPath.LocalString()) - } else if !os.IsNotExist(err) { - return fmt.Errorf("failed to stat output path %v\n\t%w", outputPath.LocalString(), err) - } - - workspaceDirStr, err := os.Getwd() - if err != nil { - return fmt.Errorf("failed to get workspace directory\n\t%w", err) - } - - workspaceDir, err := path.Parse(workspaceDirStr) - if err != nil { - return fmt.Errorf("failed to parse workspace directory\n\t%w", err) - } - - fileList, err := walkDirectory(workspaceDir) - if err != nil { - return fmt.Errorf("failed to walk through the current directory\n\t%w", err) - } - - // Pack files to a temporary zip file. - zipFilePath, err := packFilesToTemp(fileList) - if err != nil { - return fmt.Errorf("failed to pack files to a temporary zip file\n\t%w", err) - } - - // Copy the zip file to the output path. - - if err := copyFile(zipFilePath, outputPath); err != nil { - return fmt.Errorf("failed to copy the zip file from %v to %v\n\t%w", - zipFilePath.LocalString(), outputPath.LocalString(), err) - } - - return nil -} - -// validateToothJSON validates tooth.json. -func validateToothJSON(ctx *context.Context) error { - - workspaceDirStr, err := os.Getwd() - if err != nil { - return fmt.Errorf("failed to get workspace directory\n\t%w", err) - } - - workspaceDir, err := path.Parse(workspaceDirStr) - if err != nil { - return fmt.Errorf("failed to parse workspace directory\n\t%w", err) - } - - jsonBytes, err := os.ReadFile(workspaceDir.Join(path.MustParse("tooth.json")).String()) - if err != nil { - return fmt.Errorf("failed to read tooth.json\n\t%w", err) - } - - if _, err := tooth.MakeMetadata(jsonBytes); err != nil { - return fmt.Errorf("failed to parse tooth.json\n\t%w", err) - } - - return nil -} - -// walkDirectory walks the directory and returns a list of files. -func walkDirectory(dir path.Path) ([]path.Path, error) { - - ignoredDirNames := []string{ - ".git", - ".lip", - } - - fileList := make([]path.Path, 0) - err := filepath.WalkDir(".", func(pathStr string, d os.DirEntry, err error) error { - if err != nil { - return err - } - - // Do not walk through special directories. - for _, ignoredDir := range ignoredDirNames { - if d.Name() == ignoredDir { - return filepath.SkipDir - } - } - - if !d.IsDir() { - path, err := path.Parse(pathStr) - if err != nil { - return fmt.Errorf("failed to parse path\n\t%w", err) - } - - fileList = append(fileList, path) - } - - return nil - }) - if err != nil { - return nil, fmt.Errorf("failed to walk through the directory\n\t%w", err) - } - - return fileList, nil -} diff --git a/internal/cmd/cmdlipuninstall/cmdlipuninstall.go b/internal/cmd/cmdlipuninstall/cmdlipuninstall.go deleted file mode 100644 index 8226c22..0000000 --- a/internal/cmd/cmdlipuninstall/cmdlipuninstall.go +++ /dev/null @@ -1,102 +0,0 @@ -package cmdlipuninstall - -import ( - "fmt" - - "github.com/lippkg/lip/internal/context" - "github.com/lippkg/lip/internal/install" - log "github.com/sirupsen/logrus" - "github.com/urfave/cli/v2" - - "github.com/lippkg/lip/internal/tooth" -) - -func Command(ctx *context.Context) *cli.Command { - return &cli.Command{ - Name: "uninstall", - Usage: "uninstall a tooth", - ArgsUsage: " [...]", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "yes", - Aliases: []string{"y"}, - Usage: "skip confirmation", - DisableDefaultText: true, - }, - }, - Description: "Uninstall teeth.", - Action: func(cCtx *cli.Context) error { - // At least one specifier is required. - if cCtx.NArg() == 0 { - return fmt.Errorf("at least one specifier is required") - } - - toothRepoPathList := cCtx.Args().Slice() - - // 1. Check if all teeth are installed. - - for _, toothRepoPath := range toothRepoPathList { - - isInstalled, err := tooth.IsInstalled(ctx, toothRepoPath) - if err != nil { - return fmt.Errorf("failed to check if tooth is installed\n\t%w", err) - } - - if !isInstalled { - return fmt.Errorf("tooth %v is not installed", toothRepoPath) - } - } - - // 2. Prompt for confirmation. - - if !cCtx.Bool("yes") { - err := askForConfirmation(ctx, toothRepoPathList) - if err != nil { - return err - } - } - - // 3. Uninstall all teeth. - - for _, toothRepoPath := range toothRepoPathList { - err := install.Uninstall(ctx, toothRepoPath) - if err != nil { - return fmt.Errorf("failed to uninstall tooth %v\n\t%w", toothRepoPath, err) - } - } - - log.Info("Done.") - - return nil - }, - } -} - -// --------------------------------------------------------------------- - -// askForConfirmation asks for confirmation before installing the tooth. -func askForConfirmation(ctx *context.Context, - toothRepoPathList []string) error { - - // Print the list of teeth to be installed. - log.Info("The following teeth will be uninstalled:") - for _, toothRepoPath := range toothRepoPathList { - metadata, err := tooth.GetMetadata(ctx, toothRepoPath) - if err != nil { - return fmt.Errorf("failed to get installed tooth metadata\n\t%w", err) - } - - log.Infof(" %v@%v: %v", toothRepoPath, metadata.Version(), - metadata.Info().Name) - } - - // Ask for confirmation. - log.Info("Do you want to continue? [y/N]") - var ans string - fmt.Scanln(&ans) - if ans != "y" && ans != "Y" { - return fmt.Errorf("aborted") - } - - return nil -} diff --git a/internal/context/config.go b/internal/context/config.go deleted file mode 100644 index 2f4453a..0000000 --- a/internal/context/config.go +++ /dev/null @@ -1,7 +0,0 @@ -package context - -type Config struct { - GitHubMirrorURL string `json:"github_mirror_url"` - GoModuleProxyURL string `json:"go_module_proxy_url"` - ProxyURL string `json:"proxy_url"` -} diff --git a/internal/context/context.go b/internal/context/context.go deleted file mode 100644 index 3a063e5..0000000 --- a/internal/context/context.go +++ /dev/null @@ -1,221 +0,0 @@ -package context - -import ( - "encoding/json" - "fmt" - "net/url" - "os" - - "github.com/blang/semver/v4" - "github.com/lippkg/lip/internal/path" -) - -// Context is the context of the application. -type Context struct { - config Config - lipVersion semver.Version -} - -// New creates a new context. -func New(config Config, version semver.Version) *Context { - return &Context{ - config: config, - lipVersion: version, - } -} - -// Config returns the config. -func (ctx *Context) Config() *Config { - return &ctx.config -} - -// GitHubMirrorURL returns the GitHub mirror URL. -func (ctx *Context) GitHubMirrorURL() (*url.URL, error) { - gitHubMirrorURL, err := url.Parse(ctx.config.GitHubMirrorURL) - if err != nil { - return nil, fmt.Errorf("cannot parse GitHub mirror URL\n\t%w", err) - } - - return gitHubMirrorURL, nil -} - -// GoModuleProxyURL returns the go module proxy URL. -func (ctx *Context) GoModuleProxyURL() (*url.URL, error) { - goModuleProxyURL, err := url.Parse(ctx.config.GoModuleProxyURL) - if err != nil { - return nil, fmt.Errorf("cannot parse go module proxy URL\n\t%w", err) - } - - return goModuleProxyURL, nil -} - -// ProxyURL returns the proxy URL. -func (ctx *Context) ProxyURL() (*url.URL, error) { - proxyURL, err := url.Parse(ctx.config.ProxyURL) - if err != nil { - return nil, fmt.Errorf("cannot parse proxy URL\n\t%w", err) - } - - return proxyURL, nil -} - -// LipVersion returns the lip version. -func (ctx *Context) LipVersion() semver.Version { - return ctx.lipVersion -} - -// GlobalDotLipDir returns the global .lip directory. -func (ctx *Context) GlobalDotLipDir() (path.Path, error) { - - userHomeDirStr, err := os.UserHomeDir() - if err != nil { - return path.Path{}, fmt.Errorf("cannot get user home directory\n\t%w", err) - } - - userHomeDir, err := path.Parse(userHomeDirStr) - if err != nil { - return path.Path{}, fmt.Errorf("cannot parse user home directory\n\t%w", err) - } - - globalDotLipDir := userHomeDir.Join(path.MustParse(".lip")) - - return globalDotLipDir, nil -} - -// LocalDotLipDir returns the local .lip directory. -func (ctx *Context) LocalDotLipDir() (path.Path, error) { - - workspaceDirStr, err := os.Getwd() - if err != nil { - return path.Path{}, fmt.Errorf("cannot get workspace directory\n\t%w", err) - } - - workspaceDir, err := path.Parse(workspaceDirStr) - if err != nil { - return path.Path{}, fmt.Errorf("cannot parse workspace directory\n\t%w", err) - } - - path := workspaceDir.Join(path.MustParse(".lip")) - - return path, nil -} - -// CacheDir returns the cache directory. -func (ctx *Context) CacheDir() (path.Path, error) { - - globalDotLipDir, err := ctx.GlobalDotLipDir() - if err != nil { - return path.Path{}, fmt.Errorf("cannot get global .lip directory\n\t%w", err) - } - - path := globalDotLipDir.Join(path.MustParse("cache")) - - return path, nil -} - -// MetadataDir returns the metadata directory. -func (ctx *Context) MetadataDir() (path.Path, error) { - - localDotLipDir, err := ctx.LocalDotLipDir() - if err != nil { - return path.Path{}, fmt.Errorf("cannot get local .lip directory\n\t%w", err) - } - - path := localDotLipDir.Join(path.MustParse("metadata")) - - return path, nil -} - -// CreateDirStructure creates the directory structure. -func (ctx *Context) CreateDirStructure() error { - - globalDotLipDir, err := ctx.GlobalDotLipDir() - if err != nil { - return fmt.Errorf("cannot get global .lip directory\n\t%w", err) - } - - if err := os.MkdirAll(globalDotLipDir.LocalString(), 0755); err != nil { - return fmt.Errorf("cannot create global .lip directory\n\t%w", err) - } - - localDotLipDir, err := ctx.LocalDotLipDir() - if err != nil { - return fmt.Errorf("cannot get local .lip directory\n\t%w", err) - } - - if err := os.MkdirAll(localDotLipDir.LocalString(), 0755); err != nil { - return fmt.Errorf("cannot create local .lip directory\n\t%w", err) - } - - cacheDir, err := ctx.CacheDir() - if err != nil { - return fmt.Errorf("cannot get cache directory\n\t%w", err) - } - - if err := os.MkdirAll(cacheDir.LocalString(), 0755); err != nil { - return fmt.Errorf("cannot create cache directory\n\t%w", err) - } - - metadataDir, err := ctx.MetadataDir() - if err != nil { - return fmt.Errorf("cannot get metadata directory\n\t%w", err) - } - - if err := os.MkdirAll(metadataDir.LocalString(), 0755); err != nil { - return fmt.Errorf("cannot create metadata directory\n\t%w", err) - } - - return nil -} - -// LoadOrCreateConfigFile loads or creates the config file. -func (ctx *Context) LoadOrCreateConfigFile() error { - - globalDotLipDir, err := ctx.GlobalDotLipDir() - if err != nil { - return fmt.Errorf("cannot get global .lip directory\n\t%w", err) - } - - configFilePath := globalDotLipDir.Join(path.MustParse("config.json")) - - if _, err := os.Stat(configFilePath.LocalString()); os.IsNotExist(err) { - ctx.SaveConfigFile() - - } else if err != nil { - return fmt.Errorf("cannot get config file info\n\t%w", err) - - } else { - jsonBytes, err := os.ReadFile(configFilePath.LocalString()) - if err != nil { - return fmt.Errorf("cannot read config file at %v\n\t%w", configFilePath.LocalString(), err) - } - - if err := json.Unmarshal(jsonBytes, &ctx.config); err != nil { - return fmt.Errorf("cannot unmarshal config at %v\n\t%w", configFilePath.LocalString(), err) - } - } - - return nil -} - -// SaveConfigFile saves the config file. -func (ctx *Context) SaveConfigFile() error { - - globalDotLipDir, err := ctx.GlobalDotLipDir() - if err != nil { - return fmt.Errorf("cannot get global .lip directory\n\t%w", err) - } - - configFilePath := globalDotLipDir.Join(path.MustParse("config.json")) - - jsonBytes, err := json.MarshalIndent(ctx.config, "", " ") - if err != nil { - return fmt.Errorf("cannot marshal config\n\t%w", err) - } - - if err := os.WriteFile(configFilePath.LocalString(), jsonBytes, 0644); err != nil { - return fmt.Errorf("cannot write config file\n\t%w", err) - } - - return nil -} diff --git a/internal/install/commands.go b/internal/install/commands.go deleted file mode 100644 index 978e4dc..0000000 --- a/internal/install/commands.go +++ /dev/null @@ -1,44 +0,0 @@ -package install - -import ( - "fmt" - "os" - "os/exec" - "runtime" - - log "github.com/sirupsen/logrus" -) - -// runCommands runs the given commands. -func runCommands(commands []string, environs map[string]string) error { - debugLogger := log.WithFields(log.Fields{ - "package": "install", - "method": "runCommands", - }) - - for _, command := range commands { - var cmd *exec.Cmd - switch runtime.GOOS { - case "windows": - cmd = exec.Command("cmd", "/C", command) - default: - cmd = exec.Command("sh", "-c", command) - } - - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - for key, value := range environs { - cmd.Env = append(os.Environ(), fmt.Sprintf("%v=%v", key, value)) - } - - if err := cmd.Run(); err != nil { - return fmt.Errorf("failed to run command %v\n\t%w", command, err) - } - - debugLogger.Debugf("Ran command %v", command) - } - - return nil -} diff --git a/internal/install/install.go b/internal/install/install.go deleted file mode 100644 index 0768837..0000000 --- a/internal/install/install.go +++ /dev/null @@ -1,291 +0,0 @@ -package install - -import ( - "archive/tar" - "archive/zip" - "compress/gzip" - "fmt" - "io" - "net/url" - "os" - "path/filepath" - "strings" - - "github.com/lippkg/lip/internal/context" - "github.com/lippkg/lip/internal/path" - "github.com/lippkg/lip/internal/tooth" - log "github.com/sirupsen/logrus" -) - -// Install installs a tooth archive with an asset archive. If assetArchiveFilePath is empty, -// will use the tooth archive as the asset archive. -func Install(ctx *context.Context, archive tooth.Archive, yes bool) error { - debugLogger := log.WithFields(log.Fields{ - "package": "install", - "method": "Install", - }) - - commandEnvirons := make(map[string]string) - - proxyURL, err := ctx.ProxyURL() - if err != nil { - return fmt.Errorf("failed to get proxy URL\n\t%w", err) - } - - if proxyURL.String() != "" { - commandEnvirons = map[string]string{ - "HTTP_PROXY": proxyURL.String(), - "HTTPS_PROXY": proxyURL.String(), - } - } - - // 1. Check if the tooth is already installed. - - if installed, err := tooth.IsInstalled(ctx, archive.Metadata().ToothRepoPath()); err != nil { - return fmt.Errorf("failed to check if tooth is installed\n\t%w", err) - } else if installed { - return fmt.Errorf("tooth %v is already installed", archive.Metadata().ToothRepoPath()) - } - debugLogger.Debug("Checked if tooth is already installed") - - // 2. Run pre-install commands. - - if err := runCommands(archive.Metadata().Commands().PreInstall, commandEnvirons); err != nil { - return fmt.Errorf("failed to run pre-install commands\n\t%w", err) - } - debugLogger.Debug("Ran pre-install commands") - - // 3. Extract and place files. - - assetFilePath, err := archive.AssetFilePath() - if err != nil { - return fmt.Errorf("failed to get asset file path of archive %v\n\t%w", archive.FilePath().LocalString(), err) - } - - if err := placeFiles(ctx, archive.Metadata(), assetFilePath, yes); err != nil { - return fmt.Errorf("failed to place files\n\t%w", err) - } - debugLogger.Debug("Placed files") - - // 4. Run post-install commands. - - if err := runCommands(archive.Metadata().Commands().PostInstall, commandEnvirons); err != nil { - return fmt.Errorf("failed to run post-install commands\n\t%w", err) - } - debugLogger.Debug("Ran post-install commands") - - // 5. Create metadata file. - - jsonBytes, err := archive.Metadata().MarshalJSON() - if err != nil { - return fmt.Errorf("failed to marshal metadata\n\t%w", err) - } - - metadataFileName := url.QueryEscape(archive.Metadata().ToothRepoPath()) + ".json" - metadataDir, err := ctx.MetadataDir() - if err != nil { - return fmt.Errorf("failed to get metadata directory\n\t%w", err) - } - - metadataPath := metadataDir.Join(path.MustParse(metadataFileName)) - - if err := os.WriteFile(metadataPath.LocalString(), jsonBytes, 0644); err != nil { - return fmt.Errorf("failed to create metadata file\n\t%w", err) - } - - debugLogger.Debugf("Created metadata file %v", metadataPath.LocalString()) - - return nil -} - -// placeFiles places the files of the tooth. -func placeFiles(ctx *context.Context, metadata tooth.Metadata, assetArchiveFilePath path.Path, forcePlace bool) error { - debugLogger := log.WithFields(log.Fields{ - "package": "install", - "method": "placeFiles", - }) - - workspaceDirStr, err := os.Getwd() - if err != nil { - return err - } - - workspaceDir, err := path.Parse(workspaceDirStr) - if err != nil { - return err - } - - if strings.HasSuffix(assetArchiveFilePath.LocalString(), ".zip") { - // Open the archive. - r, err := zip.OpenReader(assetArchiveFilePath.LocalString()) - if err != nil { - return fmt.Errorf("failed to open zip reader\n\t%w", err) - } - defer r.Close() - - files, err := metadata.Files() - if err != nil { - return fmt.Errorf("failed to get files from metadata\n\t%w", err) - } - - for _, place := range files.Place { - relDest := place.Dest - - // Check if the destination exists. - if _, err := os.Stat(relDest.LocalString()); err == nil { - if !forcePlace { - // Ask for confirmation. - log.Infof("Destination %v already exists", relDest.LocalString()) - log.Info("Do you want to remove? [y/N]") - var ans string - fmt.Scanln(&ans) - if ans != "y" && ans != "Y" { - return fmt.Errorf("aborted") - } - } - - log.Infof("Removing destination %v", relDest.LocalString()) - - // Remove the destination if it exists. - if err := os.RemoveAll(relDest.LocalString()); err != nil { - return fmt.Errorf("failed to remove destination %v\n\t%w", relDest.LocalString(), err) - } - } - - dest := workspaceDir.Join(relDest) - - // Create the destination directory. - if err := os.MkdirAll(filepath.Dir(dest.LocalString()), 0755); err != nil { - return fmt.Errorf("failed to create destination directory\n\t%w", err) - } - debugLogger.Debugf("Created destination directory %v", filepath.Dir(dest.LocalString())) - - // Iterate through the files in the archive, - // and find the source file. - for _, f := range r.File { - // Skip directories. - if strings.HasSuffix(f.Name, "/") { - debugLogger.Debugf("Skipped %v because it is a directory", f.Name) - - continue - } - - filePath, err := path.Parse(f.Name) - if err != nil { - return fmt.Errorf("failed to parse file path from %v\n\t%w", f.Name, err) - } - - if filePath.Equal(place.Src) { - // Open the source file. - rc, err := f.Open() - if err != nil { - return fmt.Errorf("failed to open source file\n\t%w", err) - } - - fw, err := os.Create(dest.LocalString()) - if err != nil { - return fmt.Errorf("failed to create destination file\n\t%w", err) - } - - // Copy the file. - if _, err := io.Copy(fw, rc); err != nil { - return fmt.Errorf("failed to copy file\n\t%w", err) - } - - // Close the files. - rc.Close() - fw.Close() - - debugLogger.Debugf("Placed file %v to %v", f.Name, dest.LocalString()) - } - } - } - } else if strings.HasSuffix(assetArchiveFilePath.LocalString(), ".tar.gz") { - file, err := os.Open(assetArchiveFilePath.LocalString()) - if err != nil { - return fmt.Errorf("failed to open %s\n\t%w", assetArchiveFilePath.LocalString(), err) - } - gzr, err := gzip.NewReader(file) - if err != nil { - return fmt.Errorf("failed to open %s\n\t%w", assetArchiveFilePath.LocalString(), err) - } - gzr.Close() - - tarR := tar.NewReader(gzr) - files, err := metadata.Files() - if err != nil { - return fmt.Errorf("failed to get files from metadata\n\t%w", err) - } - - for _, place := range files.Place { - relDest := place.Dest - - // Check if the destination exists. - if _, err := os.Stat(relDest.LocalString()); err == nil { - if !forcePlace { - // Ask for confirmation. - log.Infof("Destination %v already exists", relDest.LocalString()) - log.Info("Do you want to remove? [y/N]") - var ans string - fmt.Scanln(&ans) - if ans != "y" && ans != "Y" { - return fmt.Errorf("aborted") - } - } - - log.Infof("Removing destination %v", relDest.LocalString()) - - // Remove the destination if it exists. - if err := os.RemoveAll(relDest.LocalString()); err != nil { - return fmt.Errorf("failed to remove destination %v\n\t%w", relDest.LocalString(), err) - } - } - - dest := workspaceDir.Join(relDest) - - // Create the destination directory. - if err := os.MkdirAll(filepath.Dir(dest.LocalString()), 0755); err != nil { - return fmt.Errorf("failed to create destination directory\n\t%w", err) - } - debugLogger.Debugf("Created destination directory %v", filepath.Dir(dest.LocalString())) - - // Iterate through the files in the archive, - // and find the source file. - for f, err := tarR.Next(); err != io.EOF; f, err = tarR.Next() { - if err != nil { - return fmt.Errorf("failed to read tar\n\t%w", err) - } - // Skip directories. - if f.Typeflag == tar.TypeDir { - debugLogger.Debugf("Skipped %v because it is a directory", f.Name) - - continue - } - - filePath, err := path.Parse(f.Name) - if err != nil { - return fmt.Errorf("failed to parse file path from %v\n\t%w", f.Name, err) - } - if filePath.Equal(place.Src) { - // Open the source file. - fw, err := os.Create(dest.LocalString()) - if err != nil { - return fmt.Errorf("failed to create destination file\n\t%w", err) - } - - // Copy the file. - if _, err := io.Copy(fw, tarR); err != nil { - return fmt.Errorf("failed to copy file\n\t%w", err) - } - - // Close the files. - fw.Close() - - debugLogger.Debugf("Placed file %v to %v", f.Name, dest.LocalString()) - } - } - } - } - - return nil -} diff --git a/internal/install/uninstall.go b/internal/install/uninstall.go deleted file mode 100644 index 5e0902a..0000000 --- a/internal/install/uninstall.go +++ /dev/null @@ -1,182 +0,0 @@ -package install - -import ( - "fmt" - "net/url" - "os" - - "github.com/lippkg/lip/internal/context" - "github.com/lippkg/lip/internal/path" - "github.com/lippkg/lip/internal/tooth" - - log "github.com/sirupsen/logrus" -) - -func Uninstall(ctx *context.Context, toothRepoPath string) error { - debugLogger := log.WithFields(log.Fields{ - "package": "install", - "method": "Uninstall", - }) - - commandEnvirons := make(map[string]string) - - proxyURL, err := ctx.ProxyURL() - if err != nil { - return fmt.Errorf("failed to get proxy URL\n\t%w", err) - } - - if proxyURL.String() != "" { - commandEnvirons = map[string]string{ - "HTTP_PROXY": proxyURL.String(), - "HTTPS_PROXY": proxyURL.String(), - } - } - - metadata, err := tooth.GetMetadata(ctx, toothRepoPath) - if err != nil { - return err - } - - // 1. Run pre-uninstall commands. - - if err := runCommands(metadata.Commands().PreUninstall, commandEnvirons); err != nil { - return fmt.Errorf("failed to run pre-uninstall commands\n\t%w", err) - } - debugLogger.Debug("Ran pre-uninstall commands") - - // 2. Delete files. - - if err := removeToothFiles(ctx, metadata); err != nil { - return fmt.Errorf("failed to delete files\n\t%w", err) - } - debugLogger.Debug("Deleted files") - - // 3. Run post-uninstall commands. - - if err := runCommands(metadata.Commands().PostUninstall, commandEnvirons); err != nil { - return fmt.Errorf("failed to run post-uninstall commands\n\t%w", err) - } - debugLogger.Debug("Ran post-uninstall commands") - - // 4. Delete the metadata file. - - metadataDir, err := ctx.MetadataDir() - if err != nil { - return fmt.Errorf("failed to get metadata directory\n\t%w", err) - } - - metadataFileName := fmt.Sprintf("%v.json", url.QueryEscape(toothRepoPath)) - metadataPath := metadataDir.Join(path.MustParse(metadataFileName)) - - if err := os.Remove(metadataPath.LocalString()); err != nil { - return fmt.Errorf("failed to delete metadata file\n\t%w", err) - } - - debugLogger.Debugf("Deleted metadata file %v", metadataPath.LocalString()) - - return nil -} - -// removeToothFiles removes the files of the tooth. -func removeToothFiles(ctx *context.Context, metadata tooth.Metadata) error { - debugLogger := log.WithFields(log.Fields{ - "package": "install", - "method": "removeToothFiles", - }) - - workspaceDirStr, err := os.Getwd() - if err != nil { - return err - } - - workspaceDir, err := path.Parse(workspaceDirStr) - if err != nil { - return fmt.Errorf("failed to parse workspace directory\n\t%w", err) - } - - files, err := metadata.Files() - if err != nil { - return fmt.Errorf("failed to get files from metadata\n\t%w", err) - } - - for _, place := range files.Place { - // Files marked as "preserve" will not be deleted. - isPreserved := false - for _, preserve := range files.Preserve { - if place.Dest.Equal(preserve) { - isPreserved = true - break - } - } - if isPreserved { - debugLogger.Debugf("Preserved file %v", place.Dest) - continue - } - - relDest := place.Dest - - dest := workspaceDir.Join(relDest) - - // Delete the file. - if err := os.RemoveAll(dest.LocalString()); err != nil { - return fmt.Errorf("failed to delete file\n\t%w", err) - } - debugLogger.Debugf("Deleted file %v", dest.LocalString()) - - // Delete all ancestor directories if they are empty until the workspace directory. - currentPath := dest - for { - dir, err := currentPath.Dir() - if err != nil { - return fmt.Errorf("failed to parse directory\n\t%w", err) - } - - if dir.Equal(workspaceDir) { - break - } - - isInWorkspaceDir := workspaceDir.IsAncestorOf(dir) - if !isInWorkspaceDir { - break - } - - fileList, err := os.ReadDir(dir.LocalString()) - if err != nil { - if os.IsNotExist(err) { - log.Errorf("directory %v does not exist, skip deleting", dir.LocalString()) - break - } else { - return fmt.Errorf("failed to read directory %v\n\t%w", dir.LocalString(), err) - } - } - - if len(fileList) != 0 { - break - } - - if err := os.Remove(dir.LocalString()); err != nil { - return fmt.Errorf("failed to delete directory\n\t%w", err) - } - debugLogger.Debugf("Deleted directory %v", dir.LocalString()) - - currentPath = dir - } - } - - // Files marked as "remove" will be deleted regardless of whether they are marked as "preserve". - files, err = metadata.Files() - if err != nil { - return fmt.Errorf("failed to get files from metadata\n\t%w", err) - } - - for _, removal := range files.Remove { - removalPath := removal - - if err := os.RemoveAll(workspaceDir.Join(removalPath).LocalString()); err != nil { - return fmt.Errorf("failed to delete file\n\t%w", err) - } - debugLogger.Debugf("Deleted file %v that is marked as \"remove\"", workspaceDir.Join(removalPath).LocalString()) - } - - return nil -} diff --git a/internal/must/must.go b/internal/must/must.go deleted file mode 100644 index d5e4d07..0000000 --- a/internal/must/must.go +++ /dev/null @@ -1,9 +0,0 @@ -package must - -// Must panics if err is not nil. -func Must[T any](t T, err error) T { - if err != nil { - panic(err) - } - return t -} diff --git a/internal/network/github.go b/internal/network/github.go deleted file mode 100644 index d29d33a..0000000 --- a/internal/network/github.go +++ /dev/null @@ -1,26 +0,0 @@ -package network - -import ( - "fmt" - gourl "net/url" -) - -// GenerateGitHubMirrorURL generates a GitHub mirror URL from a GitHub URL. -func GenerateGitHubMirrorURL(url *gourl.URL, gitHubMirrorURL *gourl.URL) (*gourl.URL, error) { - if !IsGitHubDirectDownloadURL(url) { - return nil, fmt.Errorf("not a GitHub URL: %v", url) - } - - // Replace the host of the URL with the GitHub mirror URL. - mirroredURL, err := url.Parse(fmt.Sprintf("%v%v", gitHubMirrorURL, url.Path)) - if err != nil { - return nil, fmt.Errorf("cannot parse GitHub mirror URL\n\t%w", err) - } - - return mirroredURL, nil -} - -// IsGitHubDirectDownloadURL checks if a URL is a GitHub URL that can be directly downloaded. -func IsGitHubDirectDownloadURL(url *gourl.URL) bool { - return url.Host == "github.com" && (url.Scheme == "http" || url.Scheme == "https") -} diff --git a/internal/network/gomoduleproxy.go b/internal/network/gomoduleproxy.go deleted file mode 100644 index f91c78a..0000000 --- a/internal/network/gomoduleproxy.go +++ /dev/null @@ -1,69 +0,0 @@ -package network - -import ( - "fmt" - "net/url" - "path" - - "github.com/blang/semver/v4" - "golang.org/x/mod/module" -) - -// GenerateGoModuleVersionListURL generates the URL of the version list of a Go -// module. -func GenerateGoModuleVersionListURL(goModulePath string, goProxyURL *url.URL) (*url.URL, error) { - if err := module.CheckPath(goModulePath); err != nil { - return nil, fmt.Errorf("%v is not a Go module path", goModulePath) - } - - escapedPath, err := module.EscapePath(goModulePath) - if err != nil { - return nil, fmt.Errorf("cannot escape Go module path %v\n\t%w", goModulePath, err) - } - - resultURL, err := goProxyURL.Parse(path.Join(escapedPath, "@v", "list")) - if err != nil { - return nil, fmt.Errorf("cannot parse Go proxy URL\n\t%w", err) - } - - return resultURL, nil -} - -// GenerateGoModuleZipFileURL generates the URL of a Go module zip file. -func GenerateGoModuleZipFileURL(goModulePath string, version semver.Version, goProxyURL *url.URL) (*url.URL, error) { - if err := module.CheckPath(goModulePath); err != nil { - return nil, fmt.Errorf("%v is not a Go module path", goModulePath) - } - - zipFileName, err := generateGoModuleZipFileName(version) - if err != nil { - return nil, fmt.Errorf("cannot generate Go module zip file name\n\t%w", err) - } - - escapedPath, err := module.EscapePath(goModulePath) - if err != nil { - return nil, fmt.Errorf("cannot escape Go module path %v\n\t%w", goModulePath, err) - } - - resultURL, err := goProxyURL.Parse(path.Join(escapedPath, "@v", zipFileName)) - if err != nil { - return nil, fmt.Errorf("cannot parse Go proxy URL\n\t%w", err) - } - - return resultURL, nil -} - -func generateGoModuleZipFileName(version semver.Version) (string, error) { - // To ensure that the version is a canonical version. Reference: - // https://go.dev/ref/mod#glos-canonical-version - if len(version.Build) > 0 { - return "", fmt.Errorf("cannot generate zip file name for a version with build metadata: %v", version) - } - - // Reference: https://go.dev/ref/mod#non-module-compat - if version.Major >= 2 { - return fmt.Sprintf("v%v+incompatible.zip", version.String()), nil - } else { - return fmt.Sprintf("v%v.zip", version.String()), nil - } -} diff --git a/internal/network/network.go b/internal/network/network.go deleted file mode 100644 index 9ad5e54..0000000 --- a/internal/network/network.go +++ /dev/null @@ -1,84 +0,0 @@ -package network - -import ( - "fmt" - "io" - "net/http" - "net/url" - "os" - - "github.com/lippkg/lip/internal/path" - "github.com/schollz/progressbar/v3" -) - -// DownloadFile downloads a file from a url and saves it to a local path. -func DownloadFile(url *url.URL, proxyURL *url.URL, filePath path.Path, enableProgressBar bool) error { - httpClient := getProxiedHTTPClient(proxyURL) - - resp, err := httpClient.Get(url.String()) - if err != nil { - return fmt.Errorf("cannot send HTTP request\n\t%w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("cannot download file (HTTP %v): %v", resp.Status, url) - } - - // Create the file - file, err := os.Create(filePath.LocalString() + ".tmp") - if err != nil { - return fmt.Errorf("cannot create file\n\t%w", err) - } - defer os.Rename(filePath.LocalString() + ".tmp", filePath.LocalString()) - defer file.Close() - - var writer io.Writer = file - - if enableProgressBar { - bar := progressbar.NewOptions64( - resp.ContentLength, - progressbar.OptionClearOnFinish(), - progressbar.OptionShowBytes(true), - progressbar.OptionShowCount(), - ) - writer = io.MultiWriter(file, bar) - } - - if _, err := io.Copy(writer, resp.Body); err != nil { - return fmt.Errorf("cannot download file from %v\n\t%w", url, err) - } - return nil -} - -// GetContent gets the content at once of a URL. -func GetContent(url *url.URL, proxyURL *url.URL) ([]byte, error) { - httpClient := getProxiedHTTPClient(proxyURL) - - resp, err := httpClient.Get(url.String()) - if err != nil { - return nil, fmt.Errorf("cannot send HTTP request\n\t%w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("cannot get content (HTTP %v): %v", resp.Status, url) - } - - content, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("cannot read HTTP response\n\t%w", err) - } - - return content, nil -} - -func getProxiedHTTPClient(proxyURL *url.URL) *http.Client { - if proxyURL.String() == "" { - return http.DefaultClient - } - - transport := http.DefaultTransport.(*http.Transport).Clone() - transport.Proxy = http.ProxyURL(proxyURL) - return &http.Client{Transport: transport} -} diff --git a/internal/path/path.go b/internal/path/path.go deleted file mode 100644 index f69a3f0..0000000 --- a/internal/path/path.go +++ /dev/null @@ -1,211 +0,0 @@ -package path - -import ( - "fmt" - gopath "path" - "path/filepath" - "regexp" - "strings" - - "golang.org/x/mod/module" -) - -type Path struct { - pathItems []string -} - -// MakeEmpty creates an empty path. -func MakeEmpty() Path { - return Path{ - pathItems: []string{}, - } -} - -// Parse parses a path string into a Path. -func Parse(path string) (Path, error) { - // Convert to forward slashes. - path = filepath.ToSlash(path) - path = gopath.Clean(path) - - pathItems := strings.Split(path, "/") - // Remove the last empty path item if the path ends with a slash. - if pathItems[len(pathItems)-1] == "" { - pathItems = pathItems[:len(pathItems)-1] - } - - for i, pathItem := range pathItems { - if i == 0 && (pathItem == "" || regexp.MustCompile(`^[a-zA-Z]:$`).MatchString(pathItem)) { - continue - } - - if err := module.CheckFilePath(pathItem); err != nil { - return Path{}, fmt.Errorf("invalid path item %v in path %v", pathItem, path) - } - } - // If path starts with '/', insert '/' at the begin of pathItems - if strings.HasPrefix(path, "/") { - pathItems = append([]string{"/"}, pathItems...) - } - - return Path{ - pathItems: pathItems, - }, nil -} - -// MustParse parses a path string into a Path. It panics if the path is invalid. -func MustParse(path string) Path { - p, err := Parse(path) - if err != nil { - panic(err) - } - - return p -} - -// ExtractLongestCommonPath returns the longest common path of two paths. -func ExtractLongestCommonPath(paths ...Path) Path { - if len(paths) == 0 { - return Path{} - } - - shortestPathItemCount := len(paths[0].pathItems) - for _, other := range paths { - if len(other.pathItems) < shortestPathItemCount { - shortestPathItemCount = len(other.pathItems) - } - } - - longestCommonPathItems := make([]string, 0) -outerLoop: - for i := 0; i < shortestPathItemCount; i++ { - pathItem := paths[0].pathItems[i] - - for _, other := range paths { - if other.pathItems[i] != pathItem { - break outerLoop - } - } - - longestCommonPathItems = append(longestCommonPathItems, pathItem) - } - - return Path{ - pathItems: longestCommonPathItems, - } -} - -// Base returns the base of the path. -func (f Path) Base() string { - if len(f.pathItems) == 0 { - return "" - } - - return f.pathItems[len(f.pathItems)-1] -} - -// Dir returns the directory of the path. -func (f Path) Dir() (Path, error) { - if len(f.pathItems) == 0 { - return Path{}, fmt.Errorf("cannot get directory of empty path") - } - - return Path{ - pathItems: f.pathItems[:len(f.pathItems)-1], - }, nil -} - -// Equal checks if two paths are equal. -func (f Path) Equal(other Path) bool { - if len(f.pathItems) != len(other.pathItems) { - return false - } - - for i, pathItem := range f.pathItems { - if pathItem != other.pathItems[i] { - return false - } - } - - return true -} - -// HasPrefix checks if the path has the prefix. -func (f Path) HasPrefix(prefix Path) bool { - if len(f.pathItems) < len(prefix.pathItems) { - return false - } - - for i := 0; i < len(prefix.pathItems); i++ { - if f.pathItems[i] != prefix.pathItems[i] { - return false - } - } - - return true -} - -// HasSuffix checks if the path has the suffix. -func (f Path) HasSuffix(suffix Path) bool { - if len(f.pathItems) < len(suffix.pathItems) { - return false - } - - for i := 0; i < len(suffix.pathItems); i++ { - if f.pathItems[len(f.pathItems)-len(suffix.pathItems)+i] != suffix.pathItems[i] { - return false - } - } - - return true -} - -// IsAncestorOf returns true if the path is an ancestor of the other path. -func (f Path) IsAncestorOf(path Path) bool { - longestCommonPath := ExtractLongestCommonPath(f, path) - - return longestCommonPath.Equal(f) && !longestCommonPath.Equal(path) -} - -// IsEmpty returns true if the path is empty. -func (f Path) IsEmpty() bool { - return len(f.pathItems) == 0 -} - -// Join joins two paths. -func (f Path) Join(other Path) Path { - return Path{ - pathItems: append(f.pathItems, other.pathItems...), - } -} - -// TrimPrefix trims the prefix from the path. -func (f Path) TrimPrefix(prefix Path) Path { - if !f.HasPrefix(f) { - return f - } - - return Path{ - pathItems: f.pathItems[len(prefix.pathItems):], - } -} - -// TrimSuffix trims the suffix from the path. -func (f Path) TrimSuffix(suffix Path) Path { - if !f.HasSuffix(f) { - return f - } - - return Path{ - pathItems: f.pathItems[:len(f.pathItems)-len(suffix.pathItems)], - } -} - -// String returns the string representation of a Path. -func (f Path) String() string { - return gopath.Join(f.pathItems...) -} - -// LocalString returns the local string representation of a Path. -func (f Path) LocalString() string { - return filepath.FromSlash(f.String()) -} diff --git a/internal/specifier/specifier.go b/internal/specifier/specifier.go deleted file mode 100644 index 25f62de..0000000 --- a/internal/specifier/specifier.go +++ /dev/null @@ -1,164 +0,0 @@ -package specifier - -import ( - "fmt" - "strings" - - "github.com/blang/semver/v4" - "github.com/lippkg/lip/internal/path" - "github.com/lippkg/lip/internal/tooth" -) - -// KindType is an enum that represents the type of a specifier. -type KindType int - -const ( - ToothArchiveKind KindType = iota - ToothRepoKind -) - -// Specifier is a type that can be used to specify a tooth url/file or a requirement. -type Specifier struct { - kind KindType - toothArchivePath path.Path - toothRepoPath string - - isToothVersionSpecified bool - toothVersion semver.Version -} - -// Parse creates a new specifier from the given string. -func Parse(specifierString string) (Specifier, error) { - - specifierType := getSpecifierType(specifierString) - - switch specifierType { - case ToothArchiveKind: - toothArchivePath, err := path.Parse(specifierString) - if err != nil { - return Specifier{}, fmt.Errorf("invalid requirement specifier %v\n\t%w", - specifierString, err) - } - - return Specifier{ - kind: specifierType, - toothArchivePath: toothArchivePath, - }, nil - - case ToothRepoKind: - // Parse the tooth repo and version. - splittedSpecifier := strings.Split(specifierString, "@") - - toothRepoPath := splittedSpecifier[0] - - if !tooth.IsValidToothRepoPath(toothRepoPath) { - return Specifier{}, fmt.Errorf("invalid requirement specifier %v: invalid tooth repo path", - specifierString) - } - - if len(splittedSpecifier) == 2 { - toothVersion, err := semver.Parse(splittedSpecifier[1]) - if err != nil { - return Specifier{}, fmt.Errorf("invalid requirement specifier %v\n\t%w", - specifierString, err) - } - - return Specifier{ - kind: specifierType, - toothRepoPath: toothRepoPath, - isToothVersionSpecified: true, - toothVersion: toothVersion, - }, nil - - } else if len(splittedSpecifier) == 1 { - return Specifier{ - kind: specifierType, - toothRepoPath: toothRepoPath, - isToothVersionSpecified: false, - }, nil - } else { - return Specifier{}, fmt.Errorf("invalid requirement specifier: %v: too many \"@\"s", - specifierString) - } - } - - // Never reached. - panic("unreachable") -} - -// Kind returns the type of the specifier. -func (s Specifier) Kind() KindType { - return s.kind -} - -// ToothArchivePath returns the path of the tooth archive. -func (s Specifier) ToothArchivePath() (path.Path, error) { - if s.Kind() != ToothArchiveKind { - return path.Path{}, fmt.Errorf("specifier is not a tooth archive") - } - - return s.toothArchivePath, nil -} - -// ToothRepoPath returns the tooth repo of the specifier. -func (s Specifier) ToothRepoPath() (string, error) { - if s.Kind() != ToothRepoKind { - return "", fmt.Errorf("specifier is not a tooth repo") - } - - return s.toothRepoPath, nil -} - -// IsToothVersionSpecified returns whether the specifier has a tooth version. -func (s Specifier) IsToothVersionSpecified() (bool, error) { - if s.Kind() != ToothRepoKind { - return false, fmt.Errorf("specifier is not a tooth repo") - } - - return s.isToothVersionSpecified, nil -} - -// ToothVersion returns the version of the tooth. -func (s Specifier) ToothVersion() (semver.Version, error) { - if s.Kind() != ToothRepoKind { - return semver.Version{}, fmt.Errorf("specifier is not a tooth repo") - } - - if !s.isToothVersionSpecified { - return semver.Version{}, fmt.Errorf("tooth version is not specified") - } - - return s.toothVersion, nil -} - -// String returns the string representation of the specifier. -func (s Specifier) String() string { - switch s.kind { - case ToothArchiveKind: - return s.toothArchivePath.LocalString() - - case ToothRepoKind: - if s.isToothVersionSpecified { - return s.toothRepoPath + "@" + s.toothVersion.String() - } else { - return s.toothRepoPath - } - } - - // Never reached. - panic("unreachable") -} - -func getSpecifierType(specifier string) KindType { - // Prefer tooth repo specifier over tooth archive specifier. - // This means that if a specifier is both a tooth repo specifier and a tooth archive - // specifier, it will be treated as a tooth repo specifier. - splittedSpecifier := strings.Split(specifier, "@") - if len(splittedSpecifier) == 1 && tooth.IsValidToothRepoPath(specifier) { - return ToothRepoKind - } else if len(splittedSpecifier) == 2 && tooth.IsValidToothRepoPath(splittedSpecifier[0]) { - return ToothRepoKind - } - - return ToothArchiveKind -} diff --git a/internal/tooth/archive.go b/internal/tooth/archive.go deleted file mode 100644 index af1c467..0000000 --- a/internal/tooth/archive.go +++ /dev/null @@ -1,245 +0,0 @@ -package tooth - -import ( - "archive/tar" - gozip "archive/zip" - "compress/gzip" - "fmt" - "io" - "os" - "runtime" - "strings" - - "github.com/lippkg/lip/internal/path" - "github.com/lippkg/lip/internal/zip" -) - -// Archive is an archive containing a tooth. -type Archive struct { - metadata Metadata - filePath path.Path - assetFilePath path.Path -} - -// MakeArchive creates a new archive. It will automatically convert metadata to platform-specific. -func MakeArchive(archiveFilePath path.Path) (Archive, error) { - r, err := gozip.OpenReader(archiveFilePath.LocalString()) - if err != nil { - return Archive{}, fmt.Errorf("failed to open zip reader %v\n\t%w", archiveFilePath.LocalString(), err) - } - defer r.Close() - - filePaths, err := zip.GetFilePaths(r) - if err != nil { - return Archive{}, fmt.Errorf("failed to extract file paths from %v\n\t%w", archiveFilePath.LocalString(), err) - } - - filePathRoot := path.ExtractLongestCommonPath(filePaths...) - - // If only one file, it must be tooth.json. Then we should use the directory of the file as the root. - if len(filePaths) == 1 { - filePathRootDir, err := filePathRoot.Dir() - if err != nil { - return Archive{}, fmt.Errorf("failed to get directory of tooth.json\n\t%w", err) - } - - filePathRoot = filePathRootDir - } - - // Find tooth.json. - toothJSONFilePath := filePathRoot.Join(path.MustParse("tooth.json")) - var toothJSONFile *gozip.File = nil - for _, file := range r.File { - if file.Name == toothJSONFilePath.String() { - toothJSONFile = file - break - } - } - if toothJSONFile == nil { - return Archive{}, fmt.Errorf("archive does not contain tooth.json") - } - - // Read tooth.json. - toothJSONFileReader, err := toothJSONFile.Open() - if err != nil { - return Archive{}, fmt.Errorf("failed to open tooth.json\n\t%w", err) - } - defer toothJSONFileReader.Close() - - toothJSONBytes, err := io.ReadAll(toothJSONFileReader) - if err != nil { - return Archive{}, fmt.Errorf("failed to read tooth.json\n\t%w", err) - } - - // Parse tooth.json. - metadata, err := MakeMetadata(toothJSONBytes) - if err != nil { - return Archive{}, fmt.Errorf("failed to parse tooth.json\n\t%w", err) - } - - // Replace ${version} with metadata version in AssetURL - - if metadata.rawMetadata.AssetURL != "" { - if strings.Contains(metadata.rawMetadata.AssetURL, "$(version)") { - metadata.rawMetadata.AssetURL = strings.ReplaceAll(metadata.rawMetadata.AssetURL, "$(version)", metadata.rawMetadata.Version) - } - } - - // Convert to platform-specific metadata. - metadata, err = metadata.ToPlatformSpecific(runtime.GOOS, runtime.GOARCH) - if err != nil { - return Archive{}, fmt.Errorf("failed to convert to platform-specific metadata\n\t%w", err) - } - - return Archive{ - metadata: metadata, - filePath: archiveFilePath, - assetFilePath: path.MakeEmpty(), - }, nil -} - -func (ar Archive) AssetFilePath() (path.Path, error) { - if ar.assetFilePath.IsEmpty() { - return path.MakeEmpty(), fmt.Errorf("asset file path is empty") - } - - return ar.assetFilePath, nil -} - -// FilePath returns the path of the asset archive. -func (ar Archive) FilePath() path.Path { - return ar.filePath -} - -// Metadata returns the metadata of the archive. -func (ar Archive) Metadata() Metadata { - return ar.metadata -} - -// ToAssetArchiveAttached converts the archive to an archive with asset archive attached. -// If assetArchivePath is empty, the tooth archive will be used as the asset archive. -func (ar Archive) ToAssetArchiveAttached(assetArchiveFilePath path.Path) (Archive, error) { - // Validate consistency of asset archive file path and asset URL. - assetURL, err := ar.Metadata().AssetURL() - if err != nil { - return Archive{}, fmt.Errorf("failed to get asset URL\n\t%w", err) - } - - if (assetArchiveFilePath.IsEmpty() && (assetURL.String() != "")) || - (!assetArchiveFilePath.IsEmpty() && (assetURL.String() == "")) { - return Archive{}, fmt.Errorf("asset archive file path and asset URL must be both specified or both empty") - } - - var filePaths []path.Path - if assetArchiveFilePath.IsEmpty() { - // Extract common prefix and prepend it to all file paths in file.place. - if strings.HasSuffix(ar.filePath.LocalString(), ".zip") { - r, err := gozip.OpenReader(ar.filePath.LocalString()) - if err != nil { - return Archive{}, fmt.Errorf("failed to open zip reader %v\n\t%w", ar.filePath.LocalString(), err) - } - defer r.Close() - - filePaths, err = zip.GetFilePaths(r) - if err != nil { - return Archive{}, fmt.Errorf("failed to extract file paths from %v\n\t%w", ar.filePath.LocalString(), err) - } - - } else if strings.HasSuffix(ar.filePath.LocalString(), ".tar.gz") { - file, err := os.Open(ar.filePath.LocalString()) - if err != nil { - return Archive{}, fmt.Errorf("failed to open file %v\n\t%w", ar.filePath.LocalString(), err) - } - gzr, err := gzip.NewReader(file) - if err != nil { - return Archive{}, fmt.Errorf("failed to open gzip reader %v\n\t%w", ar.filePath.LocalString(), err) - } - defer gzr.Close() - r := tar.NewReader(gzr) - - for f, err := r.Next(); err != io.EOF; f, err = r.Next() { - if err != nil { - return Archive{}, fmt.Errorf("failed to read file paths from %v\n\t%w", ar.filePath.LocalString(), err) - } - // Skip directories. - if f.Typeflag == tar.TypeDir { - continue - } - filePath, err := path.Parse(f.Name) - if err != nil { - return Archive{}, fmt.Errorf("failed to parse file paths from %v\n\t%w", ar.filePath.LocalString(), err) - } - filePaths = append(filePaths, filePath) - } - - } - filePathRoot := path.ExtractLongestCommonPath(filePaths...) - - newMetadata := ar.metadata - newMetadataPrefixPrepended := newMetadata.ToFilePathPrefixPrepended(filePathRoot) - newMetadataWildcardPopulated, err := newMetadataPrefixPrepended.ToWildcardPopulated(filePaths) - if err != nil { - return Archive{}, fmt.Errorf("failed to populate wildcards\n\t%w", err) - } - - return Archive{ - metadata: newMetadataWildcardPopulated, - filePath: ar.filePath, - assetFilePath: ar.filePath, - }, nil - } else { - var filePaths []path.Path - if strings.HasSuffix(assetArchiveFilePath.LocalString(), ".zip") { - r, err := gozip.OpenReader(assetArchiveFilePath.LocalString()) - if err != nil { - return Archive{}, fmt.Errorf("failed to open zip reader %v\n\t%w", assetArchiveFilePath.LocalString(), err) - } - defer r.Close() - - filePaths, err = zip.GetFilePaths(r) - if err != nil { - return Archive{}, fmt.Errorf("failed to extract file paths from %v\n\t%w", assetArchiveFilePath.LocalString(), err) - } - - } else if strings.HasSuffix(assetArchiveFilePath.LocalString(), ".tar.gz") { - file, err := os.Open(assetArchiveFilePath.LocalString()) - if err != nil { - return Archive{}, fmt.Errorf("failed to open file %v\n\t%w", assetArchiveFilePath.LocalString(), err) - } - gzr, err := gzip.NewReader(file) - if err != nil { - return Archive{}, fmt.Errorf("failed to open gzip reader %v\n\t%w", assetArchiveFilePath.LocalString(), err) - } - defer gzr.Close() - r := tar.NewReader(gzr) - - for f, err := r.Next(); err != io.EOF; f, err = r.Next() { - if err != nil { - return Archive{}, fmt.Errorf("failed to read file paths from %v\n\t%w", assetArchiveFilePath.LocalString(), err) - } - // Skip directories. - if f.Typeflag == tar.TypeDir { - continue - } - filePath, err := path.Parse(f.Name) - if err != nil { - return Archive{}, fmt.Errorf("failed to parse file paths from %v\n\t%w", assetArchiveFilePath.LocalString(), err) - } - filePaths = append(filePaths, filePath) - } - - } - - newMetadata := ar.metadata - newMetadataWildcardPopulated, err := newMetadata.ToWildcardPopulated(filePaths) - if err != nil { - return Archive{}, fmt.Errorf("failed to populate wildcards\n\t%w", err) - } - - return Archive{ - metadata: newMetadataWildcardPopulated, - filePath: ar.filePath, - assetFilePath: assetArchiveFilePath, - }, nil - } -} diff --git a/internal/tooth/jsonshema.go b/internal/tooth/jsonshema.go deleted file mode 100644 index 18e8818..0000000 --- a/internal/tooth/jsonshema.go +++ /dev/null @@ -1,231 +0,0 @@ -package tooth - -const metadataJSONSchema = `{ - "$schema": "https://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "format_version": { - "type": "integer", - "const": 2 - }, - "tooth": { - "type": "string" - }, - "version": { - "type": "string" - }, - "info": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "author": { - "type": "string" - }, - "tags": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[a-z0-9-]+(:[a-z0-9-]+)?$" - } - }, - "avatar_url": { - "type": "string" - } - }, - "required": [ - "name", - "description", - "author", - "tags" - ] - }, - "asset_url": { - "type": "string" - }, - "commands": { - "type": "object", - "properties": { - "pre_install": { - "type": "array", - "items": { - "type": "string" - } - }, - "post_install": { - "type": "array", - "items": { - "type": "string" - } - }, - "pre_uninstall": { - "type": "array", - "items": { - "type": "string" - } - }, - "post_uninstall": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "dependencies": { - "type": "object", - "patternProperties": { - "^.*$": { - "type": "string" - } - } - }, - "prerequisites": { - "type": "object", - "patternProperties": { - "^.*$": { - "type": "string" - } - } - }, - "files": { - "type": "object", - "properties": { - "place": { - "type": "array", - "items": { - "type": "object", - "properties": { - "src": { - "type": "string" - }, - "dest": { - "type": "string" - } - }, - "required": [ - "src", - "dest" - ] - } - }, - "preserve": { - "type": "array", - "items": { - "type": "string" - } - }, - "remove": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "platforms": { - "type": "array", - "items": { - "type": "object", - "properties": { - "goarch": { - "type": "string" - }, - "goos": { - "type": "string" - }, - "asset_url": { - "type": "string" - }, - "commands": { - "type": "object", - "properties": { - "pre_install": { - "type": "array", - "items": { - "type": "string" - } - }, - "post_install": { - "type": "array", - "items": { - "type": "string" - } - }, - "pre_uninstall": { - "type": "array", - "items": { - "type": "string" - } - }, - "post_uninstall": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "dependencies": { - "type": "object", - "patternProperties": { - "^.*$": { - "type": "string" - } - } - }, - "prerequisites": { - "type": "object", - "patternProperties": { - "^.*$": { - "type": "string" - } - } - }, - "files": { - "type": "object", - "properties": { - "place": { - "type": "array", - "items": { - "type": "object", - "properties": { - "src": { - "type": "string" - }, - "dest": { - "type": "string" - } - }, - "required": [ - "src", - "dest" - ] - } - }, - "preserve": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - }, - "required": [ - "goos" - ] - } - } - }, - "required": [ - "format_version", - "tooth", - "version", - "info" - ] -}` diff --git a/internal/tooth/metadata.go b/internal/tooth/metadata.go deleted file mode 100644 index b6fa7bf..0000000 --- a/internal/tooth/metadata.go +++ /dev/null @@ -1,404 +0,0 @@ -package tooth - -import ( - "encoding/json" - "fmt" - "net/url" - "strings" - - gopath "path" - - "github.com/blang/semver/v4" - "github.com/lippkg/lip/internal/path" - "github.com/lippkg/lip/internal/tooth/migration/v1tov2" - "github.com/xeipuuv/gojsonschema" - - log "github.com/sirupsen/logrus" -) - -type Metadata struct { - rawMetadata RawMetadata -} - -type Info struct { - Name string - Description string - Author string - Tags []string -} -type Commands struct { - PreInstall []string - PostInstall []string - PreUninstall []string - PostUninstall []string -} - -type Files struct { - Place []FilesPlaceItem - Preserve []path.Path - Remove []path.Path -} - -type FilesPlaceItem struct { - Src path.Path - Dest path.Path -} - -const expectedFormatVersion = 2 - -// MakeMetadata parses the given jsonBytes and returns a Metadata. -func MakeMetadata(jsonBytes []byte) (Metadata, error) { - // Migrate if needed. - formatVersion, err := parseFormatVersion(jsonBytes) - if err != nil { - return Metadata{}, fmt.Errorf("failed to get format version\n\t%w", err) - } - - isMigrationNeeded := false - switch formatVersion { - case 1: - migratedJSONBytes, err := v1tov2.Migrate(jsonBytes) - if err != nil { - return Metadata{}, fmt.Errorf("failed to migrate metadata\n\t%w", err) - } - - isMigrationNeeded = true - jsonBytes = migratedJSONBytes - fallthrough - - case expectedFormatVersion: - // Do nothing. - - default: - return Metadata{}, fmt.Errorf("unsupported format version: %v", formatVersion) - } - - // Validate JSON against schema - schemaLoader := gojsonschema.NewStringLoader(metadataJSONSchema) - documentLoader := gojsonschema.NewBytesLoader(jsonBytes) - - validationResult, err := gojsonschema.Validate(schemaLoader, documentLoader) - if err != nil { - return Metadata{}, fmt.Errorf("failed to validate raw metadata\n\t%w", err) - } - - if !validationResult.Valid() { - errors := make([]string, 0) - for _, err := range validationResult.Errors() { - errors = append(errors, err.String()) - } - return Metadata{}, fmt.Errorf("raw metadata is invalid: %v", - strings.Join(errors, ", ")) - } - - // Unmarshal JSON - var rawMetadata RawMetadata - if err := json.Unmarshal(jsonBytes, &rawMetadata); err != nil { - return Metadata{}, fmt.Errorf("failed to unmarshal raw metadata\n\t%w", err) - } - - metadata, err := MakeMetadataFromRaw(rawMetadata) - if err != nil { - return Metadata{}, fmt.Errorf("failed to make metadata\n\t%w", err) - } - - // Warn for obsolete tooth.json. - if isMigrationNeeded { - log.Warnf("tooth.json format version %v of %v is deprecated. This tooth might be obsolete.", - formatVersion, metadata.ToothRepoPath()) - } - - return metadata, nil -} - -// MakeMetadataFromRaw returns a Metadata from the given RawMetadata. -func MakeMetadataFromRaw(rawMetadata RawMetadata) (Metadata, error) { - // Validate metadata. - if rawMetadata.FormatVersion != expectedFormatVersion { - return Metadata{}, fmt.Errorf("unsupported format version: %v", rawMetadata.FormatVersion) - } - - if !IsValidToothRepoPath(rawMetadata.Tooth) { - return Metadata{}, fmt.Errorf("invalid tooth repo path %v", rawMetadata.Tooth) - } - - if _, err := semver.Parse(rawMetadata.Version); err != nil { - return Metadata{}, fmt.Errorf("failed to parse version\n\t%w", err) - } - - return Metadata{rawMetadata}, nil -} - -func (m Metadata) ToothRepoPath() string { - return m.rawMetadata.Tooth -} - -func (m Metadata) Version() semver.Version { - return semver.MustParse(m.rawMetadata.Version) -} - -func (m Metadata) Info() Info { - return Info(m.rawMetadata.Info) -} - -func (m Metadata) AssetURL() (*url.URL, error) { - return url.Parse(m.rawMetadata.AssetURL) -} - -func (m Metadata) Commands() Commands { - return Commands(m.rawMetadata.Commands) -} - -func (m Metadata) Dependencies() (map[string]semver.Range, error) { - dependencies := make(map[string]semver.Range) - - for toothRepoPath, dep := range m.rawMetadata.Dependencies { - versionRange, err := semver.ParseRange(dep) - if err != nil { - return nil, fmt.Errorf("failed to parse version range \"%v\" of %v\n\t%w", dep, toothRepoPath, err) - } - - dependencies[toothRepoPath] = versionRange - } - - return dependencies, nil -} - -func (m Metadata) DependenciesAsStrings() map[string]string { - dependencies := make(map[string]string) - - for toothRepoPath, dep := range m.rawMetadata.Dependencies { - dependencies[toothRepoPath] = dep - } - - return dependencies -} - -func (m Metadata) Prerequisites() (map[string]semver.Range, error) { - prerequisites := make(map[string]semver.Range) - - for toothRepoPath, prereq := range m.rawMetadata.Prerequisites { - versionRange, err := semver.ParseRange(prereq) - if err != nil { - return nil, fmt.Errorf("failed to parse version range \"%v\" of %v\n\t%w", prereq, toothRepoPath, err) - } - - prerequisites[toothRepoPath] = versionRange - } - - return prerequisites, nil -} - -func (m Metadata) PrerequisitesAsStrings() map[string]string { - prerequisites := make(map[string]string) - - for toothRepoPath, prereq := range m.rawMetadata.Prerequisites { - prerequisites[toothRepoPath] = prereq - } - - return prerequisites -} - -func (m Metadata) Files() (Files, error) { - if !m.IsWildcardPopulated() { - return Files{}, fmt.Errorf("wildcard is not populated") - } - - place := make([]FilesPlaceItem, 0) - for _, placeItem := range m.rawMetadata.Files.Place { - src, err := path.Parse(placeItem.Src) - if err != nil { - return Files{}, fmt.Errorf("failed to parse source path\n\t%w", err) - } - - dest, err := path.Parse(placeItem.Dest) - if err != nil { - return Files{}, fmt.Errorf("failed to parse destination path\n\t%w", err) - } - - place = append(place, FilesPlaceItem{ - Src: src, - Dest: dest, - }) - } - - preserve := make([]path.Path, 0) - for _, preserveItem := range m.rawMetadata.Files.Preserve { - preservePath, err := path.Parse(preserveItem) - if err != nil { - return Files{}, fmt.Errorf("failed to parse preserve path\n\t%w", err) - } - - preserve = append(preserve, preservePath) - } - - remove := make([]path.Path, 0) - for _, removeItem := range m.rawMetadata.Files.Remove { - removePath, err := path.Parse(removeItem) - if err != nil { - return Files{}, fmt.Errorf("failed to parse remove path\n\t%w", err) - } - - remove = append(remove, removePath) - } - - return Files{ - Place: place, - Preserve: preserve, - Remove: remove, - }, nil -} - -func (m Metadata) IsWildcardPopulated() bool { - for _, placeItem := range m.rawMetadata.Files.Place { - if strings.Contains(placeItem.Src, "*") { - return false - } - } - - return true -} - -func (m Metadata) MarshalJSON() ([]byte, error) { - jsonBytes, err := json.MarshalIndent(m.rawMetadata, "", " ") - - if err != nil { - return nil, fmt.Errorf("failed to marshal raw metadata\n\t%w", err) - } - - return jsonBytes, nil -} - -func (m Metadata) ToPlatformSpecific(goos string, goarch string) (Metadata, error) { - raw := m.rawMetadata - - if raw.Dependencies == nil { - raw.Dependencies = make(map[string]string) - } - if raw.Prerequisites == nil { - raw.Prerequisites = make(map[string]string) - } - raw.Platforms = nil - - for _, platformItem := range m.rawMetadata.Platforms { - if platformItem.GOOS != goos { - continue - } - - if platformItem.GOARCH != "" && platformItem.GOARCH != goarch { - continue - } - - if platformItem.AssetURL != "" { - raw.AssetURL = platformItem.AssetURL - } - - raw.Commands.PreInstall = append(raw.Commands.PreInstall, platformItem.Commands.PreInstall...) - raw.Commands.PostInstall = append(raw.Commands.PostInstall, platformItem.Commands.PostInstall...) - raw.Commands.PreUninstall = append(raw.Commands.PreUninstall, platformItem.Commands.PreUninstall...) - raw.Commands.PostUninstall = append(raw.Commands.PostUninstall, platformItem.Commands.PostUninstall...) - - for toothRepoPath, dep := range platformItem.Dependencies { - raw.Dependencies[toothRepoPath] = dep - } - - for toothRepoPath, prereq := range platformItem.Prerequisites { - raw.Prerequisites[toothRepoPath] = prereq - } - - raw.Files.Place = append(raw.Files.Place, platformItem.Files.Place...) - raw.Files.Preserve = append(raw.Files.Preserve, platformItem.Files.Preserve...) - raw.Files.Remove = append(raw.Files.Remove, platformItem.Files.Remove...) - } - - return MakeMetadataFromRaw(raw) -} - -// ToFilePathPrefixPrepended prepends the given prefix to files.place field of metadata. -func (m Metadata) ToFilePathPrefixPrepended(prefix path.Path) Metadata { - newRaw := m.rawMetadata - - newPlace := make([]RawMetadataFilesPlaceItem, 0) - - for _, placeItem := range m.rawMetadata.Files.Place { - newPlace = append(newPlace, RawMetadataFilesPlaceItem{ - Src: gopath.Join(prefix.String(), placeItem.Src), - Dest: placeItem.Dest, - }) - } - - newRaw.Files.Place = newPlace - - return Metadata{newRaw} -} - -// ToWildcardPopulated populates wildcards in files.place field of metadata. -func (m Metadata) ToWildcardPopulated(filePaths []path.Path) (Metadata, error) { - debugLogger := log.WithFields(log.Fields{ - "package": "tooth", - "method": "Metadata.ToWildcardPopulated", - }) - - newRaw := m.rawMetadata - - newPlace := make([]RawMetadataFilesPlaceItem, 0) - - for _, placeItem := range m.rawMetadata.Files.Place { - // If not wildcard, just append. - if !strings.HasSuffix(placeItem.Src, "*") { - newPlace = append(newPlace, placeItem) - continue - } - - sourcePathPrefix, err := path.Parse(strings.TrimSuffix(placeItem.Src, "*")) - if err != nil { - return Metadata{}, fmt.Errorf("failed to parse source path prefix\n\t%w", err) - } - - destPathPrefix, err := path.Parse(placeItem.Dest) - if err != nil { - return Metadata{}, fmt.Errorf("failed to parse destination path prefix\n\t%w", err) - } - - for _, filePath := range filePaths { - if !filePath.HasPrefix(sourcePathPrefix) { - continue - } - - relFilePath := filePath.TrimPrefix(sourcePathPrefix) - - newPlace = append(newPlace, RawMetadataFilesPlaceItem{ - Src: filePath.String(), - Dest: destPathPrefix.Join(relFilePath).String(), - }) - - debugLogger.Debugf("Populated %v to %v", filePath, destPathPrefix.Join(relFilePath)) - } - } - - newRaw.Files.Place = newPlace - - newMetadata := Metadata{newRaw} - - return newMetadata, nil -} - -func parseFormatVersion(jsonBytes []byte) (int, error) { - jsonData := make(map[string]interface{}) - err := json.Unmarshal(jsonBytes, &jsonData) - if err != nil { - return 0, fmt.Errorf("failed to parse json\n\t%w", err) - } - - formatVersion, ok := jsonData["format_version"] - if !ok { - return 0, fmt.Errorf("missing format_version") - } - - formatVersionFloat64, ok := formatVersion.(float64) - if !ok { - return 0, fmt.Errorf("format_version is not an int") - } - - return int(formatVersionFloat64), nil -} diff --git a/internal/tooth/migration/v1tov2/v1tov2.go b/internal/tooth/migration/v1tov2/v1tov2.go deleted file mode 100644 index 2d685e7..0000000 --- a/internal/tooth/migration/v1tov2/v1tov2.go +++ /dev/null @@ -1,299 +0,0 @@ -package v1tov2 - -import ( - "encoding/json" - "fmt" - "runtime" - "strings" - - "github.com/xeipuuv/gojsonschema" -) - -const v1JSONSchema = ` -{ - "$schema": "https://json-schema.org/draft-07/schema#", - "type": "object", - "required": [ - "format_version", - "tooth", - "version" - ], - "properties": { - "format_version": { - "const": 1 - }, - "tooth": { - "type": "string" - }, - "version": { - "type": "string" - }, - "dependencies": { - "type": "object", - "patternProperties": { - "^.*$": { - "type": "array", - "minItems": 1, - "items": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - }, - "information": { - "type": "object" - }, - "placement": { - "type": "array", - "items": { - "type": "object", - "required": [ - "source", - "destination" - ], - "properties": { - "source": { - "type": "string" - }, - "destination": { - "type": "string" - }, - "GOOS": { - "type": "string" - }, - "GOARCH": { - "type": "string" - } - } - } - }, - "possession": { - "type": "array", - "items": { - "type": "string" - } - }, - "commands": { - "type": "array", - "items": { - "type": "object", - "required": [ - "type", - "commands", - "GOOS" - ], - "properties": { - "type": { - "enum": [ - "install", - "uninstall" - ] - }, - "commands": { - "type": "array", - "items": { - "type": "string" - } - }, - "GOOS": { - "type": "string" - }, - "GOARCH": { - "type": "string" - } - } - } - } - } -} -` - -type v1RawMetadata struct { - FormatVersion int `json:"format_version"` - Tooth string `json:"tooth"` - Version string `json:"version"` - Dependencies v1Dependencies `json:"dependencies,omitempty"` - Information v1Information `json:"information,omitempty"` - Placement []v1PlacementItem `json:"placement,omitempty"` - Possession []string `json:"possession,omitempty"` - Commands []v1CommandsItem `json:"commands,omitempty"` -} - -type v1Dependencies map[string]v1DependenciesItem - -type v1DependenciesItem [][]string - -type v1Information struct { - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Author string `json:"author,omitempty"` -} - -type v1PlacementItem struct { - Source string `json:"source"` - Destination string `json:"destination"` - GOOS string `json:"GOOS,omitempty"` - GOARCH string `json:"GOARCH,omitempty"` -} - -type v1CommandsItem struct { - Type string `json:"type"` - Commands []string `json:"commands"` - GOOS string `json:"GOOS"` - GOARCH string `json:"GOARCH,omitempty"` -} - -type RawMetadata struct { - FormatVersion int `json:"format_version"` - Tooth string `json:"tooth"` - Version string `json:"version"` - Info RawMetadataInfo `json:"info"` - - AssetURL string `json:"asset_url,omitempty"` - Commands RawMetadataCommands `json:"commands,omitempty"` - Dependencies map[string]string `json:"dependencies,omitempty"` - Prerequisites map[string]string `json:"prerequisites,omitempty"` - Files RawMetadataFiles `json:"files,omitempty"` - - Platforms []RawMetadataPlatformsItem `json:"platforms,omitempty"` -} - -type RawMetadataInfo struct { - Name string `json:"name"` - Description string `json:"description"` - Author string `json:"author"` - Tags []string `json:"tags"` -} - -type RawMetadataCommands struct { - PreInstall []string `json:"pre_install,omitempty"` - PostInstall []string `json:"post_install,omitempty"` - PreUninstall []string `json:"pre_uninstall,omitempty"` - PostUninstall []string `json:"post_uninstall,omitempty"` -} - -type RawMetadataFiles struct { - Place []RawMetadataFilesPlaceItem `json:"place,omitempty"` - Preserve []string `json:"preserve,omitempty"` - Remove []string `json:"remove,omitempty"` -} - -type RawMetadataFilesPlaceItem struct { - Src string `json:"src"` - Dest string `json:"dest"` -} - -type RawMetadataPlatformsItem struct { - GOARCH string `json:"goarch,omitempty"` - GOOS string `json:"goos"` - - AssetURL string `json:"asset_url,omitempty"` - Commands RawMetadataCommands `json:"commands,omitempty"` - Dependencies map[string]string `json:"dependencies,omitempty"` - Prerequisites map[string]string `json:"prerequisites,omitempty"` - Files RawMetadataFiles `json:"files,omitempty"` -} - -// Migrate migrates the metadata from v1 to v2. -func Migrate(jsonBytes []byte) ([]byte, error) { - // Validate JSON against schema. - v1SchemaLoader := gojsonschema.NewStringLoader(v1JSONSchema) - v1DocumentLoader := gojsonschema.NewBytesLoader(jsonBytes) - - result, err := gojsonschema.Validate(v1SchemaLoader, v1DocumentLoader) - if err != nil { - return nil, fmt.Errorf("error validating JSON against schema\n\t%w", err) - } - - if !result.Valid() { - return nil, fmt.Errorf("JSON is not valid against schema: %v", result.Errors()) - } - - // Unmarshal JSON into struct. - var v1RawMetadata v1RawMetadata - if err := json.Unmarshal(jsonBytes, &v1RawMetadata); err != nil { - return nil, fmt.Errorf("error unmarshaling JSON into struct\n\t%w", err) - } - - // Migrate struct. - v2RawMetadata := RawMetadata{ - FormatVersion: 2, - Tooth: v1RawMetadata.Tooth, - Version: v1RawMetadata.Version, - Info: RawMetadataInfo{ - Name: v1RawMetadata.Information.Name, - Description: v1RawMetadata.Information.Description, - Author: v1RawMetadata.Information.Author, - Tags: make([]string, 0), - }, - Commands: RawMetadataCommands{ - PreInstall: make([]string, 0), - PostInstall: make([]string, 0), - PreUninstall: make([]string, 0), - PostUninstall: make([]string, 0), - }, - Dependencies: make(map[string]string), - Files: RawMetadataFiles{ - Place: make([]RawMetadataFilesPlaceItem, 0), - Remove: v1RawMetadata.Possession, - }, - Platforms: make([]RawMetadataPlatformsItem, 0), - } - - // Solve dependencies. - for toothRepoPath, depMatrix := range v1RawMetadata.Dependencies { - depInnerStringList := make([]string, 0) - for _, andDepList := range depMatrix { - depInnerStringList = append(depInnerStringList, strings.Join(andDepList, " ")) - } - - v2RawMetadata.Dependencies[toothRepoPath] = strings.Join(depInnerStringList, " || ") - } - - // Solve commands - for _, v1Command := range v1RawMetadata.Commands { - if v1Command.GOOS != "" && runtime.GOOS != v1Command.GOOS { - continue - } - - if v1Command.GOARCH != "" && runtime.GOARCH != v1Command.GOARCH { - continue - } - - switch v1Command.Type { - case "install": - v2RawMetadata.Commands.PostInstall = append( - v2RawMetadata.Commands.PostInstall, v1Command.Commands...) - case "uninstall": - v2RawMetadata.Commands.PreUninstall = append( - v2RawMetadata.Commands.PreUninstall, v1Command.Commands...) - } - } - - // Solve files - for _, v1Placement := range v1RawMetadata.Placement { - if v1Placement.GOOS != "" && runtime.GOOS != v1Placement.GOOS { - continue - } - - if v1Placement.GOARCH != "" && runtime.GOARCH != v1Placement.GOARCH { - continue - } - - v2RawMetadata.Files.Place = append( - v2RawMetadata.Files.Place, RawMetadataFilesPlaceItem{ - Src: v1Placement.Source, - Dest: strings.TrimSuffix(v1Placement.Destination, "*"), - }) - } - - resultJSONBytes, err := json.Marshal(v2RawMetadata) - if err != nil { - return nil, fmt.Errorf("error marshaling struct into JSON\n\t%w", err) - } - - return resultJSONBytes, nil -} diff --git a/internal/tooth/rawmetadata.go b/internal/tooth/rawmetadata.go deleted file mode 100644 index f1bbba8..0000000 --- a/internal/tooth/rawmetadata.go +++ /dev/null @@ -1,54 +0,0 @@ -package tooth - -// Why to split Metadata and RawMetadata? Because we encounter a problem when -// we want to add a getter with the same name as a field. -type RawMetadata struct { - FormatVersion int `json:"format_version"` - Tooth string `json:"tooth"` - Version string `json:"version"` - Info RawMetadataInfo `json:"info"` - - AssetURL string `json:"asset_url,omitempty"` - Commands RawMetadataCommands `json:"commands,omitempty"` - Dependencies map[string]string `json:"dependencies,omitempty"` - Prerequisites map[string]string `json:"prerequisites,omitempty"` - Files RawMetadataFiles `json:"files,omitempty"` - - Platforms []RawMetadataPlatformsItem `json:"platforms,omitempty"` -} - -type RawMetadataInfo struct { - Name string `json:"name"` - Description string `json:"description"` - Author string `json:"author"` - Tags []string `json:"tags"` -} - -type RawMetadataCommands struct { - PreInstall []string `json:"pre_install,omitempty"` - PostInstall []string `json:"post_install,omitempty"` - PreUninstall []string `json:"pre_uninstall,omitempty"` - PostUninstall []string `json:"post_uninstall,omitempty"` -} - -type RawMetadataFiles struct { - Place []RawMetadataFilesPlaceItem `json:"place,omitempty"` - Preserve []string `json:"preserve,omitempty"` - Remove []string `json:"remove,omitempty"` -} - -type RawMetadataFilesPlaceItem struct { - Src string `json:"src"` - Dest string `json:"dest"` -} - -type RawMetadataPlatformsItem struct { - GOARCH string `json:"goarch,omitempty"` - GOOS string `json:"goos"` - - AssetURL string `json:"asset_url,omitempty"` - Commands RawMetadataCommands `json:"commands,omitempty"` - Dependencies map[string]string `json:"dependencies,omitempty"` - Prerequisites map[string]string `json:"prerequisites,omitempty"` - Files RawMetadataFiles `json:"files,omitempty"` -} diff --git a/internal/tooth/utils.go b/internal/tooth/utils.go deleted file mode 100644 index f9f7fa7..0000000 --- a/internal/tooth/utils.go +++ /dev/null @@ -1,203 +0,0 @@ -package tooth - -import ( - "bufio" - "bytes" - "fmt" - "net/url" - "os" - "path/filepath" - "strings" - - "github.com/blang/semver/v4" - "github.com/lippkg/lip/internal/context" - "github.com/lippkg/lip/internal/network" - "github.com/lippkg/lip/internal/path" - - "golang.org/x/mod/module" -) - -// GetAllMetadata lists all installed tooth metadata. -func GetAllMetadata(ctx *context.Context) ([]Metadata, error) { - metadataList := make([]Metadata, 0) - - metadataDir, err := ctx.MetadataDir() - if err != nil { - return nil, fmt.Errorf("failed to get metadata directory\n\t%w", err) - } - - filePathStrings, err := filepath.Glob(filepath.Join(metadataDir.LocalString(), "*.json")) - if err != nil { - return nil, fmt.Errorf("failed to list metadata files\n\t%w", err) - } - - for _, filePathString := range filePathStrings { - filePath, err := path.Parse(filePathString) - if err != nil { - return nil, fmt.Errorf("failed to parse metadata file path\n\t%w", err) - } - - jsonBytes, err := os.ReadFile(filePath.LocalString()) - if err != nil { - return nil, fmt.Errorf("failed to read metadata file\n\t%w", err) - } - - metadata, err := MakeMetadata(jsonBytes) - if err != nil { - return nil, fmt.Errorf("failed to parse metadata file\n\t%w", err) - } - - // Check if the metadata file name matches the tooth repo path in the metadata. - expectedFileName := fmt.Sprintf("%v.json", url.QueryEscape(metadata.ToothRepoPath())) - if filePath.Base() != expectedFileName { - return nil, fmt.Errorf("metadata file name does not match: %v", filePath.LocalString()) - } - - metadataList = append(metadataList, metadata) - } - - return metadataList, nil -} - -// GetAvailableVersions fetches the version list of a tooth repository. -func GetAvailableVersions(ctx *context.Context, toothRepoPath string) (semver.Versions, - error) { - - if !IsValidToothRepoPath(toothRepoPath) { - return nil, fmt.Errorf("invalid repository path %v", toothRepoPath) - } - - goModuleProxyURL, err := ctx.GoModuleProxyURL() - if err != nil { - return nil, fmt.Errorf("failed to get go module proxy URL\n\t%w", err) - } - - versionURL, err := network.GenerateGoModuleVersionListURL(toothRepoPath, goModuleProxyURL) - if err != nil { - return nil, fmt.Errorf("failed to generate version list URL\n\t%w", err) - } - - proxyURL, err := ctx.ProxyURL() - if err != nil { - return nil, fmt.Errorf("failed to get proxy URL\n\t%w", err) - } - - content, err := network.GetContent(versionURL, proxyURL) - if err != nil { - return nil, fmt.Errorf("failed to fetch version list\n\t%w", err) - } - - reader := bytes.NewReader(content) - - // Each line is a version. - versionList := make(semver.Versions, 0) - scanner := bufio.NewScanner(reader) - for scanner.Scan() { - versionString := scanner.Text() - versionString = strings.TrimPrefix(versionString, "v") - versionString = strings.TrimSuffix(versionString, "+incompatible") - version, err := semver.Parse(versionString) - if err != nil { - continue - } - versionList = append(versionList, version) - } - - return versionList, nil -} - -// GetLatestVersion returns the latest =version of a tooth repository. -func GetLatestVersion(ctx *context.Context, - toothRepoPath string) (semver.Version, error) { - - versionRange := semver.Range(func(version semver.Version) bool { - return true - }) - return GetLatestVersionInVersionRange(ctx, toothRepoPath, versionRange) -} - -// GetLatestVersionInVersionRange returns the latest version in a version range. -func GetLatestVersionInVersionRange(ctx *context.Context, - toothRepoPath string, versionRange semver.Range) (semver.Version, error) { - - availableVersions, err := GetAvailableVersions(ctx, toothRepoPath) - if err != nil { - return semver.Version{}, fmt.Errorf( - "failed to get available version list\n\t%w", err) - } - - // Filter versions that satisfy the version range. - filteredVersions := make(semver.Versions, 0) - for _, version := range availableVersions { - if versionRange(version) { - filteredVersions = append(filteredVersions, version) - } - } - - stableVersions := make(semver.Versions, 0) - for _, version := range filteredVersions { - if len(version.Pre) == 0 { - stableVersions = append(stableVersions, version) - } - } - - semver.Sort(stableVersions) - - if len(stableVersions) >= 1 { - return stableVersions[len(stableVersions)-1], nil - } - - semver.Sort(filteredVersions) - - if len(filteredVersions) >= 1 { - return filteredVersions[len(filteredVersions)-1], nil - } - - return semver.Version{}, fmt.Errorf("no available version found") -} - -// GetMetadata finds the installed tooth metadata. -func GetMetadata(ctx *context.Context, toothRepoPath string) (Metadata, - error) { - - metadataList, err := GetAllMetadata(ctx) - if err != nil { - return Metadata{}, fmt.Errorf( - "failed to list all installed tooth metadata\n\t%w", err) - } - - for _, metadata := range metadataList { - if metadata.ToothRepoPath() == toothRepoPath { - return metadata, nil - } - } - - return Metadata{}, fmt.Errorf("cannot find installed tooth metadata: %v", - toothRepoPath) -} - -// IsInstalled checks if a tooth is installed. -func IsInstalled(ctx *context.Context, toothRepoPath string) (bool, error) { - - metadataList, err := GetAllMetadata(ctx) - if err != nil { - return false, fmt.Errorf( - "failed to list all installed tooth metadata\n\t%w", err) - } - - for _, metadata := range metadataList { - if metadata.ToothRepoPath() == toothRepoPath { - return true, nil - } - } - - return false, nil -} - -// IsValidToothRepoPath checks if the tooth repository path is valid. -func IsValidToothRepoPath(toothRepoPath string) bool { - if err := module.CheckPath(toothRepoPath); err != nil { - return false - } - return true -} diff --git a/internal/zip/zip.go b/internal/zip/zip.go deleted file mode 100644 index 8843b0a..0000000 --- a/internal/zip/zip.go +++ /dev/null @@ -1,29 +0,0 @@ -package zip - -import ( - gozip "archive/zip" - "strings" - - "github.com/lippkg/lip/internal/path" -) - -// GetFilePaths returns a list of file paths in a zip archive. Directories are skipped. -func GetFilePaths(r *gozip.ReadCloser) ([]path.Path, error) { - filePaths := make([]path.Path, 0) - - for _, file := range r.File { - // Skip directories. - if strings.HasSuffix(file.Name, "/") { - continue - } - - filePath, err := path.Parse(file.Name) - if err != nil { - return nil, err - } - - filePaths = append(filePaths, filePath) - } - - return filePaths, nil -} diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index 27712a2..0000000 --- a/mkdocs.yml +++ /dev/null @@ -1,96 +0,0 @@ -site_name: lip -repo_url: https://github.com/futrime/lip/ - -nav: - - Home: index.md - - - install.md - - - quickstart.md - - - faq.md - - - Tutorials: - - tutorials/create_a_lip_tooth.md - - - Reference: - - reference/lip.md - - reference/lip_cache.md - - reference/lip_cache_purge.md - - reference/lip_install.md - - reference/lip_list.md - - reference/lip_show.md - - reference/lip_tooth.md - - reference/lip_tooth_init.md - - reference/lip_tooth_pack.md - - reference/lip_uninstall.md - - reference/tooth_json_file_reference.md - - - Packages: https://bedrinth.com - -theme: - name: material - features: - - navigation.tabs - - navigation.tabs.sticky - favicon: images/favicon.ico - logo: images/logo.webp - palette: - - media: "(prefers-color-scheme: light)" - scheme: default - primary: white - toggle: - icon: material/brightness-7 - name: Switch to dark mode - - - media: "(prefers-color-scheme: dark)" - scheme: slate - toggle: - icon: material/brightness-4 - name: Switch to light mode - -markdown_extensions: - - abbr - - admonition - - attr_list - - def_list - - footnotes - - md_in_html - - toc - - tables - - pymdownx.arithmatex - - pymdownx.betterem - - pymdownx.caret - - pymdownx.mark - - pymdownx.tilde - - pymdownx.critic - - pymdownx.details - - pymdownx.emoji - - pymdownx.highlight: - auto_title: true - linenums: true - - pymdownx.inlinehilite - - pymdownx.keys - - pymdownx.smartsymbols - - pymdownx.snippets - - pymdownx.superfences - - pymdownx.tabbed: - alternate_style: true - - pymdownx.tasklist - -plugins: - - i18n: - languages: - - locale: en - default: true - name: English - - - locale: zh - name: 中文 - nav_translations: - Home: 首页 - Tutorials: 教程 - Reference: 参考 - Packages: 包 - - - search diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 160d6ab..0000000 --- a/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -mkdocs==1.5.2 -mkdocs-static-i18n==1.0.2 -mkdocs-material==9.1.21 diff --git a/schemas/tooth.v1.schema.json b/schemas/tooth.v1.schema.json deleted file mode 100644 index da4d5e0..0000000 --- a/schemas/tooth.v1.schema.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft-07/schema#", - "type": "object", - "required": [ - "format_version", - "tooth", - "version" - ], - "properties": { - "format_version": { - "const": 1 - }, - "tooth": { - "type": "string" - }, - "version": { - "type": "string" - }, - "dependencies": { - "type": "object", - "patternProperties": { - "^.*$": { - "type": "array", - "minItems": 1, - "items": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - }, - "information": { - "type": "object" - }, - "placement": { - "type": "array", - "items": { - "type": "object", - "required": [ - "source", - "destination" - ], - "properties": { - "source": { - "type": "string" - }, - "destination": { - "type": "string" - }, - "GOOS": { - "type": "string" - }, - "GOARCH": { - "type": "string" - } - } - } - }, - "possession": { - "type": "array", - "items": { - "type": "string" - } - }, - "commands": { - "type": "array", - "items": { - "type": "object", - "required": [ - "type", - "commands", - "GOOS" - ], - "properties": { - "type": { - "enum": [ - "install", - "uninstall" - ] - }, - "commands": { - "type": "array", - "items": { - "type": "string" - } - }, - "GOOS": { - "type": "string" - }, - "GOARCH": { - "type": "string" - } - } - } - } - } -} diff --git a/schemas/tooth.v2.schema.json b/schemas/tooth.v2.schema.json deleted file mode 100644 index 567449b..0000000 --- a/schemas/tooth.v2.schema.json +++ /dev/null @@ -1,229 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "format_version": { - "type": "integer", - "const": 2 - }, - "tooth": { - "type": "string" - }, - "version": { - "type": "string" - }, - "info": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "author": { - "type": "string" - }, - "tags": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[a-z0-9-]+(:[a-z0-9-]+)?$" - } - }, - "avatar_url": { - "type": "string" - } - }, - "required": [ - "name", - "description", - "author", - "tags" - ] - }, - "asset_url": { - "type": "string" - }, - "commands": { - "type": "object", - "properties": { - "pre_install": { - "type": "array", - "items": { - "type": "string" - } - }, - "post_install": { - "type": "array", - "items": { - "type": "string" - } - }, - "pre_uninstall": { - "type": "array", - "items": { - "type": "string" - } - }, - "post_uninstall": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "dependencies": { - "type": "object", - "patternProperties": { - "^.*$": { - "type": "string" - } - } - }, - "prerequisites": { - "type": "object", - "patternProperties": { - "^.*$": { - "type": "string" - } - } - }, - "files": { - "type": "object", - "properties": { - "place": { - "type": "array", - "items": { - "type": "object", - "properties": { - "src": { - "type": "string" - }, - "dest": { - "type": "string" - } - }, - "required": [ - "src", - "dest" - ] - } - }, - "preserve": { - "type": "array", - "items": { - "type": "string" - } - }, - "remove": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "platforms": { - "type": "array", - "items": { - "type": "object", - "properties": { - "goarch": { - "type": "string" - }, - "goos": { - "type": "string" - }, - "asset_url": { - "type": "string" - }, - "commands": { - "type": "object", - "properties": { - "pre_install": { - "type": "array", - "items": { - "type": "string" - } - }, - "post_install": { - "type": "array", - "items": { - "type": "string" - } - }, - "pre_uninstall": { - "type": "array", - "items": { - "type": "string" - } - }, - "post_uninstall": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "dependencies": { - "type": "object", - "patternProperties": { - "^.*$": { - "type": "string" - } - } - }, - "prerequisites": { - "type": "object", - "patternProperties": { - "^.*$": { - "type": "string" - } - } - }, - "files": { - "type": "object", - "properties": { - "place": { - "type": "array", - "items": { - "type": "object", - "properties": { - "src": { - "type": "string" - }, - "dest": { - "type": "string" - } - }, - "required": [ - "src", - "dest" - ] - } - }, - "preserve": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - }, - "required": [ - "goos" - ] - } - } - }, - "required": [ - "format_version", - "tooth", - "version", - "info" - ] -} diff --git a/schemas/tooth.v3.schema.json b/schemas/tooth.v3.schema.json deleted file mode 100644 index 9412422..0000000 --- a/schemas/tooth.v3.schema.json +++ /dev/null @@ -1,151 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "format_version": { - "type": "integer", - "const": 3 - }, - "tooth": { - "type": "string" - }, - "version": { - "type": "string" - }, - "info": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "tags": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[a-z0-9-]+(:[a-z0-9-]+)?$" - } - }, - "avatar_url": { - "type": "string" - } - } - } - }, - "patternProperties": { - "^([a-z0-9]+|\\*)\\/([a-z0-9]+|\\*)$": { - "type": "object", - "properties": { - "commands": { - "type": "object", - "properties": { - "pre_install": { - "type": "array", - "items": { - "type": "string" - } - }, - "post_install": { - "type": "array", - "items": { - "type": "string" - } - }, - "pre_uninstall": { - "type": "array", - "items": { - "type": "string" - } - }, - "post_uninstall": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "dependencies": { - "type": "object", - "patternProperties": { - "^.*$": { - "type": "string" - } - } - }, - "prerequisites": { - "type": "object", - "patternProperties": { - "^.*$": { - "type": "string" - } - } - }, - "assets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "download_urls": { - "type": "array", - "items": { - "type": "string" - } - }, - "type": { - "type": "string", - "enum": [ - "uncompressed", - "tar", - "tar.gz", - "zip" - ] - }, - "place": { - "type": "array", - "items": { - "type": "object", - "properties": { - "src": { - "type": "string" - }, - "dest": { - "type": "string" - } - }, - "required": [ - "src", - "dest" - ] - } - }, - "preserve": { - "type": "array", - "items": { - "type": "string" - } - }, - "remove": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "download_urls", - "type" - ] - } - } - } - } - }, - "required": [ - "format_version", - "tooth", - "version" - ] -} diff --git a/scripts/make_installer.nsi b/scripts/make_installer.nsi deleted file mode 100644 index e6802e7..0000000 --- a/scripts/make_installer.nsi +++ /dev/null @@ -1,152 +0,0 @@ -Unicode True - -!define PRODUCT_NAME "Lip" -!define PRODUCT_PUBLISHER "LiteLDev" -!define PRODUCT_WEB_SITE "https://github.com/LipPkg/Lip" -!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\lip.exe" -!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" -!define PRODUCT_UNINST_ROOT_KEY "HKLM" - -!ifdef LIP_VERSION - !define PRODUCT_VERSION "${LIP_VERSION}" -!else - !define PRODUCT_VERSION "0.0.0" -!endif - -!ifdef LIP_OS -!else - !define LIP_OS "windows" -!endif - -!ifdef LIP_ARCH -!else - !define LIP_ARCH "amd64" -!endif - -SetCompressor /SOLID lzma -SetCompressorDictSize 32 - -; MUI 1.67 compatible ------ -!include "MUI.nsh" - -; MUI Settings -!define MUI_ABORTWARNING -!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico" -!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico" - -; Language Selection Dialog Settings -!define MUI_LANGDLL_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}" -!define MUI_LANGDLL_REGISTRY_KEY "${PRODUCT_UNINST_KEY}" -!define MUI_LANGDLL_REGISTRY_VALUENAME "NSIS:Language" - -; Welcome page -!insertmacro MUI_PAGE_WELCOME -; License page -!define MUI_LICENSEPAGE_CHECKBOX -!insertmacro MUI_PAGE_LICENSE "COPYING" -; Components page -!insertmacro MUI_PAGE_COMPONENTS -; Directory page -!insertmacro MUI_PAGE_DIRECTORY -; Instfiles page -!insertmacro MUI_PAGE_INSTFILES -; Finish page -!insertmacro MUI_PAGE_FINISH - -; Uninstaller pages -!insertmacro MUI_UNPAGE_INSTFILES - -; Language files -!insertmacro MUI_LANGUAGE "English" -!insertmacro MUI_LANGUAGE "SimpChinese" - -; Reserve files -!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS - -; MUI end ------ - -Name "${PRODUCT_NAME}" -OutFile "lip-${LIP_OS}-${LIP_ARCH}-setup.exe" -InstallDir "$PROGRAMFILES64\Lip" -InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" "" -ShowInstDetails show -ShowUnInstDetails show - -Function .onInit - !insertmacro MUI_LANGDLL_DISPLAY -FunctionEnd - -Section "Lip (required)" SEC01 - SectionIn RO - SetOutPath "$INSTDIR" - SetOverwrite ifnewer - File "lip.exe" -SectionEnd - -Section "Add to PATH" SEC02 - ; Check for write access - EnVar::Check "NULL" "NULL" - - ; Set to HKLM - EnVar::SetHKLM - - ; Append a value - EnVar::AddValue "PATH" "$INSTDIR" -SectionEnd - -Section -Post - WriteUninstaller "$INSTDIR\uninst.exe" - WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\lip.exe" - WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)" - WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninst.exe$\"" - WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$\"$INSTDIR\lip.exe$\"" - WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}" - WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}" - WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}" -SectionEnd - -; Section descriptions -!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN - !insertmacro MUI_DESCRIPTION_TEXT ${SEC01} "" - !insertmacro MUI_DESCRIPTION_TEXT ${SEC02} "" -!insertmacro MUI_FUNCTION_DESCRIPTION_END - - - -Function un.onInit - ReadRegStr $0 "${PRODUCT_UNINST_ROOT_KEY}" "${PRODUCT_UNINST_KEY}" "NSIS:Language" - ${If} $0 == "1033" - MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure you want to completely remove $(^Name) and all of its components?" IDYES +2 - Abort - ${ElseIf} $0 == "2052" - MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "你确定要完全移除 $(^Name) 及其所有组件吗?" IDYES +2 - Abort - ${EndIf} -FunctionEnd - -Function un.onUninstSuccess - HideWindow - ${If} $0 == "1033" - MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) has been successfully removed." - ${ElseIf} $0 == "2052" - MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) 已成功移除。" - ${EndIf} -FunctionEnd - -Section Uninstall - Delete "$INSTDIR\uninst.exe" - Delete "$INSTDIR\lip.exe" - - RMDir /r "$INSTDIR\.lip" - RMDir "$INSTDIR" - - DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" - DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY}" - ; Check for write access - EnVar::Check "NULL" "NULL" - ; Set to HKLM - EnVar::SetHKLM - ; Delete Lip install directory from PATH - EnVar::DeleteValue "PATH" "$INSTDIR" - SetAutoClose true -SectionEnd diff --git a/scripts/setup_nsis.ps1 b/scripts/setup_nsis.ps1 deleted file mode 100644 index f9f6871..0000000 --- a/scripts/setup_nsis.ps1 +++ /dev/null @@ -1,11 +0,0 @@ -C:\msys64\usr\bin\wget.exe -q https://sourceforge.net/projects/nsis/files/NSIS%203/3.09/nsis-3.09.zip -O nsis.zip -C:\msys64\usr\bin\wget.exe -q https://sourceforge.net/projects/nsis/files/NSIS%203/3.09/nsis-3.09-strlen_8192.zip -O nsis-strlen.zip -C:\msys64\usr\bin\wget.exe -q https://nsis.sourceforge.io/mediawiki/images/7/7f/EnVar_plugin.zip -O EnVar_plugin.zip -unzip -q -o nsis.zip -mv nsis-3.09 nsis-portable -unzip -q -o nsis-strlen.zip -d nsis-portable -unzip -q -o EnVar_plugin.zip -d nsis-portable -mkdir nsis -cp ./scripts/make_installer.nsi ./nsis/ -cp ./lip.exe ./nsis/ -cp ./COPYING ./nsis/ diff --git a/scripts/validate_release.py b/scripts/validate_release.py deleted file mode 100644 index bc87351..0000000 --- a/scripts/validate_release.py +++ /dev/null @@ -1,57 +0,0 @@ -import argparse -import re -import subprocess -from typing import TypedDict - - -class Args(TypedDict): - tag: str - - -def main(): - args = get_args() - - version = args["tag"].lstrip("v") - - validate_changelog(version) - validate_code(version) - - -def get_args() -> Args: - parser = argparse.ArgumentParser() - parser.add_argument("--tag", required=True) - - args = parser.parse_args() - - return { - "tag": args.tag, - } - - -def validate_changelog(version: str): - try: - subprocess.run( - f"npx changelog --format markdownlint", - shell=True, - check=True, - ) - except subprocess.CalledProcessError as e: - print("Have you installed it by `npm i -g keep-a-changelog`?") - raise e - - with open("CHANGELOG.md", "r", encoding="utf-8") as f: - content = f.read() - - if not re.search(r"## \[{}\]".format(version), content): - raise Exception("CHANGELOG.md lacks version {}".format(version)) - - -def validate_code(version: str): - with open("cmd/lip/main.go", "r", encoding="utf-8") as f: - content = f.read() - - if not re.search(r'semver\.MustParse\("{}"\)'.format(version), content): - raise Exception("cmd/lip/main.go lacks version {}".format(version)) - -if __name__ == "__main__": - main() From 94bb3b3c06135a5abb694376ae87085750c27c01 Mon Sep 17 00:00:00 2001 From: Zijian Zhang Date: Fri, 3 Jan 2025 17:35:52 +0800 Subject: [PATCH 04/63] chore: miscellaneous stuff --- .gitattributes | 98 +++ .github/ISSUE_TEMPLATE/bug_report.yml | 45 ++ .github/ISSUE_TEMPLATE/feature_request.yml | 28 + .github/pull_request_template.md | 18 + .gitignore | 1 + CHANGELOG.md | 510 ++++++++++++++++ COPYING | 674 +++++++++++++++++++++ README.md | 50 ++ 8 files changed, 1424 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/pull_request_template.md create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 COPYING create mode 100644 README.md diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e3d22ec --- /dev/null +++ b/.gitattributes @@ -0,0 +1,98 @@ +# Common settings that generally should always be used with your language specific settings + +# Auto detect text files and perform LF normalization +* text=auto + +# +# The above will handle all files NOT found below +# + +# Documents +*.bibtex text diff=bibtex +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain +*.md text diff=markdown +*.mdx text diff=markdown +*.tex text diff=tex +*.adoc text +*.textile text +*.mustache text +*.csv text eol=crlf +*.tab text +*.tsv text +*.txt text +*.sql text +*.epub diff=astextplain + +# Graphics +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.tif binary +*.tiff binary +*.ico binary +# SVG treated as text by default. +*.svg text +# If you want to treat it as binary, +# use the following line instead. +# *.svg binary +*.eps binary + +# Scripts +*.bash text eol=lf +*.fish text eol=lf +*.ksh text eol=lf +*.sh text eol=lf +*.zsh text eol=lf +# These are explicitly windows files and should use crlf +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf + +# Serialisation +*.json text +*.toml text +*.xml text +*.yaml text +*.yml text + +# Archives +*.7z binary +*.bz binary +*.bz2 binary +*.bzip2 binary +*.gz binary +*.lz binary +*.lzma binary +*.rar binary +*.tar binary +*.taz binary +*.tbz binary +*.tbz2 binary +*.tgz binary +*.tlz binary +*.txz binary +*.xz binary +*.Z binary +*.zip binary +*.zst binary + +# Text files where line endings should be preserved +*.patch -text + +# +# Exclude files from exporting +# + +.gitattributes export-ignore +.gitignore export-ignore +.gitkeep export-ignore diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..e0aaadc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,45 @@ +name: Bug Report +description: Create a report to help us improve +title: "[Bug]: " +labels: ["bug"] +body: + - type: textarea + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. + validations: + required: true + + - type: textarea + attributes: + label: To Reproduce + description: Steps to reproduce the behavior. + validations: + required: true + + - type: textarea + attributes: + label: Expected behavior + description: A clear and concise description of what you expected to happen. + validations: + required: true + + - type: textarea + attributes: + label: Screenshots + description: If applicable, add screenshots to help explain your problem. + + - type: input + attributes: + label: Platform + description: The platform you are using. (e.g. Windows 10, macOS 10.15, Ubuntu 20.04) + + - type: input + attributes: + label: Version + description: The version of the application you are using. (e.g. 1.0.0) + + - type: textarea + attributes: + label: Additional context + description: Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..b0f4b33 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,28 @@ +name: Feature request +description: Suggest an idea for this project +title: "[Feature]: " +labels: ["enhancement"] +body: + - type: textarea + attributes: + label: Is your feature request related to a problem? Please describe. + description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + validations: + required: true + + - type: textarea + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + validations: + required: true + + - type: textarea + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + + - type: textarea + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..b3d2601 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,18 @@ +## What does this PR do? + + + +## Which issues does this PR resolve? + + + +## Checklist before merging + +Thank you for your contribution to the repository. +Before submitting this PR, please make sure: + +- [ ] Your code builds clean without any errors or warnings +- [ ] Your code follows our code style +- [ ] You have tested all functions +- [ ] You have not used code without license +- [ ] You have added statement for third-party code diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d74e21 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b5cd79d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,510 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.24.0] - 2024-10-01 + +### Added + +- `specifiers.txt` support + +### Changed + +- Allow colons in tags +- Parse arguments with `urfave/cli` + +## [0.23.2] - 2024-08-17 + +### Fixed + +- Partially resolved [#157] by adding .tmp extension to incomplete downloads (#158) + +## [0.23.1] - 2024-07-28 + +### Added + +- Add colorlog detector (#146) +- Add a line break to the end of the output of `lip --version` + +## [0.23.0] - 2024-07-12 + +### Added + +- Add version variable support for asset_url [#134] + +### Fixed + +- Fix `.lip` path in linux [#129] +- Fix unable to decompress .tar.gz [#140] +- Delete surplus tabs (#139) + +## [0.22.0] - 2024-03-23 + +### Added + +- `--no-color` flag to disable color output. +- NSIS installer. + +## [0.21.2] - 2024-02-21 + +### Fixed + +- Some ambiguous error messages. +- Not prompting for confirmation to overwrite existing files when installing a tooth. + +## [0.21.1] - 2024-02-17 + +### Fixed + +- Untidy error messages. +- Failed to install any tooth without setting a proxy. + +## [0.21.0] - 2024-02-17 + +### Added + +- Proxy configuration support. +- Support more kinds of GitHub mirrors +- Support `NO_COLOR` environment variable to disable color output. + +### Changed + +- Set `https://github.com` as the default GitHub mirror. + +### Fixed + +- Panic when dependencies and prerequisites have malformed version strings. + +## [0.20.1] - 2024-02-04 + +### Fixed + +- Version string. + +## [0.20.0] - 2024-02-03 + +### Added + +- Support Go module URL as asset URL. + +### Changed + +- Files downloaded from GitHub mirror are now cached in different paths. + +### Removed + +- `source` field in `tooth.json`. + +## [0.19.0] - 2024-01-20 + +### Added + +- Tons of debug logging. +- Show available versions without installing. + +### Changed + +- Optimize error logging. + +### Fixed + +- Wildcard not parsing correctly. +- Wrong error output when "%" is used in messages. +- Wrong version `@0.0.0` when stringify specifiers without version. + +## [0.18.1] - 2024-01-12 + +### Fixed + +- Platform-specific items in tooth.json. +- Go module path escaping. + +## [0.18.0] - 2024-01-12 + +### Added + +- Config support. +- GitHub mirror support. + +### Changed + +- Use `github.com/sirupsen/logrus` for logging. +- Use `github.com/blang/semver/v4` for versioning. +- Refactor most of the code. + +### Removed + +- Remove `lip autoremove` command. +- Percentage progress bar. + +### Fixed + +- Some bugs. + +## [0.17.0] - 2023-12-30 + +### Added + +- Warning for deprecated tooth.json format versions. +- `info.source` field for indicating the source repo of a tooth. + +### Changed + +- Check tooth repo validity with Go official module library. + +### Fixed + +- Failed to uninstall a tooth when some of its files are already removed. + +## [0.16.1] - 2023-10-11 + +### Fixed + +- Unable to specify which version to install. + +## [0.16.0] - 2023-10-09 + +### Added + +- Prerequisite support. + +### Fixed + +- Failing to parse files under root directory of a tooth. + +## [0.15.2] - 2023-09-05 + +### Fixed + +- Inconsistent tooth.json schema with lipIndex. +- Wrong help messages. + +## [0.15.1] - 2023-09-01 + +### Fixed + +- Wrong help message for `--json` flag of `lip show` command. +- Wrong regex for validating versions. + +## [0.15.0] - 2023-06-23 + +### Added + +- Tooth file preservation and removal. + +### Changed + +- Optimize output information format. +- Change tooth.json format to version 2. +- Force to specify output path in `lip tooth pack` command. +- Refactor all code and optimize performance. + +### Removed + +- `lip exec` command. +- Tooth file possession (use "preserve" and "remove" instead). +- Number-only progress bar. +- Registry support. +- Placed files display in `lip show` command. + +## [0.14.2] - 2023-04-24 + +### Fixed + +- Remove misleading message when dependencies are satisfied. +- Packed tooth file not able to be installed. + +## [0.14.1] - 2023-04-20 + +### Fixed + +- Broken GOOS and GOARCH specifiers of placement. + +## [0.14.0] - 2023-03-24 + +### Added + +- `lip tooth pack` command to pack a tooth into a tooth file. +- Questions when initializing a tooth. + +### Fixed + +- Not properly parsing placement sometimes. +- Not aborting when a tooth fails to install. + +## [0.13.0] - 2023-03-05 + +### Added + +- Aliases for subcommands. + +### Changed + +- Tool should be registered in `tooth.json` manually now. + +## [0.12.0] - 2023-02-27 + +### Added + +- Dependency version validation when installing. +- `lip autoremove` command to remove teeth not required by other teeth. + +### Fixed + +- Failing to run tools with arguments. +- Dependencies still being installed when the dependent is not going to be installed. + +## [0.11.45141] - 2023-02-19 + +### Fixed + +- Wrongly showing debug information when redirecting to local lip. + +## [0.11.4514] - 2023-02-17 + +### Added + +- Showing information about installed teeth in `lip list` command. + +## [0.11.0] - 2023-02-17 + +### Added + +- `--available` flag for `lip show` command. +- `--numeric-progress` flag for `lip install` command. +- `--no-dependencies` flag for `lip install` command. +- `confirmation` field in `tooth.json` to show messages and ask for confirmation before installing. +- Check for invalid additional arguments. +- Structured information output. +- Support for multiple GOPROXYs. +- `--keep-possession` flag for `lip uninstall` command. +- Automatic deletion of empty directories when uninstalling a tooth. +- Support for file possession. + +### Fixed + +- Remove wrongly displayed debug information. +- Failing to re-download when a broken tooth file exists in cache. + +## [0.10.0] - 2023-02-12 + +### Added + +- Verbose and quiet mode. +- JSON output support for `lip list` and `lip show`. + +## [0.9.0] - 2023-02-11 + +### Added + +- HTTP Code reporting when failing to make a request. +- `lip list --upgradable` command. +- Topological sorting for dependencies. +- Progress bar for downloading tooth files. + +### Fixed + +- No notice when a tooth file is cached. +- Tooth paths in `dependencies` field of `tooth.json` not converting to lowercase. +- Mistakes in help message of `lip cache purge`. + +## [0.8.3] - 2023-02-09 + +### Fixed + +- Mistakes in path prefix extraction when there is only one file in the tooth. + +## [0.8.2] - 2023-02-09 + +### Fixed + +- Failing to input anything to post-install and pre-uninstall commands. +- Wrong installation order of dependencies. +- Registry not working in `lip show`. +- Unstable versions can be wrongly installed when no specific version is specified. + +## [0.8.1] - 2023-02-07 + +### Fixed + +- Failing to get information from registry with other index than index.json. + +## [0.8.0] - 2023-02-06 + +### Added + +- Registry support. The default registry is . + +### Fixed + +- Failing to uninstall teeth with uppercase letters in provided tooth path. + +## [0.7.1] - 2023-02-05 + +### Fixed + +- Failing to hot update or remove lip in local directory. + +## [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 + +- Support for on-demand installation depending on OS and platform. +- Removal for downloaded tooth files that do not pass the validation. + +## [0.5.1] - 2023-01-30 + +### Fixed + +- Failing to install any tool. + +## [0.5.0] - 2023-01-30 + +### Added + +- Available version list in `lip show` command. +- Redirection to local lip executable when running `lip`. +- Support for pre-uninstall scripts. +- Support for hot update of lip. +- Support for executing tools in `.lip/tools` directory. + +## [0.4.0] - 2023-01-26 + +### Added + +- Post-install script support. +- Tooth path validation. +- Flexible tooth.json parsing. + +## [0.3.4] - 2023-01-25 + +### Changed + +- Bumped github.com/fatih/color from 1.14.0 to 1.14.1. + +### Fixed + +- Misleading error hints. +- Failing to fetch tooth with major version v0 or v1. +- Failing to match dependencies. +- Failing to fetch tooth when uppercase letters exist in tooth path. + +## [0.3.3] - 2023-01-24 + +### Fixed + +- Default to earliest version when no version is specified in tooth.json. +- Panic when tooth.json is invalid. + +## [0.3.2] - 2023-01-23 + +### Added + +- "Add to PATH" option in setup utility. +- Mac OS, Linux and OpenBSD support. +- Arm64 support. + +## [0.3.1] - 2023-01-21 + +### Added + +- Setup utility to install lip. + +## [0.3.0] - 2023-01-20 + +### Added + +- Possession keeping support when force-reinstalling or upgrading. +- `--force-reinstall` flag and `--upgrade` flag support. + +## [0.2.1] - 2023-01-18 + +### Fixed + +- Failing to fetch tooth whose version has suffix `+incompatible`. +- Failing to parse wildcards. + +## [0.2.0] - 2023-01-18 + +### Added + +- Possession field in tooth.json to specify directory to remove when uninstalling a tooth. + +### Changed + +- Change extension name of tooth files to .tth + +### Fixed + +- Fix failing to fetch tooth when the repository does not contain go.mod file. +- Fix failing to parse tooth file when the tooth is downloaded via GOPROXY. +- Fix failing to parse tooth when tooth.json is the only file in the tooth. + +## [0.1.0] - 2023-01-17 + +### Added + +- Basic functions: cache, install, list, show, tooth init, and uninstall. + +[#129]: https://github.com/futrime/lip/issues/129 +[#134]: https://github.com/futrime/lip/issues/134 +[#140]: https://github.com/futrime/lip/issues/140 +[#157]: https://github.com/futrime/lip/issues/157 + +[0.24.0]: https://github.com/futrime/lip/compare/v0.23.2...v0.24.0 +[0.23.2]: https://github.com/futrime/lip/compare/v0.23.1...v0.23.2 +[0.23.1]: https://github.com/futrime/lip/compare/v0.23.0...v0.23.1 +[0.23.0]: https://github.com/futrime/lip/compare/v0.22.0...v0.23.0 +[0.22.0]: https://github.com/futrime/lip/compare/v0.21.2...v0.22.0 +[0.21.2]: https://github.com/futrime/lip/compare/v0.21.1...v0.21.2 +[0.21.1]: https://github.com/futrime/lip/compare/v0.21.0...v0.21.1 +[0.21.0]: https://github.com/futrime/lip/compare/v0.20.1...v0.21.0 +[0.20.1]: https://github.com/futrime/lip/compare/v0.20.0...v0.20.1 +[0.20.0]: https://github.com/futrime/lip/compare/v0.19.0...v0.20.0 +[0.19.0]: https://github.com/futrime/lip/compare/v0.18.1...v0.19.0 +[0.18.1]: https://github.com/futrime/lip/compare/v0.18.0...v0.18.1 +[0.18.0]: https://github.com/futrime/lip/compare/v0.17.0...v0.18.0 +[0.17.0]: https://github.com/futrime/lip/compare/v0.16.1...v0.17.0 +[0.16.1]: https://github.com/futrime/lip/compare/v0.16.0...v0.16.1 +[0.16.0]: https://github.com/futrime/lip/compare/v0.15.2...v0.16.0 +[0.15.2]: https://github.com/futrime/lip/compare/v0.15.1...v0.15.2 +[0.15.1]: https://github.com/futrime/lip/compare/v0.15.0...v0.15.1 +[0.15.0]: https://github.com/futrime/lip/compare/v0.14.2...v0.15.0 +[0.14.2]: https://github.com/futrime/lip/compare/v0.14.1...v0.14.2 +[0.14.1]: https://github.com/futrime/lip/compare/v0.14.0...v0.14.1 +[0.14.0]: https://github.com/futrime/lip/compare/v0.13.0...v0.14.0 +[0.13.0]: https://github.com/futrime/lip/compare/v0.12.0...v0.13.0 +[0.12.0]: https://github.com/futrime/lip/compare/v0.11.45141...v0.12.0 +[0.11.45141]: https://github.com/futrime/lip/compare/v0.11.4514...v0.11.45141 +[0.11.4514]: https://github.com/futrime/lip/compare/v0.11.0...v0.11.4514 +[0.11.0]: https://github.com/futrime/lip/compare/v0.10.0...v0.11.0 +[0.10.0]: https://github.com/futrime/lip/compare/v0.9.0...v0.10.0 +[0.9.0]: https://github.com/futrime/lip/compare/v0.8.3...v0.9.0 +[0.8.3]: https://github.com/futrime/lip/compare/v0.8.2...v0.8.3 +[0.8.2]: https://github.com/futrime/lip/compare/v0.8.1...v0.8.2 +[0.8.1]: https://github.com/futrime/lip/compare/v0.8.0...v0.8.1 +[0.8.0]: https://github.com/futrime/lip/compare/v0.7.1...v0.8.0 +[0.7.1]: https://github.com/futrime/lip/compare/v0.7.0...v0.7.1 +[0.7.0]: https://github.com/futrime/lip/compare/v0.6.0...v0.7.0 +[0.6.0]: https://github.com/futrime/lip/compare/v0.5.1...v0.6.0 +[0.5.1]: https://github.com/futrime/lip/compare/v0.5.0...v0.5.1 +[0.5.0]: https://github.com/futrime/lip/compare/v0.4.0...v0.5.0 +[0.4.0]: https://github.com/futrime/lip/compare/v0.3.4...v0.4.0 +[0.3.4]: https://github.com/futrime/lip/compare/v0.3.3...v0.3.4 +[0.3.3]: https://github.com/futrime/lip/compare/v0.3.2...v0.3.3 +[0.3.2]: https://github.com/futrime/lip/compare/v0.3.1...v0.3.2 +[0.3.1]: https://github.com/futrime/lip/compare/v0.3.0...v0.3.1 +[0.3.0]: https://github.com/futrime/lip/compare/v0.2.1...v0.3.0 +[0.2.1]: https://github.com/futrime/lip/compare/v0.2.0...v0.2.1 +[0.2.0]: https://github.com/futrime/lip/compare/v0.1.0...v0.2.0 +[0.1.0]: https://github.com/futrime/lip/releases/tag/v0.1.0 diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..fc10040 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# lip + +[![Latest Tag](https://img.shields.io/github/v/tag/futrime/lip?style=for-the-badge)](https://github.com/futrime/lip/releases/latest) +[![GitHub License](https://img.shields.io/github/license/futrime/lip?style=for-the-badge)](https://github.com/futrime/lip/blob/main/COPYING) +[![Downloads](https://img.shields.io/github/downloads/futrime/lip/latest/total?style=for-the-badge)](https://github.com/futrime/lip/releases/latest) +[![Build](https://img.shields.io/github/actions/workflow/status/futrime/lip/build.yml?style=for-the-badge)](https://github.com/futrime/lip/actions/workflows/build.yml) + +A general package installer + +**lip** is a general package installer. You can use **lip** to install packages from any Git repository. + +## Security + +This software package manager (hereinafter referred to as "this software") is developed and provided by LiteLDev (hereinafter referred to as "the developer"). This software is designed to help users manage and install various software packages, but is not responsible for any content, quality, functionality, security or legality of any software package. Users should use this software at their own discretion and assume all related risks. + +The developer does not guarantee the stability, reliability, accuracy or completeness of this software. The developer is not liable for any defects, errors, viruses or other harmful components that may exist in this software. The developer is not liable for any direct or indirect damages (including but not limited to data loss, device damage, profit loss etc.) caused by the use of this software. + +The developer reserves the right to modify, update or terminate this software and its related services at any time without prior notice to users. Users should back up important data and check regularly for updates of this software. + +Users should comply with relevant laws and regulations when using this software, respect the intellectual property rights and privacy rights of others, and not use this software for any illegal or infringing activities. If users violate the above provisions and cause any damage to any third party or are claimed by any third party, the developer does not bear any responsibility. + +If you have any questions or comments about this disclaimer, please contact the developer. + +## Install + +**lip** is a self-contained executable file, so you don't need to install it. Just download the latest version from . + +## Usage + +```shell +lip +``` + +Check out [the documentation](https://futrime.github.io/lip/) for more information. + +## Contributing + +Feel free to dive in! [Open an issue](https://github.com/futrime/lip/issues/new/choose) or submit PRs. + +**lip** follows the [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) Code of Conduct. + +### Contributors + +This project exists thanks to all the people who contribute. + +![Contributors](https://contrib.rocks/image?repo=futrime/lip) + +## License + +GPL-3.0-only © 2023-2025 Zijian Zhang From e71b421cfa5868d91349c59119d1a5332880a038 Mon Sep 17 00:00:00 2001 From: Zijian Zhang Date: Fri, 3 Jan 2025 17:37:01 +0800 Subject: [PATCH 05/63] docs: fix typo in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc10040..31d767f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A general package installer ## Security -This software package manager (hereinafter referred to as "this software") is developed and provided by LiteLDev (hereinafter referred to as "the developer"). This software is designed to help users manage and install various software packages, but is not responsible for any content, quality, functionality, security or legality of any software package. Users should use this software at their own discretion and assume all related risks. +This software package manager (hereinafter referred to as "this software") is developed and provided by Zijian Zhang (hereinafter referred to as "the developer"). This software is designed to help users manage and install various software packages, but is not responsible for any content, quality, functionality, security or legality of any software package. Users should use this software at their own discretion and assume all related risks. The developer does not guarantee the stability, reliability, accuracy or completeness of this software. The developer is not liable for any defects, errors, viruses or other harmful components that may exist in this software. The developer is not liable for any direct or indirect damages (including but not limited to data loss, device damage, profit loss etc.) caused by the use of this software. From f143f53f9f2e3a534517a2514f0018935668e74f Mon Sep 17 00:00:00 2001 From: Zijian Zhang Date: Fri, 3 Jan 2025 17:51:52 +0800 Subject: [PATCH 06/63] refactor: initialize as a C# project --- .github/workflows/build.yml | 75 +++++++++++++++++++++++ .github/workflows/release.yml | 112 ++++++++++++++++++++++++++++++++++ Lip.Tests/.gitattributes | 8 +++ Lip.Tests/.gitignore | 37 +++++++++++ Lip.Tests/Lip.Tests.csproj | 27 ++++++++ Lip.Tests/UnitTest1.cs | 10 +++ Lip/.gitattributes | 8 +++ Lip/.gitignore | 37 +++++++++++ Lip/Lip.csproj | 10 +++ Lip/Program.cs | 2 + 10 files changed, 326 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/release.yml create mode 100644 Lip.Tests/.gitattributes create mode 100644 Lip.Tests/.gitignore create mode 100644 Lip.Tests/Lip.Tests.csproj create mode 100644 Lip.Tests/UnitTest1.cs create mode 100644 Lip/.gitattributes create mode 100644 Lip/.gitignore create mode 100644 Lip/Lip.csproj create mode 100644 Lip/Program.cs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..f8ef78e --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,75 @@ +on: + push: + pull_request: + workflow_dispatch: + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - runs-on: macos-latest + runtime: osx-arm64 + - runs-on: macos-latest + runtime: osx-x64 + - runs-on: ubuntu-latest + runtime: linux-arm64 + - runs-on: ubuntu-latest + runtime: linux-x64 + - runs-on: windows-latest + runtime: win-arm64 + - runs-on: windows-latest + runtime: win-x64 + runs-on: ${{ matrix.runs-on }} + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + + - run: | + dotnet publish Lip/Lip.csproj \ + -c Release \ + -o bin \ + -r ${{ matrix.runtime }} \ + -p:OptimizationPreference=Size \ + -p:PublishAot=true + + - uses: actions/upload-artifact@v4 + with: + name: ${{ github.event.repository.name }}-${{ matrix.runtime }}-${{ github.sha }} + path: bin/ + + test: + strategy: + fail-fast: false + matrix: + runs-on: + - macos-latest + - ubuntu-latest + - windows-latest + runs-on: ${{ matrix.runs-on }} + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + + - run: | + dotnet test Lip.Tests/Lip.Tests.csproj + + check-style: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + + - run: | + dotnet format --verify-no-changes Lip + dotnet format --verify-no-changes Lip.Tests diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..035559c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,112 @@ +on: + release: + types: + - published + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - runs-on: macos-latest + runtime: osx-arm64 + - runs-on: macos-latest + runtime: osx-x64 + - runs-on: ubuntu-latest + runtime: linux-arm64 + - runs-on: ubuntu-latest + runtime: linux-x64 + - runs-on: windows-latest + runtime: win-arm64 + - runs-on: windows-latest + runtime: win-x64 + runs-on: ${{ matrix.runs-on }} + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + + - run: | + dotnet publish Lip/Lip.csproj \ + -c Release \ + -o bin \ + -r ${{ matrix.runtime }} \ + -p:OptimizationPreference=Size \ + -p:PublishAot=true \ + -p:StripSymbols=true + + - uses: actions/upload-artifact@v4 + with: + name: ${{ github.event.repository.name }}-${{ matrix.runtime }}-${{ github.sha }} + path: bin/ + + update-release-notes: + permissions: + contents: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - id: extract-release-notes + uses: ffurrer2/extract-release-notes@v2 + + - uses: softprops/action-gh-release@v2 + with: + body: ${{ steps.extract-release-notes.outputs.release_notes }} + + upload-to-release: + needs: + - build + - update-release-notes + permissions: + contents: write + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + runtime: + - linux-arm64 + - linux-x64 + - osx-arm64 + - osx-x64 + - win-arm64 + - win-x64 + steps: + - uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4 + with: + name: ${{ github.event.repository.name }}-${{ matrix.runtime }}-${{ github.sha }} + path: artifact/ + + - run: | + cp CHANGELOG.md LICENSE README.md artifact/ + + - name: Pack artifact (Windows) + if: startsWith(matrix.runtime, 'win') == true + run: | + zip -r ../${{ github.event.repository.name }}-${{ matrix.runtime }}.zip * + working-directory: artifact + + - name: Pack artifact (Others) + if: startsWith(matrix.runtime, 'win') == false + run: | + tar -czvf ../${{ github.event.repository.name }}-${{ matrix.runtime }}.tar.gz * + working-directory: artifact + + - name: Upload artifact to release (Windows) + if: startsWith(matrix.runtime, 'win') == true + uses: softprops/action-gh-release@v2 + with: + files: | + ${{ github.event.repository.name }}-${{ matrix.runtime }}.zip + + - name: Upload artifact to release (Others) + if: startsWith(matrix.runtime, 'win') == false + uses: softprops/action-gh-release@v2 + with: + files: | + ${{ github.event.repository.name }}-${{ matrix.runtime }}.tar.gz diff --git a/Lip.Tests/.gitattributes b/Lip.Tests/.gitattributes new file mode 100644 index 0000000..f7c7529 --- /dev/null +++ b/Lip.Tests/.gitattributes @@ -0,0 +1,8 @@ +# Auto detect text files and perform LF normalization +* text=auto + +*.cs text diff=csharp +*.cshtml text diff=html +*.csx text diff=csharp +*.sln text eol=crlf +*.csproj text eol=crlf diff --git a/Lip.Tests/.gitignore b/Lip.Tests/.gitignore new file mode 100644 index 0000000..a437a65 --- /dev/null +++ b/Lip.Tests/.gitignore @@ -0,0 +1,37 @@ +*.swp +*.*~ +project.lock.json +.DS_Store +*.pyc +nupkg/ + +# Visual Studio Code +.vscode + +# Rider +.idea + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +msbuild.log +msbuild.err +msbuild.wrn + +# Visual Studio 2015 +.vs/ diff --git a/Lip.Tests/Lip.Tests.csproj b/Lip.Tests/Lip.Tests.csproj new file mode 100644 index 0000000..ad5e158 --- /dev/null +++ b/Lip.Tests/Lip.Tests.csproj @@ -0,0 +1,27 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + diff --git a/Lip.Tests/UnitTest1.cs b/Lip.Tests/UnitTest1.cs new file mode 100644 index 0000000..b3e8f74 --- /dev/null +++ b/Lip.Tests/UnitTest1.cs @@ -0,0 +1,10 @@ +namespace Lip.Tests; + +public class UnitTest1 +{ + [Fact] + public void Test1() + { + + } +} \ No newline at end of file diff --git a/Lip/.gitattributes b/Lip/.gitattributes new file mode 100644 index 0000000..f7c7529 --- /dev/null +++ b/Lip/.gitattributes @@ -0,0 +1,8 @@ +# Auto detect text files and perform LF normalization +* text=auto + +*.cs text diff=csharp +*.cshtml text diff=html +*.csx text diff=csharp +*.sln text eol=crlf +*.csproj text eol=crlf diff --git a/Lip/.gitignore b/Lip/.gitignore new file mode 100644 index 0000000..a437a65 --- /dev/null +++ b/Lip/.gitignore @@ -0,0 +1,37 @@ +*.swp +*.*~ +project.lock.json +.DS_Store +*.pyc +nupkg/ + +# Visual Studio Code +.vscode + +# Rider +.idea + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +msbuild.log +msbuild.err +msbuild.wrn + +# Visual Studio 2015 +.vs/ diff --git a/Lip/Lip.csproj b/Lip/Lip.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/Lip/Lip.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/Lip/Program.cs b/Lip/Program.cs new file mode 100644 index 0000000..3751555 --- /dev/null +++ b/Lip/Program.cs @@ -0,0 +1,2 @@ +// See https://aka.ms/new-console-template for more information +Console.WriteLine("Hello, World!"); From bbd66995a891833b85323eedaee5e640197ee810 Mon Sep 17 00:00:00 2001 From: Zijian Zhang Date: Fri, 3 Jan 2025 17:57:59 +0800 Subject: [PATCH 07/63] ci: use YAML folded style for line splitting --- .github/workflows/build.yml | 12 ++++++------ .github/workflows/release.yml | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f8ef78e..fb1fbf9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,12 +29,12 @@ jobs: with: dotnet-version: 8.0.x - - run: | - dotnet publish Lip/Lip.csproj \ - -c Release \ - -o bin \ - -r ${{ matrix.runtime }} \ - -p:OptimizationPreference=Size \ + - run: > + dotnet publish Lip/Lip.csproj + -c Release + -o bin + -r ${{ matrix.runtime }} + -p:OptimizationPreference=Size -p:PublishAot=true - uses: actions/upload-artifact@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 035559c..075e12b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,13 +29,13 @@ jobs: with: dotnet-version: 8.0.x - - run: | - dotnet publish Lip/Lip.csproj \ - -c Release \ - -o bin \ - -r ${{ matrix.runtime }} \ - -p:OptimizationPreference=Size \ - -p:PublishAot=true \ + - run: > + dotnet publish Lip/Lip.csproj + -c Release + -o bin + -r ${{ matrix.runtime }} + -p:OptimizationPreference=Size + -p:PublishAot=true -p:StripSymbols=true - uses: actions/upload-artifact@v4 From d9c100ea60676352b74c04e2e7e3ee656354ea63 Mon Sep 17 00:00:00 2001 From: Zijian Zhang Date: Fri, 3 Jan 2025 18:07:49 +0800 Subject: [PATCH 08/63] ci: still try to fix --- .github/workflows/build.yml | 47 ++++++++++++++++++++++------------- .github/workflows/release.yml | 36 +++++++++++++-------------- 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fb1fbf9..0343e24 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,18 +9,18 @@ jobs: fail-fast: false matrix: include: - - runs-on: macos-latest - runtime: osx-arm64 - - runs-on: macos-latest - runtime: osx-x64 - - runs-on: ubuntu-latest - runtime: linux-arm64 - - runs-on: ubuntu-latest - runtime: linux-x64 - - runs-on: windows-latest - runtime: win-arm64 - - runs-on: windows-latest - runtime: win-x64 + - runtime: osx-arm64 + runs-on: macos-latest + - runtime: osx-x64 + runs-on: macos-latest + - runtime: linux-arm64 + runs-on: ubuntu-latest + - runtime: linux-x64 + runs-on: ubuntu-latest + - runtime: win-arm64 + runs-on: windows-latest + - runtime: win-x64 + runs-on: windows-latest runs-on: ${{ matrix.runs-on }} steps: - uses: actions/checkout@v4 @@ -29,13 +29,26 @@ jobs: with: dotnet-version: 8.0.x + - if: matrix.runtime == 'linux-arm64' + run: | + sudo dpkg --add-architecture arm64 + sudo bash -c 'cat > /etc/apt/sources.list.d/arm64.list < dotnet publish Lip/Lip.csproj - -c Release - -o bin - -r ${{ matrix.runtime }} - -p:OptimizationPreference=Size - -p:PublishAot=true + -c Release + -o bin + -r ${{ matrix.runtime }} + -p:OptimizationPreference=Size + -p:PublishAot=true - uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 075e12b..749b052 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,18 +9,18 @@ jobs: fail-fast: false matrix: include: - - runs-on: macos-latest - runtime: osx-arm64 - - runs-on: macos-latest - runtime: osx-x64 - - runs-on: ubuntu-latest - runtime: linux-arm64 - - runs-on: ubuntu-latest - runtime: linux-x64 - - runs-on: windows-latest - runtime: win-arm64 - - runs-on: windows-latest - runtime: win-x64 + - runtime: linux-arm64 + runs-on: ubuntu-latest + - runtime: linux-x64 + runs-on: ubuntu-latest + - runtime: osx-arm64 + runs-on: macos-latest + - runtime: osx-x64 + runs-on: macos-latest + - runtime: win-arm64 + runs-on: windows-latest + - runtime: win-x64 + runs-on: windows-latest runs-on: ${{ matrix.runs-on }} steps: - uses: actions/checkout@v4 @@ -31,12 +31,12 @@ jobs: - run: > dotnet publish Lip/Lip.csproj - -c Release - -o bin - -r ${{ matrix.runtime }} - -p:OptimizationPreference=Size - -p:PublishAot=true - -p:StripSymbols=true + -c Release + -o bin + -r ${{ matrix.runtime }} + -p:OptimizationPreference=Size + -p:PublishAot=true + -p:StripSymbols=true - uses: actions/upload-artifact@v4 with: From 31e6493afcbd01264b776a8ce336227959b29a47 Mon Sep 17 00:00:00 2001 From: Zijian Zhang Date: Fri, 3 Jan 2025 18:12:47 +0800 Subject: [PATCH 09/63] ci: still try to fix --- .github/workflows/build.yml | 14 +++++++------- .github/workflows/release.yml | 15 ++++++++++++++- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0343e24..675c786 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,14 +9,14 @@ jobs: fail-fast: false matrix: include: + - runtime: linux-arm64 + runs-on: ubuntu-24.04 + - runtime: linux-x64 + runs-on: ubuntu-latest - runtime: osx-arm64 runs-on: macos-latest - runtime: osx-x64 runs-on: macos-latest - - runtime: linux-arm64 - runs-on: ubuntu-latest - - runtime: linux-x64 - runs-on: ubuntu-latest - runtime: win-arm64 runs-on: windows-latest - runtime: win-x64 @@ -33,9 +33,9 @@ jobs: run: | sudo dpkg --add-architecture arm64 sudo bash -c 'cat > /etc/apt/sources.list.d/arm64.list < /etc/apt/sources.list.d/arm64.list < dotnet publish Lip/Lip.csproj -c Release From 3098ffaf5257ca22a340bb6ccd03c1df2da1e4f5 Mon Sep 17 00:00:00 2001 From: Zijian Zhang Date: Fri, 3 Jan 2025 18:13:32 +0800 Subject: [PATCH 10/63] ci: do not strip symbols in releases --- .github/workflows/release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 955c989..a5e118d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,7 +49,6 @@ jobs: -r ${{ matrix.runtime }} -p:OptimizationPreference=Size -p:PublishAot=true - -p:StripSymbols=true - uses: actions/upload-artifact@v4 with: From 5d2649de0b188b9f88ff57f8714abc872407d93f Mon Sep 17 00:00:00 2001 From: Zijian Zhang Date: Fri, 3 Jan 2025 18:15:08 +0800 Subject: [PATCH 11/63] ci: try to fix --- .github/workflows/build.yml | 8 ++++---- .github/workflows/release.yml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 675c786..d9f2479 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ jobs: matrix: include: - runtime: linux-arm64 - runs-on: ubuntu-24.04 + runs-on: ubuntu-22.04 - runtime: linux-x64 runs-on: ubuntu-latest - runtime: osx-arm64 @@ -33,9 +33,9 @@ jobs: run: | sudo dpkg --add-architecture arm64 sudo bash -c 'cat > /etc/apt/sources.list.d/arm64.list < /etc/apt/sources.list.d/arm64.list < Date: Fri, 3 Jan 2025 18:18:26 +0800 Subject: [PATCH 12/63] ci: set assembly name to lip --- .github/workflows/build.yml | 1 + .github/workflows/release.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d9f2479..351f227 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,6 +47,7 @@ jobs: -c Release -o bin -r ${{ matrix.runtime }} + -p:AssemblyName=lip -p:OptimizationPreference=Size -p:PublishAot=true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d51dcbc..c423b7b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,6 +47,7 @@ jobs: -c Release -o bin -r ${{ matrix.runtime }} + -p:AssemblyName=lip -p:OptimizationPreference=Size -p:PublishAot=true From 03d48e273f95535b5b6bd023e8a31fc75faeaa6b Mon Sep 17 00:00:00 2001 From: Zijian Zhang Date: Fri, 3 Jan 2025 18:20:21 +0800 Subject: [PATCH 13/63] Revert "ci: set assembly name to lip" This reverts commit 576c8c519c5126a09aedc75a14a1905a9f5c0e8c. --- .github/workflows/build.yml | 1 - .github/workflows/release.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 351f227..d9f2479 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,7 +47,6 @@ jobs: -c Release -o bin -r ${{ matrix.runtime }} - -p:AssemblyName=lip -p:OptimizationPreference=Size -p:PublishAot=true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c423b7b..d51dcbc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,7 +47,6 @@ jobs: -c Release -o bin -r ${{ matrix.runtime }} - -p:AssemblyName=lip -p:OptimizationPreference=Size -p:PublishAot=true From 18c89f39f4733fb7ce0ad06d97ce769a8d1dd4ca Mon Sep 17 00:00:00 2001 From: Zijian Zhang Date: Fri, 3 Jan 2025 18:20:54 +0800 Subject: [PATCH 14/63] ci: set assembly name to lip --- Lip/Lip.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Lip/Lip.csproj b/Lip/Lip.csproj index 2150e37..1ae2c6a 100644 --- a/Lip/Lip.csproj +++ b/Lip/Lip.csproj @@ -5,6 +5,7 @@ net8.0 enable enable + lip From c45ef1763e8d4b51361f12fbfb0a47f5190b3bd8 Mon Sep 17 00:00:00 2001 From: Zijian Zhang Date: Fri, 3 Jan 2025 21:00:23 +0800 Subject: [PATCH 15/63] docs: add documentation --- .github/workflows/build-docs.yml | 41 ++++++++++++++ .github/workflows/build.yml | 10 +++- .gitignore | 3 ++ docs/img/favicon.ico | Bin 0 -> 4286 bytes docs/img/logo.webp | Bin 0 -> 118 bytes docs/mkdocs.yml | 83 +++++++++++++++++++++++++++++ docs/packaging-guide/index.md | 2 + docs/requirements.txt | 30 +++++++++++ docs/user-guide/basic-use.md | 1 + docs/user-guide/getting-started.md | 1 + docs/user-guide/index.md | 10 ++++ docs/user-guide/installation.md | 0 12 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/build-docs.yml create mode 100644 docs/img/favicon.ico create mode 100644 docs/img/logo.webp create mode 100644 docs/mkdocs.yml create mode 100644 docs/packaging-guide/index.md create mode 100644 docs/requirements.txt create mode 100644 docs/user-guide/basic-use.md create mode 100644 docs/user-guide/getting-started.md create mode 100644 docs/user-guide/index.md create mode 100644 docs/user-guide/installation.md diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml new file mode 100644 index 0000000..1d3e4d9 --- /dev/null +++ b/.github/workflows/build-docs.yml @@ -0,0 +1,41 @@ +on: + pull_request: + paths: + - .github/workflows/build-docs.yml + - docs/** + push: + paths: + - .github/workflows/build-docs.yml + - docs/** + workflow_dispatch: + +jobs: + build-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - run: | + pip install -r requirements.txt + working-directory: docs + + - run: | + mkdocs build -f docs/mkdocs.yml -d ../site + + - uses: actions/upload-pages-artifact@v3 + with: + path: site/ + + deploy: + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + needs: build-docs + permissions: + id-token: write + pages: write + runs-on: ubuntu-latest + steps: + - uses: actions/deploy-pages@v4 + id: deployment diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d9f2479..d947723 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,14 @@ on: - push: pull_request: + paths: + - .github/workflows/build.yml + - Lip/** + - Lip.Tests/** + push: + paths: + - .github/workflows/build.yml + - Lip/** + - Lip.Tests/** workflow_dispatch: jobs: diff --git a/.gitignore b/.gitignore index 1d74e21..9f8ed82 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ .vscode/ + +/.venv/ +/site/ diff --git a/docs/img/favicon.ico b/docs/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b60da2f7673208d2f7fecc8a09b3eecdcbe6a7eb GIT binary patch literal 4286 zcmeH~?@Gc@7{-qv^g$B?UCrt8PpDuSG!PdCelVP6kOUD43Ee|bP*``1DG9(0~o5v0R0*fd-`O#|wu7=wK2r9#NuvLWzpuY?taL^i|X()rXXE zE`XjgIuoVYr{euH74DwqsACq(mZkfUZuA7u(hbYrZC%ew4{!70@P&5jE%rjX559gB z`l@1HnW({0+qYCg4XzIr_>hASI{HDE*ALK#4;Fmr`aZn6J|t_Wv|T!O&>trBrS-#K z9|q3e*$rj8Bg%A!bJXDZAbDK`K3KM5_Gj6P$GK*WC|NVHF4ox(JymI5n&=7FhxPZv z>U9zJL)Z_?&&jYK!hQ(*f%nY~-7mr%<>x2^`2o4w%i7X%b&wyt9Cg5X5}7tpHVFC? z=<^vvqQ1LtFXhRB!?~KXJ#?k(FaB8IgJmPzPmv#|(3kFipug~;m*MjTpK+kSwsYO0 oj&NS>0AYNMefBXkTOQvLBIZYoUFaP*ng4%*e+AHo4<>K?0zF=r)&Kwi literal 0 HcmV?d00001 diff --git a/docs/img/logo.webp b/docs/img/logo.webp new file mode 100644 index 0000000000000000000000000000000000000000..369ab0cc09c315be28d88f23d72e90987eaab195 GIT binary patch literal 118 zcmV-+0Ez!nNk&F)00012MM6+kP&iCs0000lpTH*&C!ip00~SukpO}VBX<|M~0t7%2 zRpvhc{#mG$peT~mXf0p?3s{JMQyj0nI3xORHfQXwtN(V?LKJ|? Date: Fri, 3 Jan 2025 23:20:29 +0800 Subject: [PATCH 16/63] docs: finish command design --- .github/workflows/build-docs.yml | 2 +- docs/{user-guide => }/index.md | 1 - docs/packaging-guide/index.md | 2 - docs/schemas/tooth.v1.schema.json | 99 ++++++++++ docs/schemas/tooth.v2.schema.json | 229 ++++++++++++++++++++++ docs/schemas/tooth.v3.schema.json | 151 ++++++++++++++ docs/user-guide/commands/lip-cache.md | 43 ++++ docs/user-guide/commands/lip-config.md | 68 +++++++ docs/user-guide/commands/lip-init.md | 67 +++++++ docs/user-guide/commands/lip-install.md | 61 ++++++ docs/user-guide/commands/lip-list.md | 11 ++ docs/user-guide/commands/lip-pack.md | 27 +++ docs/user-guide/commands/lip-prune.md | 21 ++ docs/user-guide/commands/lip-run.md | 17 ++ docs/user-guide/commands/lip-uninstall.md | 17 ++ docs/user-guide/commands/lip-update.md | 34 ++++ docs/user-guide/commands/lip-view.md | 11 ++ docs/user-guide/commands/lip.md | 33 ++++ docs/user-guide/faq.md | 2 + docs/user-guide/installation.md | 1 + docs/user-guide/tooth-json.md | 1 + docs/mkdocs.yml => mkdocs.yml | 40 ++-- 22 files changed, 919 insertions(+), 19 deletions(-) rename docs/{user-guide => }/index.md (90%) delete mode 100644 docs/packaging-guide/index.md create mode 100644 docs/schemas/tooth.v1.schema.json create mode 100644 docs/schemas/tooth.v2.schema.json create mode 100644 docs/schemas/tooth.v3.schema.json create mode 100644 docs/user-guide/commands/lip-cache.md create mode 100644 docs/user-guide/commands/lip-config.md create mode 100644 docs/user-guide/commands/lip-init.md create mode 100644 docs/user-guide/commands/lip-install.md create mode 100644 docs/user-guide/commands/lip-list.md create mode 100644 docs/user-guide/commands/lip-pack.md create mode 100644 docs/user-guide/commands/lip-prune.md create mode 100644 docs/user-guide/commands/lip-run.md create mode 100644 docs/user-guide/commands/lip-uninstall.md create mode 100644 docs/user-guide/commands/lip-update.md create mode 100644 docs/user-guide/commands/lip-view.md create mode 100644 docs/user-guide/commands/lip.md create mode 100644 docs/user-guide/faq.md create mode 100644 docs/user-guide/tooth-json.md rename docs/mkdocs.yml => mkdocs.yml (60%) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 1d3e4d9..4eabcf6 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -20,7 +20,7 @@ jobs: working-directory: docs - run: | - mkdocs build -f docs/mkdocs.yml -d ../site + mkdocs build - uses: actions/upload-pages-artifact@v3 with: diff --git a/docs/user-guide/index.md b/docs/index.md similarity index 90% rename from docs/user-guide/index.md rename to docs/index.md index 125801d..e62d589 100644 --- a/docs/user-guide/index.md +++ b/docs/index.md @@ -5,6 +5,5 @@ lip is the general package installer that lets you install packages from any Git If you want to learn about how to use lip, check out the following resources: - [Getting Started](getting-started.md) -- [Packaging Guide](../packaging-guide/index.md) If you find bugs, need help, or want to talk to the developers, please report them on the [GitHub Issues](https://github.com/futrime/lip/issues) page. diff --git a/docs/packaging-guide/index.md b/docs/packaging-guide/index.md deleted file mode 100644 index 06800c0..0000000 --- a/docs/packaging-guide/index.md +++ /dev/null @@ -1,2 +0,0 @@ -# About Packaging - diff --git a/docs/schemas/tooth.v1.schema.json b/docs/schemas/tooth.v1.schema.json new file mode 100644 index 0000000..da4d5e0 --- /dev/null +++ b/docs/schemas/tooth.v1.schema.json @@ -0,0 +1,99 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "format_version", + "tooth", + "version" + ], + "properties": { + "format_version": { + "const": 1 + }, + "tooth": { + "type": "string" + }, + "version": { + "type": "string" + }, + "dependencies": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "array", + "minItems": 1, + "items": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "information": { + "type": "object" + }, + "placement": { + "type": "array", + "items": { + "type": "object", + "required": [ + "source", + "destination" + ], + "properties": { + "source": { + "type": "string" + }, + "destination": { + "type": "string" + }, + "GOOS": { + "type": "string" + }, + "GOARCH": { + "type": "string" + } + } + } + }, + "possession": { + "type": "array", + "items": { + "type": "string" + } + }, + "commands": { + "type": "array", + "items": { + "type": "object", + "required": [ + "type", + "commands", + "GOOS" + ], + "properties": { + "type": { + "enum": [ + "install", + "uninstall" + ] + }, + "commands": { + "type": "array", + "items": { + "type": "string" + } + }, + "GOOS": { + "type": "string" + }, + "GOARCH": { + "type": "string" + } + } + } + } + } +} diff --git a/docs/schemas/tooth.v2.schema.json b/docs/schemas/tooth.v2.schema.json new file mode 100644 index 0000000..567449b --- /dev/null +++ b/docs/schemas/tooth.v2.schema.json @@ -0,0 +1,229 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "format_version": { + "type": "integer", + "const": 2 + }, + "tooth": { + "type": "string" + }, + "version": { + "type": "string" + }, + "info": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9-]+(:[a-z0-9-]+)?$" + } + }, + "avatar_url": { + "type": "string" + } + }, + "required": [ + "name", + "description", + "author", + "tags" + ] + }, + "asset_url": { + "type": "string" + }, + "commands": { + "type": "object", + "properties": { + "pre_install": { + "type": "array", + "items": { + "type": "string" + } + }, + "post_install": { + "type": "array", + "items": { + "type": "string" + } + }, + "pre_uninstall": { + "type": "array", + "items": { + "type": "string" + } + }, + "post_uninstall": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "dependencies": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "string" + } + } + }, + "prerequisites": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "string" + } + } + }, + "files": { + "type": "object", + "properties": { + "place": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "type": "string" + }, + "dest": { + "type": "string" + } + }, + "required": [ + "src", + "dest" + ] + } + }, + "preserve": { + "type": "array", + "items": { + "type": "string" + } + }, + "remove": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "platforms": { + "type": "array", + "items": { + "type": "object", + "properties": { + "goarch": { + "type": "string" + }, + "goos": { + "type": "string" + }, + "asset_url": { + "type": "string" + }, + "commands": { + "type": "object", + "properties": { + "pre_install": { + "type": "array", + "items": { + "type": "string" + } + }, + "post_install": { + "type": "array", + "items": { + "type": "string" + } + }, + "pre_uninstall": { + "type": "array", + "items": { + "type": "string" + } + }, + "post_uninstall": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "dependencies": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "string" + } + } + }, + "prerequisites": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "string" + } + } + }, + "files": { + "type": "object", + "properties": { + "place": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "type": "string" + }, + "dest": { + "type": "string" + } + }, + "required": [ + "src", + "dest" + ] + } + }, + "preserve": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "required": [ + "goos" + ] + } + } + }, + "required": [ + "format_version", + "tooth", + "version", + "info" + ] +} diff --git a/docs/schemas/tooth.v3.schema.json b/docs/schemas/tooth.v3.schema.json new file mode 100644 index 0000000..363dc9d --- /dev/null +++ b/docs/schemas/tooth.v3.schema.json @@ -0,0 +1,151 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "format_version": { + "type": "integer", + "const": 3 + }, + "tooth": { + "type": "string" + }, + "version": { + "type": "string" + }, + "info": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9-]+(:[a-z0-9-]+)?$" + } + }, + "avatar_url": { + "type": "string" + } + } + } + }, + "patternProperties": { + "^([a-z0-9]+|\\*)\\/([a-z0-9]+|\\*)$": { + "type": "object", + "properties": { + "commands": { + "type": "object", + "properties": { + "pre_install": { + "type": "array", + "items": { + "type": "string" + } + }, + "post_install": { + "type": "array", + "items": { + "type": "string" + } + }, + "pre_uninstall": { + "type": "array", + "items": { + "type": "string" + } + }, + "post_uninstall": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "dependencies": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "string" + } + } + }, + "prerequisites": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "string" + } + } + }, + "assets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "download_urls": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "string", + "enum": [ + "uncompressed", + "tar", + "tar.gz", + "zip" + ] + }, + "place": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "type": "string" + }, + "dest": { + "type": "string" + } + }, + "required": [ + "src", + "dest" + ] + } + }, + "preserve": { + "type": "array", + "items": { + "type": "string" + } + }, + "remove": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "download_urls", + "type" + ] + } + } + } + } + }, + "required": [ + "format_version", + "tooth", + "version" + ] +} diff --git a/docs/user-guide/commands/lip-cache.md b/docs/user-guide/commands/lip-cache.md new file mode 100644 index 0000000..892d6ec --- /dev/null +++ b/docs/user-guide/commands/lip-cache.md @@ -0,0 +1,43 @@ +# lip cache + +## Usage + +```shell +lip cache add +lip cache clean +lip cache list +``` + +## Description + +Inspect and manage lip’s cache. + +lip stores cache data in `%LocalAppData%\lip-cache` for Windows and `~/.cache/lip` for POSIX-like systems by default. This can be configured by configuration item `cache.dir`. + +## Sub-commands + +### add + +```shell +lip cache add +``` + +Add a package to the cache. `` is a [package specifier](#), e.g. `github.com/futrime/example-package#subpack@1.0.0`. + +If the configuration item `download.goproxy` is set, lip will download the package via Goproxy. Otherwise, lip will download the package directly from the Git repository. + +### clean + +```shell +lip cache clean +``` + +Remove all items from the cache. + +### list + +```shell +lip cache list +``` + +List items in the cache. diff --git a/docs/user-guide/commands/lip-config.md b/docs/user-guide/commands/lip-config.md new file mode 100644 index 0000000..b703d45 --- /dev/null +++ b/docs/user-guide/commands/lip-config.md @@ -0,0 +1,68 @@ +# lip config + +## Usage + +```shell +lip config set = [= ...] +lip config get [ [ ...]] +lip config delete [ ...] +lip config list +lip config edit +lip config fix +``` + +## Description + +Manage the lip configuration files. + +lip stores configuration files in `~/.liprc`. + +## Sub-commands + +### set + +```shell +lip config set = [= ...] +``` + +Set a configuration value. `` is the configuration key, e.g. `cache.dir`. `` is the configuration value, e.g. `~/.cache/lip`. + +### get + +```shell +lip config get [ [ ...]] +``` + +Get a configuration value. `` is the configuration key, e.g. `cache.dir`. If no `` is specified, list all configuration values. + +### delete + +```shell +lip config delete [ ...] +``` + +Delete a configuration value. `` is the configuration key, e.g. `cache.dir`. + +### list + +```shell +lip config list +``` + +List all configuration values. + +### edit + +```shell +lip config edit +``` + +Edit the configuration file. This will open the configuration file in the default editor, which is determined by the `EDITOR` or `VISUAL` environment variable, or `%SYSTEMROOT%\notepad.exe` on Windows, or `vi` on Unix-like systems. + +### fix + +```shell +lip config fix +``` + +Attempt to fix the configuration file. This means to remove invalid configuration items. diff --git a/docs/user-guide/commands/lip-init.md b/docs/user-guide/commands/lip-init.md new file mode 100644 index 0000000..53478d8 --- /dev/null +++ b/docs/user-guide/commands/lip-init.md @@ -0,0 +1,67 @@ +# lip init + +## Usage + +```shell +lip init [] +``` + +## Description + +Set up a new package in the current directory. + +Initialize and writes a new `tooth.json` file in the current directory. The `tooth.json` file must not already exist. + +If a [package specifier](#) (``) is specified, the package will be initialized with the specified package. + +## Options + +- `-f, --force` + + Overwrite the existing `tooth.json` file. + +- `--init-author ` + + The author to use. + + If not specified, lip will ask for an author. + +- `--init-avatar-url ` + + The avatar URL to use. + + If not specified, lip will ask for an avatar URL. + +- `--init-description ` + + The description to use. + + If not specified, lip will ask for a description. + +- `--init-name ` + + The name to use. + + If not specified, lip will ask for a name. + +- `--init-tags ` + + The tags to use, separated by commas. + + If not specified, lip will ask for tags. + +- `--init-tooth ` + + The tooth identifier to use. + + If not specified, lip will ask for a tooth identifier. + +- `--init-version ` + + The version to use. + + If not specified, lip will ask for a version. + +- `-y, --yes` + + Skip confirmation. diff --git a/docs/user-guide/commands/lip-install.md b/docs/user-guide/commands/lip-install.md new file mode 100644 index 0000000..730fc5a --- /dev/null +++ b/docs/user-guide/commands/lip-install.md @@ -0,0 +1,61 @@ +# lip install + +## Usage + +```shell +lip install [ ...] +``` + +## Description + +Install packages and their dependencies from various sources. + +A `` can be any of the following (in order of priority): + +- A directory containing a `tooth.json` file +- An archive (`.zip`, `.tar`, `.tgz` or `.tar.gz`) containing a directory with a `tooth.json` file +- A [package specifier](#) referencing a Git repository + +If no `` is specified and the current directory contains a `tooth.json` file, lip will install the package in the current directory. Be aware that this may cause file conflicts. + +When using a package specifier, lip will use Goproxy to download the package if the `download.goproxy` configuration is enabled. Otherwise, packages are downloaded directly from their Git repositories. + +Package prerequisites must be installed manually. lip will not proceed with installation if it detects missing prerequisites. + +When resolving dependencies, lip follows these rules: +- Selects the latest stable version that meets the specified constraints +- Falls back to the latest pre-release version if no stable version is available +- Installs dependencies in topological order (dependencies before dependents) +- Rejects installation if it detects circular dependencies + +lip maintains a dependency graph to track relationships between packages. When uninstalling packages, lip checks this graph to ensure all dependent packages are handled appropriately. If dependents are found, you'll be prompted to either uninstall them or cancel the operation. + +Pre-release versions can be installed by explicitly specifying the version number. While packages can declare pre-release versions as dependencies, lip ignores pre-release versions when evaluating version ranges or wildcards. + +## Options + +- `--dry-run` + + Do not actually install any packages. Be aware that files will still be downloaded and cached. + +- `-f, --force` + + Force the installation of the package. When a dependency is already installed but its version is not compatible with the specified version, lip will uninstall the existing dependency and install the new version. + + This may break the dependency graph and cause all future installations and updates without `--force` to fail. + +- `--ignore-scripts` + + Do not run any scripts during installation. + +- `--no-dependencies` + + Do not install dependencies. Also bypass prerequisite checks. + +- `--save` + + Save the installed packages to the `tooth.json` file as dependencies. + +- `--save-prerequisites` + + Save the installed packages to the `tooth.json` file as prerequisites. diff --git a/docs/user-guide/commands/lip-list.md b/docs/user-guide/commands/lip-list.md new file mode 100644 index 0000000..5d55c88 --- /dev/null +++ b/docs/user-guide/commands/lip-list.md @@ -0,0 +1,11 @@ +# lip list + +## Usage + +```shell +lip list +``` + +## Description + +List installed packages. diff --git a/docs/user-guide/commands/lip-pack.md b/docs/user-guide/commands/lip-pack.md new file mode 100644 index 0000000..712f073 --- /dev/null +++ b/docs/user-guide/commands/lip-pack.md @@ -0,0 +1,27 @@ +# lip pack + +## Usage + +```shell +lip pack +``` + +## Description + +Create a archive from the current directory, containing all files to place specified in the `tooth.json` file. + +## Options + +- `--dry-run` + + Do not actually create the archive. + +- `--ignore-scripts` + + Do not run any scripts during packaging. + +- `--archive-format ` + + The format of the archive to create. + + Valid formats are `zip`, `tar`, `tgz` and `tar.gz`. Defaults to `zip`. diff --git a/docs/user-guide/commands/lip-prune.md b/docs/user-guide/commands/lip-prune.md new file mode 100644 index 0000000..919a4e1 --- /dev/null +++ b/docs/user-guide/commands/lip-prune.md @@ -0,0 +1,21 @@ +# lip prune + +## Usage + +```shell +lip prune [...] +``` + +## Description + +Remove unused packages. When run without arguments, removes all unused packages. Packages specified as arguments that are not installed will be ignored. Dependencies of other packages will be preserved and skipped during pruning. + +## Options + +- `--dry-run` + + Do not actually remove any packages. + +- `--ignore-scripts` + + Do not run any scripts during pruning. diff --git a/docs/user-guide/commands/lip-run.md b/docs/user-guide/commands/lip-run.md new file mode 100644 index 0000000..e67072b --- /dev/null +++ b/docs/user-guide/commands/lip-run.md @@ -0,0 +1,17 @@ +# lip run + +## Usage + +```shell +lip run +``` + +## Description + +Run a script specified in the `tooth.json` file. + +## Options + +- `--script-shell ` + + The shell to use for running the script. Defaults to `/bin/sh` for POSIX systems and `cmd.exe` for Windows. diff --git a/docs/user-guide/commands/lip-uninstall.md b/docs/user-guide/commands/lip-uninstall.md new file mode 100644 index 0000000..05c0a69 --- /dev/null +++ b/docs/user-guide/commands/lip-uninstall.md @@ -0,0 +1,17 @@ +# lip uninstall + +## Usage + +```shell +lip uninstall +``` + +## Description + +Uninstall a package. + +## Options + +- `--save` + + Remove the dependency item of the package in `tooth.json`. diff --git a/docs/user-guide/commands/lip-update.md b/docs/user-guide/commands/lip-update.md new file mode 100644 index 0000000..2cc9e22 --- /dev/null +++ b/docs/user-guide/commands/lip-update.md @@ -0,0 +1,34 @@ +# lip update + +## Usage + +```shell +lip update [...] +``` + +## Description + +Attempt to update the specified packages to the specified versions. If no package specs are provided, lip will update all packages to the latest versions. + +## Options + +- `--dry-run` + + Do not actually update any packages. Be aware that files will still be downloaded and cached. + +- `-f, --force` + + Force the update of the specified packages. This may break the dependency graph and cause all future installations and updates without `--force` to fail. + +- `--ignore-scripts` + + Do not run any scripts during updating. + +- `--no-dependencies` + + Do not update dependencies. + +- `--save` + + Save the updated packages to `tooth.json`. + diff --git a/docs/user-guide/commands/lip-view.md b/docs/user-guide/commands/lip-view.md new file mode 100644 index 0000000..4fb805e --- /dev/null +++ b/docs/user-guide/commands/lip-view.md @@ -0,0 +1,11 @@ +# lip view + +## Usage + +```shell +lip view [] [[.[<...>]]...] +``` + +## Description + +Show information about a package. If not installed and cached, lip will download the package. If no package spec is provided, and a `tooth.json` file is found, lip will show information about the package specified in the `tooth.json` file. diff --git a/docs/user-guide/commands/lip.md b/docs/user-guide/commands/lip.md new file mode 100644 index 0000000..1a2e840 --- /dev/null +++ b/docs/user-guide/commands/lip.md @@ -0,0 +1,33 @@ +# lip + +## Usage + +```shell +lip +``` + +## Description + +lip is a general package manager. + +## Options + +- `-h, --help` + + Show help. + +- `--no-color` + + Disable color output. + +- `-q, --quiet` + + Show only errors. + +- `-v, --verbose` + + Show verbose output. + +- `-V, --version` + + Show version and exit. diff --git a/docs/user-guide/faq.md b/docs/user-guide/faq.md new file mode 100644 index 0000000..a39c1bc --- /dev/null +++ b/docs/user-guide/faq.md @@ -0,0 +1,2 @@ +# FAQ + diff --git a/docs/user-guide/installation.md b/docs/user-guide/installation.md index e69de29..25267fe 100644 --- a/docs/user-guide/installation.md +++ b/docs/user-guide/installation.md @@ -0,0 +1 @@ +# Installation diff --git a/docs/user-guide/tooth-json.md b/docs/user-guide/tooth-json.md new file mode 100644 index 0000000..08bb128 --- /dev/null +++ b/docs/user-guide/tooth-json.md @@ -0,0 +1 @@ +# tooth.json diff --git a/docs/mkdocs.yml b/mkdocs.yml similarity index 60% rename from docs/mkdocs.yml rename to mkdocs.yml index 18581dc..889c588 100644 --- a/docs/mkdocs.yml +++ b/mkdocs.yml @@ -1,29 +1,36 @@ site_name: lip Docs repo_url: https://github.com/futrime/lip -docs_dir: . -exclude_docs: | - /mkdocs.yml - /requirements.txt - nav: + - index.md - User Guide: - - user-guide/index.md - user-guide/getting-started.md - user-guide/installation.md - user-guide/basic-use.md - - Topic Guides: [] - - Reference: [] - - Commands: [] - - Packaging Guide: - - packaging-guide/index.md - - Marketplace: https://bedrinth.com + - user-guide/faq.md + - Commands: + - user-guide/commands/lip.md + - user-guide/commands/lip-cache.md + - user-guide/commands/lip-config.md + - user-guide/commands/lip-init.md + - user-guide/commands/lip-install.md + - user-guide/commands/lip-list.md + - user-guide/commands/lip-pack.md + - user-guide/commands/lip-prune.md + - user-guide/commands/lip-run.md + - user-guide/commands/lip-uninstall.md + - user-guide/commands/lip-update.md + - user-guide/commands/lip-view.md + - user-guide/tooth-json.md + - Get Packages: https://bedrinth.com theme: name: material features: - - navigation.tabs - - navigation.tabs.sticky + - navigation.instant + - navigation.instant.progress + - navigation.tracking + - navigation.sections favicon: img/favicon.ico logo: img/logo.webp palette: @@ -78,6 +85,9 @@ plugins: - locale: zh name: 中文 - nav_translations: {} + nav_translations: + User Guide: 用户指南 + Commands: 命令 + Get Packages: 获取软件包 - search From d0db3ca4071c25ddbc02abd03c25fbdf3d78b540 Mon Sep 17 00:00:00 2001 From: Zijian Zhang Date: Sat, 4 Jan 2025 17:57:01 +0800 Subject: [PATCH 17/63] docs: add tooth.json schema --- docs/schemas/tooth.v3.schema.json | 210 +++++++++++++++--------- docs/user-guide/commands/lip-cache.md | 4 +- docs/user-guide/commands/lip-install.md | 28 +++- docs/user-guide/tooth-json.md | 178 ++++++++++++++++++++ 4 files changed, 338 insertions(+), 82 deletions(-) diff --git a/docs/schemas/tooth.v3.schema.json b/docs/schemas/tooth.v3.schema.json index 363dc9d..e0ecf35 100644 --- a/docs/schemas/tooth.v3.schema.json +++ b/docs/schemas/tooth.v3.schema.json @@ -6,6 +6,10 @@ "type": "integer", "const": 3 }, + "format_uuid": { + "type": "string", + "const": "289f771f-2c9a-4d73-9f3f-8492495a924d" + }, "tooth": { "type": "string" }, @@ -21,6 +25,9 @@ "description": { "type": "string" }, + "author": { + "type": "string" + }, "tags": { "type": "array", "items": { @@ -32,114 +39,161 @@ "type": "string" } } - } - }, - "patternProperties": { - "^([a-z0-9]+|\\*)\\/([a-z0-9]+|\\*)$": { - "type": "object", - "properties": { - "commands": { - "type": "object", - "properties": { - "pre_install": { - "type": "array", - "items": { - "type": "string" - } - }, - "post_install": { - "type": "array", - "items": { - "type": "string" - } - }, - "pre_uninstall": { - "type": "array", - "items": { + }, + "variants": { + "type": "array", + "items": { + "type": "object", + "properties": { + "platform": { + "type": "string" + }, + "dependencies": { + "type": "object", + "patternProperties": { + "^.*$": { "type": "string" } - }, - "post_uninstall": { - "type": "array", - "items": { + } + }, + "prerequisites": { + "type": "object", + "patternProperties": { + "^.*$": { "type": "string" } } - } - }, - "dependencies": { - "type": "object", - "patternProperties": { - "^.*$": { - "type": "string" - } - } - }, - "prerequisites": { - "type": "object", - "patternProperties": { - "^.*$": { - "type": "string" + }, + "assets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "self", + "uncompressed", + "tar", + "tar.gz", + "zip" + ] + }, + "urls": { + "type": "array", + "items": { + "type": "string" + } + }, + "place": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "file", + "dir" + ] + }, + "src": { + "type": "string" + }, + "dest": { + "type": "string" + } + }, + "required": [ + "type", + "src", + "dest" + ] + } + }, + "preserve": { + "type": "array", + "items": { + "type": "string" + } + }, + "remove": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "type" + ] } - } - }, - "assets": { - "type": "array", - "items": { + }, + "scripts": { "type": "object", "properties": { - "download_urls": { + "pre_install": { "type": "array", "items": { "type": "string" } }, - "type": { - "type": "string", - "enum": [ - "uncompressed", - "tar", - "tar.gz", - "zip" - ] + "install": { + "type": "array", + "items": { + "type": "string" + } }, - "place": { + "post_install": { "type": "array", "items": { - "type": "object", - "properties": { - "src": { - "type": "string" - }, - "dest": { - "type": "string" - } - }, - "required": [ - "src", - "dest" - ] + "type": "string" + } + }, + "pre_pack": { + "type": "array", + "items": { + "type": "string" + } + }, + "post_pack": { + "type": "array", + "items": { + "type": "string" + } + }, + "pre_uninstall": { + "type": "array", + "items": { + "type": "string" } }, - "preserve": { + "uninstall": { "type": "array", "items": { "type": "string" } }, - "remove": { + "post_uninstall": { "type": "array", "items": { "type": "string" } } }, - "required": [ - "download_urls", - "type" - ] + "patternProperties": { + "^[a-z0-9]+(-[a-z0-9]+)*$": { + "type": "array", + "items": { + "type": "string" + } + } + } } - } + }, + "required": [ + "platform" + ] } } }, diff --git a/docs/user-guide/commands/lip-cache.md b/docs/user-guide/commands/lip-cache.md index 892d6ec..51cf68f 100644 --- a/docs/user-guide/commands/lip-cache.md +++ b/docs/user-guide/commands/lip-cache.md @@ -12,7 +12,7 @@ lip cache list Inspect and manage lip’s cache. -lip stores cache data in `%LocalAppData%\lip-cache` for Windows and `~/.cache/lip` for POSIX-like systems by default. This can be configured by configuration item `cache.dir`. +lip stores cache data in `%LocalAppData%\lip-cache` for Windows and `~/.cache/lip` for POSIX-like systems by default. ## Sub-commands @@ -24,7 +24,7 @@ lip cache add Add a package to the cache. `` is a [package specifier](#), e.g. `github.com/futrime/example-package#subpack@1.0.0`. -If the configuration item `download.goproxy` is set, lip will download the package via Goproxy. Otherwise, lip will download the package directly from the Git repository. +If a Go module proxy is set in configuration, lip will download the package via Goproxy. Otherwise, lip will download the package directly from the Git repository. ### clean diff --git a/docs/user-guide/commands/lip-install.md b/docs/user-guide/commands/lip-install.md index 730fc5a..e8c4037 100644 --- a/docs/user-guide/commands/lip-install.md +++ b/docs/user-guide/commands/lip-install.md @@ -16,9 +16,9 @@ A `` can be any of the following (in order of priority): - An archive (`.zip`, `.tar`, `.tgz` or `.tar.gz`) containing a directory with a `tooth.json` file - A [package specifier](#) referencing a Git repository -If no `` is specified and the current directory contains a `tooth.json` file, lip will install the package in the current directory. Be aware that this may cause file conflicts. +If no `` is specified and the current directory contains a `tooth.json` file, lip will install the package in the current directory. Inplace file placement will not be performed. -When using a package specifier, lip will use Goproxy to download the package if the `download.goproxy` configuration is enabled. Otherwise, packages are downloaded directly from their Git repositories. +When using a package specifier, lip will use Goproxy to download the package if a Go module proxy is set in configuration. Otherwise, packages are downloaded directly from their Git repositories. Package prerequisites must be installed manually. lip will not proceed with installation if it detects missing prerequisites. @@ -59,3 +59,27 @@ Pre-release versions can be installed by explicitly specifying the version numbe - `--save-prerequisites` Save the installed packages to the `tooth.json` file as prerequisites. + +## Package Specifier + +A package specifier is a string that identifies a package, a sub-directory path, and a version. + +The format is `[#][@]`. + +- `` is the identifier of the package. +- `` is the version of the package. If omitted, lip will install the latest version. +- `` is an optional sub-directory path within the package, which must be a valid relative path beginning with a directory or a file name. + +Examples: + +- `github.com/futrime/example-package` +- `github.com/futrime/example-package#cmd/example-package` +- `github.com/futrime/example-package@v1.0.0` +- `github.com/futrime/example-package#cmd/example-package@v1.0.0-beta.1` + +Wrong examples: + +- `github.com/futrime/example-package#/cmd/example-package/` +- `github.com/futrime/example-package#~cmd/example-package/` +- `github.com/futrime/example-package#../cmd/example-package/` +- `github.com/futrime/example-package#./cmd/example-package/` diff --git a/docs/user-guide/tooth-json.md b/docs/user-guide/tooth-json.md index 08bb128..fbd75e5 100644 --- a/docs/user-guide/tooth-json.md +++ b/docs/user-guide/tooth-json.md @@ -1 +1,179 @@ # tooth.json + +The `tooth.json` file is used to describe a package, including package metadata, identifier, version, dependencies, files, and other information. This file should be written in JSON format and placed in the root directory of the package. + +For a JSON schema of `tooth.json`, see [tooth.v3.schema.json](../schemas/tooth.v3.schema.json). + +Below, the (required/optional) annotation is used to indicate whether a field is required or optional of its upper level. For example, if `variants` is optional but `variants[].platform` is required, then you can omit `variants` field in `tooth.json`, but if there is any item in `variants`, you must provide `platform` field for each item. + +## Fields + +### format_version (required) + +The version of the format. Currently, the only supported version is `3`. + +### format_uuid (required) + +The UUID of the format, used to identify the format. Currently, the only supported UUID is `289f771f-2c9a-4d73-9f3f-8492495a924d`. + +### tooth (required) + +The unique identifier of the package in the form of Go module path, i.e. a URL without scheme and any suffix. Optionally, you can specify a sub-directory path. + +Examples: + +- `github.com/LiteLDev/LeviLamina` +- `github.com/futrime/example-package#cmd/example-package` +- `github.com/LiteLDev/LegacyScriptEngine#quickjs` + +The main part must be identical to the repository URL where the package is hosted if you want to publish the package. + +### version (required) + +The version of the package. Must be a valid [semantic version](https://semver.org). + +Examples: + +- `1.0.0` +- `1.0.0-alpha.1` +- `0.1.0` + +When tagging a release, you should use the `v` prefix, e.g. `v1.0.0`. However, do not write the `v` prefix in the `version` field. Since Go module proxy will regard versions starting with `v0.0.0` as psuedo-versions, do not use `v0.0.0` as the version number. + +### info (optional) + +The metadata of the package. + +### info.name (optional) + +The name of the package. + +### info.description (optional) + +The description of the package. + +### info.author (optional) + +The author of the package. + +### info.tags (optional) + +The tags of the package. The tags can be in two formats: + +- `tag`: A single tag. +- `tag:subtag`: A key-value pair tag. + +For `tag` and `subtag`, only lowercase letters, digits, and dashes are allowed ([a-z0-9-]). Though lip treats these two formats equally, some third-party tools and platforms may use them differently. For example, [Bedrinth](https://bedrinth.com) uses `tag:subtag` format to store some additional information about the package. + +### info.avatar_url (optional) + +The URL of the avatar of the package. + +### variants (optional) + +Array of objects defining different package variants for different platforms. This is the most important field in `tooth.json`. + +When handling a package, lip will iterate over all the variants and process all variants matching the current platform in the order they are defined in `tooth.json`. + +If more than one variant matches the current platform, lip will combine the fields of all matching variants. + +However, on checking the compatibility of the package, lip will not consider the variants using glob patterns, i.e. if you want to support a wide range of platforms, you must define multiple variants, even if they are empty. + +### variants[].platform (required) + +The platform of the package variant. Can be one of the following: + +- `linux-arm64` +- `linux-x64` +- `osx-arm64` +- `osx-x64` +- `win-arm64` +- `win-x64` +- A glob pattern, e.g. `linux-*` + +### variants[].dependencies (optional) + +The dependencies of the package variant. The keys are the identifiers of the dependencies, which may contain a sub-directory path. The values are the versions or version ranges of the dependencies. + +Key examples: + +- `github.com/futrime/example-package` +- `github.com/futrime/example-package#subpath` + +Value examples: + +- `1.0.0` +- `1.0.0-alpha.1` +- `0.1.0` +- `>=0.1.0` +- `>=0.1.0 <1.0.0` + +We use [WalkerCodeRanger/semver](https://github.com/WalkerCodeRanger/semver) to parse the versions and version ranges. + +### variants[].prerequisites (optional) + +The prerequisites of the package variant. The format is the same as `variants[].dependencies`. Different from `variants[].dependencies`, the prerequisites will not be installed automatically. If a prerequisite is not installed, lip will refuse to install the package. + +### variants[].assets (optional) + +Describe how the files in the package should be handled. + +### variants[].assets[].type (required) + +The type of the asset. Can be one of the following: + +- `self`: The asset is the package itself, i.e. the files in the package directory or the package archive. +- `uncompressed`: The asset is a single uncompressed file. +- `tar`: The asset is a tar file. +- `tar.gz`: The asset is a tar.gz file. +- `zip`: The asset is a zip file. + +### variants[].assets[].urls (required) + +The URLs to fetch the asset from. lip will attempt to download the asset from the URLs in the order they are defined here. For `self` asset type, the URLs should be empty, otherwise lip will refuse to install the package. + +### variants[].assets[].place (optional) + +An array to specify how files in the tooth should be place to the workspace. + +### variants[].assets[].place[].type (required) + +The type of the place. Can be one of the following: + +- `file`: The `src` and `dest` are both files. +- `dir`: The `src` and `dest` are both directories. + +For `uncompressed` asset type, only `file` type is allowed. + +### variants[].assets[].place[].src (required) + +The source path of the file, or a directory, or a glob pattern for files. For `uncompressed` asset type, the downloaded file will be the only file that can be placed, and the corresponding `src` filed should be empty (`""`). For `file` type, `src` should be a file or a glob pattern for files, and all matched directories will be ignored. The matched files will be flattened. For example, if `src` matches `foo/bar/baz.txt` and `foo/kt.txt`, their directory structure will be flattened to `baz.txt` and `kt.txt` before being placed. For `directory` type, `src` should be a directory, and all files in the directory will be placed, keeping the directory structure. + +### variants[].assets[].place[].dest (required) + +The destination path of the file. If `src` is a file, `dest` should be a file. If `src` is a directory or a file glob pattern, `dest` should be a directory. + +### variants[].assets[].preserve (optional) + +The path or glob pattern to preserve in the workspace. The files will not be removed when the package is uninstalled. Items in this array should not exist in `remove` array. + +### variants[].assets[].remove (optional) + +The path or glob pattern to remove in the workspace. The files will be removed when the package is uninstalled. Items in this array should not exist in `preserve` array. + +### variants[].scripts (optional) + +The commands to run in the workspace. The keys are the names of the scripts, and the values are the commands to run. + +Here is a list of predefined scripts that will be run at corresponding stages: + +- `pre_install`: Before installing the package. +- `install`: After placing the files. +- `post_install`: After installing the package. +- `pre_pack`: Before packing the package. +- `post_pack`: After packing the package. +- `pre_uninstall`: Before uninstalling the package. +- `uninstall`: After removing the files. +- `post_uninstall`: After uninstalling the package. + +For other scripts, you can define your own scripts in the `scripts` field and run with `lip run