Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

cerebro-cli

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cerebro-cli - npm Package Compare versions

Comparing version 0.2.0 to 0.2.1

6

package.json
{
"name": "cerebro-cli",
"version": "0.2.0",
"version": "0.2.1",
"description": "",

@@ -8,3 +8,3 @@ "main": "./src/index.js",

"coverage": "nyc npm run test",
"lint": "standard",
"lint": "standard && markdownlint README.md",
"start": "node ./src/index.js",

@@ -26,2 +26,4 @@ "test": "mocha"

"devDependencies": {
"markdownlint": "^0.23.1",
"markdownlint-cli": "^0.27.1",
"mocha": "^8.4.0",

@@ -28,0 +30,0 @@ "nock": "^13.1.0",

# Cerebro (cerebro-cli)
> A novel recruiting tool using GitHub events.
Finding capable developers is challenging. This tool starts with a simple heuristic - the ability
to get a pull request (PR) merged given a sufficient amount of feedback, and filters from
there.
Finding capable developers is challenging. This tool starts with a simple
heuristic - the ability to get a pull request (PR) merged given a sufficient
amount of feedback, and filters from there.
The flow is currently as follows:
1. Listen to the public GitHub events firehose for pull request

@@ -13,4 +15,4 @@ _merge_ events on PRs that have a specified number of comments.

3. For each PR:
1. Check if the language is your target language
2. Check if the author of the PR is looking for a job
1. Check if the language is your target language
2. Check if the author of the PR is looking for a job

@@ -28,4 +30,6 @@ ## Table of Contents

- [Node.js](https://nodejs.org). The [nvm](https://nvm.sh) tool works well for this.
- Optional, but highly recommended: A [GH personal token] with default permissions
- [Node.js](https://nodejs.org). The [nvm](https://nvm.sh) tool works well for
this.
- Optional, but highly recommended: A [GH personal token] with default
permissions
- Optional: [Docker] and [Docker Compose].

@@ -39,3 +43,4 @@

Cerebro can be run in a number of different ways, always configured by environment variables.
Cerebro can be run in a number of different ways, always configured by environment
variables.

@@ -46,11 +51,17 @@ ### Configuration

- `LANGUAGES`: **Required.** Comma separated list of the target languages you're looking for
- `GH_TOKEN`: **Not required but highly recommended.** Your GitHub personal authentication token.
- `COMMENT_THRESHOLD`: _optional, default 3_. Show PRs with review comments greater than or equal to this number
- `SHOW_NON_HIREABLE`: _optional, default false_. Show applicants that are not explicitly marked as hireable.
- `CHANGESET_THRESHOLD`: _optional, default 5432_. Only match PRs that have a total changeset (additions + subtractions) under this number.
- `LANGUAGES`: **Required.** Comma separated list of the target languages
you're looking for
- `GH_TOKEN`: **Not required but highly recommended.** Your GitHub personal
authentication token.
- `COMMENT_THRESHOLD`: _optional, default 3_. Show PRs with review comments
greater than or equal to this number
- `SHOW_NON_HIREABLE`: _optional, default false_. Show applicants that are not
explicitly marked as hireable.
- `CHANGESET_THRESHOLD`: _optional, default 5432_. Only match PRs that have a
total changeset (additions + subtractions) under this number.
### Using `npx`
You can skip the whole installation process altogether and just run Cerebro using `npx`
You can skip the whole installation process altogether and just run Cerebro
using `npx`

@@ -71,3 +82,3 @@ ```bash

-e LANGUAGES=c++,javascript \
aphelionz/cerebro-cli:v0.1.0
aphelionz/cerebro-cli:v0.2.0
```

@@ -80,3 +91,3 @@

cerebro:
image: aphelionz/cerebro-cli:v0.1.0
image: aphelionz/cerebro-cli:v0.2.0
environment:

@@ -113,3 +124,3 @@ GH_TOKEN: XXXXX

```
```bash
git clone https://github.com/aphelionz/cerebro

@@ -132,9 +143,11 @@ cd cerebro

2. English proficiency
1. Really needs a manual overview until we find / create a good enough tool for this
1. Really needs a manual overview until we find / create a good enough tool
for this
2. Ideally would be any proficiency in language
3. "Looking for a job" false negatives, and false positives too
1. `hireable` is either null (false) or true. However null is the default because GH jobs is
opt-in. So we only make a note of this for now.
1. `hireable` is either null (false) or true. However null is the default
because GH jobs is opt-in. So we only make a note of this for now.
4. IPFS + OrbitDB integration? Or at least _some_ database
5. Readline and raw stdin integration to make a proper UI (or just make an API + website)
5. Readline and raw stdin integration to make a proper UI (or just make an
API + website)
6. Environment variable validation

@@ -141,0 +154,0 @@ 1. Is it possible to get the full list of supported GH languages?

@@ -35,4 +35,9 @@ const metrics = require('./metrics')

})
seeker.events.on('candidateFound', output.console)
function outputCandidate (candidate) {
output.console(candidate)
metrics.custom.candidatesFound.inc(1)
}
seeker.events.on('candidateFound', outputCandidate)
// Start Prometheus metrics server on the specified port

@@ -48,5 +53,5 @@ metrics.start({ port: 9100 })

console.log('stopping seeker...')
seeker.events.off('candidateFound', output.console)
seeker.events.off('candidateFound', outputCandidate)
seeker.stop()
process.exit()
})
const { Octokit } = require('@octokit/rest')
const EventEmitter = require('events')
const events = new EventEmitter()
const events = new EventEmitter()
let commentThreshold
let changeSetThreshold
let seekInterval

