{"id":13672309,"url":"https://github.com/aerys/gpm","last_synced_at":"2026-01-18T20:30:53.491Z","repository":{"id":37307460,"uuid":"140208469","full_name":"aerys/gpm","owner":"aerys","description":"Git-based Package Manager.","archived":false,"fork":false,"pushed_at":"2024-09-19T16:44:31.000Z","size":782,"stargazers_count":237,"open_issues_count":12,"forks_count":15,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-04-27T21:35:25.282Z","etag":null,"topics":["ci","git","github","gitlab","package-manager","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aerys.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-07-08T23:00:21.000Z","updated_at":"2025-04-19T09:44:50.000Z","dependencies_parsed_at":"2023-10-24T14:29:50.045Z","dependency_job_id":"69048e52-f9b2-46e3-8dca-fb02e881cfb7","html_url":"https://github.com/aerys/gpm","commit_stats":null,"previous_names":[],"tags_count":44,"template":false,"template_full_name":null,"purl":"pkg:github/aerys/gpm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aerys%2Fgpm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aerys%2Fgpm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aerys%2Fgpm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aerys%2Fgpm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aerys","download_url":"https://codeload.github.com/aerys/gpm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aerys%2Fgpm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28549807,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T19:56:05.265Z","status":"ssl_error","status_checked_at":"2026-01-18T19:55:54.685Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ci","git","github","gitlab","package-manager","rust"],"created_at":"2024-08-02T09:01:31.939Z","updated_at":"2026-01-18T20:30:53.468Z","avatar_url":"https://github.com/aerys.png","language":"Rust","funding_links":[],"categories":["Rust","rust"],"sub_categories":[],"readme":"# GPM \u003c!-- omit in toc --\u003e\n\n[![Build status](https://travis-ci.org/aerys/gpm.svg?branch=master)](https://travis-ci.org/aerys/gpm)\n[![Build status](https://ci.appveyor.com/api/projects/status/rxgs74va4o640vaa?svg=true)](https://ci.appveyor.com/project/promethe42/gpm)\n\nA statically linked, native, platform agnostic Git-based Package Manager written in Rust.\n\n![demo](./demo.gif)\n\n- [1. Install](#1-install)\n- [2. Background](#2-background)\n- [3. Features](#3-features)\n- [4. Security](#4-security)\n- [5. Build](#5-build)\n  - [5.1. Development build](#51-development-build)\n  - [5.2. Release (static) build](#52-release-static-build)\n- [6. Getting started](#6-getting-started)\n  - [6.1. Creating a package repository](#61-creating-a-package-repository)\n  - [6.2. Publishing your first package](#62-publishing-your-first-package)\n  - [6.3. Installing your first package](#63-installing-your-first-package)\n- [7. Authentication](#7-authentication)\n- [8. Package reference notation](#8-package-reference-notation)\n  - [8.1. Package name](#81-package-name)\n    - [8.1.1. Shorthand notation](#811-shorthand-notation)\n    - [8.1.2. URI notation](#812-uri-notation)\n  - [8.2. Package version](#82-package-version)\n    - [8.2.1. SemVer notation](#821-semver-notation)\n    - [8.2.2. Git refspec notation](#822-git-refspec-notation)\n- [9. Matching package references](#9-matching-package-references)\n- [10. Working with multiple package repositories](#10-working-with-multiple-package-repositories)\n- [11. Logging](#11-logging)\n- [12. Commands](#12-commands)\n  - [12.1. `update`](#121-update)\n  - [12.2. `clean`](#122-clean)\n  - [12.3. `install`](#123-install)\n  - [12.4. `download`](#124-download)\n- [13. Integrations](#13-integrations)\n  - [13.1. Travis CI](#131-travis-ci)\n  - [13.2. AppVeyor](#132-appveyor)\n  - [13.3. GitLab CI](#133-gitlab-ci)\n  - [13.4. GitLab Releases](#134-gitlab-releases)\n  - [13.5. GitHub Releases](#135-github-releases)\n- [14. FAQ](#14-faq)\n  - [14.1. Why GPM?](#141-why-gpm)\n  - [14.2. Why Git? Why not just `curl` or `wget` or whatever?](#142-why-git-why-not-just-curl-or-wget-or-whatever)\n  - [14.3. But Git does not like large binary files!](#143-but-git-does-not-like-large-binary-files)\n  - [14.4. Why storing packages as `*.tar.gz` archives?](#144-why-storing-packages-as-targz-archives)\n- [15. Contributing](#15-contributing)\n- [16. Troubleshooting](#16-troubleshooting)\n  - [16.1. \"Failed to authenticate SSH session\" error on Windows](#161-failed-to-authenticate-ssh-session-error-on-windows)\n- [17. License](#17-license)\n\n## 1. Install\n\n* Linux: `curl -Ls https://github.com/aerys/gpm-packages/raw/master/gpm-linux64/gpm-linux64.tar.gz | tar xvz`\n* Windows:\n  * cmd.exe: `curl -Ls https://github.com/aerys/gpm-packages/raw/master/gpm-windows64/gpm-windows64.tar.gz | tar xvzf -`\n  * 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`\n\n## 2. Background\n\nAs [a software company](https://aerys.in), we use Git to manage our source code.\nThus, we wanted to **use the same Git features we know and love to manage the\npackages built from that source code**.\n\nWe also use Git-enabled collaborative platforms (GitLab, GitHub, Gitea...) to:\n\n* Build, package and deploy our code using CI/CD.\n* Authenticate and authorize clients to report issues and manage projects.\n\nAnd we wanted to leverage those collaborative features to **effortlessly\ndistribute packages to authorized users**.\n\n**Introducing GPM: the Git-based Package Manager.**\n\nGPM is a platform-agnostic package manager leveraging Git capabilities to\nstore, install and update packages. Thanks to GPM:\n\n* Any Git repository becomes a package repository.\n* Any Git-powered collaborative platform (GitLab, GitHub, Gitea...) becomes\na package management and distribution platform.\n\n## 3. Features\n\n* Free, open source and decentralized package management\n* 100% backed by the Git protocol\n* Fully compatible with the Git ecosystem (GitLab, GitHub, Gitea...):\n  * commit and manage packages via your favorite Git client\n  * authentication (password, private key, deploy key, deploy token...)\n  * commit packages via Git in CI pipelines\n  * protected package version branches/tags\n  * release page integration (ex: [GPM binary package releases](https://github.com/aerys/gpm-packages/releases))\n  * [Git LFS](https://git-lfs.github.com/) support\n  * release notes via Git tag messages\n* Distribute *any* package for *any* platform as a simple `*.tar.gz` archive\n* [Semver](https://semver.org) support\n* Public package repositories\n* Private package repositories with authentication:\n  * username/password, deploy token\n  * SSH private key, deploy key\n* Modern, intuitive and clean CLI interactive mode\n* Non-interactive mode for automation/deployments\n* Transparent SSH config (`~/.ssh/config`) discovery for authentication\n* Support for all the common Git protocols (HTTP(s), SSH...)\n* Security backed by standard libraries (libgit2, libssh2, OpenSSL...)\n* Lightweight (\u003c2,5Mo) fully static 0 dependency binary\n* Cross-platform: Windows, Linux, Android (Termux), macOS (untested)\n* One-liner installation\n* Local cache to speedup package discovery and matching\n\n## 4. Security\n\nGPM leverages other standard well maintained open source libraries for all\nsensitive operations:\n\n* All Git operations (clone, pull...) are performed using [the Rust bindings for libgit2](https://crates.io/crates/git2).\n* All SSH operations (Git LFS authentication) are performed using [the Rust bindings for libssh2](https://crates.io/crates/ssh2).\n* All SSL operations (HTTPS, SSH key management) are performed using [the Rust bindings for OpenSSL](https://crates.io/crates/openssl-sys).\n\n## 5. Build\n\n### 5.1. Development build\n\nDependencies:\n\n* OpenSSL\n\n```bash\ncargo build\n```\n\n### 5.2. Release (static) build\n\nDependencies:\n\n* Docker\n\n```bash\ndocker run \\\n    --rm -it \\\n    -v \"$(pwd)\":/home/rust/src \\\n    -v \"/home/${USER}/.cargo\":/home/rust/.cargo \\\n    ekidd/rust-musl-builder \\\n    cargo build --release --target x86_64-unknown-linux-musl\n```\n\n## 6. Getting started\n\n### 6.1. Creating a package repository\n\n1. Create a [Git LFS](https://git-lfs.github.com/) enabled Git repository, for example a GitHub or GitLab repository.\n2. [Install Git LFS](https://github.com/git-lfs/git-lfs/wiki/Installation) on your local computer.\n3. Clone the newly created repository on your local computer:\n\n```bash\ngit clone ssh://path.to/my/package-repository.git\ncd package-repository\n```\n\n4. Enable [Git LFS](https://git-lfs.github.com/) tracking for `*.tar.gz` files:\n\n```bash\ngit lfs track \"*.tar.gz\"\n```\n\n5. Add, commit and push `.gitattributes`:\n\n```bash\ngit add .gitattributes\ngit commit .gitattributes -m \"Enable Git LFS.\"\ngit push\n```\n\nVoilà! You're all set to publish your first package!\n\n### 6.2. Publishing your first package\n\nIn this example, we're going to create a simple `hello-world` package and publish it.\n\n1. Make sure you are at the root of the package repository created in the previous section.\n2. Create and enter the package directory:\n\n```bash\nmkdir hello-world \u0026\u0026 cd hello-world\n```\n\n3. Create the `hello-world.sh` script:\n\n```bash\necho \"#/bin/sh\\necho 'Hello World!'\" \u003e hello-world.sh\n```\n\n4. Create your package archive:\n\n```bash\ntar -cvzf hello-world.tar.gz hello-world.sh\n```\n\n5. Add and commit your package archive:\n\n```bash\ngit add hello-world.tar.gz\ngit commit hello-world.tar.gz -m \"Publish hello-world version 0.1.0\"\n```\n\n6. Tag your package release with a specific version number:\n\n```bash\ngit tag hello-world/0.1.0\n```\n\n7. Push your new package:\n\n```bash\ngit push\ngit push --tags\n```\n\nYour `hello-world/0.1.0` package is now stored in your package repository and can be installed using `gpm`!\n\n### 6.3. Installing your first package\n\n1. Install (or build) `gpm`.\n2. Add your package repository to the `gpm` sources:\n\n```bash\nmkdir -p ~/.gpm\necho \"ssh://path.to/my/package-repository.git\" \u003e\u003e ~/.gpm/sources.list\n```\n\n3. Update the `gpm` cache:\n\n```bash\ngpm update\n```\n\n4. Install your package:\n\n```bash\ngpm install hello-world=0.1.0 --prefix ~/\n```\n\nThe version `0.1.0`of your `hello-world` package is now installed and you can run it with `sh ~/hello-world.sh`.\n\n## 7. Authentication\n\n`gpm` will behave a lot like `git` regarding authentication.\n\nIf the repository is \"public\", then no authentication should be required.\n\nOtherwise, the following authentication methods are supported:\n\n* URL encoded HTTP basic authentication (ex: `https://username:password@host.com/repo.git`);\n* SSH public/private key.\n\nIf URL encoded HTTP basic authentication is used, no additional authentication is required.\nOtherwise, `gpm` will assume SSH public/private key authentication is used.\n\n**Attention**: Windows users please read [\"Failed to authenticate SSH session\" error on Windows](#failed-to-authenticate-ssh-session-error-on-windows).\n\nIf SSH public/private key authentication is used:\n\n* 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.\n* Otherwise, if `gpm` can find the `~/.ssh/config` file, parse it and find a matching host with the `IndentityFile` option; then the corresponding\npath to the SSH private key will be used.\n* Otherwise, if `gpm` can find the `~/.ssh/id_rsa` file, it is used as the SSH private key.\n* Otherwise, `gpm` will continue without authentication.\n\nIf the SSH private key requires a passphrase, then:\n\n* If the `GPM_SSH_PASS` environment variable is set/not empty, it is used as the passphrase.\n* Otherwise, `gpm` will prompt the user to type his passphrase.\n\n## 8. Package reference notation\n\n### 8.1. Package name\n\n#### 8.1.1. Shorthand notation\n\nThis is the most trivial, obvious and simple notation: simply use the package name.\n\nExample:\n\n```\ngpm install my-package\n```\n\n`gpm` will search by name for the specified package in all the available package\nrepositories. Thus, for such package reference to be found, you *must* make sure:\n\n* The corresponding package repository remote is listed in `~/.gpm/sources.list` (see\n[Working with multiple package repositories](#7-working-with-multiple-package-repositories)).\n* The cache has been updated by calling `gpm update`.\n\n#### 8.1.2. URI notation\n\nThe complete URI notation for a package is as follow:\n\n`${remote-uri}#${package}`\n\n* `remote-uri`: the full URI to the Git remote.\n* `package`: a shorthand or `${name}=${revision}` package reference.\n\nExample:\n\n```\ngpm install ssh://github.com/my/awesome-packages.git#my-package\n```\n\nIn this case, `gpm` will clone the corresponding Git repository and look for the package there.\n`gpm` will look for the specified package *only* in the specified repository.\n\n### 8.2. Package version\n\n#### 8.2.1. SemVer notation\n\nThe version of a package can be specified using the\n[SemVer](https://semver.org/) version requirement notation:\n\n`${package}${semver_req}`\n\n* `package`: the name of the package (ex: `my-package`).\n* `semver_req`: the semver version requirement (ex: `^0.4.2`). If not\nspecified, then the latest version will be installed.\n\nIt also allows parsing of `~x.y.z` and `^x.y.z` requirements as defined\nat https://www.npmjs.org/doc/misc/semver.html.\n\nTilde requirements specify a minimal version with some updates:\n\n```\n~1.2.3 := \u003e=1.2.3 \u003c1.3.0\n~1.2   := \u003e=1.2.0 \u003c1.3.0\n~1     := \u003e=1.0.0 \u003c2.0.0\n```\n\nCaret requirements allow SemVer compatible updates to a specified verion,\n`0.x` and `0.x+1` are not considered compatible, but `1.x and `1.x+1 are.\n\n0.0.x is not considered compatible with any other version. Missing minor and\npatch versions are desugared to 0 but allow flexibility for that value.\n\n```\n^1.2.3 := \u003e=1.2.3 \u003c2.0.0\n^0.2.3 := \u003e=0.2.3 \u003c0.3.0\n^0.0.3 := \u003e=0.0.3 \u003c0.0.4\n^0.0   := \u003e=0.0.0 \u003c0.1.0\n^0     := \u003e=0.0.0 \u003c1.0.0\n```\n\nWildcard requirements allows parsing of version requirements of the formats\n`*`, `x.*` and `x.y.*`.\n\n```\n*     := \u003e=0.0.0\n1.*   := \u003e=1.0.0 \u003c2.0.0\n1.2.* := \u003e=1.2.0 \u003c1.3.0\n```\n\nExample:\n\n```bash\ngpm install my-package\u003e=2.0.0\n```\n\n#### 8.2.2. Git refspec notation\n\nA package version can also be set to a valid\n[Git refspec](https://git-scm.com/book/en/v2/Git-Internals-The-Refspec),\nsuch as a specific branch or a tag, using the `@` operator:\n\n`${package}@${refspec}`\n\n* `package` is the name of the package (ex: `my-package`).\n* `refspec` is a valid Git refspec (ex: `refs/heads/my-branch` or `refs/tags/my-tag`).\n\n## 9. Matching package references\n\nThe following section explains how `gpm` finds the package archive for a\npackage named `${package_name}` at version `${package_version}`.\n\nThe following pseudo code explains how GPM will find packages for a specific\nversion:\n\n```\nif ${package_ref} includes remote URL\n    git clone URL\n\nif ${package_ref} does not include version\n    ${package_version} is set to \"@master\"\n\nfor each ${repository} in cache\n    git checkout master\n    git reset --hard\n\n    if ${package_version} is refspec\n        git checkout ${package_version}\n    else # assume ${package_version} is semver\n        for each ${tag} in ${repository}\n            if ${tag} matches semver\n                git checkout ${tag}\n    \n    if file ${package_name}/${package_name}.tgz exists\n        if ${package_name}/${package_name}.tgz is LFS link\n            download ${package_name}/${package_name}.tgz\n        extract ${package_name}/${package_name}.tgz\n```\n\n## 10. Working with multiple package repositories\n\nSpecifying a full package URI might not be practical. It's simpler to specify\na package refspec and let `gpm` find it. But where should it look for it?\n\nWhen you specify a package using a refspec, `gpm` will have to find the proper\npackage repository. It will look for this refspec in the repositories listed\nin `~/.gpm/sources.list`.\n\nThe following command lines will fill `sources.list` with a few (dummy)\npackage repositories:\n\n```bash\necho \"ssh://path.to/my/package-repository.git\" \u003e\u003e ~/.gpm/sources.list\necho \"ssh://path.to/my/another-repository.git\" \u003e\u003e ~/.gpm/sources.list\necho \"ssh://path.to/my/yet-another-repository.git\" \u003e\u003e ~/.gpm/sources.list\n# ...\n```\n\nAfter updating `sources.list`, don't forget to call `gmp update` to update the\ncache.\n\nYou can then install packages using their refspec.\n\n## 11. Logging\n\nLogs can be enable by setting the `GPM_LOG` environment variable to one of the\nfollowing values:\n\n* `trace`\n* `debug`\n* `info`\n* `warn`\n* `error`\n\nExample:\n\n```bash\nGPM_LOG=info gpm update\n```\n\nLogs can be *very* verbose. So it's best to keep only the `gpm` and `gitlfs`\nmodule logs. For example:\n\n```bash\nGPM_LOG=\"gpm=debug,gitlfs=debug\" gpm install hello-world/1.0.0\n```\n\n## 12. Commands\n\n### 12.1. `update`\n\nUpdate the cache to feature the latest revision of each repository listed in\n`~/.gpm/sources.list`.\n\nExample:\n\n```bash\n# first add at least one remote\necho \"ssh://github.com/my/awesome-packages.git\" \u003e\u003e ~/.gpm/sources.list\necho \"ssh://github.com/my/other-packages.git\" \u003e\u003e ~/.gpm/sources.list\n# ...\n# then you can run an update:\ngpm update\n```\n\n### 12.2. `clean`\n\nClean the cache. The cache is located in `~/.gpm/cache`.\nCache can be rebuilt using the `update` command.\n\n```bash\ngpm clean\n```\n\n### 12.3. `install`\n\nDownload and install a package.\n\nExample:\n\n```bash\n# install the \"app\" package at version 2.0.0 from repository\n# ssh://github.com/my/awesome-packages.git in the /var/www/app folder\ngpm install ssh://github.com/my/awesome-packages.git#app/2.0.0 \\\n    --prefix /var/www/app\n```\n\n```bash\n# assuming the repository ssh://github.com/my/awesome-packages.git is in\n# ~/.gpm/sources.list and the cache has been updated using `gpm update`\ngpm install app/2.0.0 --prefix /var/www/app\n```\n\n### 12.4. `download`\n\nDownload a package in the current working directory.\n\nExample:\n\n```bash\n# install the \"app\" package at version 2.0.0 from repository\n# ssh://github.com/my/awesome-packages.git in the /var/www/app folder\ngpm download ssh://github.com/my/awesome-packages.git#app/2.0.0 \\\n    --prefix /var/www/app\n```\n\n```bash\n# assuming the repository ssh://github.com/my/awesome-packages.git is in\n# ~/.gpm/sources.list and the cache has been updated using `gpm update`\ngpm download app/2.0.0 --prefix /var/www/app\n```\n\n## 13. Integrations\n\n### 13.1. Travis CI\n\nHere is a working Bash script to publish a package from Travis CI:\n[script/publish.sh](script/publish.sh).\n\n### 13.2. AppVeyor\n\nHere is a working PowerShell script to publish a package from AppVeyor:\n[script/publish.ps1](script/publish.ps1).\n\n### 13.3. GitLab CI\n\nHere is a template to publish a package from GitLab CI:\n\n```yml\n.gpm_publish_template: \u0026gpm_publish_template\n  stage: publish\n  image:\n    name: alpine/git:v2.24.1\n    entrypoint: [\"/bin/sh\", \"-c\"]\n  only:\n    - tags\n  resource_group: ${PACKAGE_REPOSITORY}\n  before_script:\n    - apk add git-lfs\n    - git lfs install --skip-repo\n  script:\n    - cd ${PACKAGE_ARCHIVE_ROOT} \u0026\u0026 tar -zcf /tmp/${PACKAGE_NAME}.tar.gz ${PACKAGE_CONTENT} \u0026\u0026 cd -\n    - mkdir -p ~/.ssh \u0026\u0026 echo -e \"Host *\\n  StrictHostKeyChecking no\\n  IdentityFile /tmp/package-repository-key\" \u003e ~/.ssh/config\n    - GIT_LFS_SKIP_SMUDGE=1 git clone ${PACKAGE_REPOSITORY} /tmp/package-repository\n    - mkdir -p /tmp/package-repository/${PACKAGE_NAME}\n    - mv /tmp/${PACKAGE_NAME}.tar.gz /tmp/package-repository/${PACKAGE_NAME}\n    - cd /tmp/package-repository/${PACKAGE_NAME}\n    - git config --global user.email \"${GITLAB_USER_EMAIL}\"\n    - git config --global user.name \"${GITLAB_USER_NAME}\"\n    - git add ${PACKAGE_NAME}.tar.gz\n    - git commit ${PACKAGE_NAME}.tar.gz -m \"Publish ${PACKAGE_NAME} version ${PACKAGE_VERSION}.\"\n    - git tag -F \"${PACKAGE_CHANGELOG}\" \"${PACKAGE_NAME}/${PACKAGE_VERSION}\"\n    - git push\n    - git push --tags\n```\n\nand an example of how to use this template:\n\n```yml\npublish:\n  \u003c\u003c: *gpm_publish_template\n  variables:\n    PACKAGE_VERSION: ${CI_COMMIT_TAG} # the version of the package\n    PACKAGE_REPOSITORY: ssh://my.gitlab.com/my-packages-repository.git # the package repository to publish to\n    PACKAGE_NAME: ${CI_PROJECT_NAME} # the name of the package\n    PACKAGE_ARCHIVE_ROOT: ${CI_PROJECT_DIR}/bin # the folder containing the files to put in the package archive\n    PACKAGE_CONTENT: * # the globbing pattern/file list to put in the package archive\n    PACKAGE_CHANGELOG: ${CI_PROJECT_DIR}/CHANGELOG.md # the path to the changelog file\n```\n\nThis template relies on the `PACKAGE_REPOSITORY_KEY` GitLab CI variable.\nIt must contain a passphrase-less SSH deploy key (with write permissions) to\nyour package repository.\n\n### 13.4. GitLab Releases\n\nEach package can have its changelog available via the\n[GitLab Releases](https://about.gitlab.com/releases/) page of the corresponding\nGPM package repository.\n\nThe following GitLab CI job templates can be used to create a GitLab release\nfor a package:\n\n```yml\n.gitlab_release_template: \u0026gitlab_release_template\n  image: alpine:3.11.5\n  only:\n    - tags\n  dependencies:\n    - changelog\n  before_script:\n    - apk add jq curl\n  script: |\n    curl --fail --retry 5 \\\n      -X POST \\\n      -H 'Content-Type: application/json' \\\n      -H \"PRIVATE-TOKEN: ${RELEASE_AUTH_TOKEN}\" \\\n      https://gitlab.example.com/api/v4/projects/${RELEASE_PROJECT_ID}/releases\n      -d \"$(\n        jq -n \\\n          --arg name \"${RELEASE_NAME}\" \\\n          --arg tag_name \"${RELEASE_TAG}\" \\\n          --arg description \"$(cat ${RELEASE_NOTES})\" \\\n          {\n            name: $name,\n            tag_name: $tag_name,\n            description: $description\n          }\n      )\"\n```\n\n* `RELEASE_AUTH_TOKEN`: the GitLab Personal Access Token used for authentication.\n* `RELEASE_PROJECT_ID`: the ID of the GitLab package repository project to create the release on. (ex: `42`)\n* `RELEASE_TAG`: the tag of the released GPM package (ex: `my-awesome-package/0.42.3`).\n* `RELEASE_NAME`: the name of the released GPM package (ex: `My Awesome Package 0.42.3`).\n* `RELEASE_NOTES`: the path to the changelog file of the released GPM package.\n\nHere is an example of a CI job using that template:\n\n```yml\nrelease:\n  \u003c\u003c: *gitlab_release_template\n  variables:\n    RELEASE_AUTH_TOKEN: ${GITLAB_TOKEN}\n    RELEASE_TAG: my-awesome-package/${CI_COMMIT_TAG}\n    RELEASE_PROJECT_ID: 42\n    RELEASE_NAME: My Awesome Package ${CI_COMMIT_TAG}\n    RELEASE_NOTES: ${CI_PROJECT_DIR}/CHANGELOG.md\n```\n\n### 13.5. GitHub Releases\n\nEach package can have its changelog available via the\n[GitHub Releases](https://help.github.com/en/github/administering-a-repository/managing-releases-in-a-repository)\npage of the corresponding GPM package repository.\n\nThe [gpm-packages release page](https://github.com/aerys/gpm-packages/releases)\nis a good example of such integration.\n\nThe following `curl` command can be used to create a release for a package:\n\n```yml\ncurl --fail --retry 5 \\\n    -X POST \\\n    -H \"Content-Type:application/json\" \\\n    -H \"Authorization: token ${RELEASE_AUTH_TOKEN}\" \\\n    https://api.github.com/repos/${RELEASE_PROJECT}/releases \\\n    -d \"$(\n        jq -n \\\n            --arg tag_name \"${RELEASE_TAG}\" \\\n            --arg name \"${RELEASE_NAME}\" \\\n            --arg body \"$(cat ${RELEASE_NOTES})\" \\\n            '{\n                tag_name: $tag_name,\n                name: $name,\n                body: $body,\n                draft: false,\n                prerelease: false\n            }'\n    )\"\n```\n\n* `RELEASE_AUTH_TOKEN`: the GitHub Personal Access Token used for authentication.\n* `RELEASE_PROJECT`: the namespaced name of the GitHub package repository to create the release on (ex: `aerys/gpm-packages`).\n* `RELEASE_TAG`: the tag of the released GPM package (ex: `my-awesome-package/0.42.3`).\n* `RELEASE_NAME`: the name of the released GPM package (ex: `My Awesome Package 0.42.3`).\n* `RELEASE_NOTES`: the path to the changelog file of the released GPM package.\n\nSuch release creation mechanism can easily be integrated in the CI.\n\nGPM's own GitLab CI configuration features\n[a reusable job template](https://github.com/aerys/gpm/blob/0.13.3/.gitlab-ci.yml#L33)\nto do exactly that. The result is visible on the\n[release page of the gpm-packages repository](https://github.com/aerys/gpm-packages/releases).\n\n## 14. FAQ\n\n### 14.1. Why GPM?\n\nGPM means \"Git-based Package Manager\".\n\nThe main motivation is to have a platform-agnostic package manager, mainly\naimed at distributing binary packages as archives. GPM can be used to\nleverage any Git repository as a package repository.\n\nPlatforms like GitLab and GitHub are then very handy to manage such package\narchives, permissions, etc...\n\nGPM is also available as an all-in-one static binary.\nIt can be leveraged to download some packages that will be used to bootstrap\na more complex provisioning process.\n\n### 14.2. Why Git? Why not just `curl` or `wget` or whatever?\n\nGPM aims at leveraging the Git ecosystem and features.\n\nGit is great to manage revisions. So it's great at managing package versions!\nFor example, Git is also used by the Docker registry to store Docker images.\n\nGit also has a safe and secured native authentication/authorization strategy\nthrough SSH. With GitLab, you can safely setup\n[deploy keys](https://docs.gitlab.com/ce/ssh/README.html#deploy-keys) to give a\nread-only access to your packages.\n\n### 14.3. But Git does not like large binary files!\n\nYes. Cloning a repository full of large binary files can take a lot of time\nand space. You certainly don't want to checkout all the versions of all your\npackages everytime you want to install one of them.\n\nThat's why you should use [Git LFS](https://git-lfs.github.com/) for your\nGPM repositories.\n\nThanks to [Git LFS](https://git-lfs.github.com/), GPM will download the a\nactual binary package only when it is required.\n\n### 14.4. Why storing packages as `*.tar.gz` archives?\n\nVanilla Git will compress objects. But [Git LFS](https://git-lfs.github.com/)\ndoesn't store objects in the actual Git repository: they are stored \"somewhere\nelse\".\n\nTo make sure we don't use too much disk space/bandwidth \"somewhere else\", the\npackage archive is stored compressed.\n\n## 15. Contributing\n\n* [Bug tracker](https://github.com/aerys/gpm/issues?q=is%3Aopen+is%3Aissue+label%3Abug)\n* [Questions](https://github.com/aerys/gpm/issues?q=is%3Aopen+is%3Aissue+label%3Aquestion)\n\n## 16. Troubleshooting\n\n### 16.1. \"Failed to authenticate SSH session\" error on Windows\n\nOn Windows, only SSH private keys in the PEM format are supported. This is a limitation of libssh2 specific to Windows. Not a limitation of GPM.\n\nTo convert your SSH private key to the PEM format, please use the\nfollowing command:\n\n```\nssh-keygen.exe -m pem -f .\\id_rsa -p\n```\n\n## 17. License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faerys%2Fgpm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faerys%2Fgpm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faerys%2Fgpm/lists"}