anoma / juvix

A language for intent-centric and declarative decentralised applications
https://docs.juvix.org
GNU General Public License v3.0
457 stars 53 forks source link

Add support for dependency package sets #2905

Open paulcadman opened 4 months ago

paulcadman commented 4 months ago

Thanks to @janmasrovira for help with the design.

This issue proposes the introduction of dependency package sets (similar to stackage resolver) for use in Juvix Package.juvix.

Definitions

A package set is a collection of package versions and a Juvix compiler version that are known to work together.

A registry is a git repository that hosts versioned package sets.

Use case

Currently users must specify the versions of each dependency explicitly in the Package.juvix file. This is the most flexible way of supporting dependency versions. However the core packages that we maintain (e.g stdlib, containers, test) are updated to work together and with the latest compiler. This information is not known to the user. This issue proposes to bundle together versions of the core packages that are known to work together into a package-set that the user can depend on in the Package.juvix file.

PackageDescription.V3

Existing Package.juvix files will remain unchanged and they will work the same as before. To use a package set users must opt in to PackageDescription.V3. A draft of this module is available.

Package examples

Package declaration with default / official registry and extra dependencies that are not specified in the registry.

package : Package :=
  usingDefaultRegistry@{
    name := "myPackage";
    version := mkVersion 1 0 0;
    packageSetVersion := mkVersion 0 1 0;
    dependencies := ["stdlib"; "containers"; "anoma-stdlib"];
    extraDependencies := [github "anoma" "anoma-app-patterns" "v0.1.3"];
    overridingDependencies := [dependencyOverride "test" "v0.20.0"]
  };

Package declaration with a custom registry location.

paulsPackageSet : PackageSet :=
  mkPackageSet@{
    registryLocation :=
      githubLocation@{
        org := "paulcadman";
        repo := "juvix-registry"
      };
    packageSetVersion := mkVersion 0 1 0
  };

package : Package :=
  usingPackageSet@{
    packageSet := paulsPackageSet;
    name := "myPackage";
    version := mkVersion 2 0 0;
    dependencies := ["pauls-super-lib"]
  };

Package Registry

The definition of a package set is stored in a package registry. This is a git repository with the following structure:

.
├── metadata
│   ├── containers.yaml
│   ├── stdlib.yaml
│   └── test.yaml
└── package-set
    └── 0.1.0.yaml
    └── 0.2.0.yaml
    └── 0.2.1.yaml

The metadata directory contains information about each package that are common to every version, for example its location:

test.yaml

location:
  github:
    org: anoma
    repo: juvix-test

The package-set directory contains a file for each package-set version that lists the supported compiler version and the supported versions of each package. The version of each package refers to the git ref at the corresponding location defined in the packages metadata file.

0.1.0.yaml

compiler: 0.6.3
packages:
  stdlib: v0.4.0
  containers: v0.12.1
  test: v0.11.0
  quickcheck: v0.12.0
  anoma-stdlib: v0.2.0
  anoma-test: v0.1.1

Default registry

As described above a default registry location would be built into the compiler, for example we could host it at "https://github.com/anoma/juvix-registry".

Users may define a custom registry location in the Package.juvix or override the default registry location using a compiler flag:

For example:

juvix --default-package-registry 'https://github.com/paulcadman/my-juvix-registry' typecheck

overridingDependencies and extraDependencies fields

The extraDependencies field is to declare dependencies that are not present in the package-set.

The overridingDependencies field is to declare a dependency which overrides an existing dependency in the package-set.

Behaviour

If a package registry is used, juvix will resolve dependencies against the registry's package-set.

The following error cases apply:

PackageDescription defaultPackage

The behaviour of defaultPackage will not change. That is, it creates a Package that depends only on the defaultStdlib bundled with the compiler.