diff --git a/package.json b/package.json index f3b5b122..fad42d65 100755 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "nock": "^13.1.1", "prettier": "^2.4.1", "tsconfig-paths-jest": "^0.0.1", - "type-fest": "^2.3.3" + "type-fest": "^2.5.4" }, "devDependencies": { "@babel/preset-env": "^7.15.6", diff --git a/src/__tests__/Integration.test.ts b/src/__tests__/Integration.test.ts index 90156457..d733dc0a 100755 --- a/src/__tests__/Integration.test.ts +++ b/src/__tests__/Integration.test.ts @@ -172,7 +172,7 @@ describe("integration testing edgecases associated with editors", () => { }); describe("Pull 3623", () => { - it("should pass", async() => { + it("should pass", async () => { process.env = envFactory({ PULL_NUMBER: SavedRecord.PR3623, [EIPTypeOrCategoryToResolver[EIPCategory.erc]]: "@micahzoltu" @@ -180,6 +180,27 @@ describe("integration testing edgecases associated with editors", () => { await __MAIN_MOCK__(); expect(setFailedMock).not.toBeCalled(); - }) - }) + }); + }); + + describe("Pull 3581", () => { + it("should pass", async () => { + process.env = envFactory({ + PULL_NUMBER: SavedRecord.PR3581, + [EIPTypeOrCategoryToResolver[EIPCategory.erc]]: "@lightclient" + }); + + await __MAIN_MOCK__(); + expect(setFailedMock).not.toBeCalled(); + }); + it("should not pass if no editor approval", async () => { + process.env = envFactory({ + PULL_NUMBER: SavedRecord.PR3581, + // in this case I'm not setting the approver as an editor + }); + + await __MAIN_MOCK__(); + expect(setFailedMock).toHaveBeenCalled(); + }); + }); }); diff --git a/src/domain/Regex.ts b/src/domain/Regex.ts index d1da852a..426c1462 100755 --- a/src/domain/Regex.ts +++ b/src/domain/Regex.ts @@ -6,6 +6,8 @@ export const AUTHOR_RE = /[(<]([^>)]+)[>)]/gm; export const EIP_NUM_RE = /eip-(\d+)\.md/; /** matches github handles (includes @)*/ export const GITHUB_HANDLE = /^@[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}$/i; +/** extracts the eip number of the assets folder associated */ +export const ASSETS_EIP_NUM = /(?<=^assets\/eip-)(\d+)(?=\/.*)/ /** * This functionality is supported in es2020, but for the purposes diff --git a/src/domain/Types.ts b/src/domain/Types.ts index bfc54a66..b8f5d9da 100755 --- a/src/domain/Types.ts +++ b/src/domain/Types.ts @@ -17,7 +17,7 @@ export type Commit = PromiseValue< export type Files = PromiseValue< ReturnType >["data"]; -export type File = UnArrayify; +export type File = Files[number]; export type CommitFiles = CompareCommits["base_commit"]["files"]; export type CommitFile = UnArrayify>; export type Repo = PromiseValue>["data"]; @@ -152,6 +152,7 @@ export function isMockMethod(method): asserts method is MockMethods { export type Results = { filename: string; + successMessage?: string; errors?: string[]; mentions?: string[]; }[]; diff --git a/src/domain/exceptions.ts b/src/domain/exceptions.ts index 8e0d7628..4a76f469 100644 --- a/src/domain/exceptions.ts +++ b/src/domain/exceptions.ts @@ -1,5 +1,11 @@ import { Maybe } from "./Types"; +export enum Exceptions { + unexpectedError = "Unexpected Error", + requirementViolation = "Requirement Violation", + gracefulTermination = "Graceful Termination" +} + export class UnexpectedError { public readonly type = "Unexpected Error"; constructor(public error: Maybe = null, public data: Maybe = null){} @@ -9,3 +15,39 @@ export class RequirementViolation { public readonly type = "Requirement Violation"; constructor(public error: Maybe = null, public data: Maybe = null){} } + +/** + * this terminates the program gracefully, meaning that it will not be treated + * as an error. This is useful in cases where an invariant violation does not + * necessarily mean that the test fails. + * */ +export class GracefulTermination { + public readonly type = "Graceful Termination" + constructor(public error: Maybe = null, public data: Maybe = null){} +} + +type Handlers = {[key in keyof typeof Exceptions]: (message: string) => any} + +/** this is written out on purpose to allow for easier changes where necessary */ +export const processError = (err: any, { + gracefulTermination, + unexpectedError, + requirementViolation +}:Handlers) => { + if (err?.type === Exceptions.gracefulTermination) { + console.log(JSON.stringify(err.data, null, 2)); + return gracefulTermination(err.error); + } + + if (err?.type === Exceptions.requirementViolation) { + console.log(JSON.stringify(err.data, null, 2)); + return requirementViolation(err.error); + } + + if (err?.type === Exceptions.unexpectedError) { + console.log(JSON.stringify(err.data, null, 2)); + return unexpectedError(err.error); + } + + throw err +} diff --git a/src/main.ts b/src/main.ts index 3626d948..fc59b05b 100755 --- a/src/main.ts +++ b/src/main.ts @@ -36,6 +36,7 @@ import { import { get, uniq } from "lodash"; import { requestReviewers } from "#/approvals"; import { getFileDiff } from "#/file"; +import { processError } from "src/domain/exceptions"; const testFile = async (file: File): Promise => { // we need to define this here because the below logic can get very complicated otherwise @@ -47,7 +48,17 @@ const testFile = async (file: File): Promise => { try { file = await requireFilePreexisting(file); } catch (err: any) { - errors.fileErrors.filePreexistingError = err; + processError(err, { + gracefulTermination: () => { + throw err + }, + requirementViolation: (message) => { + errors.fileErrors.filePreexistingError = message; + }, + unexpectedError: () => { + throw err + } + }) errors.approvalErrors.isEditorApprovedError = await assertEIPEditorApproval( fileDiff ); @@ -65,7 +76,7 @@ const testFile = async (file: File): Promise => { ); errors.approvalErrors.enoughEditorApprovalsForEIP1Error = await assertEIP1EditorApprovals(fileDiff); - errors.fileErrors.validFilenameError = assertValidFilename(file); + errors.fileErrors.validFilenameError = await assertValidFilename(file); errors.headerErrors.matchingEIPNumError = assertFilenameAndFileNumbersMatch(fileDiff); errors.headerErrors.constantEIPNumError = assertConstantEipNumber(fileDiff); @@ -144,24 +155,25 @@ const _getMentions = const getMentions = _getMentions(getEditorMentions, getAuthorMentions); -const getCommentMessage = (results: Results) => { - if (!results.length) return "There were no results"; +const getCommentMessage = (results: Results, header?: string) => { + if (!results.length) return "There were no results cc @alita-moore"; const comment: string[] = []; - comment.push(COMMENT_HEADER); + comment.push(header || COMMENT_HEADER); comment.push("---"); - for (const { filename, errors } of results) { - comment.push(`## ${filename}`); + for (const { filename, errors, successMessage } of results) { if (!errors) { - comment.push(`\t passed!`); + comment.push(`## (pass) ${filename}`); + const message = `\t` + (successMessage || "passed!"); + comment.push(message); continue; } + comment.push(`## (fail) ${filename}`); for (const error of errors) { - comment.push(`- ${error}`); + comment.push(`\t- ${error}`); } } - 7; return comment.join("\n"); }; @@ -204,11 +216,42 @@ export const _main_ = async () => { // Collect the changes made in the given PR from base <-> head for eip files const files = await requireFiles(pr); - const results: Results = await Promise.all(files.map(getFileTestResults)); + let results: Results = []; + for await (const file of files) { + try { + const res = await getFileTestResults(file); + results.push(res); + } catch (err: any) { + processError(err, { + gracefulTermination: (message) => { + results.push({ + filename: file.filename, + successMessage: message + }); + }, + requirementViolation: (message) => { + results.push({ + filename: file.filename, + errors: [message] + }); + }, + unexpectedError: (message) => { + results.push({ + filename: file.filename, + errors: [message] + }); + } + }); + } + } if (!results.filter((res) => res.errors).length) { - await postComment("All tests passed; auto-merging..."); - console.log("All tests passed; auto-merging..."); + const commentMessage = getCommentMessage( + results, + "All tests passed; auto-merging..." + ); + await postComment(commentMessage); + console.log(commentMessage); return; } @@ -228,7 +271,7 @@ export const main = async () => { try { return await _main_(); } catch (error: any) { - console.log(`An Exception Occured While Linting: \n${error}`); + console.log(`An unexpected exception occured while linting: \n${error}`); setFailed(error.message); if (isProd) { await postComment(error.message); diff --git a/src/modules/assertions/Domain/types.ts b/src/modules/assertions/Domain/types.ts index b1efe7b0..530c5bee 100755 --- a/src/modules/assertions/Domain/types.ts +++ b/src/modules/assertions/Domain/types.ts @@ -6,7 +6,17 @@ export interface IRequireEditors { requireEIPEditors: (fileDiff: FileDiff) => string[]; } -export type PreexistingFile = Opaque +export type PreexistingFile = Opaque; export interface IRequireFilePreexisting { requireFilePreexisting: (fileDiff: File) => Promise; } + +export interface IAssertValidFilename { + assertValidFilename: (file: NonNullable) => Promise; +} + +export interface IRequireFilenameEIPNum { + requireFilenameEipNum: (filename: string) => Promise; + attemptAssetGracefulTermination: (filename: string) => Promise; + attemptEditorApprovalGracefulTermination: (filename: string) => Promise; +} diff --git a/src/modules/assertions/__tests__/Assertions.test.ts b/src/modules/assertions/__tests__/Assertions.test.ts index 07f5151b..2fca9163 100755 --- a/src/modules/assertions/__tests__/Assertions.test.ts +++ b/src/modules/assertions/__tests__/Assertions.test.ts @@ -7,11 +7,9 @@ import { assertConstantStatus, assertFilenameAndFileNumbersMatch, assertHasAuthors, - assertValidFilename, assertValidStatus, requireAuthors, requireEvent, - requireFilenameEipNum, requireFiles, requirePr, requirePullNumber @@ -134,19 +132,6 @@ describe("Requires", () => { }); }); - describe("requireFilenameEipNum", () => { - it("should not error if filename matches regex", () => { - const eipNum = requireFilenameEipNum("eip-123.md"); - expect(eipNum).toBe(123); - }); - it("should not explode if filename doesn't match", async () => { - await expectError(() => requireFilenameEipNum("eip-123")); - await expectError(() => requireFilenameEipNum("ep-123.md")); - await expectError(() => requireFilenameEipNum("eip-a.md")); - await expectError(() => requireFilenameEipNum("eip-123.js")); - }); - }); - describe("requireFiles", () => { const mockFiles = [FileFactory()]; const listFiles = jest @@ -212,31 +197,6 @@ describe("Asserts", () => { }); }); - describe("assertValidFilename", () => { - it("should return undefined if filename is valid", () => { - const file = FileFactory(); - const res = assertValidFilename(file); - expect(res).toBeUndefined(); - }); - - it("should return defined if filename is not valid", () => { - const files = [ - FileFactory({ filename: "eip-123" }), - FileFactory({ filename: "ep-123.md" }), - FileFactory({ filename: "eip-a.md" }), - FileFactory({ filename: "eip-123.js" }) - ]; - // @ts-expect-error below is an invalid type error - expect(assertValidFilename(files[0])).toBeDefined(); - // @ts-expect-error below is an invalid type error - expect(assertValidFilename(files[1])).toBeDefined(); - // @ts-expect-error below is an invalid type error - expect(assertValidFilename(files[2])).toBeDefined(); - // @ts-expect-error below is an invalid type error - expect(assertValidFilename(files[3])).toBeDefined(); - }); - }); - describe("assertFilenameAndFileNumbersMatch", () => { it("should return undefined if the file names and headers match", () => { const fileDiff = FileDiffFactory(); diff --git a/src/modules/assertions/__tests__/assert_valid_filename.test.ts b/src/modules/assertions/__tests__/assert_valid_filename.test.ts new file mode 100644 index 00000000..e9ac91b7 --- /dev/null +++ b/src/modules/assertions/__tests__/assert_valid_filename.test.ts @@ -0,0 +1,46 @@ +import { initGeneralTestEnv, mockGithubContext } from "src/tests/testutils"; +import { EVENTS } from "src/domain"; +import { FileFactory } from "src/tests/factories/fileFactory"; +import { AssertValidFilename } from "#/assertions/assert_valid_filename"; + +describe("require_file_preexisting", () => { + mockGithubContext({ + payload: { pull_request: { number: 1 } }, + repo: { repo: "repo", owner: "owner" }, + eventName: EVENTS.pullRequestTarget + }); + + initGeneralTestEnv(); + + const requireFilenameEipNum = jest.fn(); + const _AssertValidFilename = new AssertValidFilename( + {requireFilenameEipNum} + ); + + beforeEach(async () => { + requireFilenameEipNum.mockReturnValue(Promise.resolve(1)); + }); + + it("should return undefined if filename is valid", async () => { + const file = FileFactory(); + const res = await _AssertValidFilename.assertValidFilename(file); + expect(res).toBeUndefined(); + }); + + it("should return defined if filename is not valid", async () => { + const files = [ + FileFactory({ filename: "eip-123" }), + FileFactory({ filename: "ep-123.md" }), + FileFactory({ filename: "eip-a.md" }), + FileFactory({ filename: "eip-123.js" }) + ]; + // @ts-expect-error below is an invalid type error + expect(await _AssertValidFilename.assertValidFilename(files[0])).toBeDefined(); + // @ts-expect-error below is an invalid type error + expect(await _AssertValidFilename.assertValidFilename(files[1])).toBeDefined(); + // @ts-expect-error below is an invalid type error + expect(await _AssertValidFilename.assertValidFilename(files[2])).toBeDefined(); + // @ts-expect-error below is an invalid type error + expect(await _AssertValidFilename.assertValidFilename(files[3])).toBeDefined(); + }); +}) diff --git a/src/modules/assertions/__tests__/require_filename_eip_num.test.ts b/src/modules/assertions/__tests__/require_filename_eip_num.test.ts new file mode 100644 index 00000000..3ab025e0 --- /dev/null +++ b/src/modules/assertions/__tests__/require_filename_eip_num.test.ts @@ -0,0 +1,144 @@ +import { + expectError, + expectErrorWithHandler, + initGeneralTestEnv, + mockGithubContext +} from "src/tests/testutils"; +import { EVENTS } from "src/domain"; +import { RequireFilenameEIPNum } from "#/assertions/require_filename_eip_num"; +import { FileFactory } from "src/tests/factories/fileFactory"; +import { PRFactory } from "src/tests/factories/prFactory"; +import { Exceptions } from "src/domain/exceptions"; + +describe("requireFilenameEipNum", () => { + mockGithubContext({ + payload: { pull_request: { number: 1 } }, + repo: { repo: "repo", owner: "owner" }, + eventName: EVENTS.pullRequestTarget + }); + + initGeneralTestEnv(); + + const requireEIPEditors = jest.fn(); + const getPullRequestFiles = jest.fn(); + const requirePr = jest.fn(); + const getApprovals = jest.fn(); + const _RequireFilenameEIPNum = new RequireFilenameEIPNum({ + requireEIPEditors, + getPullRequestFiles, + requirePr, + getApprovals + }); + + const attemptEditorApproval = jest + .fn() + .mockImplementation( + _RequireFilenameEIPNum.attemptEditorApprovalGracefulTermination + ); + const attemptAsset = jest + .fn() + .mockImplementation(_RequireFilenameEIPNum.attemptAssetGracefulTermination); + + _RequireFilenameEIPNum.attemptEditorApprovalGracefulTermination = + attemptEditorApproval; + _RequireFilenameEIPNum.attemptAssetGracefulTermination = attemptAsset; + + beforeEach(async () => { + requireEIPEditors.mockReturnValue(["@test"]); + getPullRequestFiles.mockResolvedValue(FileFactory()); + requirePr.mockResolvedValue(await PRFactory()); + // no approvals + getApprovals.mockResolvedValue([]); + }); + + it("should not error if filename matches regex", async () => { + const eipNum = await _RequireFilenameEIPNum.requireFilenameEipNum( + "eip-123.md" + ); + expect(eipNum).toBe(123); + }); + + it("should not explode if filename doesn't match", async () => { + await expectError(() => + _RequireFilenameEIPNum.requireFilenameEipNum("eip-123") + ); + await expectError(() => + _RequireFilenameEIPNum.requireFilenameEipNum("ep-123.md") + ); + await expectError(() => + _RequireFilenameEIPNum.requireFilenameEipNum("eip-a.md") + ); + await expectError(() => + _RequireFilenameEIPNum.requireFilenameEipNum("eip-123.js") + ); + }); + + it("should attempt graceful termination if files don't match pattern", async () => { + await _RequireFilenameEIPNum + .requireFilenameEipNum("eip-dsd") + .catch((_) => {}); + expect(attemptEditorApproval).toBeCalledTimes(1); + expect(attemptEditorApproval).toBeCalledTimes(1); + expect(attemptEditorApproval).toBeCalledTimes(1); + }); + + describe("graceful termination routes", () => { + describe("editor approval", () => { + it("should explode with graceful termination with editor approval", async () => { + // this should return the name of one of the editors + getApprovals.mockResolvedValue(["@test"]); + await expectErrorWithHandler( + () => + _RequireFilenameEIPNum.attemptEditorApprovalGracefulTermination( + "test" + ), + (err) => { + expect(err.type).toBe(Exceptions.gracefulTermination); + }, + "should explode with graceful termination" + ); + }); + + it("should not explode with graceful if there's no editor approval", async () => { + // this should return the name of one of the editors + getApprovals.mockResolvedValue(["@test2"]); + await _RequireFilenameEIPNum.attemptEditorApprovalGracefulTermination( + "test" + ) + }); + }); + + describe("related asset changes", () => { + const expectGraceful = (filename: string) => { + return expectErrorWithHandler( + () => + _RequireFilenameEIPNum.attemptAssetGracefulTermination(filename), + (err) => { + expect(err.type).toBe(Exceptions.gracefulTermination); + }, + "related asset changes" + ); + }; + beforeEach(() => { + getPullRequestFiles.mockResolvedValue([ + FileFactory({ filename: "EIPs/eip-2.md" }) + ]); + }); + it("should explode with graceful termination if the assets are allowed", async () => { + await expectGraceful("assets/eip-2/test.md"); + }); + it("should fail if the file is not in assets folder", async () => { + await expectError( + () => expectGraceful("eip-1/test.md"), + "should fail if the file is not in assets folder" + ); + }); + it("should fail if the change is made to a file in a different eip folder", async () => { + await expectError( + () => expectGraceful("assets/eip-1/test.md"), + "should fail if the change is made to a file in a different eip folder" + ); + }); + }); + }); +}); diff --git a/src/modules/assertions/assert_valid_filename.ts b/src/modules/assertions/assert_valid_filename.ts index 056b0b2c..d49fa264 100755 --- a/src/modules/assertions/assert_valid_filename.ts +++ b/src/modules/assertions/assert_valid_filename.ts @@ -1,26 +1,36 @@ import { File, FILE_RE } from "src/domain"; -import { requireFilenameEipNum } from "#/assertions"; +import { IAssertValidFilename } from "#/assertions/Domain/types"; -/** - * Accepts a file and returns whether or not its name is valid - * - * @param errors a list to add any errors that occur to - * @returns {boolean} is the provided file's filename valid? - */ -export const assertValidFilename = (file: NonNullable) => { - const filename = file.filename; +export class AssertValidFilename implements IAssertValidFilename { + requireFilenameEipNum: (filename: string) => Promise; - // File name is formatted correctly and is in the EIPS folder - const match = filename.search(FILE_RE); - if (match === -1) { - return `Filename ${filename} is not in EIP format 'EIPS/eip-####.md'`; + constructor({ requireFilenameEipNum }: { + requireFilenameEipNum: (filename: string) => Promise; + }) { + this.requireFilenameEipNum = requireFilenameEipNum; } - // EIP number is defined within the filename and can be parsed - const filenameEipNum = requireFilenameEipNum(filename); - if (!filenameEipNum) { - return `No EIP number was found to be associated with filename ${filename}`; - } + /** + * Accepts a file and returns whether or not its name is valid + * + * @param errors a list to add any errors that occur to + * @returns {boolean} is the provided file's filename valid? + */ + assertValidFilename = async (file: NonNullable) => { + const filename = file.filename; + + // File name is formatted correctly and is in the EIPS folder + const match = filename.search(FILE_RE); + if (match === -1) { + return `Filename ${filename} is not in EIP format 'EIPS/eip-####.md'`; + } + + // EIP number is defined within the filename and can be parsed + const filenameEipNum = await this.requireFilenameEipNum(filename); + if (!filenameEipNum) { + return `No EIP number was found to be associated with filename ${filename}`; + } - return; -}; + return; + }; +} diff --git a/src/modules/assertions/index.ts b/src/modules/assertions/index.ts index 1eef24aa..5191a51b 100755 --- a/src/modules/assertions/index.ts +++ b/src/modules/assertions/index.ts @@ -12,7 +12,10 @@ import { NETWORKING_EDITORS } from "src/domain"; import { requirePr } from "#/assertions/require_pr"; -import { getRepoFilenameContent } from "src/infra"; +import { getPullRequestFiles, getRepoFilenameContent } from "src/infra"; +import { AssertValidFilename } from "#/assertions/assert_valid_filename"; +import { RequireFilenameEIPNum } from "./require_filename_eip_num"; +import { getApprovals } from "../approvals"; export * from "./require_pull_number"; export * from "./require_event"; @@ -20,8 +23,6 @@ export * from "./require_authors"; export * from "./require_pr"; export * from "./assert_has_authors"; export * from "./assert_is_approved_by_authors"; -export * from "./assert_valid_filename"; -export * from "./require_filename_eip_num"; export * from "./require_files"; export * from "./assert_filename_and_file_numbers_match"; export * from "./assert_constant_eip_number"; @@ -52,3 +53,26 @@ export const requireFilePreexisting = castTo< // @ts-ignore return _RequireFilePreexisting.requireFilePreexisting(...args); }); + +const _RequireFilenameEIPNum = new RequireFilenameEIPNum({ + getPullRequestFiles, + requirePr, + requireEIPEditors, + getApprovals +}); +export const requireFilenameEipNum = castTo< + typeof _RequireFilenameEIPNum.requireFilenameEipNum +>((...args) => { + // @ts-ignore + return _RequireFilenameEIPNum.requireFilenameEipNum(...args); +}); + +const _AssertValidFilename = new AssertValidFilename({ + requireFilenameEipNum +}); +export const assertValidFilename = castTo< + typeof _AssertValidFilename.assertValidFilename +>((...args) => { + // @ts-ignore + return _AssertValidFilename.assertValidFilename(...args); +}); diff --git a/src/modules/assertions/require_editors.ts b/src/modules/assertions/require_editors.ts index 4ec823a2..c62f3552 100755 --- a/src/modules/assertions/require_editors.ts +++ b/src/modules/assertions/require_editors.ts @@ -1,5 +1,6 @@ import { EIPCategory, EIPTypes, FileDiff } from "src/domain"; import { IRequireEditors } from "#/assertions/Domain/types"; +import _ from "lodash"; export class RequireEditors implements IRequireEditors { public requireAuthors: (fileDiff: FileDiff) => string[]; @@ -30,7 +31,7 @@ export class RequireEditors implements IRequireEditors { // injected to make testing easier _requireEIPEditors(EDITORS: string[], fileDiff?: FileDiff) { - EDITORS = EDITORS.map((i) => i.toLowerCase()); + EDITORS = _.uniq(EDITORS.map((i) => i.toLowerCase())); if (fileDiff) { const authors = this.requireAuthors(fileDiff); return EDITORS.filter((editor) => !authors.includes(editor)); @@ -55,12 +56,27 @@ export class RequireEditors implements IRequireEditors { META_EDITORS, NETWORKING_EDITORS } = this; - const isERC = fileDiff?.base.category === EIPCategory.erc; - const isCore = fileDiff?.base.category === EIPCategory.core; - const isNetworking = fileDiff?.base.category === EIPCategory.networking; - const isInterface = fileDiff?.base.category === EIPCategory.interface; - const isMeta = fileDiff?.base.type === EIPTypes.meta; - const isInformational = fileDiff?.base.type === EIPTypes.informational; + + if (!fileDiff) { + // if no fileDiff is provided then return all editors + return this._requireEIPEditors( + _.concat( + ERC_EDITORS(), + CORE_EDITORS(), + NETWORKING_EDITORS(), + INTERFACE_EDITORS(), + META_EDITORS(), + INFORMATIONAL_EDITORS() + ) + ) + } + + const isERC = fileDiff.base.category === EIPCategory.erc; + const isCore = fileDiff.base.category === EIPCategory.core; + const isNetworking = fileDiff.base.category === EIPCategory.networking; + const isInterface = fileDiff.base.category === EIPCategory.interface; + const isMeta = fileDiff.base.type === EIPTypes.meta; + const isInformational = fileDiff.base.type === EIPTypes.informational; if (isERC) { return this._requireEIPEditors(ERC_EDITORS(), fileDiff); diff --git a/src/modules/assertions/require_filename_eip_num.ts b/src/modules/assertions/require_filename_eip_num.ts index 16954906..052459bd 100755 --- a/src/modules/assertions/require_filename_eip_num.ts +++ b/src/modules/assertions/require_filename_eip_num.ts @@ -1,13 +1,121 @@ -import { EIP_NUM_RE } from "src/domain"; - -/** - * Extracts the EIP number from a given filename (or returns null) - * @param filename EIP filename - */ -export const requireFilenameEipNum = (filename: string) => { - const eipNumMatch = filename.match(EIP_NUM_RE); - if (!eipNumMatch || eipNumMatch[1] === undefined) { - throw new Error(`EIP file name must be eip-###.md`); +import { ASSETS_EIP_NUM, EIP_NUM_RE, File, FileDiff, PR } from "src/domain"; +import { + GracefulTermination, + RequirementViolation, + UnexpectedError +} from "src/domain/exceptions"; +import { IRequireFilenameEIPNum } from "#/assertions/Domain/types"; +import { multiLineString } from "../utils"; +import _ from "lodash"; + +export class RequireFilenameEIPNum implements IRequireFilenameEIPNum { + public getPullRequestFiles: (pullNumber: number) => Promise; + public requirePr: () => Promise; + public requireEIPEditors: (fileDiff?: FileDiff | undefined) => string[]; + public getApprovals: () => Promise; + + constructor({ + getPullRequestFiles, + requirePr, + requireEIPEditors, + getApprovals + }: { + getPullRequestFiles: (pullNumber: number) => Promise; + requirePr: () => Promise; + requireEIPEditors: (fileDiff?: FileDiff | undefined) => string[]; + getApprovals: () => Promise; + }) { + this.getPullRequestFiles = getPullRequestFiles; + this.requirePr = requirePr; + this.requireEIPEditors = requireEIPEditors; + this.getApprovals = getApprovals; } - return eipNumMatch && parseInt(eipNumMatch[1]); -}; + + public attemptAssetGracefulTermination = async (filename: string) => { + if (!ASSETS_EIP_NUM.test(filename)) { + return; + } + + const assetEipNumMatch = filename.match(ASSETS_EIP_NUM); + if (!assetEipNumMatch || assetEipNumMatch[1] === undefined) { + throw new UnexpectedError( + multiLineString(" ")( + `The filename '${filename}' is seen to match an asset file but`, + `the extracted eip number is undefined` + ) + ); + } + const assetEipNum = parseInt(assetEipNumMatch[1]); + const pr = await this.requirePr(); + const files = await this.getPullRequestFiles(pr.number); + + const filenames = files.map((file) => file.filename); + + for (const otherFilename of filenames) { + // if other filename is same as current one then skip + if (otherFilename === filename) { + continue; + } + + // if the filename doesn't match to an eip number skip + const eipNumMatch = otherFilename.match(EIP_NUM_RE); + if (!eipNumMatch || eipNumMatch[1] === undefined) { + continue; + } + + const eipNum = parseInt(eipNumMatch[1]); + if (eipNum === assetEipNum) { + throw new GracefulTermination( + multiLineString(" ")( + `file ${filename} is associated with EIP ${assetEipNum}; because`, + `there are also changes being made to ${otherFilename} all changes`, + `to corresponding assets are also allowed` + ) + ); + } + } + throw new RequirementViolation( + multiLineString(" ")( + `file ${filename} is associated with EIP ${assetEipNum} but there`, + `are no changes being made to corresponding EIP itself. To assure`, + `that the change is authorized by the relevant stake-holders, you must`, + `also make changes to the EIP file itself for the asset changes to`, + `be eligible for auto-merge` + ) + ); + }; + + public attemptEditorApprovalGracefulTermination = async ( + filename: string + ) => { + const editorApprovals = this.requireEIPEditors(); + const approvals = await this.getApprovals(); + + const isEditorApproved = + _.intersection(editorApprovals, approvals).length !== 0; + if (isEditorApproved) { + throw new GracefulTermination( + multiLineString(" ")( + `file ${filename} is not a valid filename, but this error has been`, + `ignored due to editor approvals` + ) + ); + } + }; + + /** + * Extracts the EIP number from a given filename (or returns null) + * @param filename EIP filename + */ + requireFilenameEipNum = async (filename: string) => { + const eipNumMatch = filename.match(EIP_NUM_RE); + if (!eipNumMatch || eipNumMatch[1] === undefined) { + await this.attemptAssetGracefulTermination(filename); + await this.attemptEditorApprovalGracefulTermination(filename); + throw new RequirementViolation( + `'${filename}' must be in eip-###.md format; this error will be overwritten upon relevant editor approval` + ); + } + return eipNumMatch && parseInt(eipNumMatch[1]); + }; +} diff --git a/src/modules/file/modules/file_diff_infra.ts b/src/modules/file/modules/file_diff_infra.ts index ef4da149..5fbae55e 100755 --- a/src/modules/file/modules/file_diff_infra.ts +++ b/src/modules/file/modules/file_diff_infra.ts @@ -19,7 +19,7 @@ import { getRepoFilenameContent, resolveUserByEmail } from "src/infra"; export class FileDiffInfra implements IFileDiff { constructor( - public requireFilenameEipNum: (filename: string) => number, + public requireFilenameEipNum: (filename: string) => Promise, public requirePr: () => Promise ) {} @@ -55,7 +55,7 @@ export class FileDiffInfra implements IFileDiff { }; formatFile = async (file: ParsedContent): Promise => { - const filenameEipNum = this.requireFilenameEipNum(file.name); + const filenameEipNum = await this.requireFilenameEipNum(file.name); if (!filenameEipNum) { throw `Failed to extract eip number from file "${file.path}"`; } diff --git a/src/tests/assets/records/3581.json b/src/tests/assets/records/3581.json new file mode 100644 index 00000000..8713a417 --- /dev/null +++ b/src/tests/assets/records/3581.json @@ -0,0 +1,898 @@ +[ + { + "req": { + "url": "https://api.github.com/repos/ethereum/EIPs/pulls/3581", + "method": "GET" + }, + "res": { + "status": 200, + "data": { + "url": "https://api.github.com/repos/ethereum/EIPs/pulls/3581", + "id": 647386241, + "node_id": "MDExOlB1bGxSZXF1ZXN0NjQ3Mzg2MjQx", + "html_url": "https://github.com/ethereum/EIPs/pull/3581", + "diff_url": "https://github.com/ethereum/EIPs/pull/3581.diff", + "patch_url": "https://github.com/ethereum/EIPs/pull/3581.patch", + "issue_url": "https://api.github.com/repos/ethereum/EIPs/issues/3581", + "number": 3581, + "state": "closed", + "locked": false, + "title": "Bump nokogiri from 1.10.9 to 1.11.4", + "user": { + "login": "dependabot[bot]", + "id": 49699333, + "node_id": "MDM6Qm90NDk2OTkzMzM=", + "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/dependabot%5Bbot%5D", + "html_url": "https://github.com/apps/dependabot", + "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", + "type": "Bot", + "site_admin": false + }, + "body": "Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.10.9 to 1.11.4.\n
\nRelease notes\n

Sourced from nokogiri's releases.

\n
\n

1.11.4 / 2021-05-14

\n

Security

\n

[CRuby] Vendored libxml2 upgraded to v2.9.12 which addresses:

\n\n

Note that two additional CVEs were addressed upstream but are not relevant to this release. CVE-2021-3516 via xmllint is not present in Nokogiri, and CVE-2020-7595 has been patched in Nokogiri since v1.10.8 (see #1992).

\n

Please see nokogiri/GHSA-7rrm-v45f-jp64 or #2233 for a more complete analysis of these CVEs and patches.

\n

Dependencies

\n
    \n
  • [CRuby] vendored libxml2 is updated from 2.9.10 to 2.9.12. (Note that 2.9.11 was skipped because it was superseded by 2.9.12 a few hours after its release.)
  • \n
\n

1.11.3 / 2021-04-07

\n

Fixed

\n
    \n
  • [CRuby] Passing non-Node objects to Document#root= now raises an ArgumentError exception. Previously this likely segfaulted. [#1900]
  • \n
  • [JRuby] Passing non-Node objects to Document#root= now raises an ArgumentError exception. Previously this raised a TypeError exception.
  • \n
  • [CRuby] arm64/aarch64 systems (like Apple's M1) can now compile libxml2 and libxslt from source (though we continue to strongly advise users to install the native gems for the best possible experience)
  • \n
\n

1.11.2 / 2021-03-11

\n

Fixed

\n
    \n
  • [CRuby] NodeSet may now safely contain Node objects from multiple documents. Previously the GC lifecycle of the parent Document objects could lead to nodes being GCed while still in scope. [#1952]
  • \n
  • [CRuby] Patch libxml2 to avoid "huge input lookup" errors on large CDATA elements. (See upstream GNOME/libxml2#200 and GNOME/libxml2!100.) [#2132].
  • \n
  • [CRuby+Windows] Enable Nokogumbo (and other downstream gems) to compile and link against nokogiri.so by including LDFLAGS in Nokogiri::VERSION_INFO. [#2167]
  • \n
  • [CRuby] {XML,HTML}::Document.parse now invokes #initialize exactly once. Previously #initialize was invoked twice on each object.
  • \n
  • [JRuby] {XML,HTML}::Document.parse now invokes #initialize exactly once. Previously #initialize was not called, which was a problem for subclassing such as done by Loofah.
  • \n
\n

Improved

\n
    \n
  • Reduce the number of object allocations needed when parsing an HTML::DocumentFragment. [#2087] (Thanks, @​ashmaroli!)
  • \n
  • [JRuby] Update the algorithm used to calculate Node#line to be wrong less-often. The underlying parser, Xerces, does not track line numbers, and so we've always used a hacky solution for this method. [#1223, #2177]
  • \n
  • Introduce --enable-system-libraries and --disable-system-libraries flags to extconf.rb. These flags provide the same functionality as --use-system-libraries and the NOKOGIRI_USE_SYSTEM_LIBRARIES environment variable, but are more idiomatic. [#2193] (Thanks, @​eregon!)
  • \n
  • [TruffleRuby] --disable-static is now the default on TruffleRuby when the packaged libraries are used. This is more flexible and compiles faster. (Note, though, that the default on TR is still to use system libraries.) [#2191, #2193] (Thanks, @​eregon!)
  • \n
\n\n
\n

... (truncated)

\n
\n
\nChangelog\n

Sourced from nokogiri's changelog.

\n
\n

1.11.4 / 2021-05-14

\n

Security

\n

[CRuby] Vendored libxml2 upgraded to v2.9.12 which addresses:

\n\n

Note that two additional CVEs were addressed upstream but are not relevant to this release. CVE-2021-3516 via xmllint is not present in Nokogiri, and CVE-2020-7595 has been patched in Nokogiri since v1.10.8 (see #1992).

\n

Please see nokogiri/GHSA-7rrm-v45f-jp64 or #2233 for a more complete analysis of these CVEs and patches.

\n

Dependencies

\n
    \n
  • [CRuby] vendored libxml2 is updated from 2.9.10 to 2.9.12. (Note that 2.9.11 was skipped because it was superseded by 2.9.12 a few hours after its release.)
  • \n
\n

1.11.3 / 2021-04-07

\n

Fixed

\n
    \n
  • [CRuby] Passing non-Node objects to Document#root= now raises an ArgumentError exception. Previously this likely segfaulted. [#1900]
  • \n
  • [JRuby] Passing non-Node objects to Document#root= now raises an ArgumentError exception. Previously this raised a TypeError exception.
  • \n
  • [CRuby] arm64/aarch64 systems (like Apple's M1) can now compile libxml2 and libxslt from source (though we continue to strongly advise users to install the native gems for the best possible experience)
  • \n
\n

1.11.2 / 2021-03-11

\n

Fixed

\n
    \n
  • [CRuby] NodeSet may now safely contain Node objects from multiple documents. Previously the GC lifecycle of the parent Document objects could lead to nodes being GCed while still in scope. [#1952]
  • \n
  • [CRuby] Patch libxml2 to avoid "huge input lookup" errors on large CDATA elements. (See upstream GNOME/libxml2#200 and GNOME/libxml2!100.) [#2132].
  • \n
  • [CRuby+Windows] Enable Nokogumbo (and other downstream gems) to compile and link against nokogiri.so by including LDFLAGS in Nokogiri::VERSION_INFO. [#2167]
  • \n
  • [CRuby] {XML,HTML}::Document.parse now invokes #initialize exactly once. Previously #initialize was invoked twice on each object.
  • \n
  • [JRuby] {XML,HTML}::Document.parse now invokes #initialize exactly once. Previously #initialize was not called, which was a problem for subclassing such as done by Loofah.
  • \n
\n

Improved

\n
    \n
  • Reduce the number of object allocations needed when parsing an HTML::DocumentFragment. [#2087] (Thanks, @​ashmaroli!)
  • \n
  • [JRuby] Update the algorithm used to calculate Node#line to be wrong less-often. The underlying parser, Xerces, does not track line numbers, and so we've always used a hacky solution for this method. [#1223, #2177]
  • \n
  • Introduce --enable-system-libraries and --disable-system-libraries flags to extconf.rb. These flags provide the same functionality as --use-system-libraries and the NOKOGIRI_USE_SYSTEM_LIBRARIES environment variable, but are more idiomatic. [#2193] (Thanks, @​eregon!)
  • \n
  • [TruffleRuby] --disable-static is now the default on TruffleRuby when the packaged libraries are used. This is more flexible and compiles faster. (Note, though, that the default on TR is still to use system libraries.) [#2191, #2193] (Thanks, @​eregon!)
  • \n
\n\n
\n

... (truncated)

\n
\n
\nCommits\n
    \n
  • 9d69b44 version bump to v1.11.4
  • \n
  • 058e87f update CHANGELOG with complete CVE information
  • \n
  • 9285251 Merge pull request #2234 from sparklemotion/2233-upgrade-to-libxml-2-9-12
  • \n
  • 5436f61 update CHANGELOG
  • \n
  • 761d320 patch: renumber libxml2 patches
  • \n
  • 889ee2a test: update behavior of namespaces in HTML
  • \n
  • 9751d85 test: remove low-value HTML::SAX::PushParser encoding test
  • \n
  • 9fcb7d2 test: adjust xpath gc test to libxml2's max recursion depth
  • \n
  • 1c99019 patch: backport libxslt configure.ac change for libxml2 config
  • \n
  • 82a253f patch: fix isnan/isinf patch to apply cleanly to libxml 2.9.12
  • \n
  • Additional commits viewable in compare view
  • \n
\n
\n
\n\n\n[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=nokogiri&package-manager=bundler&previous-version=1.10.9&new-version=1.11.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.\n\n[//]: # (dependabot-automerge-start)\n[//]: # (dependabot-automerge-end)\n\n---\n\n
\nDependabot commands and options\n
\n\nYou can trigger Dependabot actions by commenting on this PR:\n- `@dependabot rebase` will rebase this PR\n- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it\n- `@dependabot merge` will merge this PR after your CI passes on it\n- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it\n- `@dependabot cancel merge` will cancel a previously requested merge and block automerging\n- `@dependabot reopen` will reopen this PR if it is closed\n- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually\n- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)\n- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)\n- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)\n- `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language\n- `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language\n- `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language\n- `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language\n\nYou can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/ethereum/EIPs/network/alerts).\n\n
", + "created_at": "2021-05-19T08:04:10Z", + "updated_at": "2021-08-17T23:02:55Z", + "closed_at": "2021-08-17T23:02:55Z", + "merge_commit_sha": "0205ff8576da049e9eba808368dcba0455aa2257", + "assignee": null, + "assignees": [], + "requested_reviewers": [], + "requested_teams": [], + "labels": [ + { + "id": 1652436489, + "node_id": "MDU6TGFiZWwxNjUyNDM2NDg5", + "url": "https://api.github.com/repos/ethereum/EIPs/labels/dependencies", + "name": "dependencies", + "color": "0366d6", + "default": false, + "description": "Pull requests that update a dependency file" + }, + { + "id": 2328627851, + "node_id": "MDU6TGFiZWwyMzI4NjI3ODUx", + "url": "https://api.github.com/repos/ethereum/EIPs/labels/stale", + "name": "stale", + "color": "000000", + "default": false, + "description": "" + } + ], + "milestone": null, + "draft": false, + "commits_url": "https://api.github.com/repos/ethereum/EIPs/pulls/3581/commits", + "review_comments_url": "https://api.github.com/repos/ethereum/EIPs/pulls/3581/comments", + "review_comment_url": "https://api.github.com/repos/ethereum/EIPs/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/ethereum/EIPs/issues/3581/comments", + "statuses_url": "https://api.github.com/repos/ethereum/EIPs/statuses/837336cf8724bade84b82959aee96c686b48ea03", + "head": { + "label": "ethereum:dependabot/bundler/nokogiri-1.11.4", + "ref": "dependabot/bundler/nokogiri-1.11.4", + "sha": "837336cf8724bade84b82959aee96c686b48ea03", + "user": { + "login": "ethereum", + "id": 6250754, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjYyNTA3NTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/6250754?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/ethereum", + "html_url": "https://github.com/ethereum", + "followers_url": "https://api.github.com/users/ethereum/followers", + "following_url": "https://api.github.com/users/ethereum/following{/other_user}", + "gists_url": "https://api.github.com/users/ethereum/gists{/gist_id}", + "starred_url": "https://api.github.com/users/ethereum/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/ethereum/subscriptions", + "organizations_url": "https://api.github.com/users/ethereum/orgs", + "repos_url": "https://api.github.com/users/ethereum/repos", + "events_url": "https://api.github.com/users/ethereum/events{/privacy}", + "received_events_url": "https://api.github.com/users/ethereum/received_events", + "type": "Organization", + "site_admin": false + }, + "repo": { + "id": 44971752, + "node_id": "MDEwOlJlcG9zaXRvcnk0NDk3MTc1Mg==", + "name": "EIPs", + "full_name": "ethereum/EIPs", + "private": false, + "owner": { + "login": "ethereum", + "id": 6250754, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjYyNTA3NTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/6250754?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/ethereum", + "html_url": "https://github.com/ethereum", + "followers_url": "https://api.github.com/users/ethereum/followers", + "following_url": "https://api.github.com/users/ethereum/following{/other_user}", + "gists_url": "https://api.github.com/users/ethereum/gists{/gist_id}", + "starred_url": "https://api.github.com/users/ethereum/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/ethereum/subscriptions", + "organizations_url": "https://api.github.com/users/ethereum/orgs", + "repos_url": "https://api.github.com/users/ethereum/repos", + "events_url": "https://api.github.com/users/ethereum/events{/privacy}", + "received_events_url": "https://api.github.com/users/ethereum/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/ethereum/EIPs", + "description": "The Ethereum Improvement Proposal repository", + "fork": false, + "url": "https://api.github.com/repos/ethereum/EIPs", + "forks_url": "https://api.github.com/repos/ethereum/EIPs/forks", + "keys_url": "https://api.github.com/repos/ethereum/EIPs/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/ethereum/EIPs/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/ethereum/EIPs/teams", + "hooks_url": "https://api.github.com/repos/ethereum/EIPs/hooks", + "issue_events_url": "https://api.github.com/repos/ethereum/EIPs/issues/events{/number}", + "events_url": "https://api.github.com/repos/ethereum/EIPs/events", + "assignees_url": "https://api.github.com/repos/ethereum/EIPs/assignees{/user}", + "branches_url": "https://api.github.com/repos/ethereum/EIPs/branches{/branch}", + "tags_url": "https://api.github.com/repos/ethereum/EIPs/tags", + "blobs_url": "https://api.github.com/repos/ethereum/EIPs/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/ethereum/EIPs/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/ethereum/EIPs/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/ethereum/EIPs/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/ethereum/EIPs/statuses/{sha}", + "languages_url": "https://api.github.com/repos/ethereum/EIPs/languages", + "stargazers_url": "https://api.github.com/repos/ethereum/EIPs/stargazers", + "contributors_url": "https://api.github.com/repos/ethereum/EIPs/contributors", + "subscribers_url": "https://api.github.com/repos/ethereum/EIPs/subscribers", + "subscription_url": "https://api.github.com/repos/ethereum/EIPs/subscription", + "commits_url": "https://api.github.com/repos/ethereum/EIPs/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/ethereum/EIPs/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/ethereum/EIPs/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/ethereum/EIPs/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/ethereum/EIPs/contents/{+path}", + "compare_url": "https://api.github.com/repos/ethereum/EIPs/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/ethereum/EIPs/merges", + "archive_url": "https://api.github.com/repos/ethereum/EIPs/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/ethereum/EIPs/downloads", + "issues_url": "https://api.github.com/repos/ethereum/EIPs/issues{/number}", + "pulls_url": "https://api.github.com/repos/ethereum/EIPs/pulls{/number}", + "milestones_url": "https://api.github.com/repos/ethereum/EIPs/milestones{/number}", + "notifications_url": "https://api.github.com/repos/ethereum/EIPs/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/ethereum/EIPs/labels{/name}", + "releases_url": "https://api.github.com/repos/ethereum/EIPs/releases{/id}", + "deployments_url": "https://api.github.com/repos/ethereum/EIPs/deployments", + "created_at": "2015-10-26T13:57:23Z", + "updated_at": "2021-11-21T14:36:10Z", + "pushed_at": "2021-11-21T16:09:43Z", + "git_url": "git://github.com/ethereum/EIPs.git", + "ssh_url": "git@github.com:ethereum/EIPs.git", + "clone_url": "https://github.com/ethereum/EIPs.git", + "svn_url": "https://github.com/ethereum/EIPs", + "homepage": "https://eips.ethereum.org/", + "size": 21391, + "stargazers_count": 7787, + "watchers_count": 7787, + "language": "Solidity", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": false, + "has_pages": true, + "forks_count": 2958, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 480, + "license": null, + "allow_forking": true, + "is_template": false, + "topics": [], + "visibility": "public", + "forks": 2958, + "open_issues": 480, + "watchers": 7787, + "default_branch": "master" + } + }, + "base": { + "label": "ethereum:master", + "ref": "master", + "sha": "739cbf2e8cfbea7b712f2d7a6db69ca258182ae1", + "user": { + "login": "ethereum", + "id": 6250754, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjYyNTA3NTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/6250754?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/ethereum", + "html_url": "https://github.com/ethereum", + "followers_url": "https://api.github.com/users/ethereum/followers", + "following_url": "https://api.github.com/users/ethereum/following{/other_user}", + "gists_url": "https://api.github.com/users/ethereum/gists{/gist_id}", + "starred_url": "https://api.github.com/users/ethereum/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/ethereum/subscriptions", + "organizations_url": "https://api.github.com/users/ethereum/orgs", + "repos_url": "https://api.github.com/users/ethereum/repos", + "events_url": "https://api.github.com/users/ethereum/events{/privacy}", + "received_events_url": "https://api.github.com/users/ethereum/received_events", + "type": "Organization", + "site_admin": false + }, + "repo": { + "id": 44971752, + "node_id": "MDEwOlJlcG9zaXRvcnk0NDk3MTc1Mg==", + "name": "EIPs", + "full_name": "ethereum/EIPs", + "private": false, + "owner": { + "login": "ethereum", + "id": 6250754, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjYyNTA3NTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/6250754?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/ethereum", + "html_url": "https://github.com/ethereum", + "followers_url": "https://api.github.com/users/ethereum/followers", + "following_url": "https://api.github.com/users/ethereum/following{/other_user}", + "gists_url": "https://api.github.com/users/ethereum/gists{/gist_id}", + "starred_url": "https://api.github.com/users/ethereum/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/ethereum/subscriptions", + "organizations_url": "https://api.github.com/users/ethereum/orgs", + "repos_url": "https://api.github.com/users/ethereum/repos", + "events_url": "https://api.github.com/users/ethereum/events{/privacy}", + "received_events_url": "https://api.github.com/users/ethereum/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/ethereum/EIPs", + "description": "The Ethereum Improvement Proposal repository", + "fork": false, + "url": "https://api.github.com/repos/ethereum/EIPs", + "forks_url": "https://api.github.com/repos/ethereum/EIPs/forks", + "keys_url": "https://api.github.com/repos/ethereum/EIPs/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/ethereum/EIPs/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/ethereum/EIPs/teams", + "hooks_url": "https://api.github.com/repos/ethereum/EIPs/hooks", + "issue_events_url": "https://api.github.com/repos/ethereum/EIPs/issues/events{/number}", + "events_url": "https://api.github.com/repos/ethereum/EIPs/events", + "assignees_url": "https://api.github.com/repos/ethereum/EIPs/assignees{/user}", + "branches_url": "https://api.github.com/repos/ethereum/EIPs/branches{/branch}", + "tags_url": "https://api.github.com/repos/ethereum/EIPs/tags", + "blobs_url": "https://api.github.com/repos/ethereum/EIPs/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/ethereum/EIPs/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/ethereum/EIPs/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/ethereum/EIPs/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/ethereum/EIPs/statuses/{sha}", + "languages_url": "https://api.github.com/repos/ethereum/EIPs/languages", + "stargazers_url": "https://api.github.com/repos/ethereum/EIPs/stargazers", + "contributors_url": "https://api.github.com/repos/ethereum/EIPs/contributors", + "subscribers_url": "https://api.github.com/repos/ethereum/EIPs/subscribers", + "subscription_url": "https://api.github.com/repos/ethereum/EIPs/subscription", + "commits_url": "https://api.github.com/repos/ethereum/EIPs/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/ethereum/EIPs/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/ethereum/EIPs/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/ethereum/EIPs/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/ethereum/EIPs/contents/{+path}", + "compare_url": "https://api.github.com/repos/ethereum/EIPs/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/ethereum/EIPs/merges", + "archive_url": "https://api.github.com/repos/ethereum/EIPs/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/ethereum/EIPs/downloads", + "issues_url": "https://api.github.com/repos/ethereum/EIPs/issues{/number}", + "pulls_url": "https://api.github.com/repos/ethereum/EIPs/pulls{/number}", + "milestones_url": "https://api.github.com/repos/ethereum/EIPs/milestones{/number}", + "notifications_url": "https://api.github.com/repos/ethereum/EIPs/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/ethereum/EIPs/labels{/name}", + "releases_url": "https://api.github.com/repos/ethereum/EIPs/releases{/id}", + "deployments_url": "https://api.github.com/repos/ethereum/EIPs/deployments", + "created_at": "2015-10-26T13:57:23Z", + "updated_at": "2021-11-21T14:36:10Z", + "pushed_at": "2021-11-21T16:09:43Z", + "git_url": "git://github.com/ethereum/EIPs.git", + "ssh_url": "git@github.com:ethereum/EIPs.git", + "clone_url": "https://github.com/ethereum/EIPs.git", + "svn_url": "https://github.com/ethereum/EIPs", + "homepage": "https://eips.ethereum.org/", + "size": 21391, + "stargazers_count": 7787, + "watchers_count": 7787, + "language": "Solidity", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": false, + "has_pages": true, + "forks_count": 2958, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 480, + "license": null, + "allow_forking": true, + "is_template": false, + "topics": [], + "visibility": "public", + "forks": 2958, + "open_issues": 480, + "watchers": 7787, + "default_branch": "master" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/ethereum/EIPs/pulls/3581" + }, + "html": { + "href": "https://github.com/ethereum/EIPs/pull/3581" + }, + "issue": { + "href": "https://api.github.com/repos/ethereum/EIPs/issues/3581" + }, + "comments": { + "href": "https://api.github.com/repos/ethereum/EIPs/issues/3581/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/ethereum/EIPs/pulls/3581/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/ethereum/EIPs/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/ethereum/EIPs/pulls/3581/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/ethereum/EIPs/statuses/837336cf8724bade84b82959aee96c686b48ea03" + } + }, + "author_association": "CONTRIBUTOR", + "auto_merge": null, + "active_lock_reason": null, + "mergeable": null, + "rebaseable": null, + "mergeable_state": "unknown", + "comments": 8, + "review_comments": 0, + "maintainer_can_modify": false, + "commits": 4, + "additions": 5, + "deletions": 3, + "changed_files": 1 + } + } + }, + { + "req": { + "url": "https://api.github.com/repos/ethereum/EIPs/pulls/3581/files", + "method": "GET" + }, + "res": { + "status": 200, + "data": [ + { + "sha": "10bfc6faa0040e1f269fa68f5d4b178d24242960", + "filename": "Gemfile.lock", + "status": "modified", + "additions": 5, + "deletions": 3, + "changes": 8, + "blob_url": "https://github.com/ethereum/EIPs/blob/837336cf8724bade84b82959aee96c686b48ea03/Gemfile.lock", + "raw_url": "https://github.com/ethereum/EIPs/raw/837336cf8724bade84b82959aee96c686b48ea03/Gemfile.lock", + "contents_url": "https://api.github.com/repos/ethereum/EIPs/contents/Gemfile.lock?ref=837336cf8724bade84b82959aee96c686b48ea03", + "patch": "@@ -211,15 +211,16 @@ GEM\n rb-fsevent (~> 0.10, >= 0.10.3)\n rb-inotify (~> 0.9, >= 0.9.10)\n mercenary (0.3.6)\n- mini_portile2 (2.4.0)\n+ mini_portile2 (2.5.1)\n minima (2.5.1)\n jekyll (>= 3.5, < 5.0)\n jekyll-feed (~> 0.9)\n jekyll-seo-tag (~> 2.1)\n minitest (5.14.1)\n multipart-post (2.1.1)\n- nokogiri (1.10.9)\n- mini_portile2 (~> 2.4.0)\n+ nokogiri (1.11.4)\n+ mini_portile2 (~> 2.5.0)\n+ racc (~> 1.4)\n nokogumbo (2.0.2)\n nokogiri (~> 1.8, >= 1.8.4)\n octokit (4.18.0)\n@@ -229,6 +230,7 @@ GEM\n pathutil (0.16.2)\n forwardable-extended (~> 2.6)\n public_suffix (3.1.1)\n+ racc (1.5.2)\n rainbow (3.0.0)\n rb-fsevent (0.10.4)\n rb-inotify (0.10.1)" + } + ] + } + }, + { + "req": { + "url": "https://api.github.com/repos/ethereum/EIPs/contents/Gemfile.lock?ref=837336cf8724bade84b82959aee96c686b48ea03", + "method": "GET" + }, + "res": { + "status": 200, + "data": { + "name": "Gemfile.lock", + "path": "Gemfile.lock", + "sha": "10bfc6faa0040e1f269fa68f5d4b178d24242960", + "size": 7484, + "url": "https://api.github.com/repos/ethereum/EIPs/contents/Gemfile.lock?ref=837336cf8724bade84b82959aee96c686b48ea03", + "html_url": "https://github.com/ethereum/EIPs/blob/837336cf8724bade84b82959aee96c686b48ea03/Gemfile.lock", + "git_url": "https://api.github.com/repos/ethereum/EIPs/git/blobs/10bfc6faa0040e1f269fa68f5d4b178d24242960", + "download_url": "https://raw.githubusercontent.com/ethereum/EIPs/837336cf8724bade84b82959aee96c686b48ea03/Gemfile.lock", + "type": "file", + "content": "R0VNCiAgcmVtb3RlOiBodHRwczovL3J1YnlnZW1zLm9yZy8KICBzcGVjczoK\nICAgIGFjdGl2ZW1vZGVsICg2LjAuMy4xKQogICAgICBhY3RpdmVzdXBwb3J0\nICg9IDYuMC4zLjEpCiAgICBhY3RpdmVzdXBwb3J0ICg2LjAuMy4xKQogICAg\nICBjb25jdXJyZW50LXJ1YnkgKH4+IDEuMCwgPj0gMS4wLjIpCiAgICAgIGkx\nOG4gKD49IDAuNywgPCAyKQogICAgICBtaW5pdGVzdCAofj4gNS4xKQogICAg\nICB0emluZm8gKH4+IDEuMSkKICAgICAgemVpdHdlcmsgKH4+IDIuMiwgPj0g\nMi4yLjIpCiAgICBhZGRyZXNzYWJsZSAoMi43LjApCiAgICAgIHB1YmxpY19z\ndWZmaXggKD49IDIuMC4yLCA8IDUuMCkKICAgIGNvZmZlZS1zY3JpcHQgKDIu\nNC4xKQogICAgICBjb2ZmZWUtc2NyaXB0LXNvdXJjZQogICAgICBleGVjanMK\nICAgIGNvZmZlZS1zY3JpcHQtc291cmNlICgxLjExLjEpCiAgICBjb2xvcmF0\nb3IgKDEuMS4wKQogICAgY29tbW9ubWFya2VyICgwLjE3LjEzKQogICAgICBy\ndWJ5LWVudW0gKH4+IDAuNSkKICAgIGNvbmN1cnJlbnQtcnVieSAoMS4xLjYp\nCiAgICBkbnNydWJ5ICgxLjYxLjMpCiAgICAgIGFkZHJlc3NhYmxlICh+PiAy\nLjUpCiAgICBlaXBfdmFsaWRhdG9yICgwLjguMikKICAgICAgYWN0aXZlbW9k\nZWwKICAgICAgZnJvbnRfbWF0dGVyX3BhcnNlciAofj4gMC4xLjEpCiAgICBl\nbS13ZWJzb2NrZXQgKDAuNS4xKQogICAgICBldmVudG1hY2hpbmUgKD49IDAu\nMTIuOSkKICAgICAgaHR0cF9wYXJzZXIucmIgKH4+IDAuNi4wKQogICAgZXRo\nb24gKDAuMTIuMCkKICAgICAgZmZpICg+PSAxLjMuMCkKICAgIGV2ZW50bWFj\naGluZSAoMS4yLjcpCiAgICBleGVjanMgKDIuNy4wKQogICAgZmFyYWRheSAo\nMS4wLjEpCiAgICAgIG11bHRpcGFydC1wb3N0ICg+PSAxLjIsIDwgMykKICAg\nIGZmaSAoMS4xMi4yKQogICAgZm9yd2FyZGFibGUtZXh0ZW5kZWQgKDIuNi4w\nKQogICAgZnJvbnRfbWF0dGVyX3BhcnNlciAoMC4xLjEpCiAgICBnZW1vamkg\nKDMuMC4xKQogICAgZ2l0aHViLXBhZ2VzICgyMDYpCiAgICAgIGdpdGh1Yi1w\nYWdlcy1oZWFsdGgtY2hlY2sgKD0gMS4xNi4xKQogICAgICBqZWt5bGwgKD0g\nMy44LjcpCiAgICAgIGpla3lsbC1hdmF0YXIgKD0gMC43LjApCiAgICAgIGpl\na3lsbC1jb2ZmZWVzY3JpcHQgKD0gMS4xLjEpCiAgICAgIGpla3lsbC1jb21t\nb25tYXJrLWdocGFnZXMgKD0gMC4xLjYpCiAgICAgIGpla3lsbC1kZWZhdWx0\nLWxheW91dCAoPSAwLjEuNCkKICAgICAgamVreWxsLWZlZWQgKD0gMC4xMy4w\nKQogICAgICBqZWt5bGwtZ2lzdCAoPSAxLjUuMCkKICAgICAgamVreWxsLWdp\ndGh1Yi1tZXRhZGF0YSAoPSAyLjEzLjApCiAgICAgIGpla3lsbC1tZW50aW9u\ncyAoPSAxLjUuMSkKICAgICAgamVreWxsLW9wdGlvbmFsLWZyb250LW1hdHRl\nciAoPSAwLjMuMikKICAgICAgamVreWxsLXBhZ2luYXRlICg9IDEuMS4wKQog\nICAgICBqZWt5bGwtcmVhZG1lLWluZGV4ICg9IDAuMy4wKQogICAgICBqZWt5\nbGwtcmVkaXJlY3QtZnJvbSAoPSAwLjE1LjApCiAgICAgIGpla3lsbC1yZWxh\ndGl2ZS1saW5rcyAoPSAwLjYuMSkKICAgICAgamVreWxsLXJlbW90ZS10aGVt\nZSAoPSAwLjQuMSkKICAgICAgamVreWxsLXNhc3MtY29udmVydGVyICg9IDEu\nNS4yKQogICAgICBqZWt5bGwtc2VvLXRhZyAoPSAyLjYuMSkKICAgICAgamVr\neWxsLXNpdGVtYXAgKD0gMS40LjApCiAgICAgIGpla3lsbC1zd2lzcyAoPSAx\nLjAuMCkKICAgICAgamVreWxsLXRoZW1lLWFyY2hpdGVjdCAoPSAwLjEuMSkK\nICAgICAgamVreWxsLXRoZW1lLWNheW1hbiAoPSAwLjEuMSkKICAgICAgamVr\neWxsLXRoZW1lLWRpbmt5ICg9IDAuMS4xKQogICAgICBqZWt5bGwtdGhlbWUt\naGFja2VyICg9IDAuMS4xKQogICAgICBqZWt5bGwtdGhlbWUtbGVhcC1kYXkg\nKD0gMC4xLjEpCiAgICAgIGpla3lsbC10aGVtZS1tZXJsb3QgKD0gMC4xLjEp\nCiAgICAgIGpla3lsbC10aGVtZS1taWRuaWdodCAoPSAwLjEuMSkKICAgICAg\namVreWxsLXRoZW1lLW1pbmltYWwgKD0gMC4xLjEpCiAgICAgIGpla3lsbC10\naGVtZS1tb2Rlcm5pc3QgKD0gMC4xLjEpCiAgICAgIGpla3lsbC10aGVtZS1w\ncmltZXIgKD0gMC41LjQpCiAgICAgIGpla3lsbC10aGVtZS1zbGF0ZSAoPSAw\nLjEuMSkKICAgICAgamVreWxsLXRoZW1lLXRhY3RpbGUgKD0gMC4xLjEpCiAg\nICAgIGpla3lsbC10aGVtZS10aW1lLW1hY2hpbmUgKD0gMC4xLjEpCiAgICAg\nIGpla3lsbC10aXRsZXMtZnJvbS1oZWFkaW5ncyAoPSAwLjUuMykKICAgICAg\namVtb2ppICg9IDAuMTEuMSkKICAgICAga3JhbWRvd24gKD0gMS4xNy4wKQog\nICAgICBsaXF1aWQgKD0gNC4wLjMpCiAgICAgIG1lcmNlbmFyeSAofj4gMC4z\nKQogICAgICBtaW5pbWEgKD0gMi41LjEpCiAgICAgIG5va29naXJpICg+PSAx\nLjEwLjQsIDwgMi4wKQogICAgICByb3VnZSAoPSAzLjE5LjApCiAgICAgIHRl\ncm1pbmFsLXRhYmxlICh+PiAxLjQpCiAgICBnaXRodWItcGFnZXMtaGVhbHRo\nLWNoZWNrICgxLjE2LjEpCiAgICAgIGFkZHJlc3NhYmxlICh+PiAyLjMpCiAg\nICAgIGRuc3J1YnkgKH4+IDEuNjApCiAgICAgIG9jdG9raXQgKH4+IDQuMCkK\nICAgICAgcHVibGljX3N1ZmZpeCAofj4gMy4wKQogICAgICB0eXBob2V1cyAo\nfj4gMS4zKQogICAgaHRtbC1waXBlbGluZSAoMi4xMy4wKQogICAgICBhY3Rp\ndmVzdXBwb3J0ICg+PSAyKQogICAgICBub2tvZ2lyaSAoPj0gMS40KQogICAg\naHRtbC1wcm9vZmVyICgzLjE1LjMpCiAgICAgIGFkZHJlc3NhYmxlICh+PiAy\nLjMpCiAgICAgIG1lcmNlbmFyeSAofj4gMC4zKQogICAgICBub2tvZ3VtYm8g\nKH4+IDIuMCkKICAgICAgcGFyYWxsZWwgKH4+IDEuMykKICAgICAgcmFpbmJv\ndyAofj4gMy4wKQogICAgICB0eXBob2V1cyAofj4gMS4zKQogICAgICB5ZWxs\nICh+PiAyLjApCiAgICBodHRwX3BhcnNlci5yYiAoMC42LjApCiAgICBpMThu\nICgwLjkuNSkKICAgICAgY29uY3VycmVudC1ydWJ5ICh+PiAxLjApCiAgICBq\nZWt5bGwgKDMuOC43KQogICAgICBhZGRyZXNzYWJsZSAofj4gMi40KQogICAg\nICBjb2xvcmF0b3IgKH4+IDEuMCkKICAgICAgZW0td2Vic29ja2V0ICh+PiAw\nLjUpCiAgICAgIGkxOG4gKH4+IDAuNykKICAgICAgamVreWxsLXNhc3MtY29u\ndmVydGVyICh+PiAxLjApCiAgICAgIGpla3lsbC13YXRjaCAofj4gMi4wKQog\nICAgICBrcmFtZG93biAofj4gMS4xNCkKICAgICAgbGlxdWlkICh+PiA0LjAp\nCiAgICAgIG1lcmNlbmFyeSAofj4gMC4zLjMpCiAgICAgIHBhdGh1dGlsICh+\nPiAwLjkpCiAgICAgIHJvdWdlICg+PSAxLjcsIDwgNCkKICAgICAgc2FmZV95\nYW1sICh+PiAxLjApCiAgICBqZWt5bGwtYXZhdGFyICgwLjcuMCkKICAgICAg\namVreWxsICg+PSAzLjAsIDwgNS4wKQogICAgamVreWxsLWNvZmZlZXNjcmlw\ndCAoMS4xLjEpCiAgICAgIGNvZmZlZS1zY3JpcHQgKH4+IDIuMikKICAgICAg\nY29mZmVlLXNjcmlwdC1zb3VyY2UgKH4+IDEuMTEuMSkKICAgIGpla3lsbC1j\nb21tb25tYXJrICgxLjMuMSkKICAgICAgY29tbW9ubWFya2VyICh+PiAwLjE0\nKQogICAgICBqZWt5bGwgKD49IDMuNywgPCA1LjApCiAgICBqZWt5bGwtY29t\nbW9ubWFyay1naHBhZ2VzICgwLjEuNikKICAgICAgY29tbW9ubWFya2VyICh+\nPiAwLjE3LjYpCiAgICAgIGpla3lsbC1jb21tb25tYXJrICh+PiAxLjIpCiAg\nICAgIHJvdWdlICg+PSAyLjAsIDwgNC4wKQogICAgamVreWxsLWRlZmF1bHQt\nbGF5b3V0ICgwLjEuNCkKICAgICAgamVreWxsICh+PiAzLjApCiAgICBqZWt5\nbGwtZmVlZCAoMC4xMy4wKQogICAgICBqZWt5bGwgKD49IDMuNywgPCA1LjAp\nCiAgICBqZWt5bGwtZ2lzdCAoMS41LjApCiAgICAgIG9jdG9raXQgKH4+IDQu\nMikKICAgIGpla3lsbC1naXRodWItbWV0YWRhdGEgKDIuMTMuMCkKICAgICAg\namVreWxsICg+PSAzLjQsIDwgNS4wKQogICAgICBvY3Rva2l0ICh+PiA0LjAs\nICE9IDQuNC4wKQogICAgamVreWxsLW1lbnRpb25zICgxLjUuMSkKICAgICAg\naHRtbC1waXBlbGluZSAofj4gMi4zKQogICAgICBqZWt5bGwgKD49IDMuNywg\nPCA1LjApCiAgICBqZWt5bGwtb3B0aW9uYWwtZnJvbnQtbWF0dGVyICgwLjMu\nMikKICAgICAgamVreWxsICg+PSAzLjAsIDwgNS4wKQogICAgamVreWxsLXBh\nZ2luYXRlICgxLjEuMCkKICAgIGpla3lsbC1yZWFkbWUtaW5kZXggKDAuMy4w\nKQogICAgICBqZWt5bGwgKD49IDMuMCwgPCA1LjApCiAgICBqZWt5bGwtcmVk\naXJlY3QtZnJvbSAoMC4xNS4wKQogICAgICBqZWt5bGwgKD49IDMuMywgPCA1\nLjApCiAgICBqZWt5bGwtcmVsYXRpdmUtbGlua3MgKDAuNi4xKQogICAgICBq\nZWt5bGwgKD49IDMuMywgPCA1LjApCiAgICBqZWt5bGwtcmVtb3RlLXRoZW1l\nICgwLjQuMSkKICAgICAgYWRkcmVzc2FibGUgKH4+IDIuMCkKICAgICAgamVr\neWxsICg+PSAzLjUsIDwgNS4wKQogICAgICBydWJ5emlwICg+PSAxLjMuMCkK\nICAgIGpla3lsbC1zYXNzLWNvbnZlcnRlciAoMS41LjIpCiAgICAgIHNhc3Mg\nKH4+IDMuNCkKICAgIGpla3lsbC1zZW8tdGFnICgyLjYuMSkKICAgICAgamVr\neWxsICg+PSAzLjMsIDwgNS4wKQogICAgamVreWxsLXNpdGVtYXAgKDEuNC4w\nKQogICAgICBqZWt5bGwgKD49IDMuNywgPCA1LjApCiAgICBqZWt5bGwtc3dp\nc3MgKDEuMC4wKQogICAgamVreWxsLXRoZW1lLWFyY2hpdGVjdCAoMC4xLjEp\nCiAgICAgIGpla3lsbCAofj4gMy41KQogICAgICBqZWt5bGwtc2VvLXRhZyAo\nfj4gMi4wKQogICAgamVreWxsLXRoZW1lLWNheW1hbiAoMC4xLjEpCiAgICAg\nIGpla3lsbCAofj4gMy41KQogICAgICBqZWt5bGwtc2VvLXRhZyAofj4gMi4w\nKQogICAgamVreWxsLXRoZW1lLWRpbmt5ICgwLjEuMSkKICAgICAgamVreWxs\nICh+PiAzLjUpCiAgICAgIGpla3lsbC1zZW8tdGFnICh+PiAyLjApCiAgICBq\nZWt5bGwtdGhlbWUtaGFja2VyICgwLjEuMSkKICAgICAgamVreWxsICh+PiAz\nLjUpCiAgICAgIGpla3lsbC1zZW8tdGFnICh+PiAyLjApCiAgICBqZWt5bGwt\ndGhlbWUtbGVhcC1kYXkgKDAuMS4xKQogICAgICBqZWt5bGwgKH4+IDMuNSkK\nICAgICAgamVreWxsLXNlby10YWcgKH4+IDIuMCkKICAgIGpla3lsbC10aGVt\nZS1tZXJsb3QgKDAuMS4xKQogICAgICBqZWt5bGwgKH4+IDMuNSkKICAgICAg\namVreWxsLXNlby10YWcgKH4+IDIuMCkKICAgIGpla3lsbC10aGVtZS1taWRu\naWdodCAoMC4xLjEpCiAgICAgIGpla3lsbCAofj4gMy41KQogICAgICBqZWt5\nbGwtc2VvLXRhZyAofj4gMi4wKQogICAgamVreWxsLXRoZW1lLW1pbmltYWwg\nKDAuMS4xKQogICAgICBqZWt5bGwgKH4+IDMuNSkKICAgICAgamVreWxsLXNl\nby10YWcgKH4+IDIuMCkKICAgIGpla3lsbC10aGVtZS1tb2Rlcm5pc3QgKDAu\nMS4xKQogICAgICBqZWt5bGwgKH4+IDMuNSkKICAgICAgamVreWxsLXNlby10\nYWcgKH4+IDIuMCkKICAgIGpla3lsbC10aGVtZS1wcmltZXIgKDAuNS40KQog\nICAgICBqZWt5bGwgKD4gMy41LCA8IDUuMCkKICAgICAgamVreWxsLWdpdGh1\nYi1tZXRhZGF0YSAofj4gMi45KQogICAgICBqZWt5bGwtc2VvLXRhZyAofj4g\nMi4wKQogICAgamVreWxsLXRoZW1lLXNsYXRlICgwLjEuMSkKICAgICAgamVr\neWxsICh+PiAzLjUpCiAgICAgIGpla3lsbC1zZW8tdGFnICh+PiAyLjApCiAg\nICBqZWt5bGwtdGhlbWUtdGFjdGlsZSAoMC4xLjEpCiAgICAgIGpla3lsbCAo\nfj4gMy41KQogICAgICBqZWt5bGwtc2VvLXRhZyAofj4gMi4wKQogICAgamVr\neWxsLXRoZW1lLXRpbWUtbWFjaGluZSAoMC4xLjEpCiAgICAgIGpla3lsbCAo\nfj4gMy41KQogICAgICBqZWt5bGwtc2VvLXRhZyAofj4gMi4wKQogICAgamVr\neWxsLXRpdGxlcy1mcm9tLWhlYWRpbmdzICgwLjUuMykKICAgICAgamVreWxs\nICg+PSAzLjMsIDwgNS4wKQogICAgamVreWxsLXdhdGNoICgyLjIuMSkKICAg\nICAgbGlzdGVuICh+PiAzLjApCiAgICBqZW1vamkgKDAuMTEuMSkKICAgICAg\nZ2Vtb2ppICh+PiAzLjApCiAgICAgIGh0bWwtcGlwZWxpbmUgKH4+IDIuMikK\nICAgICAgamVreWxsICg+PSAzLjAsIDwgNS4wKQogICAga3JhbWRvd24gKDEu\nMTcuMCkKICAgIGxpcXVpZCAoNC4wLjMpCiAgICBsaXN0ZW4gKDMuMi4xKQog\nICAgICByYi1mc2V2ZW50ICh+PiAwLjEwLCA+PSAwLjEwLjMpCiAgICAgIHJi\nLWlub3RpZnkgKH4+IDAuOSwgPj0gMC45LjEwKQogICAgbWVyY2VuYXJ5ICgw\nLjMuNikKICAgIG1pbmlfcG9ydGlsZTIgKDIuNS4xKQogICAgbWluaW1hICgy\nLjUuMSkKICAgICAgamVreWxsICg+PSAzLjUsIDwgNS4wKQogICAgICBqZWt5\nbGwtZmVlZCAofj4gMC45KQogICAgICBqZWt5bGwtc2VvLXRhZyAofj4gMi4x\nKQogICAgbWluaXRlc3QgKDUuMTQuMSkKICAgIG11bHRpcGFydC1wb3N0ICgy\nLjEuMSkKICAgIG5va29naXJpICgxLjExLjQpCiAgICAgIG1pbmlfcG9ydGls\nZTIgKH4+IDIuNS4wKQogICAgICByYWNjICh+PiAxLjQpCiAgICBub2tvZ3Vt\nYm8gKDIuMC4yKQogICAgICBub2tvZ2lyaSAofj4gMS44LCA+PSAxLjguNCkK\nICAgIG9jdG9raXQgKDQuMTguMCkKICAgICAgZmFyYWRheSAoPj0gMC45KQog\nICAgICBzYXd5ZXIgKH4+IDAuOC4wLCA+PSAwLjUuMykKICAgIHBhcmFsbGVs\nICgxLjE5LjEpCiAgICBwYXRodXRpbCAoMC4xNi4yKQogICAgICBmb3J3YXJk\nYWJsZS1leHRlbmRlZCAofj4gMi42KQogICAgcHVibGljX3N1ZmZpeCAoMy4x\nLjEpCiAgICByYWNjICgxLjUuMikKICAgIHJhaW5ib3cgKDMuMC4wKQogICAg\ncmItZnNldmVudCAoMC4xMC40KQogICAgcmItaW5vdGlmeSAoMC4xMC4xKQog\nICAgICBmZmkgKH4+IDEuMCkKICAgIHJvdWdlICgzLjE5LjApCiAgICBydWJ5\nLWVudW0gKDAuOC4wKQogICAgICBpMThuCiAgICBydWJ5emlwICgyLjMuMCkK\nICAgIHNhZmVfeWFtbCAoMS4wLjUpCiAgICBzYXNzICgzLjcuNCkKICAgICAg\nc2Fzcy1saXN0ZW4gKH4+IDQuMC4wKQogICAgc2Fzcy1saXN0ZW4gKDQuMC4w\nKQogICAgICByYi1mc2V2ZW50ICh+PiAwLjksID49IDAuOS40KQogICAgICBy\nYi1pbm90aWZ5ICh+PiAwLjksID49IDAuOS43KQogICAgc2F3eWVyICgwLjgu\nMikKICAgICAgYWRkcmVzc2FibGUgKD49IDIuMy41KQogICAgICBmYXJhZGF5\nICg+IDAuOCwgPCAyLjApCiAgICB0ZXJtaW5hbC10YWJsZSAoMS44LjApCiAg\nICAgIHVuaWNvZGUtZGlzcGxheV93aWR0aCAofj4gMS4xLCA+PSAxLjEuMSkK\nICAgIHRocmVhZF9zYWZlICgwLjMuNikKICAgIHR5cGhvZXVzICgxLjQuMCkK\nICAgICAgZXRob24gKD49IDAuOS4wKQogICAgdHppbmZvICgxLjIuNykKICAg\nICAgdGhyZWFkX3NhZmUgKH4+IDAuMSkKICAgIHVuaWNvZGUtZGlzcGxheV93\naWR0aCAoMS43LjApCiAgICB5ZWxsICgyLjIuMikKICAgIHplaXR3ZXJrICgy\nLjMuMCkKClBMQVRGT1JNUwogIHJ1YnkKCkRFUEVOREVOQ0lFUwogIGVpcF92\nYWxpZGF0b3IgKD49IDAuOC4yKQogIGdpdGh1Yi1wYWdlcyAoPSAyMDYpCiAg\naHRtbC1wcm9vZmVyICg+PSAzLjMuMSkKICBqZWt5bGwtZmVlZCAofj4gMC4x\nMSkKICBtaW5pbWEgKH4+IDIuMCkKICB0emluZm8tZGF0YQoKQlVORExFRCBX\nSVRICiAgIDEuMTcuMgo=\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/ethereum/EIPs/contents/Gemfile.lock?ref=837336cf8724bade84b82959aee96c686b48ea03", + "git": "https://api.github.com/repos/ethereum/EIPs/git/blobs/10bfc6faa0040e1f269fa68f5d4b178d24242960", + "html": "https://github.com/ethereum/EIPs/blob/837336cf8724bade84b82959aee96c686b48ea03/Gemfile.lock" + } + } + } + }, + { + "req": { + "url": "https://api.github.com/repos/ethereum/EIPs/contents/Gemfile.lock?ref=739cbf2e8cfbea7b712f2d7a6db69ca258182ae1", + "method": "GET" + }, + "res": { + "status": 200, + "data": { + "name": "Gemfile.lock", + "path": "Gemfile.lock", + "sha": "cdce7e4246dafebfb6c5d0a809c398f205efc529", + "size": 7447, + "url": "https://api.github.com/repos/ethereum/EIPs/contents/Gemfile.lock?ref=739cbf2e8cfbea7b712f2d7a6db69ca258182ae1", + "html_url": "https://github.com/ethereum/EIPs/blob/739cbf2e8cfbea7b712f2d7a6db69ca258182ae1/Gemfile.lock", + "git_url": "https://api.github.com/repos/ethereum/EIPs/git/blobs/cdce7e4246dafebfb6c5d0a809c398f205efc529", + "download_url": "https://raw.githubusercontent.com/ethereum/EIPs/739cbf2e8cfbea7b712f2d7a6db69ca258182ae1/Gemfile.lock", + "type": "file", + "content": "R0VNCiAgcmVtb3RlOiBodHRwczovL3J1YnlnZW1zLm9yZy8KICBzcGVjczoK\nICAgIGFjdGl2ZW1vZGVsICg2LjAuMy4xKQogICAgICBhY3RpdmVzdXBwb3J0\nICg9IDYuMC4zLjEpCiAgICBhY3RpdmVzdXBwb3J0ICg2LjAuMy4xKQogICAg\nICBjb25jdXJyZW50LXJ1YnkgKH4+IDEuMCwgPj0gMS4wLjIpCiAgICAgIGkx\nOG4gKD49IDAuNywgPCAyKQogICAgICBtaW5pdGVzdCAofj4gNS4xKQogICAg\nICB0emluZm8gKH4+IDEuMSkKICAgICAgemVpdHdlcmsgKH4+IDIuMiwgPj0g\nMi4yLjIpCiAgICBhZGRyZXNzYWJsZSAoMi43LjApCiAgICAgIHB1YmxpY19z\ndWZmaXggKD49IDIuMC4yLCA8IDUuMCkKICAgIGNvZmZlZS1zY3JpcHQgKDIu\nNC4xKQogICAgICBjb2ZmZWUtc2NyaXB0LXNvdXJjZQogICAgICBleGVjanMK\nICAgIGNvZmZlZS1zY3JpcHQtc291cmNlICgxLjExLjEpCiAgICBjb2xvcmF0\nb3IgKDEuMS4wKQogICAgY29tbW9ubWFya2VyICgwLjE3LjEzKQogICAgICBy\ndWJ5LWVudW0gKH4+IDAuNSkKICAgIGNvbmN1cnJlbnQtcnVieSAoMS4xLjYp\nCiAgICBkbnNydWJ5ICgxLjYxLjMpCiAgICAgIGFkZHJlc3NhYmxlICh+PiAy\nLjUpCiAgICBlaXBfdmFsaWRhdG9yICgwLjguMikKICAgICAgYWN0aXZlbW9k\nZWwKICAgICAgZnJvbnRfbWF0dGVyX3BhcnNlciAofj4gMC4xLjEpCiAgICBl\nbS13ZWJzb2NrZXQgKDAuNS4xKQogICAgICBldmVudG1hY2hpbmUgKD49IDAu\nMTIuOSkKICAgICAgaHR0cF9wYXJzZXIucmIgKH4+IDAuNi4wKQogICAgZXRo\nb24gKDAuMTIuMCkKICAgICAgZmZpICg+PSAxLjMuMCkKICAgIGV2ZW50bWFj\naGluZSAoMS4yLjcpCiAgICBleGVjanMgKDIuNy4wKQogICAgZmFyYWRheSAo\nMS4wLjEpCiAgICAgIG11bHRpcGFydC1wb3N0ICg+PSAxLjIsIDwgMykKICAg\nIGZmaSAoMS4xMi4yKQogICAgZm9yd2FyZGFibGUtZXh0ZW5kZWQgKDIuNi4w\nKQogICAgZnJvbnRfbWF0dGVyX3BhcnNlciAoMC4xLjEpCiAgICBnZW1vamkg\nKDMuMC4xKQogICAgZ2l0aHViLXBhZ2VzICgyMDYpCiAgICAgIGdpdGh1Yi1w\nYWdlcy1oZWFsdGgtY2hlY2sgKD0gMS4xNi4xKQogICAgICBqZWt5bGwgKD0g\nMy44LjcpCiAgICAgIGpla3lsbC1hdmF0YXIgKD0gMC43LjApCiAgICAgIGpl\na3lsbC1jb2ZmZWVzY3JpcHQgKD0gMS4xLjEpCiAgICAgIGpla3lsbC1jb21t\nb25tYXJrLWdocGFnZXMgKD0gMC4xLjYpCiAgICAgIGpla3lsbC1kZWZhdWx0\nLWxheW91dCAoPSAwLjEuNCkKICAgICAgamVreWxsLWZlZWQgKD0gMC4xMy4w\nKQogICAgICBqZWt5bGwtZ2lzdCAoPSAxLjUuMCkKICAgICAgamVreWxsLWdp\ndGh1Yi1tZXRhZGF0YSAoPSAyLjEzLjApCiAgICAgIGpla3lsbC1tZW50aW9u\ncyAoPSAxLjUuMSkKICAgICAgamVreWxsLW9wdGlvbmFsLWZyb250LW1hdHRl\nciAoPSAwLjMuMikKICAgICAgamVreWxsLXBhZ2luYXRlICg9IDEuMS4wKQog\nICAgICBqZWt5bGwtcmVhZG1lLWluZGV4ICg9IDAuMy4wKQogICAgICBqZWt5\nbGwtcmVkaXJlY3QtZnJvbSAoPSAwLjE1LjApCiAgICAgIGpla3lsbC1yZWxh\ndGl2ZS1saW5rcyAoPSAwLjYuMSkKICAgICAgamVreWxsLXJlbW90ZS10aGVt\nZSAoPSAwLjQuMSkKICAgICAgamVreWxsLXNhc3MtY29udmVydGVyICg9IDEu\nNS4yKQogICAgICBqZWt5bGwtc2VvLXRhZyAoPSAyLjYuMSkKICAgICAgamVr\neWxsLXNpdGVtYXAgKD0gMS40LjApCiAgICAgIGpla3lsbC1zd2lzcyAoPSAx\nLjAuMCkKICAgICAgamVreWxsLXRoZW1lLWFyY2hpdGVjdCAoPSAwLjEuMSkK\nICAgICAgamVreWxsLXRoZW1lLWNheW1hbiAoPSAwLjEuMSkKICAgICAgamVr\neWxsLXRoZW1lLWRpbmt5ICg9IDAuMS4xKQogICAgICBqZWt5bGwtdGhlbWUt\naGFja2VyICg9IDAuMS4xKQogICAgICBqZWt5bGwtdGhlbWUtbGVhcC1kYXkg\nKD0gMC4xLjEpCiAgICAgIGpla3lsbC10aGVtZS1tZXJsb3QgKD0gMC4xLjEp\nCiAgICAgIGpla3lsbC10aGVtZS1taWRuaWdodCAoPSAwLjEuMSkKICAgICAg\namVreWxsLXRoZW1lLW1pbmltYWwgKD0gMC4xLjEpCiAgICAgIGpla3lsbC10\naGVtZS1tb2Rlcm5pc3QgKD0gMC4xLjEpCiAgICAgIGpla3lsbC10aGVtZS1w\ncmltZXIgKD0gMC41LjQpCiAgICAgIGpla3lsbC10aGVtZS1zbGF0ZSAoPSAw\nLjEuMSkKICAgICAgamVreWxsLXRoZW1lLXRhY3RpbGUgKD0gMC4xLjEpCiAg\nICAgIGpla3lsbC10aGVtZS10aW1lLW1hY2hpbmUgKD0gMC4xLjEpCiAgICAg\nIGpla3lsbC10aXRsZXMtZnJvbS1oZWFkaW5ncyAoPSAwLjUuMykKICAgICAg\namVtb2ppICg9IDAuMTEuMSkKICAgICAga3JhbWRvd24gKD0gMS4xNy4wKQog\nICAgICBsaXF1aWQgKD0gNC4wLjMpCiAgICAgIG1lcmNlbmFyeSAofj4gMC4z\nKQogICAgICBtaW5pbWEgKD0gMi41LjEpCiAgICAgIG5va29naXJpICg+PSAx\nLjEwLjQsIDwgMi4wKQogICAgICByb3VnZSAoPSAzLjE5LjApCiAgICAgIHRl\ncm1pbmFsLXRhYmxlICh+PiAxLjQpCiAgICBnaXRodWItcGFnZXMtaGVhbHRo\nLWNoZWNrICgxLjE2LjEpCiAgICAgIGFkZHJlc3NhYmxlICh+PiAyLjMpCiAg\nICAgIGRuc3J1YnkgKH4+IDEuNjApCiAgICAgIG9jdG9raXQgKH4+IDQuMCkK\nICAgICAgcHVibGljX3N1ZmZpeCAofj4gMy4wKQogICAgICB0eXBob2V1cyAo\nfj4gMS4zKQogICAgaHRtbC1waXBlbGluZSAoMi4xMy4wKQogICAgICBhY3Rp\ndmVzdXBwb3J0ICg+PSAyKQogICAgICBub2tvZ2lyaSAoPj0gMS40KQogICAg\naHRtbC1wcm9vZmVyICgzLjE1LjMpCiAgICAgIGFkZHJlc3NhYmxlICh+PiAy\nLjMpCiAgICAgIG1lcmNlbmFyeSAofj4gMC4zKQogICAgICBub2tvZ3VtYm8g\nKH4+IDIuMCkKICAgICAgcGFyYWxsZWwgKH4+IDEuMykKICAgICAgcmFpbmJv\ndyAofj4gMy4wKQogICAgICB0eXBob2V1cyAofj4gMS4zKQogICAgICB5ZWxs\nICh+PiAyLjApCiAgICBodHRwX3BhcnNlci5yYiAoMC42LjApCiAgICBpMThu\nICgwLjkuNSkKICAgICAgY29uY3VycmVudC1ydWJ5ICh+PiAxLjApCiAgICBq\nZWt5bGwgKDMuOC43KQogICAgICBhZGRyZXNzYWJsZSAofj4gMi40KQogICAg\nICBjb2xvcmF0b3IgKH4+IDEuMCkKICAgICAgZW0td2Vic29ja2V0ICh+PiAw\nLjUpCiAgICAgIGkxOG4gKH4+IDAuNykKICAgICAgamVreWxsLXNhc3MtY29u\ndmVydGVyICh+PiAxLjApCiAgICAgIGpla3lsbC13YXRjaCAofj4gMi4wKQog\nICAgICBrcmFtZG93biAofj4gMS4xNCkKICAgICAgbGlxdWlkICh+PiA0LjAp\nCiAgICAgIG1lcmNlbmFyeSAofj4gMC4zLjMpCiAgICAgIHBhdGh1dGlsICh+\nPiAwLjkpCiAgICAgIHJvdWdlICg+PSAxLjcsIDwgNCkKICAgICAgc2FmZV95\nYW1sICh+PiAxLjApCiAgICBqZWt5bGwtYXZhdGFyICgwLjcuMCkKICAgICAg\namVreWxsICg+PSAzLjAsIDwgNS4wKQogICAgamVreWxsLWNvZmZlZXNjcmlw\ndCAoMS4xLjEpCiAgICAgIGNvZmZlZS1zY3JpcHQgKH4+IDIuMikKICAgICAg\nY29mZmVlLXNjcmlwdC1zb3VyY2UgKH4+IDEuMTEuMSkKICAgIGpla3lsbC1j\nb21tb25tYXJrICgxLjMuMSkKICAgICAgY29tbW9ubWFya2VyICh+PiAwLjE0\nKQogICAgICBqZWt5bGwgKD49IDMuNywgPCA1LjApCiAgICBqZWt5bGwtY29t\nbW9ubWFyay1naHBhZ2VzICgwLjEuNikKICAgICAgY29tbW9ubWFya2VyICh+\nPiAwLjE3LjYpCiAgICAgIGpla3lsbC1jb21tb25tYXJrICh+PiAxLjIpCiAg\nICAgIHJvdWdlICg+PSAyLjAsIDwgNC4wKQogICAgamVreWxsLWRlZmF1bHQt\nbGF5b3V0ICgwLjEuNCkKICAgICAgamVreWxsICh+PiAzLjApCiAgICBqZWt5\nbGwtZmVlZCAoMC4xMy4wKQogICAgICBqZWt5bGwgKD49IDMuNywgPCA1LjAp\nCiAgICBqZWt5bGwtZ2lzdCAoMS41LjApCiAgICAgIG9jdG9raXQgKH4+IDQu\nMikKICAgIGpla3lsbC1naXRodWItbWV0YWRhdGEgKDIuMTMuMCkKICAgICAg\namVreWxsICg+PSAzLjQsIDwgNS4wKQogICAgICBvY3Rva2l0ICh+PiA0LjAs\nICE9IDQuNC4wKQogICAgamVreWxsLW1lbnRpb25zICgxLjUuMSkKICAgICAg\naHRtbC1waXBlbGluZSAofj4gMi4zKQogICAgICBqZWt5bGwgKD49IDMuNywg\nPCA1LjApCiAgICBqZWt5bGwtb3B0aW9uYWwtZnJvbnQtbWF0dGVyICgwLjMu\nMikKICAgICAgamVreWxsICg+PSAzLjAsIDwgNS4wKQogICAgamVreWxsLXBh\nZ2luYXRlICgxLjEuMCkKICAgIGpla3lsbC1yZWFkbWUtaW5kZXggKDAuMy4w\nKQogICAgICBqZWt5bGwgKD49IDMuMCwgPCA1LjApCiAgICBqZWt5bGwtcmVk\naXJlY3QtZnJvbSAoMC4xNS4wKQogICAgICBqZWt5bGwgKD49IDMuMywgPCA1\nLjApCiAgICBqZWt5bGwtcmVsYXRpdmUtbGlua3MgKDAuNi4xKQogICAgICBq\nZWt5bGwgKD49IDMuMywgPCA1LjApCiAgICBqZWt5bGwtcmVtb3RlLXRoZW1l\nICgwLjQuMSkKICAgICAgYWRkcmVzc2FibGUgKH4+IDIuMCkKICAgICAgamVr\neWxsICg+PSAzLjUsIDwgNS4wKQogICAgICBydWJ5emlwICg+PSAxLjMuMCkK\nICAgIGpla3lsbC1zYXNzLWNvbnZlcnRlciAoMS41LjIpCiAgICAgIHNhc3Mg\nKH4+IDMuNCkKICAgIGpla3lsbC1zZW8tdGFnICgyLjYuMSkKICAgICAgamVr\neWxsICg+PSAzLjMsIDwgNS4wKQogICAgamVreWxsLXNpdGVtYXAgKDEuNC4w\nKQogICAgICBqZWt5bGwgKD49IDMuNywgPCA1LjApCiAgICBqZWt5bGwtc3dp\nc3MgKDEuMC4wKQogICAgamVreWxsLXRoZW1lLWFyY2hpdGVjdCAoMC4xLjEp\nCiAgICAgIGpla3lsbCAofj4gMy41KQogICAgICBqZWt5bGwtc2VvLXRhZyAo\nfj4gMi4wKQogICAgamVreWxsLXRoZW1lLWNheW1hbiAoMC4xLjEpCiAgICAg\nIGpla3lsbCAofj4gMy41KQogICAgICBqZWt5bGwtc2VvLXRhZyAofj4gMi4w\nKQogICAgamVreWxsLXRoZW1lLWRpbmt5ICgwLjEuMSkKICAgICAgamVreWxs\nICh+PiAzLjUpCiAgICAgIGpla3lsbC1zZW8tdGFnICh+PiAyLjApCiAgICBq\nZWt5bGwtdGhlbWUtaGFja2VyICgwLjEuMSkKICAgICAgamVreWxsICh+PiAz\nLjUpCiAgICAgIGpla3lsbC1zZW8tdGFnICh+PiAyLjApCiAgICBqZWt5bGwt\ndGhlbWUtbGVhcC1kYXkgKDAuMS4xKQogICAgICBqZWt5bGwgKH4+IDMuNSkK\nICAgICAgamVreWxsLXNlby10YWcgKH4+IDIuMCkKICAgIGpla3lsbC10aGVt\nZS1tZXJsb3QgKDAuMS4xKQogICAgICBqZWt5bGwgKH4+IDMuNSkKICAgICAg\namVreWxsLXNlby10YWcgKH4+IDIuMCkKICAgIGpla3lsbC10aGVtZS1taWRu\naWdodCAoMC4xLjEpCiAgICAgIGpla3lsbCAofj4gMy41KQogICAgICBqZWt5\nbGwtc2VvLXRhZyAofj4gMi4wKQogICAgamVreWxsLXRoZW1lLW1pbmltYWwg\nKDAuMS4xKQogICAgICBqZWt5bGwgKH4+IDMuNSkKICAgICAgamVreWxsLXNl\nby10YWcgKH4+IDIuMCkKICAgIGpla3lsbC10aGVtZS1tb2Rlcm5pc3QgKDAu\nMS4xKQogICAgICBqZWt5bGwgKH4+IDMuNSkKICAgICAgamVreWxsLXNlby10\nYWcgKH4+IDIuMCkKICAgIGpla3lsbC10aGVtZS1wcmltZXIgKDAuNS40KQog\nICAgICBqZWt5bGwgKD4gMy41LCA8IDUuMCkKICAgICAgamVreWxsLWdpdGh1\nYi1tZXRhZGF0YSAofj4gMi45KQogICAgICBqZWt5bGwtc2VvLXRhZyAofj4g\nMi4wKQogICAgamVreWxsLXRoZW1lLXNsYXRlICgwLjEuMSkKICAgICAgamVr\neWxsICh+PiAzLjUpCiAgICAgIGpla3lsbC1zZW8tdGFnICh+PiAyLjApCiAg\nICBqZWt5bGwtdGhlbWUtdGFjdGlsZSAoMC4xLjEpCiAgICAgIGpla3lsbCAo\nfj4gMy41KQogICAgICBqZWt5bGwtc2VvLXRhZyAofj4gMi4wKQogICAgamVr\neWxsLXRoZW1lLXRpbWUtbWFjaGluZSAoMC4xLjEpCiAgICAgIGpla3lsbCAo\nfj4gMy41KQogICAgICBqZWt5bGwtc2VvLXRhZyAofj4gMi4wKQogICAgamVr\neWxsLXRpdGxlcy1mcm9tLWhlYWRpbmdzICgwLjUuMykKICAgICAgamVreWxs\nICg+PSAzLjMsIDwgNS4wKQogICAgamVreWxsLXdhdGNoICgyLjIuMSkKICAg\nICAgbGlzdGVuICh+PiAzLjApCiAgICBqZW1vamkgKDAuMTEuMSkKICAgICAg\nZ2Vtb2ppICh+PiAzLjApCiAgICAgIGh0bWwtcGlwZWxpbmUgKH4+IDIuMikK\nICAgICAgamVreWxsICg+PSAzLjAsIDwgNS4wKQogICAga3JhbWRvd24gKDEu\nMTcuMCkKICAgIGxpcXVpZCAoNC4wLjMpCiAgICBsaXN0ZW4gKDMuMi4xKQog\nICAgICByYi1mc2V2ZW50ICh+PiAwLjEwLCA+PSAwLjEwLjMpCiAgICAgIHJi\nLWlub3RpZnkgKH4+IDAuOSwgPj0gMC45LjEwKQogICAgbWVyY2VuYXJ5ICgw\nLjMuNikKICAgIG1pbmlfcG9ydGlsZTIgKDIuNC4wKQogICAgbWluaW1hICgy\nLjUuMSkKICAgICAgamVreWxsICg+PSAzLjUsIDwgNS4wKQogICAgICBqZWt5\nbGwtZmVlZCAofj4gMC45KQogICAgICBqZWt5bGwtc2VvLXRhZyAofj4gMi4x\nKQogICAgbWluaXRlc3QgKDUuMTQuMSkKICAgIG11bHRpcGFydC1wb3N0ICgy\nLjEuMSkKICAgIG5va29naXJpICgxLjEwLjkpCiAgICAgIG1pbmlfcG9ydGls\nZTIgKH4+IDIuNC4wKQogICAgbm9rb2d1bWJvICgyLjAuMikKICAgICAgbm9r\nb2dpcmkgKH4+IDEuOCwgPj0gMS44LjQpCiAgICBvY3Rva2l0ICg0LjE4LjAp\nCiAgICAgIGZhcmFkYXkgKD49IDAuOSkKICAgICAgc2F3eWVyICh+PiAwLjgu\nMCwgPj0gMC41LjMpCiAgICBwYXJhbGxlbCAoMS4xOS4xKQogICAgcGF0aHV0\naWwgKDAuMTYuMikKICAgICAgZm9yd2FyZGFibGUtZXh0ZW5kZWQgKH4+IDIu\nNikKICAgIHB1YmxpY19zdWZmaXggKDMuMS4xKQogICAgcmFpbmJvdyAoMy4w\nLjApCiAgICByYi1mc2V2ZW50ICgwLjEwLjQpCiAgICByYi1pbm90aWZ5ICgw\nLjEwLjEpCiAgICAgIGZmaSAofj4gMS4wKQogICAgcm91Z2UgKDMuMTkuMCkK\nICAgIHJ1YnktZW51bSAoMC44LjApCiAgICAgIGkxOG4KICAgIHJ1Ynl6aXAg\nKDIuMy4wKQogICAgc2FmZV95YW1sICgxLjAuNSkKICAgIHNhc3MgKDMuNy40\nKQogICAgICBzYXNzLWxpc3RlbiAofj4gNC4wLjApCiAgICBzYXNzLWxpc3Rl\nbiAoNC4wLjApCiAgICAgIHJiLWZzZXZlbnQgKH4+IDAuOSwgPj0gMC45LjQp\nCiAgICAgIHJiLWlub3RpZnkgKH4+IDAuOSwgPj0gMC45LjcpCiAgICBzYXd5\nZXIgKDAuOC4yKQogICAgICBhZGRyZXNzYWJsZSAoPj0gMi4zLjUpCiAgICAg\nIGZhcmFkYXkgKD4gMC44LCA8IDIuMCkKICAgIHRlcm1pbmFsLXRhYmxlICgx\nLjguMCkKICAgICAgdW5pY29kZS1kaXNwbGF5X3dpZHRoICh+PiAxLjEsID49\nIDEuMS4xKQogICAgdGhyZWFkX3NhZmUgKDAuMy42KQogICAgdHlwaG9ldXMg\nKDEuNC4wKQogICAgICBldGhvbiAoPj0gMC45LjApCiAgICB0emluZm8gKDEu\nMi43KQogICAgICB0aHJlYWRfc2FmZSAofj4gMC4xKQogICAgdW5pY29kZS1k\naXNwbGF5X3dpZHRoICgxLjcuMCkKICAgIHllbGwgKDIuMi4yKQogICAgemVp\ndHdlcmsgKDIuMy4wKQoKUExBVEZPUk1TCiAgcnVieQoKREVQRU5ERU5DSUVT\nCiAgZWlwX3ZhbGlkYXRvciAoPj0gMC44LjIpCiAgZ2l0aHViLXBhZ2VzICg9\nIDIwNikKICBodG1sLXByb29mZXIgKD49IDMuMy4xKQogIGpla3lsbC1mZWVk\nICh+PiAwLjExKQogIG1pbmltYSAofj4gMi4wKQogIHR6aW5mby1kYXRhCgpC\nVU5ETEVEIFdJVEgKICAgMS4xNy4yCg==\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/ethereum/EIPs/contents/Gemfile.lock?ref=739cbf2e8cfbea7b712f2d7a6db69ca258182ae1", + "git": "https://api.github.com/repos/ethereum/EIPs/git/blobs/cdce7e4246dafebfb6c5d0a809c398f205efc529", + "html": "https://github.com/ethereum/EIPs/blob/739cbf2e8cfbea7b712f2d7a6db69ca258182ae1/Gemfile.lock" + } + } + } + }, + { + "req": { + "url": "https://api.github.com/repos/ethereum/EIPs/pulls/3581/reviews", + "method": "GET" + }, + "res": { + "status": 200, + "data": [ + { + "id": 663236100, + "node_id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3NjYzMjM2MTAw", + "user": { + "login": "lightclient", + "id": 14004106, + "node_id": "MDQ6VXNlcjE0MDA0MTA2", + "avatar_url": "https://avatars.githubusercontent.com/u/14004106?u=17bd7e09ec0361a4b4431c6ff715a79d7de94f44&v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/lightclient", + "html_url": "https://github.com/lightclient", + "followers_url": "https://api.github.com/users/lightclient/followers", + "following_url": "https://api.github.com/users/lightclient/following{/other_user}", + "gists_url": "https://api.github.com/users/lightclient/gists{/gist_id}", + "starred_url": "https://api.github.com/users/lightclient/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/lightclient/subscriptions", + "organizations_url": "https://api.github.com/users/lightclient/orgs", + "repos_url": "https://api.github.com/users/lightclient/repos", + "events_url": "https://api.github.com/users/lightclient/events{/privacy}", + "received_events_url": "https://api.github.com/users/lightclient/received_events", + "type": "User", + "site_admin": false + }, + "body": "Looks good to me, I think we merge and make sure the EIPs site builds -- rewinding if not?", + "state": "APPROVED", + "html_url": "https://github.com/ethereum/EIPs/pull/3581#pullrequestreview-663236100", + "pull_request_url": "https://api.github.com/repos/ethereum/EIPs/pulls/3581", + "author_association": "MEMBER", + "_links": { + "html": { + "href": "https://github.com/ethereum/EIPs/pull/3581#pullrequestreview-663236100" + }, + "pull_request": { + "href": "https://api.github.com/repos/ethereum/EIPs/pulls/3581" + } + }, + "submitted_at": "2021-05-19T13:54:57Z", + "commit_id": "233c1e3c8df916c881558301f29d377c81ddb401" + } + ] + } + }, + { + "req": { + "url": "https://api.github.com/user", + "method": "GET" + }, + "res": { + "status": 200, + "data": { + "login": "eth-bot", + "id": 85952233, + "node_id": "MDQ6VXNlcjg1OTUyMjMz", + "avatar_url": "https://avatars.githubusercontent.com/u/85952233?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/eth-bot", + "html_url": "https://github.com/eth-bot", + "followers_url": "https://api.github.com/users/eth-bot/followers", + "following_url": "https://api.github.com/users/eth-bot/following{/other_user}", + "gists_url": "https://api.github.com/users/eth-bot/gists{/gist_id}", + "starred_url": "https://api.github.com/users/eth-bot/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/eth-bot/subscriptions", + "organizations_url": "https://api.github.com/users/eth-bot/orgs", + "repos_url": "https://api.github.com/users/eth-bot/repos", + "events_url": "https://api.github.com/users/eth-bot/events{/privacy}", + "received_events_url": "https://api.github.com/users/eth-bot/received_events", + "type": "User", + "site_admin": false, + "name": null, + "company": null, + "blog": "", + "location": null, + "email": null, + "hireable": null, + "bio": null, + "twitter_username": null, + "public_repos": 0, + "public_gists": 0, + "followers": 14, + "following": 0, + "created_at": "2021-06-15T15:27:49Z", + "updated_at": "2021-09-06T08:13:22Z" + } + } + }, + { + "req": { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/3581/comments", + "method": "GET" + }, + "res": { + "status": 200, + "data": [ + { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/843852256", + "html_url": "https://github.com/ethereum/EIPs/pull/3581#issuecomment-843852256", + "issue_url": "https://api.github.com/repos/ethereum/EIPs/issues/3581", + "id": 843852256, + "node_id": "MDEyOklzc3VlQ29tbWVudDg0Mzg1MjI1Ng==", + "user": { + "login": "alita-moore", + "id": 26529820, + "node_id": "MDQ6VXNlcjI2NTI5ODIw", + "avatar_url": "https://avatars.githubusercontent.com/u/26529820?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/alita-moore", + "html_url": "https://github.com/alita-moore", + "followers_url": "https://api.github.com/users/alita-moore/followers", + "following_url": "https://api.github.com/users/alita-moore/following{/other_user}", + "gists_url": "https://api.github.com/users/alita-moore/gists{/gist_id}", + "starred_url": "https://api.github.com/users/alita-moore/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/alita-moore/subscriptions", + "organizations_url": "https://api.github.com/users/alita-moore/orgs", + "repos_url": "https://api.github.com/users/alita-moore/repos", + "events_url": "https://api.github.com/users/alita-moore/events{/privacy}", + "received_events_url": "https://api.github.com/users/alita-moore/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2021-05-19T08:04:49Z", + "updated_at": "2021-05-19T08:04:49Z", + "author_association": "CONTRIBUTOR", + "body": "Hi! I'm a bot, and I wanted to automerge your PR, but couldn't because of the following issue(s):\n\n\n\t - Filename Gemfile.lock is not in EIP format 'EIPS/eip-####.md'", + "reactions": { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/843852256/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/852991686", + "html_url": "https://github.com/ethereum/EIPs/pull/3581#issuecomment-852991686", + "issue_url": "https://api.github.com/repos/ethereum/EIPs/issues/3581", + "id": 852991686, + "node_id": "MDEyOklzc3VlQ29tbWVudDg1Mjk5MTY4Ng==", + "user": { + "login": "lightclient", + "id": 14004106, + "node_id": "MDQ6VXNlcjE0MDA0MTA2", + "avatar_url": "https://avatars.githubusercontent.com/u/14004106?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/lightclient", + "html_url": "https://github.com/lightclient", + "followers_url": "https://api.github.com/users/lightclient/followers", + "following_url": "https://api.github.com/users/lightclient/following{/other_user}", + "gists_url": "https://api.github.com/users/lightclient/gists{/gist_id}", + "starred_url": "https://api.github.com/users/lightclient/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/lightclient/subscriptions", + "organizations_url": "https://api.github.com/users/lightclient/orgs", + "repos_url": "https://api.github.com/users/lightclient/repos", + "events_url": "https://api.github.com/users/lightclient/events{/privacy}", + "received_events_url": "https://api.github.com/users/lightclient/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2021-06-02T12:38:17Z", + "updated_at": "2021-06-02T12:38:17Z", + "author_association": "MEMBER", + "body": "@aliatiia seems like the eip-bot is incorrectly failing on this.", + "reactions": { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/852991686/reactions", + "total_count": 1, + "+1": 1, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/855219424", + "html_url": "https://github.com/ethereum/EIPs/pull/3581#issuecomment-855219424", + "issue_url": "https://api.github.com/repos/ethereum/EIPs/issues/3581", + "id": 855219424, + "node_id": "MDEyOklzc3VlQ29tbWVudDg1NTIxOTQyNA==", + "user": { + "login": "alita-moore", + "id": 26529820, + "node_id": "MDQ6VXNlcjI2NTI5ODIw", + "avatar_url": "https://avatars.githubusercontent.com/u/26529820?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/alita-moore", + "html_url": "https://github.com/alita-moore", + "followers_url": "https://api.github.com/users/alita-moore/followers", + "following_url": "https://api.github.com/users/alita-moore/following{/other_user}", + "gists_url": "https://api.github.com/users/alita-moore/gists{/gist_id}", + "starred_url": "https://api.github.com/users/alita-moore/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/alita-moore/subscriptions", + "organizations_url": "https://api.github.com/users/alita-moore/orgs", + "repos_url": "https://api.github.com/users/alita-moore/repos", + "events_url": "https://api.github.com/users/alita-moore/events{/privacy}", + "received_events_url": "https://api.github.com/users/alita-moore/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2021-06-05T10:33:26Z", + "updated_at": "2021-06-05T10:33:26Z", + "author_association": "CONTRIBUTOR", + "body": "no, it will only pass on eip files so in this case it needs to be merged manually I suppose. I can build in certain exception though, if that'd be useful", + "reactions": { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/855219424/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/855224786", + "html_url": "https://github.com/ethereum/EIPs/pull/3581#issuecomment-855224786", + "issue_url": "https://api.github.com/repos/ethereum/EIPs/issues/3581", + "id": 855224786, + "node_id": "MDEyOklzc3VlQ29tbWVudDg1NTIyNDc4Ng==", + "user": { + "login": "MicahZoltu", + "id": 886059, + "node_id": "MDQ6VXNlcjg4NjA1OQ==", + "avatar_url": "https://avatars.githubusercontent.com/u/886059?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/MicahZoltu", + "html_url": "https://github.com/MicahZoltu", + "followers_url": "https://api.github.com/users/MicahZoltu/followers", + "following_url": "https://api.github.com/users/MicahZoltu/following{/other_user}", + "gists_url": "https://api.github.com/users/MicahZoltu/gists{/gist_id}", + "starred_url": "https://api.github.com/users/MicahZoltu/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/MicahZoltu/subscriptions", + "organizations_url": "https://api.github.com/users/MicahZoltu/orgs", + "repos_url": "https://api.github.com/users/MicahZoltu/repos", + "events_url": "https://api.github.com/users/MicahZoltu/events{/privacy}", + "received_events_url": "https://api.github.com/users/MicahZoltu/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2021-06-05T11:16:01Z", + "updated_at": "2021-06-05T11:16:01Z", + "author_association": "COLLABORATOR", + "body": "I don't think we should automatically merge things like this. I *do* want a way to make it so PRs like this can be merged without needing to use admin override, without being auto-merged.", + "reactions": { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/855224786/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/855224834", + "html_url": "https://github.com/ethereum/EIPs/pull/3581#issuecomment-855224834", + "issue_url": "https://api.github.com/repos/ethereum/EIPs/issues/3581", + "id": 855224834, + "node_id": "MDEyOklzc3VlQ29tbWVudDg1NTIyNDgzNA==", + "user": { + "login": "MicahZoltu", + "id": 886059, + "node_id": "MDQ6VXNlcjg4NjA1OQ==", + "avatar_url": "https://avatars.githubusercontent.com/u/886059?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/MicahZoltu", + "html_url": "https://github.com/MicahZoltu", + "followers_url": "https://api.github.com/users/MicahZoltu/followers", + "following_url": "https://api.github.com/users/MicahZoltu/following{/other_user}", + "gists_url": "https://api.github.com/users/MicahZoltu/gists{/gist_id}", + "starred_url": "https://api.github.com/users/MicahZoltu/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/MicahZoltu/subscriptions", + "organizations_url": "https://api.github.com/users/MicahZoltu/orgs", + "repos_url": "https://api.github.com/users/MicahZoltu/repos", + "events_url": "https://api.github.com/users/MicahZoltu/events{/privacy}", + "received_events_url": "https://api.github.com/users/MicahZoltu/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2021-06-05T11:16:28Z", + "updated_at": "2021-06-05T11:16:28Z", + "author_association": "COLLABORATOR", + "body": "Maybe we *do* auto-merge after an editor does a PR approval review?", + "reactions": { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/855224834/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/855234473", + "html_url": "https://github.com/ethereum/EIPs/pull/3581#issuecomment-855234473", + "issue_url": "https://api.github.com/repos/ethereum/EIPs/issues/3581", + "id": 855234473, + "node_id": "MDEyOklzc3VlQ29tbWVudDg1NTIzNDQ3Mw==", + "user": { + "login": "alita-moore", + "id": 26529820, + "node_id": "MDQ6VXNlcjI2NTI5ODIw", + "avatar_url": "https://avatars.githubusercontent.com/u/26529820?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/alita-moore", + "html_url": "https://github.com/alita-moore", + "followers_url": "https://api.github.com/users/alita-moore/followers", + "following_url": "https://api.github.com/users/alita-moore/following{/other_user}", + "gists_url": "https://api.github.com/users/alita-moore/gists{/gist_id}", + "starred_url": "https://api.github.com/users/alita-moore/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/alita-moore/subscriptions", + "organizations_url": "https://api.github.com/users/alita-moore/orgs", + "repos_url": "https://api.github.com/users/alita-moore/repos", + "events_url": "https://api.github.com/users/alita-moore/events{/privacy}", + "received_events_url": "https://api.github.com/users/alita-moore/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2021-06-05T12:37:55Z", + "updated_at": "2021-06-05T12:37:55Z", + "author_association": "CONTRIBUTOR", + "body": "I have enabled the auto-merge feature so that may work but honestly github is such a pita, lol. Regardless, if there's just a way to give merge authority to commits with passing checks would be great", + "reactions": { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/855234473/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/864213983", + "html_url": "https://github.com/ethereum/EIPs/pull/3581#issuecomment-864213983", + "issue_url": "https://api.github.com/repos/ethereum/EIPs/issues/3581", + "id": 864213983, + "node_id": "MDEyOklzc3VlQ29tbWVudDg2NDIxMzk4Mw==", + "user": { + "login": "dependabot[bot]", + "id": 49699333, + "node_id": "MDM6Qm90NDk2OTkzMzM=", + "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/dependabot%5Bbot%5D", + "html_url": "https://github.com/apps/dependabot", + "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", + "type": "Bot", + "site_admin": false + }, + "created_at": "2021-06-18T18:42:01Z", + "updated_at": "2021-06-18T18:42:01Z", + "author_association": "CONTRIBUTOR", + "body": "OK, I won't notify you again about this release, but will get in touch when a new version is available. If you'd rather skip all updates until the next major or minor version, let me know by commenting `@dependabot ignore this major version` or `@dependabot ignore this minor version`.\n\nIf you change your mind, just re-open this PR and I'll resolve any conflicts on it.", + "reactions": { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/864213983/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/900560708", + "html_url": "https://github.com/ethereum/EIPs/pull/3581#issuecomment-900560708", + "issue_url": "https://api.github.com/repos/ethereum/EIPs/issues/3581", + "id": 900560708, + "node_id": "IC_kwDOAq426M41rXdE", + "user": { + "login": "github-actions[bot]", + "id": 41898282, + "node_id": "MDM6Qm90NDE4OTgyODI=", + "avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/github-actions%5Bbot%5D", + "html_url": "https://github.com/apps/github-actions", + "followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events", + "type": "Bot", + "site_admin": false + }, + "created_at": "2021-08-17T19:09:35Z", + "updated_at": "2021-08-17T19:09:35Z", + "author_association": "NONE", + "body": "There has been no activity on this pull request for two months. It will be closed in a week if no further activity occurs. If you would like to move this EIP forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review.", + "reactions": { + "url": "https://api.github.com/repos/ethereum/EIPs/issues/comments/900560708/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + } + ] + } + } +] \ No newline at end of file diff --git a/src/tests/assets/records/index.ts b/src/tests/assets/records/index.ts index affa4bbf..904a875f 100755 --- a/src/tests/assets/records/index.ts +++ b/src/tests/assets/records/index.ts @@ -84,7 +84,12 @@ export enum SavedRecord { * @summary: PR3623 was approved by the author but it didn't merge, so this * was a bug where author's approval didn't actually merge anything */ - PR3623 = "3623" + PR3623 = "3623", + /** + * @summary: PR3581 makes changes to a non-eip file which we would like to + * support, when this record was added this was a newly added feature + */ + PR3581 = "3581" } /** @@ -121,6 +126,7 @@ export const getMockRecords = async () => { const PR3654_1 = await import("./3654/1.json"); const PR3654_2 = await import("./3654/2.json"); const PR3623 = await import("./3623.json"); + const PR3581 = await import ("./3581.json"); assertMethods(PR3767); assertMethods(PR3676); @@ -132,8 +138,8 @@ export const getMockRecords = async () => { assertMethods(PR3670); assertMethods(PR3654_1); assertMethods(PR3654_2); - // @ts-ignore assertMethods(PR3623); + assertMethods(PR3581); const Records: { [k in keyof typeof SavedRecord]: MockRecord[] } = { PR3596: PR3596.default, @@ -146,8 +152,8 @@ export const getMockRecords = async () => { PR4192: PR4192.default, PR3768_1: PR3768_1.default, PR3768_2: PR3768_2.default, - // @ts-ignore - PR3623: PR3623.default + PR3623: PR3623.default, + PR3581: PR3581.default }; return Records; }; diff --git a/src/tests/testutils.ts b/src/tests/testutils.ts index f6eca9d4..c38ba3b6 100755 --- a/src/tests/testutils.ts +++ b/src/tests/testutils.ts @@ -31,6 +31,20 @@ export const expectError = async (fn, extraContext?: string) => { ); }; +export const expectErrorWithHandler = async (fn, handler: (error: any) => void, extraContext?: string) => { + let error; + try { + await fn(); + } catch (err) { + handler && handler(err); + error = err; + } + if (!error) + throw new Error( + `function ${fn.toString()} was expected to throw and error but it didn't\n\textra context: ${extraContext}` + ); +}; + export const clearContext = (context: Context) => { const paths = getAllTruthyObjectPaths(context); for (const path of paths) { diff --git a/yarn.lock b/yarn.lock index 263e76d4..9382576d 100755 --- a/yarn.lock +++ b/yarn.lock @@ -4715,10 +4715,10 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type-fest@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.3.3.tgz#2d3b07dacd3c14f4a200fb9f8e616d742ccc56a3" - integrity sha512-4r1ygCTovO9M+cJOdhlit7JkDmMgYwOXmttU1Nd/9a5UY8oebN0+wN/b5N5i/f8aQ/pTDS2oQ3CBxnu+/1p4aA== +type-fest@^2.5.4: + version "2.5.4" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.5.4.tgz#1613bf29a172ff1c66c29325466af9096fe505b5" + integrity sha512-zyPomVvb6u7+gJ/GPYUH6/nLDNiTtVOqXVUHtxFv5PmZQh6skgfeRtFYzWC01T5KeNWNIx5/0P111rKFLlkFvA== typedarray-to-buffer@^3.1.5: version "3.1.5"