github.com/replit/upm@v0.0.0-20240423230255-9ce4fc3ea24c/README.md (about) 1 # UPM 2 3 [![GoDoc](https://godoc.org/github.com/replit/upm?status.svg)](https://godoc.org/github.com/replit/upm) 4 [![Run on Repl.it](https://repl.it/badge/github/replit/upm)](https://replit.com/new/github/replit/upm) 5 6 UPM is the **Universal Package Manager**. It allows you to manage 7 packages for any (supported) programming language through the same 8 interface following the [principle of least 9 astonishment](https://en.wikipedia.org/wiki/Principle_of_least_astonishment). 10 At [Repl.it](https://repl.it/), we use UPM to provide deep package 11 manager integration for many different programming languages using the 12 same infrastructure. 13 14 UPM does not implement package management itself. Instead, it runs a 15 package manager for you. The value added is: 16 17 * you don't have to figure out whether to use Pip or Pipenv or Poetry 18 to manage your Python packages or wade into the Cabal-versus-Stack 19 holy war in Haskell-land 20 * you don't have to puzzle out why `pip search flask` doesn't return 21 Flask in the search results 22 * you don't have to debug Bundler silently dropping your command-line 23 options if you don't specify them in the right (undocumented) order 24 * you don't have to think about why the developers of NPM and Yarn 25 decided to implement two completely different and mutually 26 incompatible behaviors for `list --depth=0`, neither of which is 27 exactly what you want 28 * you don't have to investigate what format the Yarn lockfile is in 29 (turns out: almost YAML, but not quite) 30 * et cetera (I could go on all day) 31 32 In other words, UPM eliminates the need to remember a huge collection 33 of language-specific package manager quirks and weirdness, and adds a 34 few nifty extra features like dependency guessing and 35 machine-parseable specfile and lockfile listing. 36 37 ## Supported languages 38 39 * Core: `upm add`, `upm remove`, `upm lock`, `upm install`, `upm list` 40 * Index: `upm search`, `upm info` 41 * Guess: `upm guess` 42 43 | | core | index | guess | 44 |-----------------------|------|-------|-------| 45 | python-python3-pip | yes | yes | yes | 46 | python-python3-poetry | yes | yes | yes | 47 | nodejs-yarn | yes | yes | yes | 48 | nodejs-pnpm | yes | yes | yes | 49 | nodejs-npm | yes | yes | yes | 50 | ruby-bundler | yes | yes | | 51 | elisp-cask | yes | yes | yes | 52 | dart-pub.dev | yes | yes | | 53 | rlang | yes | yes | | 54 | java | yes | yes | | 55 | rust | yes | yes | | 56 | dotnet | yes | yes | | 57 | php | yes | yes | | 58 59 ## Installation 60 61 You have many options. UPM is a single binary with no dependencies, so 62 you can install it anywhere. Tarballs are available on the [releases 63 page](https://github.com/replit/upm/releases). Read on for 64 instructions on installing via a package manager. 65 66 ### macOS 67 68 Available on [Homebrew](https://brew.sh/) in a [custom 69 tap](https://docs.brew.sh/Taps). 70 71 $ brew install replit/tap/upm 72 73 ### Debian-based Linux 74 75 .deb packages are available on the [releases 76 page](https://github.com/replit/upm/releases). 77 78 ### RPM-based Linux 79 80 .rpm packages are available on the [releases 81 page](https://github.com/replit/upm/releases). 82 83 ### Arch Linux 84 85 Soon to be available on the [Arch User 86 Repository](https://aur.archlinux.org/). Right now, you can clone this 87 repository and install with `makepkg` using the PKGBUILD in 88 [`packaging/aur`](packaging/aur). 89 90 ### Windows 91 92 Available on [Scoop](https://scoop.sh/) in a [custom 93 bucket](https://github.com/lukesampson/scoop/wiki/Buckets). 94 95 $ scoop bucket add replit https://github.com/replit/scoop-bucket.git 96 $ scoop install upm 97 98 ### Snappy 99 100 Soon to be available on the [Snap Store](https://snapcraft.io/store). 101 Right now, .snap packages are available on the [releases 102 page](https://github.com/replit/upm/releases). 103 104 ### Docker 105 106 You can try out UPM right away in a Docker image based on Ubuntu that 107 has all the supported languages and package managers already 108 installed. 109 110 $ docker run -it --rm replco/upm 111 112 Additional tags are also available. `replco/upm:full` is the same as 113 the above, while `replco/upm:light` just has the UPM binary installed 114 to `/usr/local/bin` and none of the languages or package managers 115 installed. If you want to run a specific tagged release, rather than 116 the latest development snapshot, use e.g. `replco/upm:1.0`, 117 `replco/upm:1.0-full`, or `replco/upm:1.0-light`. 118 119 ## Quick start 120 121 Let's create a new Python project: 122 123 $ mkdir ~/python 124 $ cd ~/python 125 126 We'll start by adding Flask as a dependency. UPM will handle setting 127 up the project for us: 128 129 $ upm -l python add flask 130 --> python3 -m poetry init --no-interaction 131 132 This command will guide you through creating your pyproject.toml config. 133 134 135 --> python3 -m poetry add flask 136 Creating virtualenv python-py3.7 in /root/.cache/pypoetry/virtualenvs 137 Using version ^1.1 for flask 138 139 Updating dependencies 140 Resolving dependencies... (0.6s) 141 142 Writing lock file 143 144 145 Package operations: 6 installs, 0 updates, 0 removals 146 147 - Installing markupsafe (1.1.1) 148 - Installing click (7.0) 149 - Installing itsdangerous (1.1.0) 150 - Installing jinja2 (2.10.1) 151 - Installing werkzeug (0.15.4) 152 - Installing flask (1.1.1) 153 154 UPM operates on a *specfile* and *lockfile* for each project. The 155 specfile says what your project's dependencies are in a human-readable 156 format, while the lockfile specifies exact versions for everything, 157 including transitive dependencies. For Python, the specfile is 158 `pyproject.toml` and the lockfile is `poetry.lock`: 159 160 $ ls 161 poetry.lock pyproject.toml 162 163 We don't have to read them ourselves, because UPM can handle that. 164 Notice that UPM is now aware that our project uses Python, because of 165 the files that were created: 166 167 $ upm list 168 name spec 169 ----- ---- 170 flask ^1.1 171 172 $ upm list -a 173 name version 174 ------------ ------- 175 click 7.0 176 flask 1.1.1 177 itsdangerous 1.1.0 178 jinja2 2.10.1 179 markupsafe 1.1.1 180 werkzeug 0.15.4 181 182 Let's search for another dependency to add: 183 184 $ upm search nose 185 --> python3 -c '<secret sauce>' nose 186 Name Description Version 187 ----------------- ---------------------------------------------------------------------- ------- 188 nose nose extends unittest to make testing easier 1.3.7 189 nose-detecthttp A nose plugin to detect tests making http calls. 1.1.0 190 nose-picker nose plugin that picks a subset of your unit tests 0.5.5 191 nose-progressive A testrunner with a progress bar and smarter tracebacks 1.5.2 192 nose-unittest UNKNOWN 0.1.1 193 nose-blockage Raise errors when communicating outside of tests 0.1.2 194 nose-watcher A nose plugin to watch for changes within the local directory. 0.1.3 195 nose-bisect A Nose plugin which allows bisection of test failures. 0.1.0 196 nose-printlog Print log to console in nose tests 0.2.0 197 nose-json A JSON report plugin for Nose. 0.2.4 198 nose-faulthandler Nose plugin. Activates faulthandler module for test runs. 0.1 199 nose-knows 0.2 200 nose-pagerduty PagerDuty alert plugin for nose 0.2.0 201 nose-logpertest Logging nose plugin to create log per test 0.0.1 202 nose-bleed A progressive coverage plugin for Nose. 0.5.1 203 nose-numpyseterr Nose plugin to set how floating-point errors are handled by numpy 0.1 204 nose-skipreq nose plugin that will skip Google API RequestError exceptions. 2.0 205 nose-selecttests Specify whitelist of keywords for tests to be run by nose 0.5 206 nose-pacman A testrunner with a pacman progress bar 0.1.0 207 nose-switch Add special switches in code, based on options set when running tests. 0.1.5 208 209 We can get more information about a package like this: 210 211 $ upm info nose 212 --> python3 -c '<secret sauce>' nose 213 Name: nose 214 Description: nose extends unittest to make testing easier 215 Version: 1.3.7 216 Homepage: http://readthedocs.org/docs/nose/ 217 Author: Jason Pellerin <jpellerin+nose@gmail.com> 218 License: GNU LGPL 219 220 For piping into other programs, the `search` and `info` commands can 221 also output JSON: 222 223 $ upm info nose --format=json | jq 224 --> python3 -c '<secret sauce>' nose 225 { 226 "name": "nose", 227 "description": "nose extends unittest to make testing easier", 228 "version": "1.3.7", 229 "homepageURL": "http://readthedocs.org/docs/nose/", 230 "author": "Jason Pellerin <jpellerin+nose@gmail.com>", 231 "license": "GNU LGPL" 232 } 233 234 UPM can also look at your project's source code and guess what 235 packages need to be installed. We use this on Repl.it to help 236 developers get started faster. To see it in action, we'll need some 237 source code: 238 239 $ git clone https://github.com/replit/play.git ~/play 240 $ cd ~/play 241 $ upm add --guess 242 --> python3 -c '<secret sauce>' '<secret sauce>' 243 --> python3 -m poetry init --no-interaction 244 245 This command will guide you through creating your pyproject.toml config. 246 247 248 --> python3 -m poetry add pygame pymunk setuptools 249 Creating virtualenv play-py3.7 in /root/.cache/pypoetry/virtualenvs 250 Using version ^1.9 for pygame 251 Using version ^5.5 for pymunk 252 Using version ^41.0 for setuptools 253 254 Updating dependencies 255 Resolving dependencies... (1.4s) 256 257 Writing lock file 258 259 260 Package operations: 4 installs, 0 updates, 0 removals 261 262 - Installing pycparser (2.19) 263 - Installing cffi (1.12.3) 264 - Installing pygame (1.9.6) 265 - Installing pymunk (5.5.0) 266 267 You can also just get the list of guessed dependencies, if you want. 268 The `-a` flag lists all guessed dependencies, even the ones already 269 added to the specfile: 270 271 $ upm guess -a 272 pygame 273 pymunk 274 setuptools 275 276 All of this might seem a bit too simple to justify a new tool, but the 277 real power of UPM is that it works exactly the same for every 278 programming language: 279 280 $ upm -l nodejs info express 281 Name: express 282 Description: Fast, unopinionated, minimalist web framework 283 Version: 4.17.1 284 Homepage: http://expressjs.com/ 285 Source code: git+https://github.com/expressjs/express.git 286 Bug tracker: https://github.com/expressjs/express/issues 287 Author: TJ Holowaychuk <tj@vision-media.ca> 288 License: MIT 289 290 $ upm -l ruby info jekyll 291 --> ruby -e '<secret sauce>' jekyll 292 Name: jekyll 293 Description: Jekyll is a simple, blog aware, static site generator. 294 Version: 3.8.6 295 Homepage: https://github.com/jekyll/jekyll 296 Documentation: http://jekyllrb.com 297 Source code: https://github.com/jekyll/jekyll 298 Bug tracker: https://github.com/jekyll/jekyll/issues 299 Author: Tom Preston-Werner 300 License: MIT 301 Dependencies: addressable, colorator, em-websocket, i18n, jekyll-sass-converter, jekyll-watch, kramdown, liquid, mercenary, pathutil, rouge, safe_yaml 302 303 $ upm -l elisp info elnode 304 --> emacs -Q --batch --eval '<secret sauce>' /tmp/elpa552971126 info elnode 305 Name: elnode 306 Description: The Emacs webserver. 307 Version: 20190702.1509 308 Dependencies: web, dash, noflet, s, creole, fakir, db, kv 309 310 That includes adding and removing packages, listing the specfile and 311 lockfile, searching package indices, and guessing project 312 dependencies. UPM knows all the best practices for each language so 313 that you don't have to! 314 315 ## Usage 316 317 Explore the command-line interface at your leisure: 318 319 $ upm --help 320 Usage: 321 upm [command] 322 323 Available Commands: 324 which-language Query language autodetection 325 list-languages List supported languages 326 search Search for packages online 327 info Show package information from online registry 328 add Add packages to the specfile 329 remove Remove packages from the specfile 330 lock Generate the lockfile from the specfile 331 install Install packages from the lockfile 332 list List packages from the specfile (or lockfile) 333 guess Guess what packages are needed by your project 334 show-specfile Print the filename of the specfile 335 show-lockfile Print the filename of the lockfile 336 show-package-dir Print the directory where packages are installed 337 help Help about any command 338 339 Flags: 340 -h, --help display command-line usage 341 --ignored-packages strings packages to ignore when guessing (comma-separated) 342 -l, --lang string specify project language(s) manually 343 -q, --quiet don't show what commands are being run 344 -v, --version display command version 345 346 Use "upm [command] --help" for more information about a command. 347 348 Here are useful things to know that aren't obvious: 349 350 * **Language detection:** Your project's language is autodetected by 351 the files in the current directory. This can be overridden either 352 partially or completely by specifying a value for the `-l` option. 353 You can see the available languages by running `upm list-languages`. 354 In addition to a full language (e.g. `python-python3-poetry`), you 355 can specify something simpler (e.g. `python`, `python3`, `python2`, 356 `poetry`, `python-poetry`). In that case, UPM will examine all of 357 the matching languages and pick whichever one it thinks is best. You 358 can experiment with this logic by providing the `-l` option to `upm 359 which-language`. 360 * **Information flow:** Conceptually, information about packages flows 361 one way in UPM: add/remove -> specfile -> lockfile -> installed 362 packages. You run `upm add` and `upm remove`, which modifies the 363 specfile, and then the lockfile is automatically generated from the 364 specfile, and then packages are automatically installed or 365 uninstalled according to the lockfile. Just running `upm add` or 366 `upm remove` will automatically perform all of these steps. Skipping 367 steps is unfortunately not supported, because few package managers 368 support that. You can however run only later steps in the pipeline 369 by means of the `upm lock` and `upm install` commands. 370 * **Caching:** UPM maintains a simple JSON cache in the `.upm` 371 subdirectory of your project, in order to improve performance. This 372 is used to (1) skip generating the lockfile from the specfile if the 373 specfile hasn't changed since last time; (2) skip reinstalling 374 packages from the lockfile if the lockfile hasn't changed since last 375 time; and (3) skip doing a full analysis of your code on `upm guess` 376 if your imports haven't actually changed since last time (according 377 to a quick regexp search). To reset the cache, you can delete that 378 directory. However, this shouldn't be necessary very often, because 379 you can use the `--force-lock` and `--force-install` options to `upm 380 add`, `upm remove`, `upm lock`, and `upm install` (it is just 381 `--force` for `upm install` due to lack of ambiguity) in order to 382 ignore the cache for cases (1) and (2). 383 384 ### Environment variables respected 385 386 * `UPM_PROJECT`: path to top-level directory containing project files. 387 UPM uses this as its working directory. Defaults to the first parent 388 directory containing a directory entry named `.upm` (like Git 389 searches for `.git`), or the current directory if `.upm` is not 390 found. 391 * `UPM_PYTHON2`: if nonempty, use instead of `python2` when invoking 392 Python 2. 393 * `UPM_PYTHON3`: if nonempty, use instead of `python3` when invoking 394 Python 3. 395 * `UPM_SILENCE_SUBROUTINES`: if nonempty, then enable `-q` when 396 running commands that are not directly related to the operation the 397 user requested (e.g. if running `upm add`, enable `-q` when reading 398 the specfile to check which packages are already added). 399 * `UPM_STORE`: path of file used to store the JSON cache file, 400 relative or absolute. Defaults to `.upm/store.json`. 401 402 ## Dependencies 403 404 UPM itself has no dependencies. It is a single statically-linked 405 binary. However, if you wish to actually use it to manage packages for 406 a language, then the relevant language package manager needs to be 407 installed, as follows: 408 409 * `python-python3-poetry`/`python-python2-poetry` 410 * [Python 2/3](https://www.python.org/) 411 * [Pip](https://pip.pypa.io/en/stable/) for appropriate version(s) 412 of Python 413 * [Poetry](https://poetry.eustace.io/) for appropriate version(s) of 414 Python 415 * `nodejs-yarn` 416 * [Node.js](https://nodejs.org/en/) 417 * [Yarn](https://yarnpkg.com/en/) for Yarn backend 418 * [NPM](https://www.npmjs.com/get-npm) for NPM backend 419 * `nodejs-pnpm` 420 * [Node.js](https://nodejs.org/en/) 421 * [PNPM](https://pnpm.io/) for PNPM backend 422 * [NPM](https://www.npmjs.com/get-npm) for NPM backend 423 * `ruby-bundler` 424 * [Ruby](https://www.ruby-lang.org/en/) 425 * [Bundler](https://bundler.io/) 426 * [json](https://ruby-doc.org/stdlib/libdoc/json/rdoc/JSON.html) 427 * `elisp-cask` 428 * [Emacs](https://www.gnu.org/software/emacs/) 429 * [Cask](https://github.com/cask/cask) 430 * [curl](https://curl.haxx.se/) (for `search` and `info`) 431 * [SQLite](https://www.sqlite.org/index.html) (for `guess`) 432 433 All of these dependencies are already installed in the 434 `replco/upm:full` Docker image. 435 436 ## Contributing 437 438 $ make help 439 usage: 440 make upm Build the UPM binary 441 make dev Run a shell with UPM source code and all package managers inside Docker 442 make light Build a Docker image with just the UPM binary 443 make full Build a Docker image with the UPM binary and all package managers 444 make doc Open Godoc in web browser 445 make deploy Publish UPM snapshot Docker images to Docker Hub 446 make pkgbuild Update and test PKGBUILD 447 make clean Remove build artifacts 448 make help Show this message 449 450 To build UPM, run `make upm` (or just `make`), the built 451 binary can be found in `./cmd/upm/upm`. To remove build 452 artifacts, run `make clean`. 453 454 ### Using Replit (simplest) 455 456 You can develop UPM on Replit. Simply click on [this](https://replit.com/new/github/replit/upm) 457 link and the repo will start cloning into a Repl. 458 459 Once you're in your new Repl, you can hit run 460 to build a new binary (or `make` in shell). You can create 461 a test folder and use the binary and test your changes. 462 For example say you made a change to the `nodejs-npm` language 463 and want to test it, you would hit run and do the following: 464 465 $ mkdir testnpm 466 $ cd testnpm 467 $ npm init -y 468 $ ../cmd/upm/upm add left-pad -l nodejs-npm 469 470 471 ### Using Docker 472 473 You can use [Docker](https://www.docker.com/) to avoid needing to 474 install the package managers that UPM drives. To do this, run `make 475 dev`. This will build an image and launch a shell inside the container 476 with the UPM source directory on your computer synced with the 477 filesystem inside the container. The same Makefile targets are 478 available, and UPM is added to the `$PATH` automatically. You only 479 need to restart the shell if you edit the Dockerfile or the scripts 480 used by the Dockerfile. Aliases available inside the shell: 481 482 * `l`: `ls -lAhF` 483 * `mt`: create temporary directory and cd to it (convenient for 484 switching to a new "project" context) 485 * `u`: build UPM binary if source code has been modified, then run 486 with given arguments 487 * `ub`: same as `u`, but force rebuilding binary (may be useful if you 488 previously built outside the Docker container) 489 490 To build a Docker image which has only the UPM binary, for embedding 491 in other images, run `make light`. The image will be tagged as 492 `upm:light`. Alternatively, to build a Docker image which has the 493 binary and all the package managers, but not the UPM source code, run 494 `make full`. The image will be tagged as `upm:full`. These two images 495 are automatically built and deployed to [Docker 496 Hub](https://hub.docker.com/r/replco/upm) when a commit is merged to 497 `master`. 498 499 UPM does not currently have any tests; however, we plan to fix this. 500 501 ### Deployment 502 503 Whenever a commit is merged to `master`, snapshot Docker images are 504 built and pushed to Docker Hub by CircleCI. Whenever a tagged release 505 (e.g. `v1.0`) is pushed to GitHub, the following happens: 506 507 * Release Docker images are tagged and pushed to Docker Hub. 508 * The [changelog](CHANGELOG.md) is parsed and published as a GitHub 509 Release with the following assets: 510 * source code (.zip and .tar.gz) 511 * binary (.tar.gz; darwin, freebsd, linux, and windows; 386 and 512 amd64) 513 * Debian package (.deb; 386 and amd64) 514 * RPM package (.rpm; 386 and amd64) 515 * Snappy package (.snap; 386 and amd64) 516 * checksums (.txt) 517 * The [Repl.it Homebrew tap](https://github.com/replit/homebrew-tap) 518 is updated. 519 * The [Repl.it Scoop bucket](https://github.com/replit/scoop-bucket) 520 is updated. 521 522 Once you push a release and it passes CI, the following must be done 523 **manually**: 524 525 * Edit [`packaging/aur/PKGBUILD`](packaging/aur/PKGBUILD) with the new 526 version and run `make pkgbuild` to update and test the AUR package. 527 Then push a new commit. (Once we are allowed to publish to AUR, you 528 should also push `packaging/aur` there.) 529 530 <!-- Local Variables: --> 531 <!-- truncate-lines: t --> 532 <!-- End: -->