@nerdwallet/shepherd
Advanced tools
Comparing version 1.15.5 to 1.16.0
@@ -34,3 +34,3 @@ "use strict"; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const { org, search_type, search_query } = this.migrationContext.migration.spec.adapter; | ||
const { org, search_type = 'code', search_query } = this.migrationContext.migration.spec.adapter; | ||
let repoNames; | ||
@@ -37,0 +37,0 @@ // list all of an orgs active repos |
@@ -32,4 +32,5 @@ "use strict"; | ||
const repo2 = { owner: 'NerdWallet', name: 'shepherd' }; | ||
const service = new github_1.default(mocktokit); | ||
const adapter = new github_2.default(mockMigrationContext(), service); | ||
const context = mockMigrationContext(); | ||
const service = new github_1.default(context, mocktokit); | ||
const adapter = new github_2.default(context, service); | ||
expect(adapter.reposEqual(repo1, repo2)).toBe(true); | ||
@@ -41,4 +42,5 @@ }); | ||
const repo2 = { owner: 'NerdWallet', name: 'shepherd' }; | ||
const service = new github_1.default(mocktokit); | ||
const adapter = new github_2.default(mockMigrationContext(), service); | ||
const context = mockMigrationContext(); | ||
const service = new github_1.default(context, mocktokit); | ||
const adapter = new github_2.default(context, service); | ||
expect(adapter.reposEqual(repo1, repo2)).toBe(true); | ||
@@ -50,4 +52,4 @@ }); | ||
const mocktokit = {}; | ||
const migrationCtx = mockMigrationContext(); | ||
migrationCtx.migration.spec.adapter = { | ||
const context = mockMigrationContext(); | ||
context.migration.spec.adapter = { | ||
org: 'testOrg', | ||
@@ -57,4 +59,4 @@ type: 'github', | ||
}; | ||
const service = new github_1.default(mocktokit); | ||
const adapter = new github_2.default(migrationCtx, service); | ||
const service = new github_1.default(context, mocktokit); | ||
const adapter = new github_2.default(context, service); | ||
return expect(adapter.getCandidateRepos()) | ||
@@ -66,10 +68,10 @@ .rejects | ||
const mocktokit = {}; | ||
const migrationCtx = mockMigrationContext(); | ||
migrationCtx.migration.spec.adapter = { | ||
const context = mockMigrationContext(); | ||
context.migration.spec.adapter = { | ||
type: 'github', | ||
org: 'testOrg' | ||
}; | ||
const service = new github_1.default(mocktokit); | ||
const service = new github_1.default(context, mocktokit); | ||
service.getActiveReposForOrg.mockResolvedValue(['testOrg/test-repo']); | ||
const adapter = new github_2.default(migrationCtx, service); | ||
const adapter = new github_2.default(context, service); | ||
const result = yield adapter.getCandidateRepos(); | ||
@@ -81,4 +83,4 @@ expect(service.getActiveReposForOrg).toBeCalledWith({ org: 'testOrg' }); | ||
const mocktokit = {}; | ||
const migrationCtx = mockMigrationContext(); | ||
migrationCtx.migration.spec.adapter = { | ||
const context = mockMigrationContext(); | ||
context.migration.spec.adapter = { | ||
type: 'github', | ||
@@ -88,5 +90,5 @@ search_type: 'repositories', | ||
}; | ||
const service = new github_1.default(mocktokit); | ||
const service = new github_1.default(context, mocktokit); | ||
service.getActiveReposForSearchTypeAndQuery.mockResolvedValue(['repoownername/test-repo']); | ||
const adapter = new github_2.default(migrationCtx, service); | ||
const adapter = new github_2.default(context, service); | ||
const result = yield adapter.getCandidateRepos(); | ||
@@ -99,6 +101,6 @@ expect(service.getActiveReposForSearchTypeAndQuery).toBeCalledWith({ | ||
})); | ||
it(`performs code search and returns expected result if search_type is 'code' or is not provided`, () => __awaiter(void 0, void 0, void 0, function* () { | ||
it(`performs code search and returns expected result if search_type is 'code'`, () => __awaiter(void 0, void 0, void 0, function* () { | ||
const mocktokit = {}; | ||
const migrationCtx = mockMigrationContext(); | ||
migrationCtx.migration.spec.adapter = { | ||
const context = mockMigrationContext(); | ||
context.migration.spec.adapter = { | ||
type: 'github', | ||
@@ -108,17 +110,25 @@ search_type: 'code', | ||
}; | ||
const migrationCtxWithoutSearchType = mockMigrationContext(); | ||
migrationCtxWithoutSearchType.migration.spec.adapter = { | ||
const service = new github_1.default(context, mocktokit); | ||
service.getActiveReposForSearchTypeAndQuery.mockResolvedValue(['repoownername/test-repo']); | ||
const adapter = new github_2.default(context, service); | ||
const result = yield adapter.getCandidateRepos(); | ||
expect(service.getActiveReposForSearchTypeAndQuery).toBeCalledTimes(1); | ||
expect(service.getActiveReposForSearchTypeAndQuery).toBeCalledWith({ | ||
search_type: 'code', | ||
search_query: 'path:/ filename:package.json in:path' | ||
}); | ||
expect(result).toStrictEqual([{ owner: 'repoownername', name: 'test-repo' }]); | ||
})); | ||
it(`performs code search and returns expected result if search_type is not provided`, () => __awaiter(void 0, void 0, void 0, function* () { | ||
const mocktokit = {}; | ||
const context = mockMigrationContext(); | ||
context.migration.spec.adapter = { | ||
type: 'github', | ||
search_query: 'path:/ filename:package.json in:path' | ||
}; | ||
const service = new github_1.default(mocktokit); | ||
const service = new github_1.default(context, mocktokit); | ||
service.getActiveReposForSearchTypeAndQuery.mockResolvedValue(['repoownername/test-repo']); | ||
const adapterWithSearchType = new github_2.default(migrationCtx, service); | ||
const adapterWithoutSearchType = new github_2.default(migrationCtxWithoutSearchType, service); | ||
const getCandidateRepos = [ | ||
adapterWithSearchType.getCandidateRepos(), | ||
adapterWithoutSearchType.getCandidateRepos() | ||
]; | ||
const results = yield Promise.all(getCandidateRepos); | ||
expect(service.getActiveReposForSearchTypeAndQuery).toBeCalledTimes(2); | ||
const adapter = new github_2.default(context, service); | ||
const result = yield adapter.getCandidateRepos(); | ||
expect(service.getActiveReposForSearchTypeAndQuery).toBeCalledTimes(1); | ||
expect(service.getActiveReposForSearchTypeAndQuery).toBeCalledWith({ | ||
@@ -128,4 +138,3 @@ search_type: 'code', | ||
}); | ||
expect(results[0]).toStrictEqual([{ owner: 'repoownername', name: 'test-repo' }]); | ||
expect(results[1]).toStrictEqual([{ owner: 'repoownername', name: 'test-repo' }]); | ||
expect(result).toStrictEqual([{ owner: 'repoownername', name: 'test-repo' }]); | ||
})); | ||
@@ -135,6 +144,6 @@ }); | ||
it('throws if owner or name not found in repo string', () => { | ||
const context = mockMigrationContext(); | ||
const mocktokit = {}; | ||
const migrationCtx = mockMigrationContext(); | ||
const service = new github_1.default(mocktokit); | ||
const adapter = new github_2.default(migrationCtx, service); | ||
const service = new github_1.default(context, mocktokit); | ||
const adapter = new github_2.default(context, service); | ||
const calledWithoutName = adapter.parseRepo.bind(null, 'ownerbutnoname/'); | ||
@@ -150,4 +159,5 @@ const calledWithoutOwner = adapter.parseRepo.bind(null, '/namebutnoowner'); | ||
it('saves the default branch', () => __awaiter(void 0, void 0, void 0, function* () { | ||
const context = mockMigrationContext(); | ||
const mocktokit = {}; | ||
const service = new github_1.default(mocktokit); | ||
const service = new github_1.default(context, mocktokit); | ||
const repo = { | ||
@@ -158,3 +168,3 @@ owner: 'NerdWallet', | ||
service.getDefaultBranchForRepo.mockResolvedValue('develop'); | ||
const adapter = new github_2.default(mockMigrationContext(), service); | ||
const adapter = new github_2.default(context, service); | ||
const mappedRepo = yield adapter.mapRepoAfterCheckout(repo); | ||
@@ -173,6 +183,7 @@ expect(service.getDefaultBranchForRepo).toBeCalledTimes(1); | ||
it('creates a new PR if one does not exist', () => __awaiter(void 0, void 0, void 0, function* () { | ||
const context = mockMigrationContext(); | ||
const octokit = {}; | ||
const service = new github_1.default(octokit); | ||
const service = new github_1.default(context, octokit); | ||
service.listPullRequests.mockResolvedValue([]); | ||
const adapter = new github_2.default(mockMigrationContext(), service); | ||
const adapter = new github_2.default(context, service); | ||
yield adapter.createPullRequest(REPO, 'Test PR message'); | ||
@@ -194,4 +205,5 @@ expect(service.listPullRequests).toBeCalledWith({ | ||
it('updates a PR if one exists and is open', () => __awaiter(void 0, void 0, void 0, function* () { | ||
const context = mockMigrationContext(); | ||
const octokit = {}; | ||
const service = new github_1.default(octokit); | ||
const service = new github_1.default(context, octokit); | ||
service.listPullRequests.mockResolvedValue([{ | ||
@@ -201,3 +213,3 @@ number: 1234, | ||
}]); | ||
const adapter = new github_2.default(mockMigrationContext(), service); | ||
const adapter = new github_2.default(context, service); | ||
yield adapter.createPullRequest(REPO, 'Test PR message, part 2'); | ||
@@ -213,4 +225,5 @@ expect(service.updatePullRequest).toBeCalledWith({ | ||
it('does not update a closed PR', () => __awaiter(void 0, void 0, void 0, function* () { | ||
const context = mockMigrationContext(); | ||
const octokit = {}; | ||
const service = new github_1.default(octokit); | ||
const service = new github_1.default(context, octokit); | ||
service.listPullRequests.mockResolvedValue([{ | ||
@@ -220,3 +233,3 @@ number: 1234, | ||
}]); | ||
const adapter = new github_2.default(mockMigrationContext(), service); | ||
const adapter = new github_2.default(context, service); | ||
yield expect(adapter.createPullRequest(REPO, 'Test PR message, part 2')).rejects.toThrow(); | ||
@@ -223,0 +236,0 @@ expect(service.updatePullRequest).not.toBeCalled(); |
@@ -12,3 +12,3 @@ "use strict"; | ||
case 'github': { | ||
const githubService = new github_2.default(); | ||
const githubService = new github_2.default(context); | ||
return new github_1.default(context, githubService); | ||
@@ -15,0 +15,0 @@ } |
import { Octokit } from '@octokit/rest'; | ||
import { RestEndpointMethodTypes } from '@octokit/plugin-rest-endpoint-methods'; | ||
import type { RestEndpointMethodTypes } from '@octokit/plugin-rest-endpoint-methods'; | ||
import { IMigrationContext } from '../migration-context'; | ||
interface SearchTypeAndQueryParams { | ||
search_type?: string; | ||
search_type: 'repositories' | 'code'; | ||
search_query: string; | ||
@@ -9,7 +10,7 @@ } | ||
private octokit; | ||
constructor(octokit?: Octokit); | ||
constructor(context: IMigrationContext, octokit?: Octokit); | ||
private paginateRest; | ||
private findReposByMetadata; | ||
private findReposByCode; | ||
private getRepos; | ||
private getRepo; | ||
private listOrgRepos; | ||
@@ -24,4 +25,4 @@ getDefaultBranchForRepo(criteria: RestEndpointMethodTypes['repos']['get']['parameters']): Promise<string>; | ||
getBranch(criteria: RestEndpointMethodTypes['repos']['getBranch']['parameters']): Promise<RestEndpointMethodTypes['repos']['getBranch']['response']>; | ||
getActiveReposForSearchTypeAndQuery({ search_type, search_query }: SearchTypeAndQueryParams): Promise<any>; | ||
getActiveReposForSearchTypeAndQuery({ search_type, search_query }: SearchTypeAndQueryParams): Promise<string[]>; | ||
} | ||
export {}; |
@@ -18,8 +18,8 @@ "use strict"; | ||
const plugin_retry_1 = require("@octokit/plugin-retry"); | ||
const plugin_throttling_1 = require("@octokit/plugin-throttling"); | ||
const lodash_1 = __importDefault(require("lodash")); | ||
const netrc_1 = __importDefault(require("netrc")); | ||
const VALID_SEARCH_TYPES = ['code', 'repositories']; | ||
const RetryableOctokit = rest_1.Octokit.plugin(plugin_retry_1.retry); | ||
const RetryableThrottledOctokit = rest_1.Octokit.plugin(plugin_throttling_1.throttling, plugin_retry_1.retry); | ||
class GithubService { | ||
constructor(octokit) { | ||
constructor(context, octokit) { | ||
if (octokit) { | ||
@@ -35,8 +35,16 @@ this.octokit = octokit; | ||
} | ||
this.octokit = new RetryableOctokit({ | ||
this.octokit = new RetryableThrottledOctokit({ | ||
auth: token, | ||
retry: { | ||
// By default the doNotRetry setting includes 403, which means we face secondary rate limits | ||
doNotRetry: [400, 401, 404, 422], | ||
} | ||
throttle: { | ||
onRateLimit: (retryAfter, options) => { | ||
context.logger.warn(`Hit rate limit for ${options.method} ${options.url}`); | ||
context.logger.warn(`Retrying in ${retryAfter} second(s)`); | ||
return options.request.retryCount < 5; | ||
}, | ||
onAbuseLimit: (retryAfter, options) => { | ||
context.logger.warn(`Hit abuse limit for ${options.method} ${options.url}`); | ||
context.logger.warn(`Retrying in ${retryAfter} second(s)`); | ||
return options.request.retryCount < 5; | ||
}, | ||
}, | ||
}); | ||
@@ -56,7 +64,6 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const searchResults = yield this.paginateRest(this.octokit.search.code, criteria); | ||
return searchResults.map((r) => lodash_1.default.get(r, 'repository.full_name')).sort(); | ||
return this.paginateRest(this.octokit.search.code, criteria); | ||
}); | ||
} | ||
getRepos(criteria) { | ||
getRepo(criteria) { | ||
return this.octokit.repos.get(criteria); | ||
@@ -69,3 +76,3 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const { data } = yield this.getRepos(criteria); | ||
const { data } = yield this.getRepo(criteria); | ||
return data.default_branch; | ||
@@ -102,13 +109,21 @@ }); | ||
getActiveReposForSearchTypeAndQuery({ search_type, search_query }) { | ||
if (search_type && !VALID_SEARCH_TYPES.includes(search_type)) { | ||
throw new Error(`"search_type" must be one of the following: | ||
${VALID_SEARCH_TYPES.map(e => `'${e}'`).join(' | ')}`); | ||
} | ||
switch (search_type) { | ||
case 'repositories': | ||
return this.findReposByMetadata({ q: search_query }); | ||
case 'code': | ||
default: | ||
return this.findReposByCode({ q: search_query }); | ||
} | ||
return __awaiter(this, void 0, void 0, function* () { | ||
switch (search_type) { | ||
case 'repositories': { | ||
return this.findReposByMetadata({ q: search_query }); | ||
} | ||
case 'code': { | ||
const repos = yield this.findReposByCode({ q: search_query }); | ||
const archived = yield Promise.all(repos.map((r) => __awaiter(this, void 0, void 0, function* () { | ||
const { owner: { login: owner }, name } = r.repository; | ||
const { data } = yield this.getRepo({ owner, repo: name }); | ||
return data.archived; | ||
}))); | ||
return repos.filter((_r, i) => !archived[i]).map((r) => r.repository.full_name); | ||
} | ||
default: { | ||
throw new Error(`Invalid search_type: ${search_type}`); | ||
} | ||
} | ||
}); | ||
} | ||
@@ -115,0 +130,0 @@ } |
@@ -16,2 +16,3 @@ "use strict"; | ||
const github_1 = __importDefault(require("./github")); | ||
const mockMigrationContext = () => ({}); | ||
describe('GithubService', () => { | ||
@@ -29,3 +30,3 @@ describe('getDefaultBranchForRepo', () => { | ||
}; | ||
const service = new github_1.default(mocktokit); | ||
const service = new github_1.default(mockMigrationContext(), mocktokit); | ||
const searchCriteria = { | ||
@@ -67,3 +68,3 @@ owner: 'NerdwalletOSS', | ||
}; | ||
const service = new github_1.default(mocktokit); | ||
const service = new github_1.default(mockMigrationContext(), mocktokit); | ||
const searchCriteria = { org: 'testOrg' }; | ||
@@ -91,3 +92,3 @@ const result = yield service.getActiveReposForOrg(searchCriteria); | ||
}; | ||
const service = new github_1.default(mocktokit); | ||
const service = new github_1.default(mockMigrationContext(), mocktokit); | ||
const searchCriteria = { | ||
@@ -119,3 +120,3 @@ owner: 'testOrg', | ||
}; | ||
const service = new github_1.default(mocktokit); | ||
const service = new github_1.default(mockMigrationContext(), mocktokit); | ||
const searchCriteria = { | ||
@@ -144,3 +145,3 @@ owner: 'testOrg', | ||
}; | ||
const service = new github_1.default(mocktokit); | ||
const service = new github_1.default(mockMigrationContext(), mocktokit); | ||
const prCreateParams = { | ||
@@ -171,3 +172,3 @@ owner: 'testOrg', | ||
}; | ||
const service = new github_1.default(mocktokit); | ||
const service = new github_1.default(mockMigrationContext(), mocktokit); | ||
const prUpdateParams = { | ||
@@ -200,3 +201,3 @@ owner: 'testOrg', | ||
}; | ||
const service = new github_1.default(mocktokit); | ||
const service = new github_1.default(mockMigrationContext(), mocktokit); | ||
const criteria = { | ||
@@ -225,3 +226,3 @@ owner: 'testOrg', | ||
}; | ||
const service = new github_1.default(mocktokit); | ||
const service = new github_1.default(mockMigrationContext(), mocktokit); | ||
const criteria = { | ||
@@ -240,3 +241,3 @@ owner: 'testOrg', | ||
const mocktokit = {}; | ||
const service = new github_1.default(mocktokit); | ||
const service = new github_1.default(mockMigrationContext(), mocktokit); | ||
const criteria = { | ||
@@ -246,7 +247,6 @@ search_type: 'invalid_search_type', | ||
}; | ||
const fn = service.getActiveReposForSearchTypeAndQuery.bind(service, criteria); | ||
expect(fn).toThrowError(`"search_type" must be one of the following: | ||
'code' | 'repositories'`); | ||
// @ts-expect-error -- Testing invalid `search_type` | ||
yield expect(service.getActiveReposForSearchTypeAndQuery(criteria)).rejects.toThrow('Invalid search_type: invalid_search_type'); | ||
})); | ||
it('finds repos by metadata if repository search is specified & returns results', () => __awaiter(void 0, void 0, void 0, function* () { | ||
it('finds repos by metadata if repository search is specified', () => __awaiter(void 0, void 0, void 0, function* () { | ||
const repoSearchResponse = [ | ||
@@ -256,2 +256,5 @@ { | ||
full_name: 'testOrg/repo1', | ||
owner: { | ||
login: 'testOrg', | ||
}, | ||
}, | ||
@@ -261,2 +264,5 @@ { | ||
full_name: 'testOrg/repo2', | ||
owner: { | ||
login: 'testOrg', | ||
}, | ||
} | ||
@@ -270,12 +276,12 @@ ]; | ||
}; | ||
const service = new github_1.default(mocktokit); | ||
const criteria = { | ||
const service = new github_1.default(mockMigrationContext(), mocktokit); | ||
const SEARCH_QUERY = 'topics:test'; | ||
const result = yield service.getActiveReposForSearchTypeAndQuery({ | ||
search_type: 'repositories', | ||
search_query: 'topics:test' | ||
}; | ||
const result = yield service.getActiveReposForSearchTypeAndQuery(criteria); | ||
expect(mocktokit.paginate).toBeCalledWith(mocktokit.search.repos, { q: criteria.search_query }); | ||
search_query: SEARCH_QUERY | ||
}); | ||
expect(mocktokit.paginate).toBeCalledWith(mocktokit.search.repos, { q: SEARCH_QUERY }); | ||
expect(result).toEqual(repoSearchResponse.map((o) => o.full_name)); | ||
})); | ||
it('finds repos by code if code search specified or search type omitted & returns results', () => __awaiter(void 0, void 0, void 0, function* () { | ||
it('finds repos by code if code search specified', () => __awaiter(void 0, void 0, void 0, function* () { | ||
const codeSearchResponse = [ | ||
@@ -287,2 +293,5 @@ { | ||
full_name: 'testOrg/repo1', | ||
owner: { | ||
login: 'testOrg', | ||
}, | ||
}, | ||
@@ -295,2 +304,5 @@ }, | ||
full_name: 'testOrg/repo2', | ||
owner: { | ||
login: 'testOrg', | ||
}, | ||
}, | ||
@@ -301,2 +313,9 @@ } | ||
paginate: jest.fn().mockResolvedValue(codeSearchResponse), | ||
repos: { | ||
get: jest.fn().mockResolvedValue({ | ||
data: { | ||
archived: false, | ||
}, | ||
}), | ||
}, | ||
search: { | ||
@@ -306,18 +325,64 @@ code: jest.fn() | ||
}; | ||
const service = new github_1.default(mocktokit); | ||
const criteria1 = { | ||
const service = new github_1.default(mockMigrationContext(), mocktokit); | ||
const SEARCH_QUERY = 'org:testOrg path:/ filename:package.json in:path'; | ||
const result = yield service.getActiveReposForSearchTypeAndQuery({ | ||
search_type: 'code', | ||
search_query: 'org:testOrg path:/ filename:package.json in:path' | ||
search_query: SEARCH_QUERY, | ||
}); | ||
expect(mocktokit.paginate).toBeCalledWith(mocktokit.search.code, { q: SEARCH_QUERY }); | ||
expect(result).toEqual(['testOrg/repo1', 'testOrg/repo2']); | ||
})); | ||
it('filters out archived repos when using code search', () => __awaiter(void 0, void 0, void 0, function* () { | ||
const codeSearchResponse = [ | ||
{ | ||
name: 'package.json', | ||
repository: { | ||
name: 'repo1', | ||
full_name: 'testOrg/repo1', | ||
owner: { | ||
login: 'testOrg', | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: 'package.json', | ||
repository: { | ||
name: 'repo2', | ||
full_name: 'testOrg/repo2', | ||
owner: { | ||
login: 'testOrg', | ||
}, | ||
}, | ||
} | ||
]; | ||
const mocktokit = { | ||
paginate: jest.fn().mockResolvedValue(codeSearchResponse), | ||
repos: { | ||
get: jest.fn().mockImplementation((params) => { | ||
if (params.repo === 'repo2') { | ||
return { | ||
data: { | ||
archived: true, | ||
}, | ||
}; | ||
} | ||
return { | ||
data: { | ||
archived: false, | ||
}, | ||
}; | ||
}), | ||
}, | ||
search: { | ||
code: jest.fn() | ||
} | ||
}; | ||
const criteria2 = { | ||
search_query: 'org:testOrg path:/ filename:package.json in:path' | ||
}; | ||
const results = yield Promise.all([ | ||
service.getActiveReposForSearchTypeAndQuery(criteria1), | ||
service.getActiveReposForSearchTypeAndQuery(criteria2) | ||
]); | ||
expect(mocktokit.paginate).toBeCalledWith(mocktokit.search.code, { q: criteria1.search_query }); | ||
expect(mocktokit.paginate).toBeCalledWith(mocktokit.search.code, { q: criteria2.search_query }); | ||
expect(results[0]).toEqual(codeSearchResponse.map((o) => o.repository.full_name)); | ||
expect(results[1]).toEqual(codeSearchResponse.map((o) => o.repository.full_name)); | ||
const service = new github_1.default(mockMigrationContext(), mocktokit); | ||
const SEARCH_QUERY = 'org:testOrg path:/ filename:package.json in:path'; | ||
const result = yield service.getActiveReposForSearchTypeAndQuery({ | ||
search_type: 'code', | ||
search_query: SEARCH_QUERY, | ||
}); | ||
expect(mocktokit.paginate).toBeCalledWith(mocktokit.search.code, { q: SEARCH_QUERY }); | ||
expect(result).toEqual(['testOrg/repo1']); | ||
})); | ||
@@ -324,0 +389,0 @@ }); |
{ | ||
"name": "@nerdwallet/shepherd", | ||
"version": "1.15.5", | ||
"version": "1.16.0", | ||
"description": "A utility for applying code changes across many repositories", | ||
@@ -25,6 +25,6 @@ "keywords": [ | ||
"build": "tsc", | ||
"build:watch": "npm run build -- --watch", | ||
"fix-lint": "npm run lint -- --fix", | ||
"build:watch": "yarn build --watch", | ||
"fix-lint": "yarn lint --fix", | ||
"lint": "eslint src/**/*.ts", | ||
"prepublishOnly": "npm run test && npm run build", | ||
"prepublishOnly": "yarn test && yarn build", | ||
"test": "jest --coverage src/" | ||
@@ -51,3 +51,4 @@ }, | ||
"@octokit/plugin-retry": "^3.0.9", | ||
"@octokit/rest": "^18.1.0", | ||
"@octokit/plugin-throttling": "^3.5.2", | ||
"@octokit/rest": "^18.12.0", | ||
"@types/js-yaml": "^3.12.6", | ||
@@ -73,6 +74,6 @@ "chalk": "^4.1.0", | ||
"@types/fs-extra": "^9.0.6", | ||
"@types/jest": "^26.0.19", | ||
"@types/jest": "^27.0.0", | ||
"@types/lodash": "^4.14.167", | ||
"@types/log-symbols": "^2.0.0", | ||
"@types/node": "^14.14.25", | ||
"@types/node": "^16.0.0", | ||
"@typescript-eslint/eslint-plugin": "^4.15.0", | ||
@@ -83,5 +84,5 @@ "@typescript-eslint/parser": "^4.15.0", | ||
"eslint-plugin-prefer-arrow": "^1.2.3", | ||
"jest": "^26.6.3", | ||
"jest": "^27.0.0", | ||
"semantic-release": "^17.3.8", | ||
"ts-jest": "^26.5.1", | ||
"ts-jest": "^27.0.0", | ||
"typescript": "^3.9.9" | ||
@@ -88,0 +89,0 @@ }, |
@@ -133,8 +133,13 @@ # Shepherd | ||
Run `npm install` to install dependencies, and then `npm install -g` to make the `shepherd` executable available on your `PATH`. | ||
Run `yarn` to install dependencies. | ||
Shepherd is written in TypeScript, which requires compilation to JavaScript. When developing Shepherd, it's recommended to run `npm run build:watch` in a separate terminal. This will incrementally compile the source code as you edit it. | ||
Shepherd is written in TypeScript, which requires compilation to JavaScript. When developing Shepherd, it's recommended to run `yarn build:watch` in a separate terminal. This will incrementally compile the source code as you edit it. You can then invoke the Shepherd CLI by referencing the absolute path to the compiled `cli.js` file: | ||
Shepherd currently has minimal test coverage, but we're aiming to improve that with each new PR. Tests are written with Jest and should be named in a `*.test.ts` alongside the file under test. To run the test suite, run `npm run test`. | ||
```sh | ||
cd ../my-other-project | ||
../shepherd/lib/cli.js checkout path/to/migration | ||
``` | ||
We use [TSLint](https://github.com/palantir/tslint) to ensure a consistent coding style and to help prevent certain classes of problems. Run `npm run lint` to run the linter, and `npm run fix-lint` to automatically fix applicable problems. | ||
Shepherd currently has minimal test coverage, but we're aiming to improve that with each new PR. Tests are written with Jest and should be named in a `*.test.ts` alongside the file under test. To run the test suite, run `yarn test`. | ||
We use [ESLint](https://eslint.org/) to ensure a consistent coding style and to help prevent certain classes of problems. Run `yarn lint` to run the linter, and `yarn fix-lint` to automatically fix applicable problems. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
2621
144
216531
16
93
+ Added@octokit/plugin-throttling@3.7.0(transitive)
Updated@octokit/rest@^18.12.0