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:               -->