diff --git a/scripts/lib/gitea-reviews.js b/scripts/lib/gitea-reviews.js index 4df6084..014be12 100644 --- a/scripts/lib/gitea-reviews.js +++ b/scripts/lib/gitea-reviews.js @@ -1,29 +1,25 @@ -const fetch = require('node-fetch'); +const axios = require('axios'); module.exports = class GiteaReviews { - token = null; + client = null; kreditsAmounts = null; - pageLimit = 100; + pageLimit = 50; constructor (token, kreditsAmounts) { - this.token = token; this.kreditsAmounts = kreditsAmounts; - } - async request (path) { - return fetch( - `https://gitea.kosmos.org/api/v1${path}`, - { - headers: { - 'accepts': 'application/json', - 'Authorization': `token ${this.token}` - } + this.client = axios.create({ + baseURL: 'https://gitea.kosmos.org/api/v1', + headers: { + 'accepts': 'application/json', + 'Authorization': `token ${token}` } - ).then(response => response.json()); + }); } async getReviewContributions (repos, startDate, endDate) { + let pulls = []; let reviewContributions = {} await Promise.all(repos.map(async (repo) => { @@ -32,17 +28,17 @@ module.exports = class GiteaReviews { do { try { - result = await this.request(`/repos/${repo}/pulls?state=closed&limit=${this.pageLimit}&page=${page}`); + result = await this.client.get(`/repos/${repo}/pulls?state=closed&limit=${this.pageLimit}&page=${page}`); } catch(error) { console.log(`failed to fetch PRs for repo ${repo}:`, error.message); continue; } - if (!result || result.length === 0) { + if (!result || !result.data || result.data.length === 0) { continue; } - let pullRequests = result.filter(pr => { + let pullRequests = result.data.filter(pr => { if (!pr.merged) return false; // only interested in merged PRs // check if the PR has been merged in the given timeframe @@ -56,17 +52,17 @@ module.exports = class GiteaReviews { await Promise.all(pullRequests.map(async (pr) => { let reviews; try { - reviews = await this.request(`/repos/${repo}/pulls/${pr.number}/reviews`); + reviews = await this.client.get(`/repos/${repo}/pulls/${pr.number}/reviews`); } catch(error) { console.log(`failed to fetch reviews for repo ${repo}, PR ${pr.number}:`, error.message); return; } - if (!reviews || reviews.length === 0) { + if (!reviews || !reviews.data || reviews.data.length === 0) { return; } - reviews = reviews.filter(review => { + reviews = reviews.data.filter(review => { return ['APPROVED', 'REJECTED'].includes(review.state); }); @@ -88,7 +84,7 @@ module.exports = class GiteaReviews { })); page++; - } while (result && result.length > 0); + } while (result && result.data && result.data.length > 0); })); return reviewContributions; diff --git a/scripts/lib/github-reviews.js b/scripts/lib/github-reviews.js index 3b210f8..708bb6f 100644 --- a/scripts/lib/github-reviews.js +++ b/scripts/lib/github-reviews.js @@ -1,30 +1,26 @@ -const fetch = require('node-fetch'); +const axios = require('axios'); module.exports = class GithubReviews { - token = null; + client = null; kreditsAmounts = null; pageLimit = 100; constructor (token, kreditsAmounts) { - this.token = token; this.kreditsAmounts = kreditsAmounts; - } - async request (path) { - return fetch( - `https://api.github.com${path}`, - { - headers: { - 'Accept': 'application/vnd.github.v3+json', - 'User-Agent': 'Kosmos Kredits for reviews', - 'Authorization': `token ${this.token}` - } + this.client = axios.create({ + baseURL: 'https://api.github.com', + headers: { + 'Accept': 'application/vnd.github.v3+json', + 'User-Agent': 'Kosmos Kredits for reviews', + 'Authorization': `token ${token}` } - ).then(response => response.json()); + }); } async getReviewContributions (repos, startDate, endDate) { + let pulls = []; let reviewContributions = {} await Promise.all(repos.map(async (repo) => { @@ -33,17 +29,17 @@ module.exports = class GithubReviews { do { try { - result = await this.request(`/repos/${repo}/pulls?state=closed&perPage=${this.pageLimit}&page=${page}`); + result = await this.client.get(`/repos/${repo}/pulls?state=closed&perPage=${this.pageLimit}&page=${page}`); } catch(error) { console.log(`failed to fetch PRs for repo ${repo}:`, error.message); continue; } - if (!result || result.length === 0) { + if (!result || !result.data || result.data.length === 0) { continue; } - let pullRequests = result.filter(pr => { + let pullRequests = result.data.filter(pr => { if (!pr.merged_at) return false; // only interested in merged PRs // check if the PR has been merged in the given timeframe @@ -57,17 +53,17 @@ module.exports = class GithubReviews { await Promise.all(pullRequests.map(async (pr) => { let reviews; try { - reviews = await this.request(`/repos/${repo}/pulls/${pr.number}/reviews`); + reviews = await this.client.get(`/repos/${repo}/pulls/${pr.number}/reviews`); } catch(error) { console.log(`failed to fetch reviews for repo ${repo}, PR ${pr.number}:`, error.message); return; } - if (!reviews || reviews.length === 0) { + if (!reviews || !reviews.data || reviews.data.length === 0) { return; } - reviews = reviews.filter(review => { + reviews = reviews.data.filter(review => { return ['APPROVED', 'REJECTED'].includes(review.state); }); @@ -89,7 +85,7 @@ module.exports = class GithubReviews { })); page++; - } while (result && result.length > 0); + } while (result && result.data && result.data.length > 0); })); return reviewContributions; diff --git a/scripts/review-kredits.js b/scripts/review-kredits.js index 6f41e51..951acc0 100755 --- a/scripts/review-kredits.js +++ b/scripts/review-kredits.js @@ -1,62 +1,63 @@ #!/usr/bin/env node -require('dotenv').config({ path: '.env' }); +require('dotenv').config(); const GiteaReviews = require('./lib/gitea-reviews'); const GithubReviews = require('./lib/github-reviews'); const ethers = require('ethers'); -const NonceManager = require('@ethersproject/experimental').NonceManager; const Kredits = require('kredits-contracts'); const util = require('util'); const yargs = require('yargs/yargs'); const { hideBin } = require('yargs/helpers'); -const fs = require('fs'); -const walletPath = process.env.KREDITS_WALLET_PATH || '../wallet.json'; -const walletJson = fs.readFileSync(walletPath); const providerUrl = process.env.KREDITS_PROVIDER_URL; -const daoAddress = process.env.KREDITS_DAO_ADDRESS; +const daoAddress = process.env.KREDITS_DAO_ADDRESS; const ipfsConfig = { host: process.env.IPFS_API_HOST || 'localhost', port: process.env.IPFS_API_PORT || '5001', - protocol: process.env.IPFS_API_PROTOCOL || 'http' + protocol: process.env.IPFS_API_PROTOCOL || 'http', }; const kreditsAmounts = { 'kredits-1': 100, 'kredits-2': 300, - 'kredits-3': 1000 + 'kredits-3': 1000, }; -const repos = require('../repos.json'); +const repos = require('./repos.json'); const argv = yargs(hideBin(process.argv)) .option('start', { alias: 's', - description: 'Include reviews for PRs merged after this date' + description: 'Include reviews for PRs merged after this date', }) .option('end', { alias: 'e', - description: 'Include reviews for PRs merged before this date' + description: 'Include reviews for PRs merged before this date', }) .option('dry', { alias: 'd', type: 'boolean', - description: 'Only list contribution details without creating them' + description: 'Only list contribution details without creating them', }) .help() .version() .demandOption('start', 'Please provide a start date') - .default('end', function now () { - return (new Date()).toISOString().split('.')[0]+"Z"; + .default('end', function now() { + return new Date().toISOString().split('.')[0] + 'Z'; }) .example([ - ['$0 --start 2020-11-01 --end 2020-11-30T23:59:59Z', 'Create contributions for reviews of pull requests merged in November 2020'], - ['$0 --start 2021-01-01', 'Create contributions for reviews of pull requests merged from Januar 2021 until now'], - ]) - .argv + [ + '$0 --start 2020-11-01 --end 2020-11-30T23:59:59Z', + 'Create contributions for reviews of pull requests merged in November 2020', + ], + [ + '$0 --start 2021-01-01', + 'Create contributions for reviews of pull requests merged from Januar 2021 until now', + ], + ]).argv; const startTimestamp = Date.parse(argv.start); const endTimestamp = Date.parse(argv.end); @@ -71,40 +72,28 @@ if (isNaN(endTimestamp)) { process.exit(1); } -// check for existence of GITHUB_TOKEN and GITEA_TOKEN -if (!process.env.GITHUB_TOKEN || !process.env.GITEA_TOKEN) { - console.log('Please set both GITHUB_TOKEN and GITEA_TOKEN'); - process.exit(1); -} - const startDate = new Date(startTimestamp); const endDate = new Date(endTimestamp); async function getAllReviews(repos, startDate, endDate) { - const githubReviews = new GithubReviews(process.env.GITHUB_TOKEN, kreditsAmounts); - const giteaReviews = new GiteaReviews(process.env.GITEA_TOKEN, kreditsAmounts); + const githubReviews = new GithubReviews( + process.env.GITHUB_TOKEN, + kreditsAmounts + ); + const giteaReviews = new GiteaReviews( + process.env.GITEA_TOKEN, + kreditsAmounts + ); return Promise.all([ githubReviews.getReviewContributions(repos.github, startDate, endDate), - giteaReviews.getReviewContributions(repos.gitea, startDate, endDate) - ]).then(reviews => { - return { github: reviews[0], gitea: reviews[1] } + giteaReviews.getReviewContributions(repos.gitea, startDate, endDate), + ]).then((reviews) => { + return { github: reviews[0], gitea: reviews[1] }; }); } -async function initializeKredits () { - // - // Ethereum wallet setup - // - - let wallet; - try { - wallet = await ethers.Wallet.fromEncryptedJson(walletJson, process.env.KREDITS_WALLET_PASSWORD); - } catch(error) { - console.log('Could not load wallet:', error); - process.exit(1); - } - +async function initializeKredits() { // // Ethereum provider/node setup // @@ -115,7 +104,6 @@ async function initializeKredits () { } else { ethProvider = new ethers.getDefaultProvider('rinkeby'); } - const signer = new NonceManager(wallet.connect(ethProvider)); // // Kredits contracts setup @@ -128,8 +116,8 @@ async function initializeKredits () { let kredits; try { - kredits = await new Kredits(signer.provider, signer, opts).init(); - } catch(error) { + kredits = await new Kredits(ethProvider, null, opts).init(); + } catch (error) { console.log('Could not set up kredits:', error); process.exit(1); } @@ -137,36 +125,28 @@ async function initializeKredits () { return kredits; } -function createContribution(contributorName, contributionAttributes, Contribution) { - console.log(`Creating contribution token for ${contributionAttributes.amount}₭S to ${contributorName} for ${contributionAttributes.description}...`); - - return Contribution.add(contributionAttributes).catch(error => { - console.log(`I tried to add a contribution for ${contributorName}, but I encountered an error when submitting the tx:`); - console.log(`Error:`, error); - console.log('Contribution attributes:'); - console.log(util.inspect(contributionAttributes, { depth: 2, colors: true })); - }); -} - -async function generateContributionData(reviews, Contributor, startDate, endDate) { - const dateFormatOptions = { year: 'numeric', month: 'long', day: 'numeric' }; +async function generateContributionData(reviews, Contributor) { const contributors = await Contributor.all(); const contributionData = {}; - const now = (new Date()).toISOString().split('.')[0]+"Z"; + const now = new Date().toISOString().split('.')[0] + 'Z'; [date, time] = now.split('T'); function addContributionDataForPlatform(platform) { - for (const [username, platformReviews] of Object.entries(reviews[platform])) { - const contributor = contributors.find(c => { + for (const [username, platformReviews] of Object.entries( + reviews[platform] + )) { + const contributor = contributors.find((c) => { return c[`${platform}_username`] === username; }); if (!contributor) { - console.log(`Could not find contributor for ${platform} user "${username}"`); + console.log( + `Could not find contributor for ${platform} user "${username}"` + ); continue; } - const urls = platformReviews.map(review => review.pr.html_url); + const urls = platformReviews.map((review) => review.pr.html_url); const kreditsAmount = platformReviews.reduce((amount, review) => { return review.kredits + amount; }, 0); @@ -175,9 +155,6 @@ async function generateContributionData(reviews, Contributor, startDate, endDate contributionData[contributor.name].amount += kreditsAmount; contributionData[contributor.name].details.pullRequests.push(...urls); } else { - const formattedStartDate = startDate.toLocaleString('en-us', dateFormatOptions); - const formattedEndDate = endDate.toLocaleString('en-us', dateFormatOptions); - contributionData[contributor.name] = { contributorId: contributor.id, contributorIpfsHash: contributor.ipfsHash, @@ -185,11 +162,11 @@ async function generateContributionData(reviews, Contributor, startDate, endDate time, amount: kreditsAmount, kind: 'dev', - description: `PR reviews from ${formattedStartDate} to ${formattedEndDate}`, + description: 'PR reviews', details: { - 'pullRequests': urls - } - } + pullRequests: urls, + }, + }; } } } @@ -200,20 +177,20 @@ async function generateContributionData(reviews, Contributor, startDate, endDate return contributionData; } -Promise.all([initializeKredits(), getAllReviews(repos, startDate, endDate)]).then((values) => { +Promise.all([ + initializeKredits(), + getAllReviews(repos, startDate, endDate), +]).then((values) => { const kredits = values[0]; const reviews = values[1]; - generateContributionData(reviews, kredits.Contributor, startDate, endDate).then(contributionData => { - if (argv.dry) { - console.log('Contributions:'); - console.log(util.inspect(contributionData, { depth: 3, colors: true })); - } else { - // create contributions - for (const [username, contributionAttributes] of Object.entries(contributionData)) { - createContribution(username, contributionAttributes, kredits.Contribution); + generateContributionData(reviews, kredits.Contributor).then( + (contributionData) => { + if (argv.dry) { + console.log('contributions:'); + console.log(util.inspect(contributionData, { depth: 3, colors: true })); + return; } } - }); + ); }); -