gpm
所属分类:Git
开发工具:Rust
文件大小:0KB
下载次数:0
上传日期:2021-11-09 16:14:57
上 传 者:
sh-1993
说明: 基于Git的包管理器。
(Git-based Package Manager.)
文件列表:
.cargo/ (0, 2023-12-18)
.cargo/config (77, 2023-12-18)
.dockerignore (9, 2023-12-18)
.travis.yml (104, 2023-12-18)
Cargo.lock (55431, 2023-12-18)
Cargo.toml (843, 2023-12-18)
Dockerfile (471, 2023-12-18)
LICENSE (1062, 2023-12-18)
appveyor.yml (3550, 2023-12-18)
build.rs (205, 2023-12-18)
demo.gif (86465, 2023-12-18)
gitlfs/ (0, 2023-12-18)
gitlfs/Cargo.lock (32364, 2023-12-18)
gitlfs/Cargo.toml (281, 2023-12-18)
gitlfs/src/ (0, 2023-12-18)
gitlfs/src/lib.rs (11921, 2023-12-18)
renovate.json (49, 2023-12-18)
script/ (0, 2023-12-18)
script/build-release.sh (757, 2023-12-18)
script/release.sh (3022, 2023-12-18)
src/ (0, 2023-12-18)
src/gpm.rs (89, 2023-12-18)
src/gpm/ (0, 2023-12-18)
src/gpm/command.rs (1907, 2023-12-18)
src/gpm/command/ (0, 2023-12-18)
src/gpm/command/clean.rs (1376, 2023-12-18)
src/gpm/command/download.rs (4837, 2023-12-18)
src/gpm/command/install.rs (6013, 2023-12-18)
src/gpm/command/update.rs (3534, 2023-12-18)
src/gpm/file.rs (3671, 2023-12-18)
src/gpm/git.rs (12565, 2023-12-18)
src/gpm/package.rs (8098, 2023-12-18)
src/gpm/ssh.rs (9597, 2023-12-18)
src/gpm/ssh_config.pest (450, 2023-12-18)
src/gpm/style.rs (649, 2023-12-18)
src/main.rs (2456, 2023-12-18)
# 1. GPM
[![Build status](https://travis-ci.org/aerys/gpm.svg?branch=master)](https://travis-ci.org/aerys/gpm)
[![Build status](https://ci.appveyor.com/api/projects/status/rxgs74va4o640vaa?svg=true)](https://ci.appveyor.com/project/promethe42/gpm)
A statically linked, native, platform agnostic Git-based Package Manager written in Rust.
![demo](./demo.gif)
- [1. Install](#1-install)
- [2. Background](#2-background)
- [3. Features](#3-features)
- [4. Security](#4-security)
- [5. Build](#5-build)
- [5.1. Development build](#51-development-build)
- [5.2. Release (static) build](#52-release-static-build)
- [6. Getting started](#6-getting-started)
- [6.1. Creating a package repository](#61-creating-a-package-repository)
- [6.2. Publishing your first package](#62-publishing-your-first-package)
- [6.3. Installing your first package](#63-installing-your-first-package)
- [7. Authentication](#7-authentication)
- [8. Package reference notation](#8-package-reference-notation)
- [8.1. Package name](#81-package-name)
- [8.1.1. Shorthand notation](#811-shorthand-notation)
- [8.1.2. URI notation](#812-uri-notation)
- [8.2. Package version](#82-package-version)
- [8.2.1. SemVer notation](#821-semver-notation)
- [8.2.2. Git refspec notation](#822-git-refspec-notation)
- [9. Matching package references](#9-matching-package-references)
- [10. Working with multiple package repositories](#10-working-with-multiple-package-repositories)
- [11. Logging](#11-logging)
- [12. Commands](#12-commands)
- [12.1. `update`](#121-update)
- [12.2. `clean`](#122-clean)
- [12.3. `install`](#123-install)
- [12.4. `download`](#124-download)
- [13. Integrations](#13-integrations)
- [13.1. Travis CI](#131-travis-ci)
- [13.2. AppVeyor](#132-appveyor)
- [13.3. GitLab CI](#133-gitlab-ci)
- [13.4. GitLab Releases](#134-gitlab-releases)
- [13.5. GitHub Releases](#135-github-releases)
- [14. FAQ](#14-faq)
- [14.1. Why GPM?](#141-why-gpm)
- [14.2. Why Git? Why not just `curl` or `wget` or whatever?](#142-why-git-why-not-just-curl-or-wget-or-whatever)
- [14.3. But Git does not like large binary files!](#143-but-git-does-not-like-large-binary-files)
- [14.4. Why storing packages as `*.tar.gz` archives?](#144-why-storing-packages-as-targz-archives)
- [15. Contributing](#15-contributing)
- [16. Troubleshooting](#16-troubleshooting)
- [16.1. "Failed to authenticate SSH session" error on Windows](#161-failed-to-authenticate-ssh-session-error-on-windows)
- [17. License](#17-license)
## 1. Install
* Linux: `curl -Ls https://github.com/aerys/gpm-packages/raw/master/gpm-linux64/gpm-linux64.tar.gz | tar xvz`
* Windows:
* cmd.exe: `curl -Ls https://github.com/aerys/gpm-packages/raw/master/gpm-windows64/gpm-windows64.tar.gz | tar xvzf -`
* PowerShell: `$tmp = New-TemporaryFile ; Invoke-WebRequest -OutFile $tmp https://github.com/aerys/gpm-packages/raw/master/gpm-windows64/gpm-windows64.tar.gz ; tar xf $tmp`
## 2. Background
As [a software company](https://aerys.in), we use Git to manage our source code.
Thus, we wanted to **use the same Git features we know and love to manage the
packages built from that source code**.
We also use Git-enabled collaborative platforms (GitLab, GitHub, Gitea...) to:
* Build, package and deploy our code using CI/CD.
* Authenticate and authorize clients to report issues and manage projects.
And we wanted to leverage those collaborative features to **effortlessly
distribute packages to authorized users**.
**Introducing GPM: the Git-based Package Manager.**
GPM is a platform-agnostic package manager leveraging Git capabilities to
store, install and update packages. Thanks to GPM:
* Any Git repository becomes a package repository.
* Any Git-powered collaborative platform (GitLab, GitHub, Gitea...) becomes
a package management and distribution platform.
## 3. Features
* Free, open source and decentralized package management
* 100% backed by the Git protocol
* Fully compatible with the Git ecosystem (GitLab, GitHub, Gitea...):
* commit and manage packages via your favorite Git client
* authentication (password, private key, deploy key, deploy token...)
* commit packages via Git in CI pipelines
* protected package version branches/tags
* release page integration (ex: [GPM binary package releases](https://github.com/aerys/gpm-packages/releases))
* [Git LFS](https://git-lfs.github.com/) support
* release notes via Git tag messages
* Distribute *any* package for *any* platform as a simple `*.tar.gz` archive
* [Semver](https://semver.org) support
* Public package repositories
* Private package repositories with authentication:
* username/password, deploy token
* SSH private key, deploy key
* Modern, intuitive and clean CLI interactive mode
* Non-interactive mode for automation/deployments
* Transparent SSH config (`~/.ssh/config`) discovery for authentication
* Support for all the common Git protocols (HTTP(s), SSH...)
* Security backed by standard libraries (libgit2, libssh2, OpenSSL...)
* Lightweight (<2,5Mo) fully static 0 dependency binary
* Cross-platform: Windows, Linux, Android (Termux), macOS (untested)
* One-liner installation
* Local cache to speedup package discovery and matching
## 4. Security
GPM leverages other standard well maintained open source libraries for all
sensitive operations:
* All Git operations (clone, pull...) are performed using [the Rust bindings for libgit2](https://crates.io/crates/git2).
* All SSH operations (Git LFS authentication) are performed using [the Rust bindings for libssh2](https://crates.io/crates/ssh2).
* All SSL operations (HTTPS, SSH key management) are performed using [the Rust bindings for OpenSSL](https://crates.io/crates/openssl-sys).
## 5. Build
### 5.1. Development build
Dependencies:
* OpenSSL
```bash
cargo build
```
### 5.2. Release (static) build
Dependencies:
* Docker
```bash
docker run \
--rm -it \
-v "$(pwd)":/home/rust/src \
-v "/home/${USER}/.cargo":/home/rust/.cargo \
ekidd/rust-musl-builder \
cargo build --release --target x86_64-unknown-linux-musl
```
## 6. Getting started
### 6.1. Creating a package repository
1. Create a [Git LFS](https://git-lfs.github.com/) enabled Git repository, for example a GitHub or GitLab repository.
2. [Install Git LFS](https://github.com/git-lfs/git-lfs/wiki/Installation) on your local computer.
3. Clone the newly created repository on your local computer:
```bash
git clone ssh://path.to/my/package-repository.git
cd package-repository
```
4. Enable [Git LFS](https://git-lfs.github.com/) tracking for `*.tar.gz` files:
```bash
git lfs track "*.tar.gz"
```
5. Add, commit and push `.gitattributes`:
```bash
git add .gitattributes
git commit .gitattributes -m "Enable Git LFS."
git push
```
Voilà! You're all set to publish your first package!
### 6.2. Publishing your first package
In this example, we're going to create a simple `hello-world` package and publish it.
1. Make sure you are at the root of the package repository created in the previous section.
2. Create and enter the package directory:
```bash
mkdir hello-world && cd hello-world
```
3. Create the `hello-world.sh` script:
```bash
echo "#/bin/sh\necho 'Hello World!'" > hello-world.sh
```
4. Create your package archive:
```bash
tar -cvzf hello-world.tar.gz hello-world.sh
```
5. Add and commit your package archive:
```bash
git add hello-world.tar.gz
git commit hello-world.tar.gz -m "Publish hello-world version 0.1.0"
```
6. Tag your package release with a specific version number:
```bash
git tag hello-world/0.1.0
```
7. Push your new package:
```bash
git push
git push --tags
```
Your `hello-world/0.1.0` package is now stored in your package repository and can be installed using `gpm`!
### 6.3. Installing your first package
1. Install (or build) `gpm`.
2. Add your package repository to the `gpm` sources:
```bash
mkdir -p ~/.gpm
echo "ssh://path.to/my/package-repository.git" >> ~/.gpm/sources.list
```
3. Update the `gpm` cache:
```bash
gpm update
```
4. Install your package:
```bash
gpm install hello-world=0.1.0 --prefix ~/
```
The version `0.1.0`of your `hello-world` package is now installed and you can run it with `sh ~/hello-world.sh`.
## 7. Authentication
`gpm` will behave a lot like `git` regarding authentication.
If the repository is "public", then no authentication should be required.
Otherwise, the following authentication methods are supported:
* URL encoded HTTP basic authentication (ex: `https://username:password@host.com/repo.git`);
* SSH public/private key.
If URL encoded HTTP basic authentication is used, no additional authentication is required.
Otherwise, `gpm` will assume SSH public/private key authentication is used.
**Attention**: Windows users please read ["Failed to authenticate SSH session" error on Windows](#failed-to-authenticate-ssh-session-error-on-windows).
If SSH public/private key authentication is used:
* If the `GPM_SSH_KEY` environment variable is set to a path that exists/is a file, then its value is used as the path to the SSH private key.
* Otherwise, if `gpm` can find the `~/.ssh/config` file, parse it and find a matching host with the `IndentityFile` option; then the corresponding
path to the SSH private key will be used.
* Otherwise, if `gpm` can find the `~/.ssh/id_rsa` file, it is used as the SSH private key.
* Otherwise, `gpm` will continue without authentication.
If the SSH private key requires a passphrase, then:
* If the `GPM_SSH_PASS` environment variable is set/not empty, it is used as the passphrase.
* Otherwise, `gpm` will prompt the user to type his passphrase.
## 8. Package reference notation
### 8.1. Package name
#### 8.1.1. Shorthand notation
This is the most trivial, obvious and simple notation: simply use the package name.
Example:
```
gpm install my-package
```
`gpm` will search by name for the specified package in all the available package
repositories. Thus, for such package reference to be found, you *must* make sure:
* The corresponding package repository remote is listed in `~/.gpm/sources.list` (see
[Working with multiple package repositories](#7-working-with-multiple-package-repositories)).
* The cache has been updated by calling `gpm update`.
#### 8.1.2. URI notation
The complete URI notation for a package is as follow:
`${remote-uri}#${package}`
* `remote-uri`: the full URI to the Git remote.
* `package`: a shorthand or `${name}=${revision}` package reference.
Example:
```
gpm install ssh://github.com/my/awesome-packages.git#my-package
```
In this case, `gpm` will clone the corresponding Git repository and look for the package there.
`gpm` will look for the specified package *only* in the specified repository.
### 8.2. Package version
#### 8.2.1. SemVer notation
The version of a package can be specified using the
[SemVer](https://semver.org/) version requirement notation:
`${package}${semver_req}`
* `package`: the name of the package (ex: `my-package`).
* `semver_req`: the semver version requirement (ex: `^0.4.2`). If not
specified, then the latest version will be installed.
It also allows parsing of `~x.y.z` and `^x.y.z` requirements as defined
at https://www.npmjs.org/doc/misc/semver.html.
Tilde requirements specify a minimal version with some updates:
```
~1.2.3 := >=1.2.3 <1.3.0
~1.2 := >=1.2.0 <1.3.0
~1 := >=1.0.0 <2.0.0
```
Caret requirements allow SemVer compatible updates to a specified verion,
`0.x` and `0.x+1` are not considered compatible, but `1.x and `1.x+1 are.
0.0.x is not considered compatible with any other version. Missing minor and
patch versions are desugared to 0 but allow flexibility for that value.
```
^1.2.3 := >=1.2.3 <2.0.0
^0.2.3 := >=0.2.3 <0.3.0
^0.0.3 := >=0.0.3 <0.0.4
^0.0 := >=0.0.0 <0.1.0
^0 := >=0.0.0 <1.0.0
```
Wildcard requirements allows parsing of version requirements of the formats
`*`, `x.*` and `x.y.*`.
```
* := >=0.0.0
1.* := >=1.0.0 <2.0.0
1.2.* := >=1.2.0 <1.3.0
```
Example:
```bash
gpm install my-package>=2.0.0
```
#### 8.2.2. Git refspec notation
A package version can also be set to a valid
[Git refspec](https://git-scm.com/book/en/v2/Git-Internals-The-Refspec),
such as a specific branch or a tag, using the `@` operator:
`${package}@${refspec}`
* `package` is the name of the package (ex: `my-package`).
* `refspec` is a valid Git refspec (ex: `refs/heads/my-branch` or `refs/tags/my-tag`).
## 9. Matching package references
The following section explains how `gpm` finds the package archive for a
package named `${package_name}` at version `${package_version}`.
The following pseudo code explains how GPM will find packages for a specific
version:
```
if ${package_ref} includes remote URL
git clone URL
if ${package_ref} does not include version
${package_version} is set to "@master"
for each ${repository} in cache
git checkout master
git reset --hard
if ${package_version} is refspec
git checkout ${package_version}
else # assume ${package_version} is semver
for each ${tag} in ${repository}
if ${tag} matches semver
git checkout ${tag}
if file ${package_name}/${package_name}.tgz exists
if ${package_name}/${package_name}.tgz is LFS link
download ${package_name}/${package_name}.tgz
extract ${package_name}/${package_name}.tgz
```
## 10. Working with multiple package repositories
Specifying a full package URI might not be practical. It's simpler to specify
a package refspec and let `gpm` find it. But where should it look for it?
When you specify a package using a refspec, `gpm` will have to find the proper
package repository. It will look for this refspec in the repositories listed
in `~/.gpm/sources.list`.
The following command lines will fill `sources.list` with a few (dummy)
package repositories:
```bash
echo "ssh://path.to/my/package-repository.git" >> ~/.gpm/sources.list
echo "ssh://path.to/my/another-repository.git" >> ~/.gpm/sources.list
echo "ssh://path.to/my/yet-another-repository.git" >> ~/.gpm/sources.list
# ...
```
After updating `sources.list`, don't forget to call `gmp update` to update the
cache.
You can then install packages using their refspec.
## 11. Logging
Logs can be enable by setting the `GPM_LOG` environment variable to one of the
following values:
* `trace`
* `debug`
* `info`
* `warn`
* `error`
Example:
```bash
GPM_LOG=info gpm update
```
Logs can be *very* verbose. So it's best to keep only the `gpm` and `gitlfs`
module logs. For example:
```bash
GPM_LOG="gpm=debug,gitlfs=debug" gpm install hello-world/1.0.0
```
## 12. Commands
### 12.1. `update`
Update the cache to feature the latest revision of each repository listed in
`~/.gpm/sources.list`.
Example:
```bash
# first add at least one remote
echo "ssh://github.com/my/awesome-packages.git" >> ~/.gpm/sources.list
echo "ssh://github.com/my/other-packages.git" >> ~/.gpm/sources.list
# ...
# then you can run an update:
gpm update
```
### 12.2. `clean`
Clean the cache. The cache is located in `~/.gpm/cache`.
Cache can be rebuilt using the `update` command.
```bash
gpm clean
```
### 12.3. `install`
Download and install a package.
Example:
```bash
# install the "app" package at version 2.0.0 from repository
# ssh://github.com/my/awesome-packages.git in the /var/www/app folder
gpm install ssh://github.com/my/awesome-packages.git#app/2.0.0 \
--prefix /var/www/app
```
```bash
# assuming the repository ssh://github.com/my/awesome-packages.git is in
# ~/.gpm/sources.list and the cache has been updated using `gpm update`
gpm install app/2.0.0 --prefix /var/www/app
```
### 12.4. `download`
Download a package in the current working directory.
Example:
```bash
# install the "app" package at version 2.0.0 from repository
# ssh://github.com/my/awesome-packages.git in the /var/www/app folder
gpm download ssh://github.com/my/awesome-packages.git#app/2.0.0 \
--prefix /var/www/app
```
```bash
# assuming the repository ssh://github.com/my/awesome-packages.git is in
# ~/.gpm/sources.list and the cache has been updated using `gpm update`
gpm download app/2.0.0 --prefix /var/www/app
```
## 13. Integrations
### 13.1. Travis CI
Here is a working Bash script to publish a package from Travis CI:
[script/publish.sh](script/publish.sh).
### 13.2. AppVeyor
Here is a working PowerShell script to publish a package from AppVeyor:
[script/publish.ps1](script/publish.ps1).
### 13.3. GitLab CI
Here is a template to publish a package from GitLab CI:
```yml
.gpm_publish_template: &gpm_publish_template
stage: publish
image:
name: alpine/git:v2.24.1
entrypoint: ["/bin/sh", "-c"]
only:
- tags
resource_group: ${PACKAGE_REPOSITORY}
before_script:
- apk add git-lfs
- git lfs install --skip-repo
script:
- cd ${PACKAGE_ARCHIVE_ROOT} && tar -zcf /tmp/${PACKAGE_NAME}.tar.gz ${PACKAGE_CONTENT} && cd -
- mkdir -p ~/.ssh && echo -e "Host *\n StrictHostKeyChecking no\n IdentityFile /tmp/package-repository-key" > ~/.ssh/config
- GIT_LFS_SKIP_SMUDGE=1 git clone ${PACKAGE_REPOSITORY} /tmp/package-repository
- mkdir -p /tmp/package-repository/${PACKAGE_NAME}
- mv /tmp/${PACKAGE_NAME}.tar.gz /tmp/package-repository/${PACKAGE_NAME}
- cd /tmp/package-repository/${PACKAGE_NAME}
- git config --global user.email "${GITLAB_USER_EMAIL}"
- git config --global user.name "${GITLAB_USER_NAME}"
- git add ${PACKAGE_NAME}.tar.gz
- git commit ${PACKAGE_NAME}.tar.gz -m "Publish ${PACKAGE_NAME} version ${PACKAGE_VERSION}."
- git tag -F "${PACKAGE_CHANGELOG}" "${PACKAGE_NAME}/${PACKAGE_VERSION}"
- git push
- git push --tags
```
and an example of how to use this template:
```yml
publish:
<<: *gpm_publish_template
variables:
PACKAGE_VERSION: ${CI_COMMIT_TAG} # the version of the package
PACKAGE_REPOSITORY: ssh://my.gitlab.com/my-packages-repository.git # the package repository to publish to
PACKAGE_NAME: ${CI_PROJECT_NAME} # the name of the package
PACKAGE_ARCHIVE_ROOT: ${CI_PROJECT_DIR}/bin # the folder containing the files to put in the package archive
PACKAGE_CONTENT: * # the globbing pattern/file list to put in the package archive
PACKAGE_CHANGELOG: ${CI_PROJECT_DIR}/CHANGELOG.md # the path to the changelog file
```
This template relies on the `PACKAGE_REPOSITORY_KEY` GitLab CI variable.
It must contain a passphrase-less SSH deploy key (with write permissions) to
your package repository.
### 13.4. GitLab Releases
Each package can have its changelog available via the
[GitLab Releases](https://about.gitlab.com/releases/) page of the corresponding
GPM package repository.
The following GitLab CI job templates can be used to create a GitLab release
for a package:
```yml
.gitlab_release_template: &gitlab_release_template
image: alpine:3.11.5
only:
- tags
dependencies:
- changelog
before_script:
- apk add jq curl
script: |
curl --fail --retry 5 \
-X POST \
-H 'Content-Type: application/json' \
-H "PRIVATE-TOKEN: ${RELEASE_AUTH_TOKEN}" \
https://gitlab.example.com/api/v4/projects/${RELEASE_PROJECT_ID}/releases
-d "$(
jq -n \
--arg name "${RELEASE_NAME}" \
--arg tag_name "${RELEASE_TAG}" \
--arg description "$(cat ${RELEASE ... ...
近期下载者:
相关文件:
收藏者: