diff --git a/sharness/__snapshots__/example-github-load/projects/c291cmNlY3JlZC10ZXN0L2V4YW1wbGUtZ2l0aHVi/project.json b/sharness/__snapshots__/example-github-load/projects/c291cmNlY3JlZC10ZXN0L2V4YW1wbGUtZ2l0aHVi/project.json index 01562ef..cdb4686 100644 --- a/sharness/__snapshots__/example-github-load/projects/c291cmNlY3JlZC10ZXN0L2V4YW1wbGUtZ2l0aHVi/project.json +++ b/sharness/__snapshots__/example-github-load/projects/c291cmNlY3JlZC10ZXN0L2V4YW1wbGUtZ2l0aHVi/project.json @@ -1 +1 @@ -[{"type":"sourcecred/project","version":"0.5.0"},{"discourseServer":null,"id":"sourcecred-test/example-github","identities":[],"initiatives":null,"repoIds":[{"name":"example-github","owner":"sourcecred-test"}]}] \ No newline at end of file +[{"type":"sourcecred/project","version":"0.5.1"},{"discourseServer":null,"id":"sourcecred-test/example-github","identities":[],"initiatives":null,"repoIds":[{"name":"example-github","owner":"sourcecred-test"}],"timelineCredParams":null}] \ No newline at end of file diff --git a/src/analysis/timeline/params.js b/src/analysis/timeline/params.js index 8ebfea6..180db79 100644 --- a/src/analysis/timeline/params.js +++ b/src/analysis/timeline/params.js @@ -62,7 +62,7 @@ export function defaultParams(): TimelineCredParameters { * Fill in default values for timeline cred parameters. */ export function partialParams( - partial: $Shape + partial: $Shape | null ): TimelineCredParameters { return {...defaultParams(), ...partial}; } diff --git a/src/cli/load.js b/src/cli/load.js index 4586312..a629401 100644 --- a/src/cli/load.js +++ b/src/cli/load.js @@ -6,7 +6,7 @@ import {LoggingTaskReporter} from "../util/taskReporter"; import type {Command} from "./command"; import * as Common from "./common"; import * as Weights from "../core/weights"; -import {projectFromJSON} from "../core/project"; +import {projectFromJSON, type Project} from "../core/project"; import {load} from "../api/load"; import {specToProject} from "../plugins/github/specToProject"; import fs from "fs-extra"; @@ -14,7 +14,7 @@ import {type PluginDeclaration} from "../analysis/pluginDeclaration"; import {declaration as discourseDeclaration} from "../plugins/discourse/declaration"; import {declaration as githubDeclaration} from "../plugins/github/declaration"; import {declaration as identityDeclaration} from "../plugins/identity/declaration"; -import {defaultParams} from "../analysis/timeline/params"; +import {partialParams} from "../analysis/timeline/params"; function usage(print: (string) => void): void { print( @@ -126,7 +126,7 @@ const loadCommand: Command = async (args, std) => { const taskReporter = new LoggingTaskReporter(); - const specProjects = await Promise.all( + const specProjects: $ReadOnlyArray = await Promise.all( projectSpecs.map((s) => specToProject(s, githubToken)) ); const manualProjects = await Promise.all(projectPaths.map(loadProject)); @@ -142,9 +142,11 @@ const loadCommand: Command = async (args, std) => { if (project.identities.length) { plugins.push(identityDeclaration); } + const params = partialParams(project.timelineCredParams); + return { project, - params: defaultParams(), + params, weightsOverrides: weights, plugins, sourcecredDirectory: Common.sourcecredDirectory(), diff --git a/src/core/project.js b/src/core/project.js index 7170087..9c22536 100644 --- a/src/core/project.js +++ b/src/core/project.js @@ -6,6 +6,7 @@ import {toCompat, fromCompat, type Compatible} from "../util/compat"; import {type ProjectParameters as Initiatives} from "../plugins/initiatives/params"; import {type Identity} from "../plugins/identity/identity"; import {type DiscourseServer} from "../plugins/discourse/server"; +import type {TimelineCredParameters} from "../analysis/timeline/params"; export type ProjectId = string; @@ -25,22 +26,24 @@ export type ProjectId = string; * the future (e.g. showing the last update time for each of the project's data * dependencies). */ -export type Project = ProjectV050; +export type Project = ProjectV051; export type SupportedProject = | ProjectV030 | ProjectV031 | ProjectV040 + | ProjectV051 | ProjectV050; -export type ProjectV050 = {| +export type ProjectV051 = {| +id: ProjectId, +initiatives: Initiatives | null, +repoIds: $ReadOnlyArray, +discourseServer: DiscourseServer | null, +identities: $ReadOnlyArray, + +timelineCredParams: $Shape | null, |}; -const COMPAT_INFO = {type: "sourcecred/project", version: "0.5.0"}; +const COMPAT_INFO = {type: "sourcecred/project", version: "0.5.1"}; /** * Creates a new Project instance with default values. @@ -57,6 +60,7 @@ export function createProject(p: $Shape): Project { identities: [], discourseServer: null, initiatives: null, + timelineCredParams: null, ...p, }; } @@ -79,11 +83,25 @@ export function encodeProjectId(id: ProjectId): string { return base64url.encode(id); } -const upgradeFrom040 = (p: ProjectV040): ProjectV050 => ({ +const upgradeFrom050 = (p: ProjectV050): ProjectV051 => ({ ...p, - initiatives: null, + timelineCredParams: {}, }); +export type ProjectV050 = {| + +id: ProjectId, + +initiatives: Initiatives | null, + +repoIds: $ReadOnlyArray, + +discourseServer: DiscourseServer | null, + +identities: $ReadOnlyArray, +|}; + +const upgradeFrom040 = (p: ProjectV040) => + upgradeFrom050({ + ...p, + initiatives: null, + }); + export type ProjectV040 = {| +id: ProjectId, +repoIds: $ReadOnlyArray, @@ -124,4 +142,5 @@ const upgrades = { "0.3.0": upgradeFrom030, "0.3.1": upgradeFrom030, "0.4.0": upgradeFrom040, + "0.5.0": upgradeFrom050, }; diff --git a/src/core/project.test.js b/src/core/project.test.js index e6dc398..08ad85d 100644 --- a/src/core/project.test.js +++ b/src/core/project.test.js @@ -15,6 +15,7 @@ import { import {makeRepoId} from "../plugins/github/repoId"; import {toCompat} from "../util/compat"; +import type {ProjectV050} from "./project"; describe("core/project", () => { const foobar = deepFreeze(makeRepoId("foo", "bar")); @@ -25,6 +26,7 @@ describe("core/project", () => { discourseServer: null, initiatives: null, identities: [], + timelineCredParams: null, }); const p2: Project = deepFreeze({ id: "@foo", @@ -37,6 +39,7 @@ describe("core/project", () => { aliases: ["github/example"], }, ], + timelineCredParams: null, }); describe("to/from JSON", () => { it("round trip is identity", () => { @@ -74,6 +77,7 @@ describe("core/project", () => { // It should strip the apiUsername field, keeping just serverUrl. discourseServer: {serverUrl: "https://example.com"}, initiatives: null, + timelineCredParams: {}, }: Project) ); }); @@ -103,6 +107,7 @@ describe("core/project", () => { // It should strip the apiUsername field, keeping just serverUrl. discourseServer: {serverUrl: "https://example.com"}, initiatives: null, + timelineCredParams: {}, }: Project) ); }); @@ -128,6 +133,33 @@ describe("core/project", () => { ...body, // It should add a default initiatives field. initiatives: null, + timelineCredParams: {}, + }: Project) + ); + }); + it("should upgrade from 0.5.0 formatting", () => { + // Given + const body: ProjectV050 = { + id: "example-050", + repoIds: [foobar, foozod], + discourseServer: {serverUrl: "https://example.com"}, + identities: [], + initiatives: null, + }; + const compat = toCompat( + {type: "sourcecred/project", version: "0.5.0"}, + body + ); + + // When + const project = projectFromJSON(compat); + + // Then + expect(project).toEqual( + ({ + ...body, + // It should add default params field. + timelineCredParams: {}, }: Project) ); }); @@ -169,6 +201,7 @@ describe("core/project", () => { initiatives: null, repoIds: [], identities: [], + timelineCredParams: null, }); }); it("treats input shape as overrides", () => { @@ -185,6 +218,7 @@ describe("core/project", () => { aliases: ["github/example"], }, ], + timelineCredParams: {alpha: 0.2, intervalDecay: 0.5}, }; // When diff --git a/src/core/project_io.test.js b/src/core/project_io.test.js index 9c2cee9..03f972f 100644 --- a/src/core/project_io.test.js +++ b/src/core/project_io.test.js @@ -38,6 +38,7 @@ describe("core/project_io", () => { discourseServer: {serverUrl: "https://example.com"}, identities: [{username: "foo", aliases: ["github/foo", "discourse/foo"]}], initiatives: {remoteUrl: "https://example.com/initiatives"}, + timelineCredParams: {alpha: 0.2, intervalDecay: 0.5}, }); it("setupProjectDirectory results in a loadable project", async () => {