@@ -9,36 +11,14 @@ let octokit

// Authenticated GitHub requests can be made 5000 times per
// hour, which means one request can be made every 714.3ms
// However, the public events feed isn't that busy so we just
// round up to 1000ms or 1 second.
//
// Non-authenticated users can only make 60 requests per hour :(
// TODO: Look into getting this from Octokit or the GH headers?
function start (auth, {
commentThreshold = 3,
changeSetThreshold = 5432,
showNonHireable = false,
targetLanguages
} = {}) {
const interval = auth ? 1000 : 60000
octokit = new Octokit({ auth })
seekInterval = setInterval((function seek () {
octokit.activity.listPublicEvents({ per_page: 100 })
.then(res => {
return firstFilter(res.data, { commentThreshold, changeSetThreshold })
})
.then((data) => secondFilter(data, { targetLanguages, showNonHireable }))
.catch(console.error)
function getNewEvents (ghEvents) {
const uniqueEvents = ghEvents.filter(d => parseInt(d.id, 10) > cursor)
events.emit('_debug.uniqueEvents', uniqueEvents.length)
return seek
}()), interval)
// Update cursor to greatest known ID
cursor = ghEvents.map(e => parseInt(e.id, 10)).sort()[ghEvents.length - 1]
return Promise.resolve(uniqueEvents)
}
function stop () { clearInterval(seekInterval) }
function firstFilter (ghEvents, { commentThreshold, changeSetThreshold }) {
const uniqueEvents = ghEvents.filter(d => parseInt(d.id, 10) > cursor)
events.emit('_debug.uniqueEvents', uniqueEvents.length)
const suitablePRs = uniqueEvents
function getSuitablePRs (newEvents, { commentThreshold, changeSetThreshold }) {
const suitablePRs = newEvents
// Get merged pull requests
.filter(d => d.type === 'PullRequestEvent')

@@ -53,5 +33,9 @@ .filter(p => p.payload.action === 'closed')

.filter(pr => pr.user.login.indexOf('bot') === -1)
events.emit('_debug.suitablePRs', suitablePRs.length)
return Promise.resolve(suitablePRs)
}
const filteredEvents = suitablePRs.map(pr => ({
async function formatSuitablePRs (suitablePRs) {
const formattedPRs = suitablePRs.map(pr => ({
prHtmlUrl: pr.html_url,

@@ -61,8 +45,32 @@ languagesUrl: pr.url.replace(/pulls(.*)$/g, 'languages'),

}))
events.emit('_debug.filteredEvents', filteredEvents.length)
events.emit('_debug.filteredEvents', formattedPRs.length)
return Promise.resolve(formattedPRs)
}
cursor = ghEvents.map(e => parseInt(e.id, 10)).sort()[ghEvents.length - 1]
return Promise.resolve(filteredEvents)
function start (auth, {
commentThreshold = 3,
changeSetThreshold = 5432,
showNonHireable = false,
targetLanguages
} = {}) {
// 5000 authenticated requests/hour (rounded up) or 60 for non-auth :(
// TODO: Look into getting this from Octokit somehow? GH headers don't provide.
const interval = auth ? 1000 : 60000
if(!octokit) { octokit = new Octokit({ auth }) }
seekInterval = setInterval((function seek () {
const rawEvents = octokit.activity.listPublicEvents({ per_page: 100 })
.then(res => res.data)
.then(getNewEvents)
.then(newEvents => getSuitablePRs(newEvents, { commentThreshold, changeSetThreshold }))
.then(formatSuitablePRs)
.then((data) => secondFilter(data, { targetLanguages, showNonHireable }))
.catch(console.error)
return seek
}()), interval) // IIFE executes automatically
}
function stop () { clearInterval(seekInterval) }
async function secondFilter (results, { targetLanguages, showNonHireable }) {

@@ -81,3 +89,2 @@ for (let i = 0; i < results.length; i++) {

// TODO: Refactor to "output" function of some sort
const candidate = (await octokit.users.getByUsername({ username })).data

@@ -84,0 +91,0 @@ if (candidate.hireable || showNonHireable) {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc