diff --git a/.pullapprove.yml b/.pullapprove.yml index cadcc38376..d4d03bcd3a 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -952,6 +952,7 @@ groups: 'tools/browsers/**', 'tools/build/**', 'tools/circular_dependency_test/**', + 'tools/contributing-stats/**', 'tools/gulp-tasks/**', 'tools/ng_rollup_bundle/**', 'tools/ngcontainer/**', diff --git a/package.json b/package.json index d318143028..f01148536f 100644 --- a/package.json +++ b/package.json @@ -147,6 +147,7 @@ "@bazel/bazel": "2.1.0", "@bazel/buildifier": "^0.29.0", "@bazel/ibazel": "^0.11.1", + "@octokit/graphql": "^4.3.1", "@types/minimist": "^1.2.0", "@yarnpkg/lockfile": "^1.1.0", "browserstacktunnel-wrapper": "2.0.1", @@ -175,9 +176,11 @@ "rewire": "2.5.2", "sauce-connect": "https://saucelabs.com/downloads/sc-4.5.1-linux.tar.gz", "semver": "^6.3.0", + "ts-node": "^8.6.2", "tslint-eslint-rules": "5.4.0", "tslint-no-toplevel-property-access": "0.0.2", "tsutils": "2.27.2", + "typed-graphqlify": "^2.3.0", "universal-analytics": "0.4.15", "vlq": "0.2.2", "vrsource-tslint-rules": "5.1.1" diff --git a/tools/contributing-stats/get-data.ts b/tools/contributing-stats/get-data.ts new file mode 100644 index 0000000000..c4cf31028b --- /dev/null +++ b/tools/contributing-stats/get-data.ts @@ -0,0 +1,244 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** + * This script gets contribution stats for all members of the angular org, + * since a provided date. + * The script expects the following flag(s): + * + * required: + * --since [date] The data after which contributions are queried for. + * Uses githubs search format for dates, e.g. "2020-01-21". + * See + * https://help.github.com/en/github/searching-for-information-on-github/understanding-the-search-syntax#query-for-dates + * + * optional: + * --use-created [boolean] If the created timestamp should be used for + * time comparisons, defaults otherwise to the updated timestamp. + */ + +import {graphql as unauthenticatedGraphql} from '@octokit/graphql'; +import * as minimist from 'minimist'; +import {alias, params, query as graphqlQuery, types} from 'typed-graphqlify'; + +// The organization to be considered for the queries. +const ORG = 'angular'; +// The repositories to be considered for the queries. +const REPOS = ['angular', 'components', 'angular-cli']; + +/** + * Handle flags for the script. + */ +const args = minimist(process.argv.slice(2), { + string: ['since'], + boolean: ['use-created'], + unknown: (option: string) => { + console.error(`Unknown option: ${option}`); + process.exit(1); + } +}); + +if (!args['since']) { + console.error(`Please provide --since [date]`); + process.exit(1); +} + +/** + * Authenticated instance of Github GraphQl API service, relies on a + * personal access token being available in the TOKEN environment variable. + */ +const graphql = unauthenticatedGraphql.defaults({ + headers: { + // TODO(josephperrott): Remove reference to TOKEN environment variable as part of larger + // effort to migrate to expecting tokens via GITHUB_ACCESS_TOKEN environment variables. + authorization: `token ${process.env.TOKEN || process.env.GITHUB_ACCESS_TOKEN}`, + } +}); + +/** + * Retrieves all current members of an organization. + */ +async function getAllOrgMembers() { + // The GraphQL query object to get a page of members of an organization. + const MEMBERS_QUERY = params( + { + $first: 'Int', // How many entries to get with each request + $after: 'String', // The cursor to start the page at + $owner: 'String!', // The organization to query for + }, + { + organization: params({login: '$owner'}, { + membersWithRole: params( + { + first: '$first', + after: '$after', + }, + { + nodes: [{login: types.string}], + pageInfo: { + hasNextPage: types.boolean, + endCursor: types.string, + }, + }), + }) + }); + const query = graphqlQuery('members', MEMBERS_QUERY); + + /** + * Gets the query and queryParams for a specific page of entries. + */ + const queryBuilder = (count: number, cursor?: string) => { + return { + query, + params: { + after: cursor || null, + first: count, + owner: ORG, + }, + }; + }; + + // The current cursor + let cursor = undefined; + // If an additional page of members is expected + let hasNextPage = true; + // Array of Github usernames of the organization + const members: string[] = []; + + while (hasNextPage) { + const {query, params} = queryBuilder(100, cursor); + const results = await graphql(query, params) as typeof MEMBERS_QUERY; + + results.organization.membersWithRole.nodes.forEach( + (node: {login: string}) => members.push(node.login)); + hasNextPage = results.organization.membersWithRole.pageInfo.hasNextPage; + cursor = results.organization.membersWithRole.pageInfo.endCursor; + } + return members; +} + +/** + * Build metadata for making requests for a specific user and date. + * + * Builds GraphQL query string, Query Params and Labels for making queries to GraphQl. + */ +function buildQueryAndParams(username: string, date: string) { + // Whether the updated or created timestamp should be used. + const updatedOrCreated = args['use-created'] ? 'created' : 'updated'; + let dataQueries: {[key: string]: {query: string, label: string}} = {}; + // Add queries and params for all values queried for each repo. + for (let repo of REPOS) { + dataQueries = { + ...dataQueries, + [`${repo.replace(/[\/\-]/g, '_')}_issue_author`]: { + query: `repo:${ORG}/${repo} is:issue author:${username} ${updatedOrCreated}:>${date}`, + label: `${ORG}/${repo} Issue Authored`, + }, + [`${repo.replace(/[\/\-]/g, '_')}_issues_involved`]: { + query: + `repo:${ORG}/${repo} is:issue -author:${username} involves:${username} ${updatedOrCreated}:>${date}`, + label: `${ORG}/${repo} Issue Involved`, + }, + [`${repo.replace(/[\/\-]/g, '_')}_pr_author`]: { + query: `repo:${ORG}/${repo} is:pr author:${username} ${updatedOrCreated}:>${date}`, + label: `${ORG}/${repo} PR Author`, + }, + [`${repo.replace(/[\/\-]/g, '_')}_pr_involved`]: { + query: `repo:${ORG}/${repo} is:pr involves:${username} ${updatedOrCreated}:>${date}`, + label: `${ORG}/${repo} PR Involved`, + }, + [`${repo.replace(/[\/\-]/g, '_')}_pr_reviewed`]: { + query: + `repo:${ORG}/${repo} is:pr -author:${username} reviewed-by:${username} ${updatedOrCreated}:>${date}`, + label: `${ORG}/${repo} PR Reviewed`, + }, + [`${repo.replace(/[\/\-]/g, '_')}_pr_commented`]: { + query: + `repo:${ORG}/${repo} is:pr -author:${username} commenter:${username} ${updatedOrCreated}:>${date}`, + label: `${ORG}/${repo} PR Commented`, + }, + }; + } + // Add queries and params for all values queried for the org. + dataQueries = { + ...dataQueries, + [`${ORG}_org_issue_author`]: { + query: `org:${ORG} is:issue author:${username} ${updatedOrCreated}:>${date}`, + label: `${ORG} org Issue Authored`, + }, + [`${ORG}_org_issues_involved`]: { + query: + `org:${ORG} is:issue -author:${username} involves:${username} ${updatedOrCreated}:>${date}`, + label: `${ORG} org Issue Involved`, + }, + [`${ORG}_org_pr_author`]: { + query: `org:${ORG} is:pr author:${username} ${updatedOrCreated}:>${date}`, + label: `${ORG} org PR Author`, + }, + [`${ORG}_org_pr_involved`]: { + query: `org:${ORG} is:pr involves:${username} ${updatedOrCreated}:>${date}`, + label: `${ORG} org PR Involved`, + }, + [`${ORG}_org_pr_reviewed`]: { + query: + `org:${ORG} is:pr -author:${username} reviewed-by:${username} ${updatedOrCreated}:>${date}`, + label: `${ORG} org PR Reviewed`, + }, + [`${ORG}_org_pr_commented`]: { + query: + `org:${ORG} is:pr -author:${username} commenter:${username} ${updatedOrCreated}:>${date}`, + label: `${ORG} org PR Commented`, + }, + }; + + /** + * Gets the labels for each requested value to be used as headers. + */ + function getLabels(pairs: typeof dataQueries) { + return Object.values(pairs).map(val => val.label); + } + + /** + * Gets the graphql query object for the GraphQL query. + */ + function getQuery(pairs: typeof dataQueries) { + const output: {[key: string]: {}} = {}; + Object.entries(pairs).map(([key, val]) => { + output[alias(key, 'search')] = params( + { + query: `"${val.query}"`, + type: 'ISSUE', + }, + { + issueCount: types.number, + }); + }); + return output; + } + + return { + query: graphqlQuery(getQuery(dataQueries)), + labels: getLabels(dataQueries), + }; +} + +/** + * Runs the script to create a CSV string with the requested data for each member + * of the organization. + */ +async function run(date: string) { + console.info(['Username'].concat(buildQueryAndParams('', date).labels).join(',')); + + for (let username of await getAllOrgMembers()) { + const results = await graphql(buildQueryAndParams(username, date).query); + const values = Object.values(results).map(result => `${result.issueCount}`); + console.info([username].concat(values).join(',')); + } +} + +run(args['since']); diff --git a/yarn.lock b/yarn.lock index a416790581..029438f1e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1274,6 +1274,54 @@ rxjs "6.5.3" webpack-sources "1.4.3" +"@octokit/endpoint@^5.5.0": + version "5.5.3" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-5.5.3.tgz#0397d1baaca687a4c8454ba424a627699d97c978" + integrity sha512-EzKwkwcxeegYYah5ukEeAI/gYRLv2Y9U5PpIsseGSFDk+G3RbipQGBs8GuYS1TLCtQaqoO66+aQGtITPalxsNQ== + dependencies: + "@octokit/types" "^2.0.0" + is-plain-object "^3.0.0" + universal-user-agent "^5.0.0" + +"@octokit/graphql@^4.3.1": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.3.1.tgz#9ee840e04ed2906c7d6763807632de84cdecf418" + integrity sha512-hCdTjfvrK+ilU2keAdqNBWOk+gm1kai1ZcdjRfB30oA3/T6n53UVJb7w0L5cR3/rhU91xT3HSqCd+qbvH06yxA== + dependencies: + "@octokit/request" "^5.3.0" + "@octokit/types" "^2.0.0" + universal-user-agent "^4.0.0" + +"@octokit/request-error@^1.0.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-1.2.1.tgz#ede0714c773f32347576c25649dc013ae6b31801" + integrity sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA== + dependencies: + "@octokit/types" "^2.0.0" + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request@^5.3.0": + version "5.3.2" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.3.2.tgz#1ca8b90a407772a1ee1ab758e7e0aced213b9883" + integrity sha512-7NPJpg19wVQy1cs2xqXjjRq/RmtSomja/VSWnptfYwuBxLdbYh2UjhGi0Wx7B1v5Iw5GKhfFDQL7jM7SSp7K2g== + dependencies: + "@octokit/endpoint" "^5.5.0" + "@octokit/request-error" "^1.0.1" + "@octokit/types" "^2.0.0" + deprecation "^2.0.0" + is-plain-object "^3.0.0" + node-fetch "^2.3.0" + once "^1.4.0" + universal-user-agent "^5.0.0" + +"@octokit/types@^2.0.0": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-2.3.1.tgz#40cd61c125a6161cfb3bfabc75805ac7a54213b4" + integrity sha512-rvJP1Y9A/+Cky2C3var1vsw3Lf5Rjn/0sojNl2AjCX+WbpIHYccaJ46abrZoIxMYnOToul6S9tPytUVkFI7CXQ== + dependencies: + "@types/node" ">= 8" + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -1549,6 +1597,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.5.8.tgz#92509422653f10e9c0ac18d87e0610b39f9821c7" integrity sha512-8KmlRxwbKZfjUHFIt3q8TF5S2B+/E5BaAoo/3mgc5h6FJzqxXkCK/VMetO+IRDtwtU6HUvovHMBn+XRj7SV9Qg== +"@types/node@>= 8": + version "13.7.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.7.tgz#1628e6461ba8cc9b53196dfeaeec7b07fa6eea99" + integrity sha512-Uo4chgKbnPNlxQwoFmYIwctkQVkMMmsAoGGU4JKwLuvBefF0pCq4FybNSnfkfRCpC7ZW7kttcC/TrRtAJsvGtg== + "@types/node@^10.1.0": version "10.14.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.4.tgz#1c586b991457cbb58fef51bc4e0cfcfa347714b5" @@ -2347,6 +2400,11 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^1.0.7, argparse@~1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -4853,6 +4911,11 @@ deprecated@^0.0.1: resolved "https://registry.yarnpkg.com/deprecated/-/deprecated-0.0.1.tgz#f9c9af5464afa1e7a971458a8bdef2aa94d5bb19" integrity sha1-+cmvVGSvoeepcUWKi97yqpTVuxk= +deprecation@^2.0.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== + des.js@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" @@ -8119,6 +8182,13 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-plain-object@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.0.tgz#47bfc5da1b5d50d64110806c199359482e75a928" + integrity sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg== + dependencies: + isobject "^4.0.0" + is-posix-bracket@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" @@ -8331,6 +8401,11 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isobject@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" + integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== + isstream@0.1.x, isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -9476,6 +9551,11 @@ lru-queue@0.1: dependencies: es5-ext "~0.10.2" +macos-release@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.3.0.tgz#eb1930b036c0800adebccd5f17bc4c12de8bb71f" + integrity sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA== + madge@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/madge/-/madge-3.6.0.tgz#f69e7c3e15a18a195e6bcd7942cc36efabcd9b9a" @@ -9539,6 +9619,11 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + make-fetch-happen@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.0.tgz#a8e3fe41d3415dd656fe7b8e8172e1fb4458b38d" @@ -10904,6 +10989,14 @@ os-locale@^3.0.0, os-locale@^3.1.0: lcid "^2.0.0" mem "^4.0.0" +os-name@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-name/-/os-name-3.1.0.tgz#dec19d966296e1cd62d701a5a66ee1ddeae70801" + integrity sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg== + dependencies: + macos-release "^2.2.0" + windows-release "^3.1.0" + os-shim@^0.1.2: version "0.1.3" resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" @@ -13485,7 +13578,7 @@ source-map-support@0.4.3: dependencies: source-map "^0.5.3" -source-map-support@0.5.16, source-map-support@^0.5.5, source-map-support@~0.5.12: +source-map-support@0.5.16, source-map-support@^0.5.5, source-map-support@^0.5.6, source-map-support@~0.5.12: version "0.5.16" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== @@ -14603,6 +14696,17 @@ try-require@^1.0.0: resolved "https://registry.yarnpkg.com/try-require/-/try-require-1.2.1.tgz#34489a2cac0c09c1cc10ed91ba011594d4333be2" integrity sha1-NEiaLKwMCcHMEO2RugEVlNQzO+I= +ts-node@^8.6.2: + version "8.6.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.6.2.tgz#7419a01391a818fbafa6f826a33c1a13e9464e35" + integrity sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg== + dependencies: + arg "^4.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.6" + yn "3.1.1" + tsickle@0.38.0: version "0.38.0" resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.38.0.tgz#89f5952c9bb3ba0b36dc384975e23cf90e584822" @@ -14754,6 +14858,11 @@ type@^2.0.0: resolved "https://registry.yarnpkg.com/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3" integrity sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow== +typed-graphqlify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/typed-graphqlify/-/typed-graphqlify-2.3.0.tgz#c85f09b65b56e6da2c993899a34e551e2616bf91" + integrity sha512-iRjAneX/0gHBOrJnZGF5Nwl6OjHDiYBYxQzqenRJExVod0A0QHto9npX4d68i+2OuSHAzatNgSVtJoRgnBHeYQ== + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -14912,6 +15021,20 @@ universal-analytics@^0.4.16, universal-analytics@^0.4.20: request "^2.88.0" uuid "^3.0.0" +universal-user-agent@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-4.0.1.tgz#fd8d6cb773a679a709e967ef8288a31fcc03e557" + integrity sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg== + dependencies: + os-name "^3.1.0" + +universal-user-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-5.0.0.tgz#a3182aa758069bf0e79952570ca757de3579c1d9" + integrity sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q== + dependencies: + os-name "^3.1.0" + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -15486,6 +15609,13 @@ widest-line@^2.0.0: dependencies: string-width "^2.1.1" +windows-release@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.2.0.tgz#8122dad5afc303d833422380680a79cdfa91785f" + integrity sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA== + dependencies: + execa "^1.0.0" + winreg@0.0.12: version "0.0.12" resolved "https://registry.yarnpkg.com/winreg/-/winreg-0.0.12.tgz#07105554ba1a9d08979251d129475bffae3006b7" @@ -15770,6 +15900,11 @@ yeast@0.1.2: resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + z-schema@~3.18.3: version "3.18.4" resolved "https://registry.yarnpkg.com/z-schema/-/z-schema-3.18.4.tgz#ea8132b279533ee60be2485a02f7e3e42541a9a2"