// Releases: a git tag with notes and downloadable files (binaries, installers, checksums). The // files themselves are kept on disk under data/releases/; these records describe them. const text = (name, opts = {}) => ({ type: "text", name, max: 1000, ...opts }); const number = (name, opts = {}) => ({ type: "number", name, onlyInt: true, ...opts }); const time = (name, opts = {}) => number(name, { min: 0, ...opts }); const relation = (name, collection, opts = {}) => ({ type: "relation", name, collectionId: collection.id, maxSelect: 1, cascadeDelete: false, ...opts, }); const parent = (name, collection) => relation(name, collection, { required: true, cascadeDelete: true }); function create(app, name, fields, indexes = []) { const collection = new Collection({ type: "base", name, listRule: null, viewRule: null, createRule: null, updateRule: null, deleteRule: null, fields, indexes, }); app.save(collection); return collection; } migrate( (app) => { const users = app.findCollectionByNameOrId("users"); const repos = app.findCollectionByNameOrId("repos"); const releases = create( app, "releases", [ parent("repo", repos), // The tag name without refs/tags/. A draft's tag may not exist yet. text("tag", { required: true, max: 255 }), // The branch or commit the tag is created from, if it doesn't exist when the release is published text("target", { max: 255 }), text("title", { max: 256 }), text("body", { max: 65536 }), { type: "bool", name: "draft" }, { type: "bool", name: "prerelease" }, relation("author", users), time("created_at", { required: true }), time("updated_at", { required: true }), // 0 while it's a draft time("published_at"), ], ["CREATE UNIQUE INDEX idx_releases_repo_tag ON releases (repo, tag)", "CREATE INDEX idx_releases_published ON releases (repo, published_at)"], ); create( app, "release_assets", [ parent("release", releases), text("name", { required: true, max: 255 }), number("size", { min: 0 }), // hex text("sha256", { required: true, max: 64 }), number("download_count", { min: 0 }), relation("uploader", users), time("created_at", { required: true }), // When the file was last replaced time("updated_at", { required: true }), ], ["CREATE UNIQUE INDEX idx_release_assets_name ON release_assets (release, name)"], ); }, (app) => { app.delete(app.findCollectionByNameOrId("release_assets")); app.delete(app.findCollectionByNameOrId("releases")); }, );