Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Kool + updates #1

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
root = true

[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
charset = utf-8

[{*.json,.*rc,*.yml}]
indent_style = space
indent_size = 2
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
.vscode/
.env
# kool.deploy.env
22 changes: 13 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# StarWatcher

Watch a github repository and push a notification at every new star
Watch a github repository and push a notification at every new star.

## Deploy with one line

Using [now](https://zeit.co/now), you don't even need to checkout and build this
repository.
You can deploy with [Kool](https://kool.dev). Just clone the repository, update your `.env` and then:

`$ now -e REPO="<username>/<repository>" -e
SLACK_URL="<your_slack_incoming_webhook>" chabou/starwatcher`
`$ kool deploy`

That's it.

Or [now](https://zeit.co/now), you don't even need to checkout and build this repository.

`$ now -e REPO="<username>/<repository>" -e SLACK_URL="<your_slack_incoming_webhook>" chabou/starwatcher`

That's it.

Expand All @@ -19,10 +23,10 @@ That's it.
| `REPO` | `zeit/hyper` | Github repository with username. Example: `chabou/hyper-pane` |
| `CHECK_INTERVAL` | `2m` | Time to wait between 2 GitHub API calls. Example: `1000`, `2m`, `1h`. Be careful to [Github rate limiting](https://developer.github.com/v3/#rate-limiting) |
| `NOTIFY_INTERVAL` | `1` | Divisor use to perform a modulo operation to determine if a new star should trigger a notification. Example: a value of `5` will trigger notifications for a 5, 10 or 15 star count. |
| `SLACK_URL` | none | Slack webhook URL. Example: `https://hooks.slack.com/services/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` |
| `SLACK_CHANNEL` | none | Slack channel where notifications will be post. Example: `general` |
| `SLACK_USERNAME` | `StarWatcher` | Name used as sender in slack notification |
| `INCOMING_WEBHOOK_URL` | none | Slack or Mattermost webhook URL. Example: `https://hooks.slack.com/services/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` |
| `NOTIFICATION_CHANNEL` | none | Channel where notifications will be post. Example: `general` |
| `NOTIFICATION_USERNAME` | `StarWatcher` | Name used as sender in Slack or Mattermost notification |

## Notification

Only Slack webhook is supported
Only Slack or Mattermost incoming webhooks are supported.
1 change: 1 addition & 0 deletions docker-compose.yml
175 changes: 88 additions & 87 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,87 +1,88 @@
const sleep = require('then-sleep')
const got = require('got')
const ms = require('ms')

const CHECK_INTERVAL = process.env.CHECK_INTERVAL || '2m'
const REPO = process.env.REPO || 'zeit/hyper'
const NOTIFY_INTERVAL = process.env.NOTIFY_INTERVAL || 1
const SLACK_URL = process.env.SLACK_URL
const SLACK_CHANNEL = process.env.SLACK_CHANNEL
const SLACK_USERNAME = process.env.SLACK_NAME || 'StarWatcher'

const INTERVAL = ms(CHECK_INTERVAL)
const REPO_URL = `https://github.com/${REPO}`
const API_URL = `https://api.github.com/repos/${REPO}`

let stars = 0
let loop = true
const stats = {
lastChange: null,
lastNotification: null
}

const notify = async () => {
if (!SLACK_URL) {
return
}
const payload = {
text: `<${REPO_URL}|*${REPO}*> just reached ${stars} :star:`,
icon_emoji: ':star:',
username: SLACK_USERNAME
}
if (SLACK_CHANNEL) {
payload['channel'] = SLACK_CHANNEL
}
try {
await got.post(SLACK_URL, {
body: JSON.stringify(payload)
})
console.log(`Notification sent: ${payload.text}`)
} catch (e) {
console.error('Notification failed', e)
}
}

const getStars = async () => {
try {
const response = await got(API_URL, {
json: true
})
const {stargazers_count} = response.body
return stargazers_count
} catch (e) {
console.error(e)
return stars
}
}

const main = async () => {
// Init
let count = await getStars()
stars = count
console.log(`Watching from ${stars}`)
while (loop) {
console.log(`Waiting ${INTERVAL}ms`)
await sleep(INTERVAL)
count = await getStars()

if (stars === count) {
console.log(`Same Stars count: ${count}`)
continue
}
console.log(`Stars count changed: ${stars} -> ${count}`)
stats.lastChange = new Date()
stars = count
if (stars % NOTIFY_INTERVAL === 0) {
notify()
stats.lastNotification = new Date()
}
}
}
main()

module.exports = async (req, res) => {
const count = stars || (await getStars())

return count
}
const sleep = require('then-sleep')
const got = require('got')
const ms = require('ms')

const CHECK_INTERVAL = process.env.CHECK_INTERVAL || '2m'
const REPO = process.env.REPO || 'zeit/hyper'
const NOTIFY_INTERVAL = process.env.NOTIFY_INTERVAL || 1
const INCOMING_WEBHOOK_URL = process.env.INCOMING_WEBHOOK_URL
const NOTIFICATION_CHANNEL = process.env.NOTIFICATION_CHANNEL
const NOTIFICATION_USERNAME = process.env.NOTIFICATION_USERNAME || 'StarWatcher'

const INTERVAL = ms(CHECK_INTERVAL)
const REPO_URL = `https://github.com/${REPO}`
const API_URL = `https://api.github.com/repos/${REPO}`

let stars = 0
let loop = true
const stats = {
lastChange: null,
lastNotification: null
}

const notify = async () => {
if (!INCOMING_WEBHOOK_URL) {
console.log('No INCOMING_WEBHOOK_URL provided; nothing to watch for')
return
}
const payload = {
text: `<${REPO_URL}|*${REPO}*> just reached ${stars} :star:`,
icon_emoji: ':star:',
username: NOTIFICATION_USERNAME
}
if (NOTIFICATION_CHANNEL) {
payload['channel'] = NOTIFICATION_CHANNEL
}
try {
await got.post(INCOMING_WEBHOOK_URL, {
body: JSON.stringify(payload)
})
console.log(`Notification sent: ${payload.text}`)
} catch (e) {
console.error('Notification failed', e)
}
}

const getStars = async () => {
try {
const response = await got(API_URL, {
json: true
})
const {stargazers_count} = response.body
return stargazers_count
} catch (e) {
console.error(e)
return stars
}
}

const main = async () => {
// Init
let count = await getStars()
stars = count
console.log(`Watching from ${stars}`)
while (loop) {
console.log(`Waiting ${INTERVAL}ms`)
await sleep(INTERVAL)
count = await getStars()

if (stars === count) {
console.log(`Same Stars count: ${count}`)
continue
}
console.log(`Stars count changed: ${stars} -> ${count}`)
stats.lastChange = new Date()
stars = count
if (stars % NOTIFY_INTERVAL === 0) {
notify()
stats.lastNotification = new Date()
}
}
}
main()

module.exports = async (req, res) => {
const count = stars || (await getStars())

return count
}
7 changes: 7 additions & 0 deletions kool.deploy.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM kooldev/node

COPY . /app

RUN yarn install

CMD [ "yarn", "start" ]
3 changes: 3 additions & 0 deletions kool.deploy.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
REPO=kool-dev/kool
SLACK_CHANNEL=kool
SLACK_URL=
11 changes: 11 additions & 0 deletions kool.deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Here we can override service configurations made at kool.services.yml
# for deployments only.
services:
watcher:
# Appications require a special image built for deployment
build: kool.deploy.dockerfile

# Environment refers to a file to be used for deployment only and will make those variables
# available for the deployed application. There are also some special keys kool deploy will
# override in this file upon deployment, i.e the deployed database host.
environment: kool.deploy.env
12 changes: 12 additions & 0 deletions kool.services.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: '3.8'
services:
watcher:
image: kooldev/node
command: [ 'yarn', 'start' ]
environment:
REPO: "${REPO:-kool-dev/kool}"
SLACK_URL: "${SLACK_URL}"
SLACK_CHANNEL: "${SLACK_CHANNEL}"
CHECK_INTERVAL: "${CHECK_INTERVAL}"
volumes:
- ./:/app
2 changes: 2 additions & 0 deletions kool.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
scripts:
yarn: kool docker kooldev/node yarn
18 changes: 13 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
},
"dependencies": {
"got": "^8.0.0",
"micro": "^9.0.2",
"ms": "^2.0.0",
"micro": "9.3.4",
"ms": "2.1.2",
"then-sleep": "^1.0.1"
},
"devDependencies": {
Expand All @@ -21,8 +21,13 @@
"prettier": "^1.8.2"
},
"eslintConfig": {
"plugins": ["prettier"],
"extends": ["eslint:recommended", "prettier"],
"plugins": [
"prettier"
],
"extends": [
"eslint:recommended",
"prettier"
],
"parserOptions": {
"ecmaVersion": 8,
"sourceType": "module",
Expand All @@ -37,7 +42,10 @@
"node": true
},
"rules": {
"func-names": ["error", "as-needed"],
"func-names": [
"error",
"as-needed"
],
"no-shadow": "error",
"no-extra-semi": 0,
"prettier/prettier": [
Expand Down
21 changes: 16 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ are-we-there-yet@~1.1.2:
delegates "^1.0.0"
readable-stream "^2.0.6"

arg@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.0.tgz#583c518199419e0037abb74062c37f8519e575f0"
integrity sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==

argparse@^1.0.7:
version "1.0.9"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86"
Expand Down Expand Up @@ -1382,13 +1387,14 @@ micro@9.0.1:
mri "1.1.0"
raw-body "2.3.2"

micro@^9.0.2:
version "9.0.2"
resolved "https://registry.yarnpkg.com/micro/-/micro-9.0.2.tgz#fa51f12d09fa29bdf9767d6eac43414ae3fb6068"
micro@9.3.4:
version "9.3.4"
resolved "https://registry.yarnpkg.com/micro/-/micro-9.3.4.tgz#745a494e53c8916f64fb6a729f8cbf2a506b35ad"
integrity sha512-smz9naZwTG7qaFnEZ2vn248YZq9XR+XoOH3auieZbkhDL4xLOxiE+KqG8qqnBeKfXA9c1uEFGCxPN1D+nT6N7w==
dependencies:
arg "4.1.0"
content-type "1.0.4"
is-stream "1.1.0"
mri "1.1.0"
raw-body "2.3.2"

micromatch@^2.1.5:
Expand Down Expand Up @@ -1451,10 +1457,15 @@ mri@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.0.tgz#5c0a3f29c8ccffbbb1ec941dcec09d71fa32f36a"

ms@2.0.0, ms@^2.0.0:
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"

ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==

mute-stream@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
Expand Down