Halibot / halibot

The world's greatest saltwater multi-protocol chat bot framework!
https://halibot.fish
BSD 3-Clause "New" or "Revised" License
7 stars 6 forks source link

JSON per-package config #101

Open sjrct opened 6 years ago

sjrct commented 6 years ago

Storing package metadata within the python code of a package itself, namely that this involves invoking the python interpreter as a prerequisite to consuming this data. This is a problem primarily because when checking the minimum halibot or python version, the loading of the code may fail on a version mismatch while it would work properly with the correct version. It is not clear to the programmer that it is because of a version mismatch or from an actually improperly implemented package. Also, in general, it feels ugly to have to read in the whole of the module when all you care about is a little bit of metadata.

The proposed solution is to use a per-packge JSON configuration file, named fish.json. This removes the need to load the actual python code and also provides a singular place to store all package metadata. Another goal here is also to allow for the implementation of easy package deployment schemes.

Proposed fish.json schema

Right now I put the halibot dependencies within the pythonDependencies field, the same field that contains the the list of other python dependencies. I feel like this will be the most elegant, especially when we do end up deploying halibot as a python package in pip or similar, but I could be convinced otherwise.

{
  "title": "Package configuration",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "The package name",
    },
    "description": {
      "description": "A description of the package"
      "oneOf"
    },
    "keywords": {
      "type": "string",
      "description": "Keywords associated with the package to aid in searching"
    },
    "author": {
      "description": "The author or authors of the package",
      "oneOf": [
        {
          "$ref": "#/author"
        },
        {
          "type": "array",
          "items": { "$ref": "#author" }
        }
      ]
    },
    "license": {
      "type": "string",
      "description": "Name of license the package is licensed under"
    },
    "source": {
      "type": [ "string", "object" ],
      "description": "The location of a the source code, either just a URL or an object containing the URL and associated metadata",
      "required": [ "url" ],
      "properties": {
        "url": {
          "type": "string",
          "description": "The URL of the source code"
        },
        "type": {
          "type": "string",
          "description": "The type of resource pointed to by the URL",
          "enum": [ "git", "svn", "hg", "tar" ]
        }
      },
      "additionalProperties": false
    },
    "version": {
      "$ref": "#version",
      "description": "The version of this package",
    },
    "dependencies": {
      "type": "object",
      "description": "A map of halibot module dependency names to required versions",
      "additionalProperties": {
        "$ref": "#version-range"
      }
    },
    "pythonDependencies": {
      "type": "object",
      "description": "A map of python dependency names to required versions",
      "required": [ "halibot" ],
      "additionalProperties": {
        "$ref": "#version-range"
      }
    }
  },
  "required": [ "name", "version", "pythonDependencies" ],
  "additionalProperties": false
}

#author schema

{
  "type": "object",
  "properties": {
    "name":  { "type": "string" },
    "email": { "type": "string" }
  },
  "additionalProperties": false
}

#version schema

This could be expanded to included other possible versioning schemes

{
  "type": "string",
  "pattern": "[0-9]+(\.[0-9]+(\.[0-9]+)?)?"
}

#version-range schema

These regular expressions are not valid, but they are much easier to read. Replace {#version} with the patterns for the #version schema. Should probably also have keyword versions, like "latest".

{
  "type": "string",
  "oneOf": [
    { "pattern": "(<|<=|>|>=)?{#version}" },
    { "pattern": "{#version}-{#version}" }
  ]
}

Example usage

{
  "name": "haiku",
  "description": "Makes prose into poems",
  "author": {
    "name": "Chris Harding",
    "email": "sjrct0@gmail.com"
  },
  "version": "1.2.0",
  "license": "BSD-3",
  "source": "https://github.com/halibot-extra/haiku",
  "dependencies": {
    "syllabizer": "0.5 - 1.2.3"
  },
  "pythonDependencies": {
    "halibot": ">= 0.2",
    "pyhaiku": "< 4.5"
  }
}
richteer commented 6 years ago

Regarding dependencies - I think it makes sense to include halibot version as part of the pythonDependencies, though that might be slightly confusing considering dependencies is really halibot-specific dependencies. Should it maybe just be a top-level key? That way both dependencies and pythonDependencies can be optional keys, and continue to enforce halibot-version or whatever as "mandatory".

And on optional keys -- I assume most of these are recommended but not mandatory (for loading, at least)? We should probably enforce certain ones for publishing to the repo server, but probably aren't as useful for just trying to get a module started. Not sure if you want to include which are optional or not -- might be worth including a summary table of the required & optional keys.

Otherwise, I don't think I have much else to comment on, looks good.

sjrct commented 6 years ago

I was thinking name, version, and the halibot version would be required, otherwise optional.

sjrct commented 6 years ago

An author an description should probably be required for publishing to a repository, although I imagine that would truly be up to the repo's admin.