Compare commits

...

633 Commits

Author SHA1 Message Date
renovate[bot] e952c65fda
build(deps): lock file maintenance (#2017)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-09 09:34:02 -07:00
renovate[bot] d92f72bc35
build(deps): lock file maintenance (#2015)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-07 00:20:56 -04:00
renovate[bot] ef7b9df5e4
fix(deps): update dependencies pino to v9, pino-http to v10 (#2007) 2024-05-07 04:20:10 +00:00
Gregor Martynus 93007b63c4
fix(probot): passes logger to webhooks (#2011) 2024-04-30 20:35:38 -04:00
renovate[bot] dbeda67e4a
chore(deps): update dependency sonic-boom to v4 (#2008)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-30 15:19:37 -04:00
renovate[bot] de39bb672e
build(deps): lock file maintenance (#2009)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-30 08:31:27 -07:00
renovate[bot] 6c55f0a96f
chore(deps): update dependency supertest to v7
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-25 10:08:01 -07:00
renovate[bot] 5ef04a10a9
build(deps): lock file maintenance (#2005)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-23 16:00:50 -07:00
renovate[bot] 55f56032e0
build(deps): lock file maintenance (#2002)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-16 08:08:37 -07:00
Zxilly 26665e66fe
docs: add necessary env suggestion for vercel (#2000)
Co-authored-by: Gregor Martynus <39992+gr2m@users.noreply.github.com>
2024-04-10 20:36:21 +00:00
renovate[bot] 1f21a63c7d
build(deps): lock file maintenance (#1998)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-09 09:53:22 -07:00
Aaron Dewes 3f2032077b
feat(security): Configure provenance (#1997)
This adds a cryptographic signature to NPM uploads to show that a upload was done by GitHub actions and not somewhere else.
Such a feature should help detect malicious uploads to NPM.
This feature could be extended to other repositories too.
2024-04-03 15:29:31 +02:00
renovate[bot] 8e9dd47e69
build(deps): lock file maintenance (#1996)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-02 09:57:56 -07:00
renovate[bot] e64f48519f
chore(deps): update dependency tsd to ^0.31.0
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-30 12:02:19 -07:00
renovate[bot] b1d3ac3c2e
fix(deps): update dependency express to v4.19.2 [security]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-26 12:59:16 -07:00
renovate[bot] 3c655e8292
build(deps): lock file maintenance
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-26 12:59:11 -07:00
dependabot[bot] 052a8c63a1
build(deps): bump express from 4.18.3 to 4.19.2
Bumps [express](https://github.com/expressjs/express) from 4.18.3 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.3...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-26 12:59:06 -07:00
Valentin D. Pinkman 52de532918
fix(deps): move bottleneck to dependencies (#1992)
fix(bottleneck): move bottleneck to dependency as it is directly imported in get-octokit-throttle-options.ts
2024-03-26 16:11:17 +00:00
renovate[bot] 2549886e3c
build(deps): lock file maintenance (#1988)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-19 08:56:04 +01:00
Harry Dobrev d1952641f2
feat(run): allow passing `Octokit` and `log` (#1984) 2024-03-12 10:00:08 -07:00
renovate[bot] c62652e368
build(deps): lock file maintenance (#1986)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-12 08:30:54 -07:00
Aaron Dewes 7583bdf293
docs: Make examples ESM (#1957)
* docs: Make examples ESM

* Revert accidential change
2024-03-08 21:42:11 +01:00
renovate[bot] b93e73aab6
build(deps): lock file maintenance (#1983)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-05 04:43:35 +01:00
renovate[bot] 8ade04032f
build(deps): lock file maintenance (#1981)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-27 08:20:44 -08:00
renovate[bot] 2d10214781
build(deps): lock file maintenance (#1978)
* build(deps): lock file maintenance

* style: prettier

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Gregor Martynus <39992+gr2m@users.noreply.github.com>
2024-02-20 08:39:18 +01:00
renovate[bot] eb39500dd7
chore(deps): update dependency @octokit/tsconfig to v3 (#1976)
* chore(deps): update dependency @octokit/tsconfig to v3

* build: deduplicate options with `@octokit/tsconfig`

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: wolfy1339 <4595477+wolfy1339@users.noreply.github.com>
2024-02-17 19:40:03 +00:00
renovate[bot] eaa2d7cc25
build(deps): lock file maintenance
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-12 21:08:23 -08:00
renovate[bot] 2c12d3dd93
build(deps): lock file maintenance (#1967)
* build(deps): lock file maintenance

* style: prettier

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Gregor Martynus <39992+gr2m@users.noreply.github.com>
2024-02-06 18:01:07 +00:00
renovate[bot] 737835fb8a
fix(deps): update dependency commander to v12
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-03 11:07:21 -08:00
Aras Abbasi 1eb03896e9
chore: apply linting on tsconfig.json (#1963) 2024-01-30 09:07:44 +01:00
renovate[bot] f7e4588b16
build(deps): lock file maintenance (#1962)
* build(deps): lock file maintenance

* exclude tsconfig.json from linting

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: uzlopak <aras.abbasi@googlemail.com>
2024-01-30 05:12:43 +00:00
Aaron Dewes f97fc64dac
test: Ensure we can run both ESM and CJS (#1956) 2024-01-28 17:29:34 +01:00
renovate[bot] 5d95b7386e
fix(deps): update dependency pino-http to v9 (#1953)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-26 06:18:37 +01:00
renovate[bot] 380d24dfc5
ci(action): update actions/dependency-review-action action to v4 (#1951)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-26 05:45:28 +01:00
renovate[bot] c065c15483
ci(action): update github/codeql-action action to v3 (#1952)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-26 05:44:53 +01:00
Aras Abbasi 39ced7e3ba
chore: repair typedoc (#1950) 2024-01-25 19:18:42 -05:00
renovate[bot] 33776dd2ad
chore(deps): update dependency semantic-release to v23 (#1947)
* chore(deps): update dependency semantic-release to v23

* use explicitly node 20

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: uzlopak <aras.abbasi@googlemail.com>
2024-01-25 22:18:53 +00:00
dependabot[bot] 8a403e8e50
build(deps-dev): bump vite from 5.0.2 to 5.0.12 (#1949)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.0.2 to 5.0.12.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.0.12/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.0.12/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-25 23:12:36 +01:00
renovate[bot] d7ae857632
chore(deps): update vitest monorepo to v1 (major) (#1948)
* chore(deps): update vitest monorepo to v1

* update snapshots

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: uzlopak <aras.abbasi@googlemail.com>
2024-01-25 22:07:37 +00:00
renovate[bot] 6ed90dac20
chore(deps): update dependency @types/node to v20 (#1875)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-25 22:54:37 +01:00
renovate[bot] 6bca1bdb79
chore(deps): update dependency tsd to ^0.30.0 (#1945)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-25 22:51:44 +01:00
renovate[bot] cd0c4a7bfa
chore(deps): update dependency @types/supertest to v6 (#1946)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-25 22:51:20 +01:00
wolfy1339 948a1b7147
feat: v13 (#1874)
BREAKING CHANGE: Drop support for NodeJS < 18
BREAKING CHANGE: replace node-fetch with the Fetch API
BREAKING CHANGE: default webhookPath is now /api/github/webhooks
BREAKING CHANGE: probot receive now only supports payloads in JSON format, previously also (unintionally) allowed JS.

closes #1643


---------

Co-authored-by: Aras Abbasi <aras.abbasi@googlemail.com>
Co-authored-by: Alexander Fortin <shaftoe@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Aaron Dewes <aaron.dewes@protonmail.com>
Co-authored-by: Gregor Martynus <39992+gr2m@users.noreply.github.com>
Co-authored-by: Aaron Dewes <aaron@runcitadel.space>
Co-authored-by: prettier-toc-me[bot] <56236715+prettier-toc-me[bot]@users.noreply.github.com>
Co-authored-by: Yaseen <9275716+ynx0@users.noreply.github.com>
2024-01-25 16:03:28 -05:00
Gregor Martynus 02d81f886c
fix(deps): `@octokit/webhooks` security update (#1911) 2023-11-15 10:34:01 -08:00
David Brodski fc6886d3b3
fix: Fix async main function type (#1672)
This should better reflect the already allowed async function as main
2023-11-14 10:02:57 +01:00
Sangwon Kim 76f3376070
docs: Fix 404 links (#1678)
Co-authored-by: scon.io <iam@scon.io>
Co-authored-by: Aaron Dewes <aaron.dewes@protonmail.com>
2023-11-14 09:57:55 +01:00
Aaron Dewes c1f0767427
docs: Fix previous/next button (#1898) 2023-11-14 07:41:04 +01:00
Victor Scaiceanu 7b09369f4b
fix(typescript): simplify ProbotWebhooks object (#1833) 2023-06-14 13:48:11 -07:00
Shingo Sato 6c5840d2df
feat(server): add logging options (#1645) 2023-01-16 16:01:34 +01:00
ElisarEisenbach 6e3070f978
docs: fix link (#1774) 2023-01-09 17:11:24 -08:00
wolfy1339 229a01c7bf
fix(typescript): add missing import (#1783) 2023-01-09 17:09:25 -08:00
Gregor Martynus 776f6a782f build(typescript): make TS happy 2023-01-09 17:02:58 -08:00
Gregor Martynus 85bd6e5d55 build(package): lock file 2023-01-07 17:01:56 -08:00
Gregor Martynus 99c7b129cd build(release.yml): set node-version to lts/* 2023-01-07 16:40:51 -08:00
dependabot[bot] ff524af791
build(deps): bump json5 from 2.2.0 to 2.2.3
Bumps [json5](https://github.com/json5/json5) from 2.2.0 to 2.2.3.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v2.2.0...v2.2.3)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-07 15:39:21 -08:00
dependabot[bot] a03417e6a5
build(deps): bump decode-uri-component from 0.2.0 to 0.2.2
Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
- [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
- [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2)

---
updated-dependencies:
- dependency-name: decode-uri-component
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-04 19:38:20 -08:00
Oscar Dominguez f89d27175f
build(deps): upgrade @octokit/types to v8 (#1763) 2022-11-23 15:20:16 -08:00
Tomer Figenblat fac5b43584
docs: added an example reference for a bot tested with mocha and sinon (#1744)
Signed-off-by: Tomer Figenblat <tomer.figenblat@gmail.com>
2022-09-11 14:18:36 -07:00
renovate[bot] 4d6ee86700
chore(deps): update dependency @types/node to v16
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-05 23:39:25 -07:00
renovate[bot] 75a82ad2c5
chore(deps): update dependency @octokit/webhooks-methods to v3
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-05 22:28:55 -07:00
renovate[bot] 0a863acb74
chore(deps): update dependency tsd to ^0.23.0 (#1729)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-05 16:50:25 -07:00
Gregor Martynus 0fcf5ded94 build: renovate setup 2022-09-05 14:35:24 -07:00
Gregor Martynus d668f24c00 build: remove renovate setup from package.json 2022-09-05 14:15:57 -07:00
Gregor Martynus 5be04fd99e build: renovate setup 2022-09-05 14:03:25 -07:00
Gregor Martynus f81a134dd2 build: renovate setup 2022-09-05 12:56:00 -07:00
Gregor Martynus 98f4b302c9 build: revert 3c6dca6c1d 2022-09-05 12:45:08 -07:00
Karan Shah eff5553253
fix: Make `probot receive` support complex Probot apps (#1714)
The command `probot receive` fails to work when:

1. The Probot appFn is asynchronous
2. The Probot appFn requires `getRouter`

The command `probot run` works fine though with the same app.

This attempts to add support for these sort of Probot apps when calling `probot receive`.
2022-09-05 12:40:05 -07:00
Gregor Martynus 78cb0197a6 style: prettier 2022-08-31 12:23:39 -07:00
Gregor Martynus 3c6dca6c1d build: renovate setup 2022-08-31 11:11:05 -07:00
wgillaspy 68c9b91b89
fix(receive): `--base-url` option and `GHE_HOST` (#1719) 2022-08-29 19:35:11 +00:00
Oscar Dominguez be45120fab
fix(deps): upgrade octokit/types to v7.1.1 (#1724) 2022-08-29 12:09:09 -07:00
borgmanJeremy d5e22e3979
docs(deployment): Update Heroku branch name (#1701)
Update deployment.md

Heroku default deployment branch is now named "main" not "master"
2022-07-08 09:56:41 -07:00
João Paulo Silva de Souza 05297fbd50
fix: remove update-notifier (#1706)
it's better to catch outdated dependencies before deployment, with tools such as dependabot, instead of only finding about it when you're about to run the application

the motivation for having update-notifier isn't strong as per https://github.com/probot/probot/issues/1102#issuecomment-570598406 and it pulls in needless dependencies as showcased in https://github.com/probot/probot/issues/1703, therefore it's better to remove it
2022-06-30 13:04:41 -07:00
dependabot[bot] 529197ed60
build(deps-dev): bump got from 11.8.2 to 11.8.5
Bumps [got](https://github.com/sindresorhus/got) from 11.8.2 to 11.8.5.
- [Release notes](https://github.com/sindresorhus/got/releases)
- [Commits](https://github.com/sindresorhus/got/compare/v11.8.2...v11.8.5)

---
updated-dependencies:
- dependency-name: got
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-22 10:09:56 -07:00
dependabot[bot] 3d30acc854
build(deps-dev): bump semantic-release from 17.4.7 to 19.0.3
Bumps [semantic-release](https://github.com/semantic-release/semantic-release) from 17.4.7 to 19.0.3.
- [Release notes](https://github.com/semantic-release/semantic-release/releases)
- [Commits](https://github.com/semantic-release/semantic-release/compare/v17.4.7...v19.0.3)

---
updated-dependencies:
- dependency-name: semantic-release
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-09 20:12:16 -07:00
dependabot[bot] 4b1ccf860b
build(deps): bump semver-regex from 3.1.3 to 3.1.4
Bumps [semver-regex](https://github.com/sindresorhus/semver-regex) from 3.1.3 to 3.1.4.
- [Release notes](https://github.com/sindresorhus/semver-regex/releases)
- [Commits](https://github.com/sindresorhus/semver-regex/commits/v3.1.4)

---
updated-dependencies:
- dependency-name: semver-regex
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-04 13:36:12 -07:00
David Brodski 646b6a9276
fix: logging of first octokit instance is not set (#1676) - thanks @kammerjaeger @markjm 2022-06-01 16:41:57 -07:00
dependabot[bot] 7fd06d6975
fix(deps): bump eventsource from 1.1.0 to 2.0.2
Bumps [eventsource](https://github.com/EventSource/eventsource) from 1.1.0 to 2.0.2.
- [Release notes](https://github.com/EventSource/eventsource/releases)
- [Changelog](https://github.com/EventSource/eventsource/blob/master/HISTORY.md)
- [Commits](https://github.com/EventSource/eventsource/compare/v1.1.0...v2.0.2)

---
updated-dependencies:
- dependency-name: eventsource
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-25 16:58:30 -07:00
Nick Partridge 559f4374d4
docs: fix localhost link protocol (#1691) 2022-05-23 15:24:38 -07:00
Tomer Figenblat 3a7bbb2e1a
docs: added an example of an aws deployment without the serverless framework (#1683)
Signed-off-by: Tomer Figenblat <tomer.figenblat@gmail.com>
2022-05-19 13:32:58 -07:00
Alex Tymchuk 12d9b05d4e
docs: wrong log call in `onError` method (#1681) 2022-04-26 13:43:36 -07:00
dependabot[bot] 1a73ff40a6
build(deps): bump minimist from 1.2.5 to 1.2.6
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-14 07:45:58 -07:00
Aadil Siddiqui 420f88612d
fix: Replaced hbs with express-handlebars (#1659)
Addresses https://securitylab.github.com/advisories/GHSL-2021-020-pillarjs-hbs/
2022-03-08 07:50:55 -08:00
dependabot[bot] 07fe1a709b
build(deps): bump url-parse from 1.5.7 to 1.5.10
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.7 to 1.5.10.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.7...1.5.10)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-28 09:08:36 -08:00
dependabot[bot] ce9c3d13fb
build(deps): bump node-fetch from 2.6.1 to 2.6.7
Bumps [node-fetch](https://github.com/node-fetch/node-fetch) from 2.6.1 to 2.6.7.
- [Release notes](https://github.com/node-fetch/node-fetch/releases)
- [Commits](https://github.com/node-fetch/node-fetch/compare/v2.6.1...v2.6.7)

---
updated-dependencies:
- dependency-name: node-fetch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-17 13:12:53 -08:00
dependabot[bot] 9cf9be235f
build(deps): bump url-parse from 1.5.3 to 1.5.7
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.3 to 1.5.7.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.3...1.5.7)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-17 12:08:12 -08:00
Zane c34f64d2f4
docs: fix 404 links for classes and rel links (#1649)
Co-authored-by: Aaron Dewes <aaron.dewes@protonmail.com>
2022-02-17 10:05:57 -08:00
Allen Hernandez 5d232e2d86
docs(webhooks): `onAny` code sample fix (#1648) 2022-02-10 13:28:11 -08:00
Joseph Petersen dd9f5ae98e
fix(security): bump minimal version of `hbs` (#1638) 2022-01-19 07:43:42 -08:00
Joseph Petersen a85c7a6307
build(package): bump package and package-lock (#1636) 2022-01-18 09:46:02 -08:00
chris-schra ec92db2c0c
docs(development): fix `ProbotOctokit` usage example (#1637) 2022-01-18 17:45:40 +00:00
Yadhav Jayaraman 992b480073
feat: customize account name for manifest creation flow using `GH_ORG` environment variable (#1606) 2022-01-10 16:34:23 -08:00
Gregor Martynus c83edcbbfc style: prettier 2022-01-03 12:21:15 -08:00
Jan Macku 073f08793d
fix(types): export `ApplicationFunction` (#1631)
Related to: https://github.com/probot/adapter-github-actions/pull/35
2022-01-03 12:17:58 -08:00
rethab bb068d9ae2
fix: log json in production (#1598)
Co-authored-by: Gregor Martynus <39992+gr2m@users.noreply.github.com>
2021-12-08 10:33:37 -08:00
Gregor Martynus 25a4eca970 build(package): lock file 2021-12-06 10:11:44 -08:00
David Brodski 02a57d1bd2
refactor: remove fallback to `.owner.name` when `.owner.login` is not set. `.owner.login` is always set (#1623) 2021-11-29 16:30:21 -08:00
David Brodski 638a3b2255
fix(typescript): add types for `context.{repo,issue,pullRequest}` (#1622) 2021-11-29 12:25:31 -08:00
Thomas Knickman 718025d161
docs(logging): typo (#1601) 2021-10-15 16:20:16 +00:00
dependabot[bot] 2641ab5b60
build(deps): bump semver-regex from 3.1.2 to 3.1.3
Bumps [semver-regex](https://github.com/sindresorhus/semver-regex) from 3.1.2 to 3.1.3.
- [Release notes](https://github.com/sindresorhus/semver-regex/releases)
- [Commits](https://github.com/sindresorhus/semver-regex/commits)

---
updated-dependencies:
- dependency-name: semver-regex
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-21 15:13:52 -07:00
dependabot[bot] 33d5fac956
build(deps): bump tmpl from 1.0.4 to 1.0.5
Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/daaku/nodejs-tmpl/releases)
- [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5)

---
updated-dependencies:
- dependency-name: tmpl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-21 15:13:43 -07:00
Gregor Martynus 0a170e8e8b build(test.yml): set node-version to 16 2021-09-17 16:58:28 -07:00
Gregor Martynus 279af98d77 build(release.yml): set node-version to 16 2021-09-17 16:58:26 -07:00
Gregor Martynus 6b7e684235 build(docs.yml): set node-version to 16 2021-09-17 16:58:25 -07:00
dependabot[bot] d6b0d4ebbd
build(deps): bump url-parse from 1.5.1 to 1.5.3
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.1 to 1.5.3.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.1...1.5.3)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-01 12:46:30 -07:00
rethab f02c5498fd
fix: update ioredis to non-vulnerable version (#1589)
the previously used version of ioredis was vulnerable to a prototype pollution attack: https://github.com/luin/ioredis/issues/1267
2021-09-01 19:35:23 +00:00
Maximous Black 624b934155
docs: add `webhooksPath` option for `createNodeMiddleware` (#1588) 2021-08-31 18:31:23 +00:00
Salih Özdemir 803fa2f00d
docs: Minor fixes (#1582) 2021-08-15 12:33:27 -07:00
Lisa Dean 41f2bb1bbb
docs: fix broken image link in development doc (#1578) 2021-07-15 10:33:00 -07:00
Gregor Martynus bdbb396581
ci(docs): do not update docs when tags are created (#1575)
close #1327
2021-07-09 15:40:49 -07:00
Travis Northcutt ef70c58148
docs: Correct wrong link to `Developing an App` page (#1572)
Co-authored-by: Aaron Dewes <aaron.dewes@web.de>
2021-07-08 11:54:46 -07:00
Oscar Dominguez f58a88e1b2
ci(workflow): add 'npm' cache for actions/setup-node in .github/workflows (#1566) 2021-07-05 10:46:26 -07:00
Hidde Wieringa 758fb5fdb5
docs: fix port variable in Probot server configuration documentation (#1567)
Something else: the link at the bottom of https://probot.github.io/docs/configuration/ to edit the documentation also does not work.
2021-07-05 10:30:02 -07:00
Aaron Dewes 7a43e9e5d4
feat(website): 11ty 🚀 (#1516) 2021-07-02 17:51:45 -07:00
Gregor Martynus 4b3ae0eee5 feat: update `@octokit/webhooks` to v9 (#1559)
BREAKING CHANGE: remove '*' event
BREAKING CHANGE: app.webhooks.middleware has been removed in `@octokit/webhooks` v9
BREAKING CHANGE: removes the `webhookPath` option on `new Probot({})` for the webhooks middleware

Co-authored-by: wolfy1339 <webmaster@wolfy1339.com>
2021-06-24 12:00:09 -07:00
Alain Hélaïli be1711ee6d
docs(development): Need to restart once before receiving (#1442)
Co-authored-by: Gregor Martynus <39992+gr2m@users.noreply.github.com>
2021-06-21 22:14:12 +00:00
dependabot[bot] 9d12a1eb23
build(deps): bump normalize-url from 4.5.0 to 4.5.1
Bumps [normalize-url](https://github.com/sindresorhus/normalize-url) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/sindresorhus/normalize-url/releases)
- [Commits](https://github.com/sindresorhus/normalize-url/commits)

---
updated-dependencies:
- dependency-name: normalize-url
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-09 10:22:29 -07:00
dependabot[bot] 23e2af28c7
build(deps): bump trim-newlines from 3.0.0 to 3.0.1
Bumps [trim-newlines](https://github.com/sindresorhus/trim-newlines) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/sindresorhus/trim-newlines/releases)
- [Commits](https://github.com/sindresorhus/trim-newlines/commits)

---
updated-dependencies:
- dependency-name: trim-newlines
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-08 07:56:10 -07:00
Gregor Martynus 453ddd29bb
fix: support setting `baseUrl` on Octokit constructor instead of Probot constructor (#1552) 2021-06-07 11:38:32 -07:00
Jacopo Daeli 01c2006700
feat(logger): custom message key (#1546) 2021-06-01 20:23:40 +00:00
Gregor Martynus d8a3186154 style: prettier 2021-05-29 15:36:38 -07:00
David Brodski acd47a6ea0
fix: skip smee setup by setting `NO_SMEE_SETUP` to `"true"` (#1544) 2021-05-29 15:30:57 -07:00
dependabot[bot] fdd14486b5
build(deps): bump ws from 7.4.5 to 7.4.6
Bumps [ws](https://github.com/websockets/ws) from 7.4.5 to 7.4.6.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.4.5...7.4.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-29 14:20:06 -07:00
David Brodski 4d70d69c92
fix(setup): do not enter setup mode if `HOST` environment variable is set (#1538) 2021-05-28 12:17:02 -07:00
zuigon 6dec1fa5f0
docs: fix missing backtick (#1523)
Add missing backtick in redis code example
2021-05-07 13:17:33 -07:00
wolfy1339 474b7735bd
feat: deprecate usage of the "*" event name (#1518) 2021-04-28 22:17:10 -07:00
David Brodski 8cc15900ca
fix(run): await `server.load()` (#1517) 2021-04-27 12:55:37 -07:00
Aaron Dewes 72b0531d60
fix: Update to Contributor Covenant v2 (#1515) 2021-04-26 10:14:48 -07:00
Gregor Martynus e11b91e838
fix: add workaround for "appId option is required" when in setup mode (#1513) 2021-04-23 12:12:29 -07:00
Gregor Martynus 9342caf1ab
fix: bump `@octokit/plugin-rest-endpoint-methods` to v5 (#1511) 2021-04-21 12:54:56 -07:00
Aaron Dewes ce96884930
feat: Add dark mode to builtin pages (#1509) 2021-04-20 10:48:24 -07:00
Ravgeet Dhillon 9eb3e973a8
docs(deployment): netlify functions (#1507) 2021-04-20 00:43:50 -07:00
Aaron Dewes 0e37427938
fix: update design of probot landing page (#1508) 2021-04-19 17:12:32 -07:00
Froilán Irizarry Rivera fa4a95c69b
Remove grave accents (`) from header texts (#1493)
The ` punctuation in header texts are breaking anchor links. The anchor links are being rendered with a
code prefix and suffix by "probot/probot.github.io", Eg. #use-coderuncode.

I've removed the ` punctuations from the header texts to fix this.
2021-04-05 19:12:36 -07:00
Gregor Martynus d70c4af748 build(package): fix description 2021-04-05 19:11:56 -07:00
Gregor Martynus c956d25c98 build(package): lock file 2021-04-05 19:11:34 -07:00
Jaylen Wimbish 5b79ce2f89
docs: update sentence to include the word use (#1500)
add the word "use" to the sentence in the section "use probot"
2021-04-05 18:56:52 -07:00
dependabot[bot] 57ad735d1c
build(deps): bump y18n from 4.0.0 to 4.0.1 (#1495)
Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-30 05:36:22 -07:00
Zack Koppert f053bf84ec
docs(deployment): GitHub Actions example repo link (#1484) 2021-03-10 16:30:28 -08:00
Gregor Martynus b2fe1f595f build(package): simplify repository fields
created using @octoherd and https://github.com/gr2m/octoherd-script-normalize-package-repository-field
2021-03-05 10:29:55 -08:00
Riyadh Al Nur 3f57c70185
docs: new example for AWS Lambda (#1479) 2021-03-03 10:27:14 -08:00
wolfy1339 9a24f9dcb5
feat: add `onAny` and `onError` methods from `@octokit/webhooks` (#1480)
Co-authored-by: Gregor Martynus <39992+gr2m@users.noreply.github.com>
2021-03-01 10:14:14 -08:00
Wissam Abirached 2c97c99ea8
docs(deployment): remove obsolete "serverless" section (#1478)
The _serverless_ section had a broken link since the specific _serverless_ page was removed [here](c612fc903d#). Furthermore, it feels redundant to keep this section now that we have a section about _serverless functions_ in this _deployment_ page.

* Update deployment.md

Co-authored-by: Gregor Martynus <39992+gr2m@users.noreply.github.com>
2021-02-18 13:24:09 -08:00
Gregor Martynus c0710a20b2 docs: update next links 2021-02-12 13:25:59 -08:00
Gregor Martynus c612fc903d
docs: update for serverless/function milestone and new v11 APIs (#1467) 2021-02-12 13:08:51 -08:00
wolfy1339 cd14dd472d
fix(deps): pin version of @octokit/webhooks (#1472)
The new types introduced in `@octokit/webhooks@7.22.0`, are incompatible with the current codebase. This will prevent users from getting anything newer than the pinned version in order to avoid type errors for end-users
2021-02-03 21:14:14 -08:00
Chris 5f1831b99a
fix: clarify error message in case of invalid app authentication (#1465) thanks @eXpire163 2021-01-20 14:09:40 -08:00
James Bradshaw a5779ff835
fix(TypeScript): fix description of `context.pullRequest` method (#1461) 2021-01-17 11:18:25 -08:00
Björn Brauer 2275698633
fix: correctly import (transpiled) app function for run and receive (#1457) thanks @ZauberNerd 2021-01-14 15:38:32 -08:00
Gregor Martynus 01bb6788f3
fix(typescript): remove `options.webhookProxy` from `Probot` constructor (#1459) 2021-01-14 15:32:09 -08:00
Armin Sebastian dc7dc324c2
docs: remove probot-messages extension (#1455) 2021-01-08 11:30:35 -08:00
dependabot[bot] 59f07c9bfc
build(deps): bump node-notifier from 8.0.0 to 8.0.1 (#1449)
Bumps [node-notifier](https://github.com/mikaelbr/node-notifier) from 8.0.0 to 8.0.1.
- [Release notes](https://github.com/mikaelbr/node-notifier/releases)
- [Changelog](https://github.com/mikaelbr/node-notifier/blob/v8.0.1/CHANGELOG.md)
- [Commits](https://github.com/mikaelbr/node-notifier/compare/v8.0.0...v8.0.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-21 12:40:36 -08:00
Gregor Martynus 335684509e
fix: import app functions transpiled from TypeScript (#1448) 2020-12-17 15:03:59 -08:00
Gregor Martynus 15e4b00702 feat: probot v11
BREAKING CHANGE: TBD
2020-12-15 13:54:49 -08:00
Gregor Martynus 42b043e588
feat: un-deprecate `(app) => {}`. Deprecate `({ app, getRouter }) => {}` in favor of `(app, { getRouter }) => {}` (#1441)
see discussion at https://github.com/probot/probot/issues/1286#issuecomment-744094299
2020-12-15 11:04:32 -08:00
Oscar Romero 41820c3358
docs(webhooks): typo in usage example (#1445) thanks @homeles 2020-12-14 14:56:45 -08:00
dependabot[bot] a028c3d793
build(deps): bump highlight.js from 10.3.1 to 10.4.1 (#1429)
Bumps [highlight.js](https://github.com/highlightjs/highlight.js) from 10.3.1 to 10.4.1.
- [Release notes](https://github.com/highlightjs/highlight.js/releases)
- [Changelog](https://github.com/highlightjs/highlight.js/blob/master/CHANGES.md)
- [Commits](https://github.com/highlightjs/highlight.js/compare/10.3.1...10.4.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-08 12:19:53 -08:00
Gregor Martynus 3aa0d5fd53
docs: log stubbing (#1355)
resolves #1273
2020-12-08 11:58:27 -08:00
Gregor Martynus f6a7970292
refactor: `webhooks.on("*")` is now `webhooks.onAny()` (#1401) 2020-12-07 21:41:58 -08:00
Gregor Martynus 60b702bd05 fix: load app function only once when using createNodeMiddleware (#1432) 2020-12-07 21:14:05 -08:00
Gregor Martynus 8c01e90ecc fix: `createProbot() without options 2020-12-07 21:14:05 -08:00
Gregor Martynus d315f0c2b6 feat: `createProbot()`, `createNodeMiddleware(app, { probot })`. Deprecates `getOptions`, `createNodeMiddleware(app, { Probot })` (#1431) 2020-12-07 21:14:05 -08:00
Gregor Martynus d1a66fc59e test: remove `test/fixtures/test-private-key.pem` from gitignore 2020-12-07 21:14:05 -08:00
Gregor Martynus 0343d5119e docs(Serverless deployment): add node middleware 2020-12-07 21:14:05 -08:00
Gregor Martynus bdbe94ee42 feat: createNodeMiddleware 2020-12-07 21:14:05 -08:00
Gregor Martynus a771d25303 feat: `getOptions({ env, defaults, overrides })` 2020-12-07 21:14:05 -08:00
Gregor Martynus 3d4b363261 feat: deprecate `probot.load()` 2020-12-07 21:14:05 -08:00
Gregor Martynus 7a8f268561 feat: deprecate `probot.start()` / `probot.stop()` / `probot.setup()` 2020-12-07 21:14:05 -08:00
Gregor Martynus a5c78b18e4 docs(testing): remove unused `id` option from code example 2020-12-07 21:14:05 -08:00
Gregor Martynus a94fdca9fd feat: `new Probot({ appId })`. Deprecates `new Probot({ id })` 2020-12-07 21:14:05 -08:00
Gregor Martynus 2ff5d21e2a feat: `Probot.version`, `Probot.defaults()` 2020-12-07 21:14:05 -08:00
Gregor Martynus e0d34c7f80 test: move server tests into `test/server.test.ts` 2020-12-07 21:14:05 -08:00
Gregor Martynus 3d90806c95 feat: `run(appFn, { env })` 2020-12-07 21:14:05 -08:00
Gregor Martynus 5c2d3c39d5 test: rewrite server tests to test new `Server` class 2020-12-07 21:14:05 -08:00
Gregor Martynus 9d767e191e fix(server): log error requests as `[METHOD] /[PATH] [STATUS] - [NUM]ms`, e.g `POST / 500 - 123ms` 2020-12-07 21:14:05 -08:00
Gregor Martynus 8a3599db82 feat: use new `Server` class when using `probot run` binary 2020-12-07 21:14:05 -08:00
Gregor Martynus 966ea5aabe test: rename `test/index.ts` to `test/probot.ts` 2020-12-07 21:14:05 -08:00
Gregor Martynus 6af93d0d3d test(run): move tests to `test/run.test.js` 2020-12-07 21:14:05 -08:00
Gregor Martynus d673f3b8fd test: import types 2020-12-07 21:14:05 -08:00
Gregor Martynus 5e8dcc6798 refactor: cleanup 2020-12-07 21:14:05 -08:00
Gregor Martynus f41af1827e build: typescript 4.1 2020-12-07 21:14:05 -08:00
Gregor Martynus e2c35c20d6
docs(README): remove pull reminders badge 2020-12-07 10:54:03 -08:00
Gregor Martynus d203219707 fix: `app.route()` with `(app) => {}` app function (#1430) 2020-12-06 15:04:35 -08:00
Gregor Martynus 0ec5f23098
fix: remove `GHE_HOST` deprecation message when using `probot run` cli (#1423)
fixes #1422
2020-11-22 11:04:36 -08:00
Gregor Martynus 49153b8dd4 fix: set default log level correctly to `"info"` 2020-11-19 21:14:42 -08:00
Gregor Martynus 7abbef776c feat: `new Probot({ baseUrl })`. Deprecates `GHE_HOST` / `GHE_PROTOCOL` when using with the `Probot` constructor 2020-11-19 16:36:25 -08:00
Gregor Martynus cea4318106 docs: `context.github` is now `context.octokit` 2020-11-19 16:36:25 -08:00
Gregor Martynus e67b78511f refactor: move GHE_HOST check into `bin/probot` 2020-11-19 16:36:25 -08:00
Gregor Martynus 1dbd999982 feat: deprecate `REDIS_URL` environment variable when using with the `Probot` constructor. Use `new Probot({ redisConfig: "redis://..." })` instead 2020-11-19 16:36:25 -08:00
Gregor Martynus 514c7645e8 feat: deprecate `LOG_FORMAT`, `LOG_LEVEL_IN_STRING`, `SENTRY_DSN` environment variables when using `Probot` constructor. Pass a custom log instance instead:
```
import { Probot } from "probot";
import pino from "pino";
import { getTransformStream } from "@probot/pino";

const transform = getTransformStream({
  level: "info",
  logFormat: "pretty",
  logLevelInString: false,
  sentryDsn: "https://1234abcd@sentry.io/12345
});

transform.pipe(pino.destination(1));
const log = pino(pinoOptions, transform);

new Probot({ log })
```
2020-11-19 16:36:25 -08:00
Gregor Martynus f50f68f485 refactor: `getLog(level)` -> `getLog({ level })` 2020-11-19 16:36:25 -08:00
Gregor Martynus 7c46218da8 feat: `new Probot({ logLevel })`. Deprecates `LOG_LEVEL` when using `Probot` constructor 2020-11-19 16:36:25 -08:00
Gregor Martynus 8ee18cdd4a refactor: move `Probot` code to `src/probot.ts` 2020-11-19 16:36:25 -08:00
Gregor Martynus f35b58a54c feat: `import { run } from "probot"`. Deprecates Probot.run() 2020-11-19 16:36:25 -08:00
Gregor Martynus 3f0974b4d2 refactor: move probot commands into `src/bin/` folder 2020-11-19 16:36:25 -08:00
Gregor Martynus 74f1f16bcd build(package): lock file 2020-11-19 16:36:25 -08:00
Gregor Martynus f829f9505a test: remove unused nock import 2020-11-19 16:36:25 -08:00
Gregor Martynus d96c65f09f refactor: remove obsolete TODO from deprecated Application class 2020-11-19 16:36:25 -08:00
Gregor Martynus dfc59fc20c feat: deprecate `INSTALLATION_TOKEN_TTL` 2020-11-19 16:36:25 -08:00
dependabot[bot] 39675d8c7e
build(deps-dev): bump semantic-release from 17.2.1 to 17.2.3
Bumps [semantic-release](https://github.com/semantic-release/semantic-release) from 17.2.1 to 17.2.3.
- [Release notes](https://github.com/semantic-release/semantic-release/releases)
- [Commits](https://github.com/semantic-release/semantic-release/compare/v17.2.1...v17.2.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-11-18 15:40:56 -08:00
Gregor Martynus 47d9f3a2e1
feat: use `@probot/get-private-key` (#1414)
part of #1309
2020-11-18 12:14:03 -08:00
Gregor Martynus 0527b98039
feat: `context.octokit`. Deprecates `context.github` (#1413) 2020-11-17 16:43:43 -08:00
Gregor Martynus 9351df4b5e
fix(deps): update `@octokit/core` to latest (#1412) 2020-11-16 10:04:36 -08:00
Gregor Martynus 2fcc0b5c5c docs(hello-world): `app` is now a `Probot` instance 2020-11-12 16:38:57 -08:00
Gregor Martynus 0e52e05319
feat: deprecate `{ Application }` export. Use `{ Probot }` instead, it has the same APIs now. (#1408) 2020-11-12 11:46:22 -08:00
Gregor Martynus 1812cfeb2f
feat: `probot.on()` / `probot.receive()` / `probot.auth()` (#1407) 2020-11-12 09:27:21 -08:00
Gregor Martynus de3adc1169
feat: `getRouter` argument for app function (`({ app, getRouter }) => {}`) (#1406) 2020-11-11 22:28:49 -08:00
Gregor Martynus 4bfae5a54f
feat: `(app) => {}` is now `({ app }) => {}` (#1405)
deprecates `(app) => {}`
2020-11-11 14:25:36 -08:00
Gregor Martynus ab6fcb1dc4 fix: stop using `.webhooks.on("*", handler)` in favor of `.webhooks.onAny(handler) 2020-11-11 11:39:22 -08:00
Gregor Martynus 133b332b0c test: `probot.load()` accepts array of app functions 2020-11-10 11:33:08 -08:00
Gregor Martynus 3a02fa3d19 refactor: remove support for undocumented/untested `"plugins"` array in `package.json` 2020-11-05 23:51:28 -08:00
Gregor Martynus 58b2e1ddfd refactor: remove private `probot.apps` property 2020-11-05 23:35:03 -08:00
Christophe Hurpeau 2b30518fbd
fix: correct payload type by omitting context keys (#1389) 2020-11-01 14:26:47 -08:00
Gregor Martynus 8ba3a8e8a4
feat: use `octokit-auth-probot` (#1392) 2020-10-28 12:24:47 -07:00
Jimgao 506a5ed08b
docs(webhooks): update links to GitHub docs (#1393) 2020-10-27 21:47:34 -07:00
AJ Schmidt a5b36b3344
fix: use `webhooks.onError()` instead of deprecated `webhooks.on("error", ...)` (#1390) 2020-10-26 12:02:16 -07:00
Gregor Martynus 41ee70cbf1 fix(typescript): TypeScript issues TS2305,TS2707,TS7006
fixes #1387
2020-10-22 10:00:39 -07:00
Ankeet Maini c7ead98f78
refactor: use `WebhookEvents` export instead of an internal one. (#1381) 2020-10-13 11:38:13 -07:00
Gregor Martynus 8ddac1f6bb style: prettier 2020-10-08 10:20:13 -07:00
Gregor Martynus 05abeef921
fix(types): set correct type for `context` passed to event handler (#1378)
follow up to https://github.com/probot/probot/pull/1374/files\#r501871740
2020-10-08 10:09:24 -07:00
Gregor Martynus 630d78e347
fix(typescript): adapt for latest `@octokit/webhooks` (#1374)
Co-authored-by: Ankeet Maini <ankeet.maini@gmail.com>
2020-10-08 09:36:13 -07:00
Gregor Martynus 9483546c74
fix: do not overwrite `options.throttle` passed to `{Octokit: ProbotOctokit.defaults(options)} (#1373) 2020-10-04 14:11:15 -07:00
Gregor Martynus f537204987
feat: deprecate `new Application({ throttleOptions })` (#1365) 2020-09-28 22:46:11 -07:00
Gregor Martynus c758898b8a build(package): lock file 2020-09-28 20:40:04 -07:00
Gregor Martynus a235671fe9
fix: use `@probot/octokit-plugin-config` for `context.config` (#1362)
* build(deps): `@probot/octokit-plugin-config`

* test(context.config): adapt http mocks for `@probot/octokit-plugin-config`

* fix: use `@probot/octokit-plugin-config`

* test(context.config): remove tests that test internal behavior of `@probot/octokit-plugin-config`
2020-09-28 16:15:59 -07:00
Gregor Martynus 89538b073e
test(context.config): rewrite to integration tests (#1363) 2020-09-28 15:28:59 -07:00
Gregor Martynus c8f7f7374d
docs(development): update for latest changes to `probot` and `create-probot-app` (#1356) 2020-09-21 16:09:16 -07:00
Max Baumann a03c610a49
feat: add restart message (#1353) 2020-09-19 11:17:05 -07:00
Alex dc4cd53198
fix: remove redundant v from Node.js version log string (#1352) 2020-09-18 12:45:52 -07:00
Max Baumann d907bbda0c
feat(setup): support registration using an existing GitHub app (#1345) 2020-09-18 09:17:51 -07:00
Max Baumann 31fdd619ac
feat: export WebhookPayloadWithRepository (#1348) 2020-09-18 09:13:35 -07:00
Alex 86b0408f13
feat: log Node.js version at startup (#1346)
Co-authored-by: Gregor Martynus <gregor@martynus.net>
2020-09-18 09:12:31 -07:00
Max Baumann 235e308e35
fix: use custom host in new welcome message (#1347) 2020-09-18 08:46:55 -07:00
Max Baumann 7c1b4880a0
ci: add CODECOV_TOKEN env variable (#1350) 2020-09-18 08:37:45 -07:00
Alex ed7a5134be
feat: add HOST/--host option (#1335) 2020-09-17 23:33:32 -07:00
Max Baumann 240e77ca3c
feat: increase readability of welcome message at setup (#1343) 2020-09-16 17:09:52 -07:00
dependabot[bot] b9904882f4
build(deps): bump node-fetch from 2.6.0 to 2.6.1 (#1339)
Bumps [node-fetch](https://github.com/bitinn/node-fetch) from 2.6.0 to 2.6.1.
- [Release notes](https://github.com/bitinn/node-fetch/releases)
- [Changelog](https://github.com/node-fetch/node-fetch/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/bitinn/node-fetch/compare/v2.6.0...v2.6.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-09-11 12:46:41 -07:00
Gregor Martynus 4cf7de9902
feat(sentry): set user ID to `installation.id` and username to repository owner login (when present) (#1337) 2020-09-07 12:55:21 -07:00
Frank Zhao e0760b0ef9
fix: remove redundant string in deprecation warning (#1336)
Signed-off-by: Frankzhaopku <syzhao1988@126.com>
2020-09-07 10:23:54 -07:00
Gregor Martynus 86c19733ca fix: prevent double new lines when using `LOG_FORMAT=json`
fixes https://github.com/probot/probot/issues/1334
2020-09-06 13:01:34 -07:00
Gregor Martynus d671d82642
fix: "Cannot find module `../lib/private-key`" error when running `probot receive` (#1332) 2020-09-03 09:58:31 -07:00
Gregor Martynus 1f4a038400 ci(docs): update docs when new tag was created 2020-09-01 16:00:41 -07:00
Gregor Martynus 890483da26 build: trigger docs build 2020-09-01 15:44:52 -07:00
Gregor Martynus 4c88da9b81 fix: trigger release to update docs on probot.github.com 2020-09-01 15:27:04 -07:00
Gregor Martynus f97c6ea619 build: trigger docs build 2020-09-01 14:18:00 -07:00
Gregor Martynus db03885d39 fix: `app.auth(installationId)` returns `octokit` instance with all required installation authentication settings (#1326) 2020-09-01 10:09:40 -07:00
Alex 5dcb8ae895 feat: log Probot version at startup (#1321) 2020-09-01 10:09:40 -07:00
Alex 8b07e385c2 build(package): remove broken `start` script from package.json (#1320) 2020-09-01 10:09:40 -07:00
Gregor Martynus ca07116bf1 docs(pagination): use correct endpoint method 2020-09-01 10:09:40 -07:00
Gregor Martynus c43910364f fix: throttleOption defaults and REDIS_URL configuration (#1313) 2020-09-01 10:09:40 -07:00
Gregor Martynus ad3216497c test(deprecation): `octokit.repos.createStatus()` 2020-09-01 10:09:40 -07:00
Gregor Martynus 072b8c26b5 v10.0.0
CHANGELOG: https://github.com/probot/probot/releases/tag/v10.0.0
2020-09-01 10:09:40 -07:00
Gregor Martynus e3f6cf583b
fix: revert "v9.14.0" (#1314)
This reverts commit ee65669677.

It was merged accidentally and incorrectly using squash & merge, which resulted in a 9.x feature relaese instead of tagging the the latest 10.x release as latest
2020-08-21 10:46:00 -07:00
Gregor Martynus ee65669677
feat: v10 (#1310) 2020-08-21 10:38:30 -07:00
Gregor Martynus 83b625b782 fix: revert v9.13.1 2020-08-20 12:39:57 -07:00
Gregor Martynus 4e132c5710 faulty release 2020-08-20 12:37:16 -07:00
Geoff Petrie 589f891feb
chore: Update README.md (#1284)
Just a quick PR to fix the links to GitHub Apps to the one that the current [link](https://developer.github.com/apps/) points to.
2020-07-27 13:52:00 -04:00
Gregor Martynus 2714ebd06c ci(release): support next, beta, and maintenance releases 2020-07-22 12:07:24 -07:00
Gregor Martynus ac86ffb01c
feat: `throttleOptions` for Probot constructor (#1272) 2020-07-21 15:36:57 -07:00
Gregor Martynus 1c31415759
feat: `GET /probot/stats` is deprecated and will be removed in v10 (#1268)
see https://github.com/probot/probot/issues/1267
2020-07-20 08:01:50 -07:00
dependabot[bot] 7d1abafd58
build(deps): bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-07-16 10:18:55 -07:00
Max Baumann 1445abe6f2
fix: handle "port in use" (#1260) 2020-07-12 11:55:35 -07:00
FoseFx 8f626590cb
fix(TypeScript): context.config() type (#1252) 2020-07-10 12:04:46 -07:00
dependabot[bot] 3f0d529d1d
build(deps): bump npm from 6.13.7 to 6.14.6
Bumps [npm](https://github.com/npm/cli) from 6.13.7 to 6.14.6.
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md)
- [Commits](https://github.com/npm/cli/compare/v6.13.7...v6.14.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-07-08 12:57:37 -07:00
Markus Wolf 48a5b089c9
fix: add runtime types as dependencies (#1241)
These typings are used and reexported by probot and therefore are
dependencies which need to be installed by users as well.

Relates to #759
2020-06-04 12:18:13 -07:00
greenkeeper[bot] f603499a22
build(deps): update @types/node to version 14.0.6 (#1238) 2020-05-31 16:21:32 -07:00
greenkeeper[bot] a1aa894d0c
Update ts-jest to the latest version 🚀 (#1239) 2020-05-30 15:56:29 -07:00
Gregor Martynus e2a87f84eb fix(package): npm audit 2020-05-28 14:09:26 -07:00
Tim Brust a95f0ef364
ci: updates docs push action by fetching tags & setting the correct tag (#1236) 2020-05-26 14:02:01 -07:00
Tim Brust 8a057d2b01
ci: fixes docs publishing by upgrading typescript dependency (#1235) 2020-05-26 11:49:22 -07:00
Tim Brust 69d5cf6737
docs: remove Vercel Now (#1234)
Sadly, probot-serverless-now a) no longer works and b) was archived today

See 5552cfa45e for more information
2020-05-26 10:29:09 -07:00
Shohei Ueda 948670faa5
ci: bump actions/checkout from v1 to v2 (#1230) 2020-05-22 11:41:56 -07:00
Josh Soref 3dc2e34877
docs(configuration): Remove periods (#1157) 2020-05-13 12:26:54 -07:00
Asif Kamran Malick 5100488ef3
docs: Correct URL (#1204)
Current hyperlink takes you to a non-existent page. 
Before Change : https://github.com/octokit/webhooks.js/blob/master/lib/webhook-names.json
After Change : https://github.com/octokit/webhooks.js/
2020-05-05 11:37:49 -07:00
Hidde Wieringa 3647867bc5
docs: Add documentation for including branch/fork context into configuration (#1188)
Co-authored-by: Gregor Martynus <gregor@martynus.net>
2020-04-22 10:26:25 -07:00
Shodipo Ayomide 87163def8e
docs: switch zeit now to Vercel Now for gridsome deployment (#1189)
Zeit just rebranded to Vercel and this PR changes the deployment guidelines to deployment for gridsome
2020-04-21 10:27:40 -07:00
Gregor Martynus b838b26786
ci: run codecov only on `ubuntu-latest` and node 12.x (#1185) 2020-04-19 14:36:06 -07:00
Ricki Hastings 2fdb253d85
fix: quotation marks in setup description (#1175) 2020-04-12 11:38:15 -07:00
Paul M. Rodriguez 78f61e03ac
docs: Update minimum Node version (#1174) 2020-04-02 12:58:22 -04:00
Jonathan Keslin 6aeeabceb5 fix(github): Octokit is optional 2020-03-25 09:49:35 -07:00
Jonathan Keslin 0bb1d19b8f
fix(github): provide default ProbotOctokit (#1169) 2020-03-23 14:09:37 -07:00
Jonathan Keslin 982e411360
feat: const { GitHubAPI } = require('probot') (#1165)
Co-authored-by: Gregor Martynus <gregor@martynus.net>
2020-03-20 16:54:54 -07:00
greenkeeper[bot] 15468b1b9e chore(package): update lockfile package-lock.json 2020-03-16 08:58:46 -07:00
greenkeeper[bot] 459404a8cd chore(package): update typedoc to version 0.17.0 2020-03-16 08:58:46 -07:00
greenkeeper[bot] 6e45ad7aa0 chore(package): update lockfile package-lock.json 2020-03-14 16:20:26 -07:00
greenkeeper[bot] 694f4fdd89 fix(package): update commander to version 5.0.0 2020-03-14 16:20:26 -07:00
dependabot[bot] a174158c71 build(deps): bump acorn from 6.4.0 to 6.4.1
Bumps [acorn](https://github.com/acornjs/acorn) from 6.4.0 to 6.4.1.
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/6.4.0...6.4.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-13 16:18:45 -07:00
Gregor Martynus 45a21fcc3f fix: Revert "feat: mention that app must be activated after install (#1155)"
This reverts commit 05c9240792.

The `[ ] active` checkbox is now set automatically
2020-03-12 16:03:08 -07:00
Carl Simon Adorf 05c9240792
feat: mention that app must be activated after install (#1155)
Recent change in the GitHub app installation workflow now 
requires that an app must be activated after installation. That
should be mentioned in the "success" page.

Co-authored-by: Gregor Martynus <gregor@martynus.net>
2020-03-11 11:56:33 -07:00
Justin Beckwith a3fc9182cd
fix: remove unused dependencies (#1151) 2020-03-09 12:52:03 -07:00
greenkeeper[bot] 787932b977
fix(package): update is-base64 to version 1.1.0 (#1148) 2020-03-03 20:24:03 -08:00
greenkeeper[bot] 75551d2288
build(package): update @types/uuid to version 7.0.0 (#1147) 2020-02-27 15:12:31 -08:00
greenkeeper[bot] c6551d0a89
fix(package): update uuid to version 7.0.0 (#1146) 2020-02-24 09:23:53 -08:00
greenkeeper[bot] 1a81280d82 chore(package): update lockfile package-lock.json 2020-02-21 11:32:27 -08:00
greenkeeper[bot] fbf6031ba3 fix(package): update cache-manager to version 3.0.0 2020-02-21 11:32:27 -08:00
greenkeeper[bot] 52b590a7e1 chore(package): update lockfile package-lock.json 2020-02-16 13:54:28 -08:00
greenkeeper[bot] d05c8c5f3c chore(package): update nock to version 12.0.0 2020-02-16 13:54:28 -08:00
Steven 81a9fad0ac docs: update ZEIT Now deployment instructions (#1130) 2020-02-09 17:22:09 -08:00
Gregor Martynus c5c8158d84 fix: trigger release to publish latest version to npm
Past two builds failed due to permission errors https://github.com/probot/probot/runs/424228885
2020-02-07 13:35:42 -08:00
Gregor Martynus 9dadedd7ff build(package): lock file 2020-02-06 16:20:48 -08:00
Maxim Devoir 9e2f381637 ci(docs): correctly set `REPO` 2020-02-03 15:53:59 -08:00
Gregor Martynus 22d8215a63 fix: adapt for latest @octokit/rest 2020-02-03 14:44:54 -08:00
Gregor Martynus 0490ea463b build(package): lock file 2020-02-03 14:44:54 -08:00
Gregor Martynus 6719615ee3 build(package): @octokit/rest 16.43.1 2020-02-03 14:44:54 -08:00
Maxim Devoir 44701f81b2
ci(docs): fix Git setup on docs action (#1129) 2020-02-03 11:45:45 -08:00
Maxim Devoir 8e8700d6bc
ci(docs): run script via default shell (#1124) 2020-02-03 01:52:57 -05:00
Gregor Martynus f5e8407486 build(package): lock file 2020-02-02 22:42:26 -08:00
Gregor Martynus db1d270cbd build(package): update jest to latest 2020-02-02 22:42:26 -08:00
greenkeeper[bot] 1be85410bd chore(package): update lockfile package-lock.json 2020-02-02 22:42:26 -08:00
greenkeeper[bot] c4dc7e129b chore(package): update @octokit/rest to version 16.43.0 2020-02-02 22:42:26 -08:00
Maxim Devoir 4a4a97df92 fix: disable webhook-event-check in common test environments 2020-02-02 22:29:13 -08:00
Maxim Devoir 1e6473c497 chore: use optional chaining 2020-02-02 22:29:13 -08:00
Zeke Sikelianos 37c878efe3 docs: use single quotes 2020-02-02 10:56:42 -08:00
greenkeeper[bot] 653868e74b chore(package): update lockfile package-lock.json 2020-02-02 10:56:42 -08:00
greenkeeper[bot] bb7cc86d81 chore(package): update standard-markdown to version 6.0.0 2020-02-02 10:56:42 -08:00
greenkeeper[bot] a0ea1de803 chore(package): update lockfile package-lock.json 2020-01-30 11:57:12 -08:00
greenkeeper[bot] b0af073bbb chore(package): update ts-jest to version 25.1.0 2020-01-30 11:57:12 -08:00
Gregor Martynus 2cf107949e build(package): lock file 2020-01-27 15:58:22 -08:00
greenkeeper[bot] 86af7614a4 chore(package): update @types/jest to version 25.1.0 2020-01-27 15:58:22 -08:00
greenkeeper[bot] 7140cac92c chore(package): update lockfile package-lock.json 2020-01-27 15:57:09 -08:00
greenkeeper[bot] 4a14d15496 chore(package): update @types/semver to version 7.1.0 2020-01-27 15:57:09 -08:00
greenkeeper[bot] de228a354f chore(package): update lockfile package-lock.json 2020-01-27 15:37:56 -08:00
greenkeeper[bot] 189d9c9521 chore(package): update semantic-release to version 17.0.0 2020-01-27 15:37:56 -08:00
Anders Richardsson 11e9de5b0a docs: change phrasing on how to get the private key (#1116) 2020-01-27 00:37:03 -05:00
Gregor Martynus c5687fff38 ci(docs): set env.OCTOKITBOT_PAT 2020-01-21 06:02:31 -08:00
Gregor Martynus a2e1358208 ci: run for Greenkeeper branches including scoped packages 2020-01-21 06:02:31 -08:00
Gregor Martynus 256bee273d ci(docs): use PAT 2020-01-21 06:02:31 -08:00
Gregor Martynus 837459bb99 ci: run for greenkeeper branches 2020-01-21 06:02:31 -08:00
Gregor Martynus ec2b353725 ci(docs): initial version 2020-01-21 06:02:31 -08:00
Gregor Martynus ee4c43e495 ci: update publish-docs script for GitHub Actions 2020-01-21 06:02:31 -08:00
Gregor Martynus 015c743664 ci: remove .travis.yml 2020-01-21 06:02:31 -08:00
Gregor Martynus 7bddf159b5 docs(README): replace Travis CI badge with GH Actions badge 2020-01-21 06:02:31 -08:00
Gregor Martynus 54a61a25b8 ci(release): run semantic-release via GitHub Actions 2020-01-21 06:02:31 -08:00
Julian Nadeau 85b33ff6bd Add support for GHE_PROTOCOL 2020-01-17 15:17:48 -08:00
Gregor Martynus 1a19bdd16c refactor: adapt for `@octokit/plugin-throttling` v3 2020-01-16 09:47:05 -08:00
greenkeeper[bot] bb07be821a chore(package): update lockfile package-lock.json 2020-01-16 09:33:46 -08:00
greenkeeper[bot] de0aa01a2c fix(package): update @octokit/plugin-throttling to version 3.0.0 2020-01-16 09:33:34 -08:00
Gregor Martynus 9da282d2a2 build(package): lock file 2020-01-16 09:32:31 -08:00
Gregor Martynus 419855b45d refactor: adapt for `@octokit/plugin-retry` v3 2020-01-16 09:10:50 -08:00
greenkeeper[bot] d006c1035e
chore(package): update lockfile package-lock.json 2020-01-16 05:01:04 +00:00
greenkeeper[bot] 0b53898946
fix(package): update @octokit/plugin-retry to version 3.0.1
Closes #1110
2020-01-16 05:01:00 +00:00
Marc Cornellà 35ae977a8d chore(package): update lockfile package-lock.json
Updates istanbul-reports to the latest version which doesn't use
handlebars 5.7.2, which was printing warnings on the console
due to https://github.com/facebook/jest/issues/9396.
2020-01-15 08:49:39 -08:00
Marc Cornellà 1c1f098249 chore(package): update lockfile package-lock.json 2020-01-15 08:49:39 -08:00
Marc Cornellà 9e14526180 chore(package): update hbs to version 4.1.0 2020-01-15 08:49:39 -08:00
greenkeeper[bot] 59af701f3e chore(package): update lockfile package-lock.json 2020-01-12 12:31:10 -08:00
greenkeeper[bot] 05d500c467 chore(package): update typedoc to version 0.16.0 2020-01-12 12:31:10 -08:00
greenkeeper[bot] bf5a28dda1 chore(package): update lockfile package-lock.json 2020-01-09 16:13:21 -08:00
greenkeeper[bot] 8254889675 chore(package): update semantic-release to version 16.0.0 2020-01-09 16:13:21 -08:00
jamesgeorge007 804f62be0e feat: configure update-notifier 2020-01-06 10:10:36 -08:00
Maxim Devoir 3a4e661519 chore: remove tag creation all-together 2020-01-05 10:25:46 -08:00
James George 4fdcde33a1 chore: minor code refactoring (#1103)
* refactor: minor tweak

* chore: revert
2020-01-05 08:31:45 -08:00
Maxim Devoir 30b33021d9 ci: publish docs (#1101)
* fix: use Bash for script

Using `/bin/sh` lead to multiple warnings that could result in errors in some POSIX environments. See [SC2039](https://github.com/koalaman/shellcheck/wiki/SC2039).

* In POSIX sh, [[ ]] is undefined.shellcheck(SC2039)
* In POSIX sh, string replacement is undefined.shellcheck(SC2039)
* Using /bin/sh leads to authentication errors for my CI | https://travis-ci.org/MaximDevoir/probot/builds/631990067

* fix: get latest tag for `VERSION`

Previous commit would retrieve an older tag
2020-01-02 14:30:31 -08:00
James George e0d3072751 fix(cli): fixup invalid args validation (#1099) 2020-01-02 15:56:57 +01:00
Maxim Devoir f90057e9b0 fix: Add `publish-docs` stage to Travis build (#1100) 2020-01-02 15:55:59 +01:00
Maxim Devoir 28a4d43185 fix: Limit webhook-event-check to known events in `/app` response (#1092)
Because `GET /app` does not include many events (e.g. `fork`, `installation`, etc.), we don't want to compare some events to the results of `GET /app`.

In these cases, webhook-event-check will silently ignore verifying whether the GitHub App is subscribed to the event.
2019-12-25 10:37:06 +01:00
jamesgeorge007 f368013729 feat: warn on passing in unknown commands 2019-12-25 10:36:04 +01:00
greenkeeper[bot] 5b86e1bb6a chore(package): update lockfile package-lock.json 2019-12-24 10:41:36 +01:00
greenkeeper[bot] 5c2070de51 chore(package): update @types/node to version 13.1.0 2019-12-24 10:41:36 +01:00
greenkeeper[bot] b8410d3dcf chore(package): update lockfile package-lock.json 2019-12-24 10:41:28 +01:00
greenkeeper[bot] b35379450b chore(package): update @types/resolve to version 1.14.0 2019-12-24 10:41:28 +01:00
Maxim Devoir d8aa4e05ca feat: Initial webhook-event-check feature (#1078) 2019-12-19 17:34:55 +01:00
Maxim Devoir 64254159c5 docs: update create-probot-app section (#1085) 2019-12-19 15:02:57 +01:00
greenkeeper[bot] 467eb67f18 Update semver to the latest version 🚀 (#1083)
* fix(package): update semver to version 7.0.0
* chore(package): update lockfile package-lock.json
2019-12-14 15:30:11 -05:00
Jason Etcovitch df27ed7180
chore(ci): Use GitHub Actions for CI (#1082)
* Add Actions YAML file

* Add macOS, windows

* Use the correct windows OS
2019-12-14 14:00:58 -05:00
Forbes Lindesay 95994a0927 docs: add example using Postgres and update MySQL (#1076)
* docs: add example using Postgres and update MySQL
* docs: update to pass linter
2019-12-13 15:01:51 -05:00
dependabot[bot] 0f777079a2 build(deps): bump npm from 6.12.1 to 6.13.4 (#1081)
Bumps [npm](https://github.com/npm/cli) from 6.12.1 to 6.13.4.
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md)
- [Commits](https://github.com/npm/cli/compare/v6.12.1...v6.13.4)

Signed-off-by: dependabot[bot] <support@github.com>
2019-12-13 14:24:26 -05:00
Gregor Martynus 61c05ea76e fix: update bunyan-format and remove earlier type assertion patch
trigger release for changes in https://github.com/probot/probot/pull/1073 /cc @MaximDevoir
2019-11-18 15:34:53 -08:00
Maxim Devoir 96c32db837 chore: remove type assertion 2019-11-18 15:22:38 -08:00
Maxim Devoir 48200a940c chore(package): update lockfile package-lock.json 2019-11-18 15:22:38 -08:00
Maxim Devoir c2f477c5b9 chore(package): update @types/bunyan-format to version 0.2.2 2019-11-18 15:22:38 -08:00
Maxim Devoir d35e3b730e fix: assert stream type 2019-11-17 16:27:25 -08:00
greenkeeper[bot] 24bdbb1f41 chore(package): update lockfile package-lock.json 2019-11-17 16:27:25 -08:00
greenkeeper[bot] 1ea4a750fb chore(package): update @types/bunyan-format to version 0.2.1 2019-11-17 16:27:25 -08:00
Karan Shah e9d1d34a59 docs: proper nock setup (#1067)
Nock should be setup properly so that net connect ONLY gets disabled before each test and then gets enabled and reset again AFTER each test. This is recommended by them here: https://github.com/nock/nock#resetting-netconnect

I bring this up because this really bit me as I used this example in the Probot docs for my Probot tests and when I was testing my express code in a complete separate test, when running `npm test` it would fail. But running the test by itself wouldn't.

This was because `nock.disableNetConnect()` was _bleeding_ onto other tests because it wasn't encapsulated properly.
2019-11-08 12:12:49 -08:00
John Bohannon 9ac0ee1ca9 Fix: bump cache-manager version to allow Probot compilation by @zeit/ncc 2019-11-06 10:13:56 -08:00
Gregor Martynus af2be40db4 fix: adapt for @octokit/plugin-enterprise-compatibility@^1.2.1 2019-11-04 22:00:03 -08:00
Gregor Martynus 7768a970f9 build(package): lock file 2019-11-04 22:00:03 -08:00
Gregor Martynus 677e8f58d1 build(package): downgrade semantic-relaese to stable version 2019-11-04 22:00:03 -08:00
Gregor Martynus a0e90f4d3b build(package): @octokit/plugin-enterprise-compatibility@^1.2.1 2019-11-04 22:00:03 -08:00
greenkeeper[bot] 39c0d83f72 chore(package): update lockfile package-lock.json 2019-11-03 10:26:31 -08:00
greenkeeper[bot] e22571703b chore(package): update tslint-config-standard to version 9.0.0 2019-11-03 10:26:31 -08:00
Maxim Devoir 9cf0f98a82 fix: Use return value of `this` instead of a class (#1060)
Updates return type for changes made in @types/express-serve-static-core by https://github.com/DefinitelyTyped/DefinitelyTyped/pull/39025
2019-11-01 19:27:26 -07:00
greenkeeper[bot] 1d87b2dc32 chore(package): update lockfile package-lock.json 2019-11-01 10:24:19 -07:00
greenkeeper[bot] 15e28d59a5 fix(package): update commander to version 4.0.0 2019-11-01 10:24:19 -07:00
Michelle Tilley 159c215219 🎨 Refactor GitHub test 2019-10-30 15:26:24 -07:00
Michelle Tilley b9bb41280a fix: Correctly retry requests after hitting rate or abuse limits 2019-10-30 15:26:24 -07:00
Michelle Tilley edc9f8059a Add tests for default retry and throttle options 2019-10-30 15:26:24 -07:00
Michelle Tilley 30532782cc fix(typescript): add done callback to Paginate interface 2019-10-30 15:24:14 -07:00
greenkeeper[bot] 56415e919e chore(package): update lockfile package-lock.json 2019-10-27 10:38:06 -07:00
greenkeeper[bot] 79f8809e37 chore(package): update @types/dotenv to version 8.2.0 2019-10-27 10:38:06 -07:00
Eric Stern ca3d2f8a58 feat: throw errors during startup if NODE_ENV is production and the application is not in a usable state (#1046) 2019-10-23 12:21:53 -07:00
greenkeeper[bot] 65382de391 chore(package): Update dotenv to the latest version 🚀 (#1040)
* fix(package): update dotenv to version 8.2.0

* chore(package): update lockfile package-lock.json
2019-10-15 22:10:33 -04:00
Maxim Devoir e2599d64dd docs: remove GitHub Actions deployment documentation (#1038) 2019-10-12 14:59:59 -07:00
Justin Beckwith ed1cdf7aba fix: update insecure dependencies 2019-10-12 10:46:21 -07:00
Maxim Devoir c5f7a32e12 fix: read environment variables before initiating logging (#1036) 2019-10-09 15:22:15 -07:00
greenkeeper[bot] 956e260614 chore(package): update lockfile package-lock.json 2019-10-07 16:35:49 -07:00
greenkeeper[bot] 422f7da5fe fix(package): update deepmerge to version 4.1.0 2019-10-07 16:35:49 -07:00
Gregor Martynus bf5275d41f refactor: use Typescripts ReturnType<> instead of mocking all required properties
ping @JasonEtco - like that better?
2019-09-19 09:41:53 -07:00
Gregor Martynus 796a118c9b build: lock file 2019-09-19 09:41:53 -07:00
Gregor Martynus 61be0fd496 feat: @octokit/rest v16.29.0. Besides new methods also plenty of new Typescript definitions 2019-09-19 09:41:53 -07:00
Gregor Martynus 31c3602e96 fix(config): handle cases when `.github/<app name>.yml` is not a file, but a directory, symlink or submodule 2019-09-19 09:41:53 -07:00
Gregor Martynus 62645eb08e test: upcoming @octokit/rest version has plenty of new Typescript definitions
This change will make it happy.
2019-09-19 09:41:53 -07:00
Gregor Martynus 2af6673932 style(docs): standard 2019-09-19 09:41:53 -07:00
Flavio Percoco bf354ffc80 feat: add LOG_LEVEL_IN_STRING to set bunyan-format levelInString option (#1015) 2019-09-11 22:20:52 -07:00
greenkeeper[bot] e4623840e6 chore(package): update lockfile package-lock.json 2019-09-11 16:21:34 -07:00
greenkeeper[bot] a8c97599ff chore(package): update @types/nock to version 11.1.0 2019-09-11 16:21:34 -07:00
greenkeeper[bot] 5d22f261cd chore(package): update lockfile package-lock.json 2019-09-11 11:24:34 -07:00
greenkeeper[bot] 9574d66765 chore(package): update @types/cache-manager to version 2.10.0 2019-09-11 11:24:34 -07:00
dependabot[bot] 42342a58b4 build(deps): bump lodash from 4.17.11 to 4.17.15
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.11 to 4.17.15.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.11...4.17.15)

Signed-off-by: dependabot[bot] <support@github.com>
2019-09-09 11:56:50 -07:00
dependabot[bot] 98de360352 build(deps): bump handlebars from 4.0.13 to 4.0.14
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.0.13 to 4.0.14.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/wycats/handlebars.js/blob/v4.0.14/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.0.13...v4.0.14)

Signed-off-by: dependabot[bot] <support@github.com>
2019-09-09 11:52:12 -07:00
Maxim Devoir 27cecb0e7e fix: update `@octokit/graphql` to v4 (#1006) 2019-09-09 11:46:44 -07:00
greenkeeper[bot] 1328ac4c1d chore(package): update lockfile package-lock.json 2019-09-04 14:30:26 -07:00
greenkeeper[bot] 1011bb56e4 chore(package): update nock to version 11.3.2
Closes #1004
2019-09-04 14:30:26 -07:00
greenkeeper[bot] f457477e50 chore(package): update lockfile package-lock.json 2019-09-01 22:40:32 -07:00
greenkeeper[bot] 95978f5bd8 fix(package): update is-base64 to version 1.0.0 2019-09-01 22:40:32 -07:00
dependabot[bot] 618d73885f build(deps): bump mixin-deep from 1.3.1 to 1.3.2
Bumps [mixin-deep](https://github.com/jonschlinkert/mixin-deep) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/jonschlinkert/mixin-deep/releases)
- [Commits](https://github.com/jonschlinkert/mixin-deep/compare/1.3.1...1.3.2)

Signed-off-by: dependabot[bot] <support@github.com>
2019-08-28 10:25:20 -07:00
dependabot[bot] be11adc313 build(deps): bump eslint-utils from 1.3.1 to 1.4.2
Bumps [eslint-utils](https://github.com/mysticatea/eslint-utils) from 1.3.1 to 1.4.2.
- [Release notes](https://github.com/mysticatea/eslint-utils/releases)
- [Commits](https://github.com/mysticatea/eslint-utils/compare/v1.3.1...v1.4.2)

Signed-off-by: dependabot[bot] <support@github.com>
2019-08-26 12:42:00 -07:00
greenkeeper[bot] b6d1ee09fa chore(package): update lockfile package-lock.json 2019-08-23 11:30:40 -07:00
greenkeeper[bot] ef33656bfe chore(package): update standard to version 14.0.2
Closes #990
2019-08-23 11:30:40 -07:00
greenkeeper[bot] e20d3ce8f1 chore(package): update lockfile package-lock.json 2019-08-18 15:34:43 -07:00
greenkeeper[bot] fd24686721 fix(package): update dotenv to version 8.1.0 2019-08-18 15:34:43 -07:00
greenkeeper[bot] b656ef668e chore(package): update lockfile package-lock.json 2019-08-11 09:35:18 -07:00
greenkeeper[bot] 1867b3986d fix(package): update commander to version 3.0.0 2019-08-11 09:35:18 -07:00
Michael Smith 3f0491f63b feat: Implement probot-config on context.config (#619)
Implements the functionality of https://github.com/probot/probot-config
in context.config. This adds the ability to have config fallback to a
`.github` project in the target repository's organization, or for
repository configs to specify a particular repository to merge config
from.
2019-08-05 14:26:25 -07:00
JimNero009 de79aab554 fix: update hbs dependency to ^4.0.4 (#973) 2019-07-24 08:07:28 -07:00
Daniel Gront 7ceb3b68aa docs(development): improve wording (#968) 2019-07-17 14:45:02 -07:00
greenkeeper[bot] 56d4e2f12a chore(package): update lockfile package-lock.json 2019-07-12 14:12:58 -07:00
greenkeeper[bot] e59aba5d0c fix(package): update @octokit/app to version 4.0.0 2019-07-12 14:12:58 -07:00
greenkeeper[bot] b73ae3cb2a chore(package): update lockfile package-lock.json 2019-07-12 13:37:57 -07:00
greenkeeper[bot] a3ee78a055 fix(package): update @octokit/graphql to version 3.0.1 2019-07-12 13:37:57 -07:00
greenkeeper[bot] 41fb455cd5 chore(package): update lockfile package-lock.json 2019-07-12 12:59:56 -07:00
greenkeeper[bot] 9d1878d8e1 fix(package): update @octokit/graphql to version 3.0.0 2019-07-12 12:59:56 -07:00
greenkeeper[bot] 0fa713ce0c chore(package): update lockfile package-lock.json 2019-07-11 18:08:28 -07:00
greenkeeper[bot] 07bfe79643 chore(package): update standard to version 13.0.1 2019-07-11 18:08:28 -07:00
greenkeeper[bot] 17be86a111 chore(package): update lockfile package-lock.json 2019-07-09 09:27:57 -07:00
greenkeeper[bot] b2d7eadc82 fix(package): update @octokit/graphql to version 2.1.3 2019-07-09 09:27:57 -07:00
Christopher Constable a632bdc176 docs(testing): update for Probot 9.x (#952)
The previous testing example does not compile with Probot 9.x.
2019-06-25 13:10:38 -07:00
greenkeeper[bot] 3d909b4ba0 chore(package): update lockfile package-lock.json 2019-06-23 15:18:59 -07:00
greenkeeper[bot] a29e131803 chore(package): update eslint to version 6.0.0 2019-06-23 15:18:59 -07:00
Michael Fix 05af801c90 fix(package): bump @octokit/app dependency (#944) 2019-06-12 14:33:38 -07:00
greenkeeper[bot] 460e5787c8 chore(package): update lockfile package-lock.json 2019-06-11 11:10:13 -07:00
greenkeeper[bot] fc282f3801 fix(package): update supports-color to version 7.0.0 2019-06-11 11:10:13 -07:00
Olivier Combe ade14ae1dc fix(typescript): use typescript `@deprecated` tag for `context.github.query` (#938)
This improves the discovery of the deprecated method as IDEs and linters will mark the method as deprecated if you use the correct tag.
2019-05-24 08:05:37 -07:00
Gregor fd6d6febf1 refactor: adapt for @octokit/app v3 2019-05-22 08:24:14 -07:00
greenkeeper[bot] 23257cb485 chore(package): update lockfile package-lock.json 2019-05-22 08:24:14 -07:00
greenkeeper[bot] 232ce07ff1 fix(package): update @octokit/app to version 3.0.0 2019-05-22 08:24:14 -07:00
greenkeeper[bot] 8f86e8d1c8 chore(package): update lockfile package-lock.json 2019-05-16 22:28:36 -07:00
greenkeeper[bot] aa16435fd1 fix(package): update @octokit/graphql to version 2.1.2 2019-05-16 22:28:36 -07:00
greenkeeper[bot] 9567c991f9 chore(package): update lockfile package-lock.json 2019-05-03 17:45:07 -07:00
greenkeeper[bot] c7aed320f6 chore(package): update @types/node to version 12.0.0 2019-05-03 17:45:07 -07:00
greenkeeper[bot] 90b54ac05e chore(package): update lockfile package-lock.json 2019-05-02 21:27:21 -07:00
greenkeeper[bot] c3357483d3 fix(package): update dotenv to version 8.0.0 2019-05-02 21:27:21 -07:00
Bob van der Linden 6f7559996f fix(typescript): log.child options (#924) 2019-04-28 14:17:35 -07:00
greenkeeper[bot] 397d6325f4 chore(package): update lockfile package-lock.json 2019-04-26 14:51:16 -07:00
greenkeeper[bot] 3352bad383 chore(package): update @types/nock to version 10.0.0 2019-04-26 14:51:16 -07:00
Gregor 93d6899c8e fix: remove deprecation message on /probot/stats endpoint 2019-04-24 09:37:59 -07:00
Gregor 087d9674d2 build(package): bump @octokit/rest minimal version number 2019-04-24 09:37:59 -07:00
Gregor def87c05c0 test: make describe() function sync 2019-04-24 09:37:59 -07:00
Steve Winton 66a3b14242 fix: Support GitHub Enterprise Server in GitHub App Manifest flow implementation (#902) 2019-04-22 12:31:22 -04:00
Enguerrand des Vaux e831d3e38f fix: update js-yaml (#916)
See npmjs.com/advisories/813
2019-04-18 11:18:53 -07:00
Adam Netočný 9cc6131ed2 fix: check if err.message exists (#914)
In `errMessage` the `err.message` may be undefined. We need to do a null check here.
2019-04-15 08:11:06 -07:00
Hamel Husain f33ab54847 chore: Create issue_label_bot.yaml (#913) 2019-04-13 11:17:38 -04:00
Zeke Sikelianos 8dffb996ae link to list in @octokit/webhooks.js 2019-04-11 21:04:29 -07:00
wolfy1339 ecae3fc1da fix(package): bump @octokit/app (#910) 2019-04-11 11:26:58 -07:00
Adam Netočný de871e1800 fix: propagate errors to @octokit/webhooks (#907)
Probot "eats" errors from application code. These are logged, but not propagated to `@octokit/webhooks` and not reported to Github. `@octokit/webhooks` responds with `200 OK` instead.

With this change the error is propagated to `@octokit/webhooks` and it correctly respond with error code `500`.
2019-04-11 08:32:40 -07:00
Denys Vuika ee2589a776 docs(development): quotes (#908)
use single quotes as double-quite causes error during "npm install"
2019-04-10 12:58:47 -07:00
greenkeeper[bot] 4b137a2414 chore(package): update lockfile package-lock.json 2019-04-09 12:49:20 -07:00
greenkeeper[bot] 89a27c6bf0 fix(package): update @octokit/graphql to version 2.1.1
Closes #903
2019-04-09 12:49:20 -07:00
greenkeeper[bot] def3703ed7 chore(package): update lockfile package-lock.json 2019-04-05 17:44:31 -07:00
greenkeeper[bot] 172a0b3968 fix(package): update @octokit/webhooks to version 6.0.0 2019-04-05 17:44:31 -07:00
Tommy Byrd 23e8eabf4a feat: Update @octokit/rest to 16.22.0 (#901) 2019-04-03 08:57:13 -07:00
greenkeeper[bot] 363d9a4255 chore(package): update lockfile package-lock.json 2019-04-01 15:20:01 -07:00
greenkeeper[bot] 1d485c067e chore(package): update @types/semver to version 6.0.0 2019-04-01 15:20:01 -07:00
greenkeeper[bot] 2d6b3fc6d5 chore(package): update lockfile package-lock.json 2019-03-29 19:54:17 -07:00
greenkeeper[bot] bd0a3cf91e chore(package): update @types/dotenv to version 6.1.1 2019-03-29 19:54:17 -07:00
greenkeeper[bot] 61e99ab8a8 chore(package): update lockfile package-lock.json 2019-03-27 09:15:46 -07:00
greenkeeper[bot] fd63724fd5 fix(package): update semver to version 6.0.0
Closes #897
2019-03-27 09:15:46 -07:00
Christophe Hurpeau 036ee3be83 feat(typescript): export type OnCallback (#896) 2019-03-25 12:56:00 -07:00
greenkeeper[bot] 7824645f8f chore(package): update lockfile package-lock.json 2019-03-19 10:44:55 -07:00
greenkeeper[bot] e584124c7f fix(package): update @octokit/graphql to version 2.0.2 2019-03-19 10:44:55 -07:00
greenkeeper[bot] f768901c6a chore(package): update lockfile package-lock.json 2019-03-17 13:03:21 -07:00
greenkeeper[bot] 12ebbd5bf5 chore(package): update supertest to version 4.0.2
Closes #887
2019-03-17 13:03:21 -07:00
greenkeeper[bot] 52e7223959 chore(package): update lockfile package-lock.json 2019-03-13 11:05:01 -07:00
greenkeeper[bot] 28ead7e0ec fix(package): update dotenv to version 7.0.0 2019-03-13 11:05:01 -07:00
wolfy1339 b2f3858c7c test: Convert manifest creation test to TS (#884) 2019-03-12 15:24:03 -07:00
wolfy1339 620e40b172 build(package): update typescript to 2.9 (#886) 2019-03-12 15:15:54 -07:00
Gregor 9265609c91 docs(README): remove npm `@next` badge 2019-03-08 11:33:34 -08:00
Karol Majewski 707a25dd44 test: Use the default Jest preset
With this setting, ts-jest will take care of .ts and .tsx files only, leaving JavaScript files as-is.

This means we don't have to tell ts-jest to skipBabel anymore.

See https://kulshekhar.github.io/ts-jest/user/config/#the-3-presets
2019-03-08 11:18:29 -08:00
Karol Majewski 3c40cd92c8 test: Remove redundant settings
These are redundant anyway.
See https://jestjs.io/docs/en/configuration.html
2019-03-08 11:18:29 -08:00
Karol Majewski 0e871fd0e8 chore(dependencies): Update vulnerable packages 2019-03-08 11:18:29 -08:00
Jason Etcovitch 596a42d0a0 fix: error.code => error.status (#871) 2019-03-08 11:18:18 -08:00
Jason Etcovitch 1581315234 fix: make smee-client requirement optional again (#868) 2019-03-08 11:18:18 -08:00
Gregor 7582227266 docs(README): npm beta badge -> next badge 2019-03-08 11:18:18 -08:00
greenkeeper[bot] fb42aec2af chore(package): update lockfile package-lock.json 2019-03-08 11:18:18 -08:00
greenkeeper[bot] b77e06c621 fix(package): update pkg-conf to version 3.0.0 2019-03-08 11:18:18 -08:00
wolfy1339 fb4c06950c test: convert webhook proxy test to TS (#851) 2019-03-08 11:18:18 -08:00
wolfy1339 d76c70ba23 test: convert final test to TypeScript (#850) 2019-03-08 11:18:05 -08:00
Karol Majewski 2501243a32 build(package): Update Jest to v24 (#863) 2019-03-08 11:18:05 -08:00
wolfy1339 19fb9d2b53 feat: Add overloads for all Webhook events (#858) 2019-03-08 11:17:52 -08:00
Jakub Kadlubiec 8c47d1f9f8 fix: remove unnecessary escaping in private key error message (#862) 2019-03-08 11:17:52 -08:00
Alberto Gimeno bd42feba23 feat: standalone run command (#751) 2019-03-08 11:17:52 -08:00
greenkeeper[bot] f331ff014d chore(package): update lockfile package-lock.json 2019-03-08 11:17:35 -08:00
greenkeeper[bot] 6d5347b034 chore(package): update ts-jest to version 24.0.0 2019-03-08 11:17:22 -08:00
Adam Netočný 9b11084404 feat: options.Octokit to pass custom Octokit version (#840) 2019-03-08 11:17:22 -08:00
wolfy1339 9aa2812263 chore(tests): Convert setup script to TS (#849) 2019-03-08 11:17:22 -08:00
greenkeeper[bot] 597ba9d525 chore(package): update lockfile package-lock.json 2019-03-08 11:17:22 -08:00
greenkeeper[bot] 65014ebcb4 chore(package): update @types/node to version 11.9.0 2019-03-08 11:17:11 -08:00
Michael Steward 30ac1a4200 fix(typescript): @octokit/webhooks import (#854)
The import of Webhooks from the `@octokit/webhooks` was incorrect, it is not a named export (see https://github.com/octokit/webhooks.js/blob/master/index.d.ts) but rather a default export
2019-03-08 11:17:11 -08:00
wolfy1339 067e26b4e6 test: Convert fixtures to TS (#848) 2019-03-08 11:17:11 -08:00
Chris Hiestand 4bbf29c245 feat: add constructor option for redis config (#846)
* add constructor option for redis config

close #845
2019-03-08 11:17:11 -08:00
Adam Netočný eab6851c53 fix: pass baseUrl option to @octokit/app (#839) 2019-03-08 11:16:59 -08:00
Gregor 9e9b808a08 test: remove deprecations 2019-03-08 11:16:59 -08:00
Gregor 1f76ec618c fix: remove handling deprecated `.event` property
BREAKING CHANGE: Deprecated `event.event` property is no longer handled. Use `event.name` instead
2019-03-08 11:16:59 -08:00
Gregor f46c41cb89 fix: remove deprecated `catchErrors` constructor option
BREAKING CHANGE: Deprecated `new Probot({catchErrors})` has been removed
2019-03-08 11:16:59 -08:00
Gregor 57587b83b8 fix: remove deprecated "simulate" command
BREAKING CHANGE: `probot simulate` hase been removed, use `probot receive` instead
2019-03-08 11:16:59 -08:00
Gregor 6638331030 fix: create only a single redis connection when REDIS_URL is set 2019-03-08 11:16:59 -08:00
Gregor 2a0c1de266 test: create single redis connection in probot constructor 2019-03-08 11:16:59 -08:00
Gregor 0a3c828ff9 style: standard 2019-03-08 11:16:59 -08:00
Alberto Gimeno 7e59638bb5 refactor: use webhooks type definitions from @octokit/webhooks 2019-03-08 11:16:59 -08:00
Gregor 08047e25ca build(package): lock file 2019-03-08 11:16:59 -08:00
Gregor f86f362e59 build(package): update @octokit/webhooks and @octokit/rest 2019-03-08 11:16:45 -08:00
Gregor 86f5312ad5 feat: Cluster support for request throttling 2019-03-08 11:16:45 -08:00
Gregor b0f0f50602 test: throttle using Redis-backed Bottleneck for cluster support 2019-03-08 11:16:44 -08:00
Gregor 7ce6902b71 test: request error logs 2019-03-08 11:16:44 -08:00
Gregor 328d9f519b build(package): lock file 2019-03-08 11:16:44 -08:00
Gregor c4845298bc docs(configuration): REDIS_URL 2019-03-08 11:16:29 -08:00
Gregor 189ff9ba33 build(package): bottleneck, ioredis 2019-03-08 11:16:29 -08:00
JC cb21ca7a22 docs(testing): typo in code example (#834) 2019-03-08 10:00:43 -08:00
Gregor 20e7c65a50 refactor: use "auth" constructor option instead of `octokit.authenticate()` 2019-03-08 10:00:43 -08:00
Gregor d3ea6114e7 build(package): lock file 2019-03-08 10:00:43 -08:00
Gregor 328ea0c640 build(package): @octokit/rest v16.13.0 2019-03-08 10:00:43 -08:00
Gregor a3da7b231c refactor: remove obsolete `debug` option for Octokit
This has been removed in v13: https://github.com/octokit/rest.js/releases/tag/v13.0.0
2019-03-08 10:00:43 -08:00
Gregor 64f5928356 test: adapt tests for `@octokit/app`. We no longer need to test caching of installation tokens, this is implemented within `@octokit/app 2019-03-08 10:00:43 -08:00
Gregor 3f8a50a993 refactor: replace `github-app.ts` with `@github/app` 2019-03-08 10:00:43 -08:00
Gregor 0821b969f3 build(package): lock file 2019-03-08 10:00:43 -08:00
Gregor d6dcaebcdd build(package): @octokit/app 2019-03-08 10:00:43 -08:00
Gregor 74e040ea43 docs(CONTRIBUTING): 🏗 Probot v8 beta 2019-03-08 10:00:43 -08:00
Gregor e87187c3ba docs(README): npm beta badge 2019-03-08 10:00:43 -08:00
Gregor f85a223cf1 build(packag): lock file 2019-03-08 10:00:43 -08:00
Gregor a9fafdd56d refactor: cache entire Octokit instances instead of just the auth token 2019-03-08 10:00:43 -08:00
Gregor 4b71bca6de build(package): @octokit/plugin-retry@^2.11, @octokit/plugin-throttling@^2.21 2019-03-08 10:00:43 -08:00
Gregor cf24e4c773 test: disable throttling for github tests 2019-03-08 10:00:43 -08:00
Gregor c1141ca6e6 build(package): lock-file 2019-03-08 10:00:43 -08:00
Gregor 70a58f6947 build(package): update versions for octokit throttle/retry plugins 2019-03-08 10:00:43 -08:00
Gregor e5974b6489 build: lock file -> https URLs 2019-03-08 10:00:43 -08:00
Gregor aa1c57072d fix: make `.issues.addLabels()` compatible with current GHE versions 2019-03-08 10:00:19 -08:00
Gregor c6cbc97bf0 feat: new throttling & automated retries 2019-03-08 10:00:19 -08:00
Gregor 39f1fc087a docs(pagination): lookie lookie async iterators 👀 2019-03-08 10:00:19 -08:00
Gregor 6a6f456620 docs(github-api): github.graphql is the new github.query and then some 2019-03-08 10:00:19 -08:00
Gregor e0868f10b4 feat: new github.paginate API, github.paginate.iterator() 2019-03-08 10:00:19 -08:00
Gregor fa9a228197 feat: moar Typescript foo 2019-03-08 10:00:19 -08:00
Gregor 83b42b930f refactor: use @octokit/graphql 2019-03-08 10:00:19 -08:00
Gregor 624bae2d61 refactor: use the new github.paginate syntax 2019-03-08 10:00:19 -08:00
Gregor 6dc8dccdf8 refactor: github.request(route, options) is so pretty 2019-03-08 10:00:19 -08:00
Gregor 291824960c fix(logging): error.status is the new error.code. The old error.status is no more 2019-03-08 10:00:19 -08:00
Gregor f06d98479f refactor: Typescript does not like .bind() too much 2019-03-08 10:00:19 -08:00
Gregor 3f83dae4e7 test(pagination): test deprecations 2019-03-08 10:00:19 -08:00
Gregor a486f7c03b test: fix mocked responses for pagination requests
Responses are always arrays, never single objects
2019-03-08 10:00:19 -08:00
Gregor c8e72aeeed test(graphql): add tests for deprecations, refactor for new .graphql implementation (no breaking changes) 2019-03-08 10:00:19 -08:00
Gregor 5567e97b32 refactor: spy on `github.repos.getContents` function without removing function properties 2019-03-08 10:00:19 -08:00
Gregor 20e5914b3b test: suppress logs from `console.warn`, use spy to make sure it was called 2019-03-08 10:00:19 -08:00
Gregor ab557bb8a4 build(package): lock file update 2019-03-08 10:00:19 -08:00
Gregor 0f464c00c9 build(package): @octokit/rest v16, @octokit/graphql, octokit-pagination-methods
BREAKING CHANGE: _fill in from https://github.com/probot/probot/pull/774#issue-232419817_
2019-03-08 09:59:59 -08:00
David Klebanoff 22ce38c86a docs(README): fixes wording (#880) 2019-03-06 10:17:43 -08:00
Gregor c95f5edb9b build(package): lock file 2019-03-03 14:45:24 -08:00
Gregor b42f83db11 build(package): update semantic-release 2019-03-03 14:45:12 -08:00
Jason Etcovitch d32ce60588 fix: Update dependencies (#870) 2019-03-03 14:44:13 -08:00
Tommy Byrd e975bbda00
update spelling
Updating this change from #852 in the master branch so we can publish to the site.
2019-02-09 20:27:55 -05:00
Gregor beb84661b1 docs(CONTRIBUTING): Merging the Pull Request & releasing a new version 2019-01-18 17:18:39 -08:00
Gregor 0dbb499826 ci(travis): semantic-release setup 2019-01-18 17:18:39 -08:00
Gregor d742bc7bdd build: lock file 2019-01-18 17:18:39 -08:00
Gregor d835553926 build(package): semantic-release setup 2019-01-18 17:18:39 -08:00
Shelley Vohr c8ed774382 doc: update GitHub actions workflow reference (#823) 2019-01-13 15:30:41 -05:00
Gregor Martynus 7d938d67fe docs(README): fix link to travis 2019-01-10 11:21:56 -08:00
Gregor ca853910cd 7.5.0 2019-01-10 11:18:03 -08:00
Gregor cc5b80b31e fix: pass utf8 as 2nd argument when reading private key from file using `fs.readFileSync` 2019-01-10 10:49:06 -08:00
Gregor 301fc4775a test: pass utf8 as 2nd argument when reading private key from file using `fs.readFileSync` 2019-01-10 10:49:06 -08:00
Gregor 222256ab92 fix: GitHubAPI.paginate callback signature in stats
follow up for https://github.com/probot/probot/pull/775
2019-01-10 10:48:40 -08:00
wolfy1339 33f321a92b fix(typescript): Webhooks class constructor definition (#798) 2019-01-10 09:14:02 -08:00
Alberto Gimeno 6ad29f86af fix(typescript): webhook callback definitions (#793) 2019-01-10 09:12:00 -08:00
Abi Noda f6d7bdcdfb Add pull reminders badge to README (#814)
* docs: add pull reminders badge

* fix img alt attributre
2019-01-09 17:07:50 -05:00
Adam Netočný 2b78263e82 fix(typescript): GitHubAPI.paginate callback signature (#775) 2019-01-09 12:10:58 -08:00
Gregor fe99d0b2d3 build(package): lock file update 2019-01-07 22:35:07 -08:00
Gregor Martynus 8d8292bca8
build(npmrc): bye bye bey
![bye bye bye](https://media.giphy.com/media/hFXwY4lER3oBO/giphy.gif)
2019-01-07 22:33:40 -08:00
MarvinJWendt d23ebba0e2 Update year to 2019 (#812) 2019-01-05 22:10:56 -05:00
Pietro Bongiovanni 6b73d5a746 docs: Fix URL for Serverless GCF repository (#805) 2019-01-04 13:42:31 -05:00
Kentaro Matsushita dc558dcef5 docs: Fix sample code for serverless deployment (#807) 2019-01-04 13:41:25 -05:00
Luke Browne 6620ba18da tests: Removed allowed failure configuration for node 10 (#802) 2019-01-03 12:54:26 -05:00
Josh Soref 444fcac73d fix: Spelling (#800) 2019-01-03 12:15:02 -05:00
KINOSHITA Minoru 15fbadca2d fix: path.resolve instead of path.join to resolve payload paths. (#785) 2019-01-02 13:45:42 -08:00
Mayank Jethva bb06c51485 docs(simulating-webhooks.md): typo and `pull_request.labeled` keyword highlight (#796) 2018-12-21 10:36:39 -08:00
greenkeeper[bot] e85341c637 chore(package): update lockfile package-lock.json 2018-12-21 10:35:23 -08:00
greenkeeper[bot] 2c0a342c89 fix(package): update supports-color to version 6.0.0 2018-12-21 10:35:23 -08:00
Hayden 53564a9119 build: package lock file (#791) 2018-12-18 10:02:30 -08:00
Chad Fawcett 5063300606 Fix link to "probot-metadata" extension (#787)
Fix broken link
2018-12-12 01:03:43 -05:00
Chad Fawcett e1d80a96f3 Fix link to "Create the GitHub App" (#786) 2018-12-11 23:37:56 -05:00
Andrew Hinett fee5451482 docs: fix misspellings of receive (#783) 2018-12-11 07:57:08 -08:00
Nikhil Mehra cc87051c0c Invalid PRIVATE_KEY_PATH error handling (#782) 2018-12-10 14:44:34 -05:00
greenkeeper[bot] a96b06ab25 chore(package): update @types/jsonwebtoken to version 8.3.0 (#757) 2018-12-10 14:43:43 -05:00
greenkeeper[bot] 213b12f5fb fix(package): update dotenv to version 6.2.0 (#781) 2018-12-10 14:42:25 -05:00
Kunall Banerjee 4a5b8619c0 Prefill Glitch app name as GitHub app name for app manifests (#742)
* Use Glitch app name as GitHub app name for app manifests
2018-12-10 11:03:46 +00:00
wolfy1339 3b3e555f8b Fix typings for stats app (#728)
* Fix typings for stats app

* Fix problems with callback functions that should be async

According to the Octokit types, the paginate() function's callback has an argument of type `Promise<AnyResponse>`, but it was being used as if it wasn't in a Promise

* update octokit methods
2018-11-25 22:49:49 -05:00
Bex Warner 58559ff22d 7.4.0 2018-11-20 13:23:57 -05:00
Gregor 59bf271254 refactor: rename deprecated methods 2018-11-20 09:50:06 -08:00
Gregor 9d159250cd fix(package): @octokit/rest@^15.18.0 2018-11-20 09:50:06 -08:00
Matan Kushner 5c622e4ee9 docs: links in "Serverless Deployment" (#773) 2018-11-19 14:27:42 -08:00
Wilhelm Klopp 107c4e6749
Include version in manifest (#763) 2018-11-15 09:52:35 +00:00
Alberto Gimeno 7831ef0c4d Fix for `probot receive` to allow app auth instead in addition to token auth (#754) 2018-11-02 11:14:25 -05:00
Timon van Spronsen 5443ce03c8 Add documentation about template flag (#752)
* Add documentation about template flag

* Update docs/development.md

Co-Authored-By: TimonVS <timonvanspronsen@outlook.com>

* Update docs/development.md

Co-Authored-By: TimonVS <timonvanspronsen@outlook.com>

* Improve `template` argument documentation

* update template step
2018-10-31 23:02:15 -04:00
Bex Warner 9e60d34710
Follow GitHub Casing Standards (#756)
* casing is lyfe #gIThUB

* Update docs/configuration.md

Co-Authored-By: hiimbex <bexmwarner@gmail.com>

* Update docs/configuration.md

Co-Authored-By: hiimbex <bexmwarner@gmail.com>
2018-10-31 22:42:03 -04:00
greenkeeper[bot] e1e1b32db1 fix(package): update is-base64 to version 0.1.0 (#750) 2018-10-26 11:56:25 -04:00
Bex Warner 286f2d4916 Improve documentation on testing Probot apps with nock (#734) 2018-10-25 15:35:24 -05:00
Bob van der Linden 35dd36da69 Improve GraphQL query for TypeScript (#710) 2018-10-25 15:33:16 -05:00
Brian Douglas 787e36f2ad Create serverless-deployment.md (#698)
* Create serverless-deployment.md

I have worked alongside @tcbyrd and @JasonEtco testing out their Serverless plugins in preparation for a Serverless conference I am attending this week. Having a Serverless section would be nice to share while at the conference, but not required.

This PR adds a new Serverless section to the docs (open to suggestions on placement), @hiimbex mentioned creating a new section to assist not over-crowding the (already existing) deployment.md.

After this week, I plan to begin working with another provider (Azure).

* Update deployment.md

* Bdougie pr serverless fixes (#2)

* adds link to serverless docs

* link to deployments section for repeated content

* Bdougie pr serverless fixes (#3)

* adds link to serverless docs

* link to deployments section for repeated content

* Update serverless-deployment.md

* adds serverless blurb

* Update deployment.md

* Update serverless-deployment.md

* Update deployment.md

* Update serverless-deployment.md

* a few grammar and word changes
2018-10-25 15:15:04 -04:00
mackie f82d647f51 chore(test): convert apps/stats test to TypeScript (#704) 2018-10-25 14:13:09 -05:00
mackie 85502cf3da chore(tests): convert logging test to TypeScript (#696) 2018-10-25 14:11:42 -05:00
Timon van Spronsen 494fd3cbb3 Add done parameter to paginate callback in GitHubAPI interface (#718) 2018-10-25 14:58:42 -04:00
Kunall Banerjee 06d2767607 docs: Fix typo (#741) 2018-10-23 01:39:27 -04:00
Jason Etcovitch 1fc1e905b1
docs: Pretty readme (#740) 2018-10-19 17:10:25 -07:00
Brandon Keepers 76b0417745
robot => app 2018-10-16 11:20:53 -07:00
Brandon Keepers 133496bc88
Document deployment to GitHub Actions (#739) 2018-10-16 11:16:26 -07:00
Brandon Keepers 43acbbc843
Fix: Allow starting probot without token or id/cert (#738) 2018-10-15 13:10:02 -07:00
Brandon Keepers 5dc37f6bdf
7.3.0 2018-10-15 11:38:01 -07:00
Bex Warner 76b5edd13d Update docs for manually receiving events (#730) 2018-10-15 11:31:31 -07:00
Alberto Gimeno ad06bd5e32 Deprecate `probot simulate` in favor of `probot receive` (#726) 2018-10-15 11:21:36 -07:00
Wilhelm Klopp aef4d72d95
Wrap pem in quotes to make Glitch happy (#737)
* Wrap pem in quotes to make Glitch happy
2018-10-15 09:17:07 -07:00
Bex Warner 8964e7c613
Update development.md (#735) 2018-10-10 23:28:37 -04:00
Bex Warner 6b5e9a2a72
chore(package): update @octokit/rest to version 15.13.1 (#732) 2018-10-10 12:02:58 -04:00
greenkeeper[bot] 914d968045 Update standard-markdown to the latest version 🚀and lint docs
* chore(package): update standard-markdown to version 5.0.0

* chore(package): update standard-markdown to version 5.0.1

Closes #689

* fix linting
2018-10-10 11:54:31 -04:00
greenkeeper[bot] 00f0e192a5 chore(package): update typedoc to version 0.13.0 (#731) 2018-10-10 11:27:00 -04:00
Bex Warner 0036fee389
chore(package): update commander to version 2.19.0 (#729) 2018-10-10 09:23:05 -04:00
Héricles Emanuel f657a9733f Create README.pt-br.md (#711) 2018-10-10 01:08:01 -04:00
greenkeeper[bot] 7cb1167dc9 fix(package): update dotenv to version 6.1.0 (#724) 2018-10-09 09:30:33 -05:00
169 changed files with 22580 additions and 4157 deletions

3
.codesandbox/ci.json Normal file
View File

@ -0,0 +1,3 @@
{
"sandboxes": ["new", "vanilla"]
}

View File

@ -9,21 +9,23 @@ about: If something isn't working as expected 🤔.
A clear and concise description of the behavior.
```js
// Your code here
module.exports = robot => {
robot.log('There is a bug')
}
// Your code here
export default (app) => {
app.log.info("There is a bug");
};
```
**Expected behavior/code**
A clear and concise description of what you expected to happen (or code).
**Environment**
- Probot version(s): [e.g. v6.0.0]
- Node/npm version: [e.g. Node 8/npm 5]
- OS: [e.g. OSX 10.13.4, Windows 10]
**Possible Solution**
<!--- Only if you have suggestions on a fix for the bug -->
**Additional context/Screenshots**

4
.github/issue_label_bot.yaml vendored Normal file
View File

@ -0,0 +1,4 @@
label-alias:
bug: 'bug 🐞'
feature_request: 'enhancement '
question: 'question ❓'

View File

@ -3,7 +3,7 @@ template: >
- [ ] Sort changes into relevant sections below
- [ ] Replace `master` with next tag in "See full changelog" link
- [ ] Delete this TODO list
- [ ] Hit pubish
- [ ] Hit publish
## Breaking Changes

5
.github/renovate.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"extends": [
"github>probot/.github"
]
}

42
.github/workflows/codeql.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: "CodeQL"
on:
push:
branches: [ "master", "*.x", "beta", "next" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master", "beta" ]
schedule:
- cron: '24 7 * * 2'
jobs:
analyze:
name: Analyze
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript-typescript' ]
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
queries: security-and-quality
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

25
.github/workflows/docs.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: Docs
on:
push:
branches:
- master
jobs:
docs:
name: docs
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- run: git fetch --depth=20 origin +refs/tags/*:refs/tags/*
- uses: actions/setup-node@v4
with:
node-version: lts/*
cache: npm
- name: Install dependencies
run: npm ci
- name: Build probot
run: npm run build
- name: Publish documentation
run: ./script/publish-docs
env:
OCTOKITBOT_PAT: ${{ secrets.OCTOKITBOT_PAT }}

37
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,37 @@
name: Release
on:
push:
branches:
- master
- next
- beta
- "*.x"
# These are recommended by the semantic-release docs: https://github.com/semantic-release/npm#npm-provenance
permissions:
contents: write # to be able to publish a GitHub release
issues: write # to be able to comment on released issues
pull-requests: write # to be able to comment on released pull requests
id-token: write # to enable use of OIDC for npm provenance
jobs:
release:
name: release
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Release
run: npx semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

146
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,146 @@
name: Test
on:
push:
branches:
- master
pull_request:
types:
- opened
- synchronize
permissions:
contents: read
jobs:
dependency-review:
name: Dependency Review
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Dependency review
uses: actions/dependency-review-action@v4
license-check:
name: Check Licenses
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install dependencies
run: npm ci
- name: Check Licenses
run: npx license-checker --production --summary --onlyAllow="0BSD;Apache-2.0;Apache 2.0;Python-2.0;BSD-2-Clause;BSD-3-Clause;ISC;MIT"
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: npm
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
test-unit:
name: Test on Node ${{ matrix.node-version }} and ${{ matrix.os }}
strategy:
matrix:
node-version:
- 18
- 20
- 21
os:
- ubuntu-latest
- macos-latest
- windows-latest
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
- name: Install dependencies
run: npm ci
- name: Test
run: npm run test
test-redis:
name: Test on Node LTS and Redis 7
runs-on: ubuntu-latest
services:
redis:
image: 'redis:7'
ports:
- '6379:6379'
options: '--entrypoint redis-server'
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use Node.js LTS
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: npm
- name: Install Dependencies
run: npm ci
- name: Test
run: npm run test
env:
REDIS_HOST: 127.0.0.1
test:
runs-on: ubuntu-latest
needs:
- test-unit
- test-redis
steps:
- run: exit 1
if: ${{ needs.test-unit.result != 'success' || needs.test-redis.result != 'success' }}
- name: Check out repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Use Node.js LTS
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: npm
- name: Install Dependencies
run: npm ci
- name: Build
run: npm run build
- name: Test
run: npm run test:coverage
- name: Update Codecov
run: npx codecov
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
if: ${{ always() }}

2
.gitignore vendored
View File

@ -2,10 +2,10 @@ npm-debug.log
.DS_Store
node_modules/
*.pem
!test/**/*.pem
.env
out
tmp
package-lock.json
coverage
lib/
.idea

1
.npmrc
View File

@ -1 +0,0 @@
package-lock=false

View File

@ -1,32 +0,0 @@
sudo: required
language: node_js
node_js:
- "8"
env:
matrix:
include:
- node_js: "10"
allow_failures:
- node_js: "10"
notifications:
disabled: true
before_install:
- npm install -g codecov
install:
- npm install
- npm run build
branches:
except:
- /^v\d+\.\d+\.\d+$/
script:
- npm test
- codecov

View File

@ -1,17 +1,12 @@
{
"mode": "file",
"entryPoints": ["src"],
"out": "out",
"exclude": "test",
"theme": "minimal",
"theme": "default",
"excludePrivate": true,
"excludeNotExported": true,
"excludeExternals": true,
"listInvalidSymbolLinks": true,
"target": "ES5",
"moduleResolution": "node",
"preserveConstEnums": "true",
"stripInternal": "true",
"module": "commonjs",
"excludeInternal": true,
"entryPointStrategy": "expand",
"readme": "none",
"gaID": "UA-102577034-2"
}

View File

@ -2,45 +2,131 @@
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
Examples of behavior that contributes to a positive environment for our
community include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior by participants include:
Examples of unacceptable behavior include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
## Enforcement Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at opensource+probot@github.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
opensource+probot@github.com.
All complaints will be reviewed and investigated promptly and fairly.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][mozilla coc].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][faq]. Translations are available
at [https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
[mozilla coc]: https://github.com/mozilla/diversity
[faq]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

View File

@ -1,13 +1,4 @@
## Contributing
[![Build Status](https://travis-ci.org/probot/probot.svg?branch=master)](https://travis-ci.org/probot/probot) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/32r7s2skrgm9ubva?svg=true)](https://ci.appveyor.com/api/projects/status/32r7s2skrgm9ubva?svg=true) [![Codecov](https://img.shields.io/codecov/c/github/probot/probot.svg)](https://codecov.io/gh/probot/probot/) [![Greenkeeper badge](https://badges.greenkeeper.io/probot/probot.svg)](https://greenkeeper.io/)
[fork]: https://github.com/probot/probot/fork
[pr]: https://github.com/probot/probot/compare
[style]: https://standardjs.com/
[code-of-conduct]: CODE_OF_CONDUCT.md
[good-first-issue-search]: https://github.com/search?utf8=%E2%9C%93&q=topic%3Aprobot+topic%3Aprobot-app+good-first-issues%3A%3E0&type=
[linter]: https://github.com/probot/probot/blob/ts-readme/tslint.json
# Contributing
Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
@ -17,7 +8,7 @@ Please note that this project is released with a [Contributor Code of Conduct][c
1. [Fork][fork] and clone the repository
1. Configure and install the dependencies: `npm install`
1. Make sure the tests pass on your machine: `npm test`, note: these tests also apply the [linter][linter] and run the TypeScript compiler (`tsc`) to check for type errors, so there's no need to run these commands separately.
1. Make sure the tests pass on your machine: `npm test`, note: these tests also run the TypeScript compiler (`tsc`) to check for type errors, so there's no need to run these commands separately.
1. Create a new branch: `git checkout -b my-branch-name`
1. Make your change, add tests, and make sure the tests still pass
1. Push to your fork and [submit a pull request][pr]
@ -25,13 +16,35 @@ Please note that this project is released with a [Contributor Code of Conduct][c
Here are a few things you can do that will increase the likelihood of your pull request being accepted:
- Follow the [style guide][style] which is using standard. Any linting errors should be shown when running `npm test`
- Follow the [style guide][style] which is using Prettier. Any linting errors should be shown when running `npm test`
- Write and update tests.
- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.
- Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
Work in Progress pull requests are also welcome to get feedback early on, or if there is something that blocked you.
To submit a pull request for a previous version of Probot, checkout the maintenance branch before creating a new branch, e.g. `git checkout 7.x && git checkout -b my-branch-name`. When submitting the pull request, set the base branch to the same maintenance branch.
## Merging the Pull Request & releasing a new version
Releases are automated using [semantic-release](https://github.com/semantic-release/semantic-release).
The following three commit message conventions determine which version is released:
1. `fix: ...` or `fix(scope name): ...` prefix in subject: bumps fix version, e.g. `1.2.3``1.2.4`
2. `feat: ...` or `feat(scope name): ...` prefix in subject: bumps feature version, e.g. `1.2.3``1.3.0`
3. `BREAKING CHANGE:` in body: bumps breaking version, e.g. `1.2.3``2.0.0`
Only one version number is bumped at a time, the highest version change trumps the others. Besides publishing a new version to npm, semantic-release also creates a git tag and release on GitHub, generates changelogs from the commit messages and puts them into the release notes.
New versions are released from the following branches
1. `master`: new versions are published to npm using the `@latest` dist-tag
1. `next`: new versions are published to npm using the `@next` dist-tag. When merging the `next` branch into `master`, the `@latest` dist-tag will be bumped automatically.
1. `beta`: a new `@beta` version will be released. For example, if the current version is `7.5.0`, then the first version will be published as `8.0.0-beta` using the `@beta` dist-tag. The next version will be `8.0.0-beta.1`, etc. When merged into master, the `8.0.0` version will be released automatically and all release notes will be combined into the `8.0.0` release. See also [Publishing pre-releases](https://github.com/semantic-release/semantic-release/blob/beta/docs/recipes/pre-releases.md).
1. `<version>.x`: For example `7.x`. New bug fix & feature releases are published the given version. No breaking changes are allowed. See also [Publishing maintenance releases](https://github.com/semantic-release/semantic-release/blob/beta/docs/recipes/maintenance-releases.md).
When in doubt, ping [@gr2m](https://github.com/gr2m).
## Just starting out? Looking for how to help?
Use [this search][good-first-issue-search] to find Probot apps that have issues marked with the `good-first-issue` label.
@ -41,3 +54,10 @@ Use [this search][good-first-issue-search] to find Probot apps that have issues
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
- [GitHub Help](https://help.github.com)
[fork]: https://github.com/probot/probot/fork
[pr]: https://github.com/probot/probot/compare
[style]: https://prettier.io/
[code-of-conduct]: CODE_OF_CONDUCT.md
[good-first-issue-search]: https://github.com/search?utf8=%E2%9C%93&q=topic%3Aprobot+topic%3Aprobot-app+good-first-issues%3A%3E0&type=
[linter]: https://github.com/probot/probot/blob/ts-readme/tslint.json

View File

@ -1,6 +1,6 @@
ISC License
Copyright (c) 2016-2018 Brandon Keepers
Copyright (c) 2016-2020 Brandon Keepers and Probot Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above

View File

@ -1,22 +1,35 @@
# Probot
<p align="center">
<a href="https://probot.github.io"><img src="/static/robot.svg" width="160" alt="Probot's logo, a cartoon robot" /></a>
</p>
<h3 align="center"><a href="https://probot.github.io">Probot</a></h3>
<p align="center">A framework for building GitHub Apps to automate and improve your workflow<p>
<p align="center"><a href="https://npmjs.com/package/probot"><img src="https://badgen.net/npm/v/probot" alt="npm"></a> <a href="https://github.com/probot/probot/actions?query=workflow%3ACI"><img src="https://github.com/probot/probot/workflows/CI/badge.svg" alt="Build Status"></a> <a href="https://codecov.io/gh/probot/probot/"><img src="https://badgen.now.sh/codecov/c/github/probot/probot" alt="Codecov"></a> <a href="https://twitter.com/ProbotTheRobot"><img src="https://img.shields.io/twitter/follow/ProbotTheRobot.svg?style=social&logo=twitter&label=Follow" alt="@ProbotTheRobot on Twitter"></a>
[![npm version](https://img.shields.io/npm/v/probot.svg)](https://www.npmjs.com/package/probot) [![](https://img.shields.io/twitter/follow/ProbotTheRobot.svg?style=social&logo=twitter&label=Follow)](https://twitter.com/ProbotTheRobot)
---
> 🤖 A framework for building GitHub Apps to automate and improve your workflow
If you've ever thought, "wouldn't it be cool if GitHub could…"; I'm going to stop you right there. Most features can actually be added via [GitHub Apps](https://developer.github.com/apps/), which extend GitHub and can be installed directly on organizations and user accounts and granted access to specific repositories. They come with granular permissions and built-in webhooks. Apps are first class actors within GitHub.
If you've ever thought, "wouldn't it be cool if GitHub could…"; I'm going to stop you right there. Most features can actually be added via [GitHub Apps](https://docs.github.com/en/developers/apps), which extend GitHub and can be installed directly on organizations and user accounts and granted access to specific repositories. They come with granular permissions and built-in webhooks. Apps are first class actors within GitHub.
## How it works
**Probot is a framework for building [GitHub Apps](http://developer.github.com/apps) in [Node.js](https://nodejs.org/)**, written in [TypeScript](https://www.typescriptlang.org/). GitHub Apps can listen to webhook events sent by a repository or organization. Probot uses its internal event emitter to perform actions based on those events. A simple Probot App might look like this:
**Probot is a framework for building [GitHub Apps](https://docs.github.com/en/developers/apps) in [Node.js](https://nodejs.org/)**, written in [TypeScript](https://www.typescriptlang.org/). GitHub Apps can listen to webhook events sent by a repository or organization. Probot uses its internal event emitter to perform actions based on those events. A simple Probot App might look like this:
```js
module.exports = (app) => {
app.on('issues.opened', async context => {
const issueComment = context.issue({ body: 'Thanks for opening this issue!' })
return context.github.issues.createComment(issueComment)
})
}
export default (app) => {
app.on("issues.opened", async (context) => {
const issueComment = context.issue({
body: "Thanks for opening this issue!",
});
return context.octokit.issues.createComment(issueComment);
});
app.onAny(async (context) => {
context.log.info({ event: context.name, action: context.payload.action });
});
app.onError(async (error) => {
app.log.error(error);
});
};
```
## Building a Probot App
@ -31,7 +44,7 @@ Probot is built by people just like you! Most of the interesting things are buil
If you're interested in contributing to Probot itself, check out our [contributing docs](CONTRIBUTING.md) to get started.
Want to chat with Probot users and contributors? [Join us in Slack](https://probot-slackin.herokuapp.com/)!
Want to discuss with Probot users and contributors? [Discuss on GitHub](https://github.com/probot/probot/discussions)!
## Ideas

View File

@ -1,23 +0,0 @@
{
"name": "Probot",
"description": "a trainable robot that responds to activity on GitHub",
"keywords": ["Probot", "node", "github"],
"repository": "https://github.com/probot/probot",
"logo": "https://github.com/probot.png",
"env": {
"PRIVATE_KEY": {
"description": "the private key you downloaded when creating the GitHub App"
},
"APP_ID": {
"description": "the ID of your GitHub App"
},
"WEBHOOK_SECRET": {
"description": "the secret configured for your GitHub App"
},
"LOG_LEVEL": {
"required": false,
"value": "info",
"description": "trace, debug, info, warn, error, or fatal; default: info"
}
}
}

View File

@ -1,21 +0,0 @@
# Test against the latest version of this Node.js version
environment:
nodejs_version: "8"
# Install scripts. (runs after repo cloning)
install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
# install modules
- npm install
# Post-install test scripts.
test_script:
# Output useful info for debugging.
- node --version
- npm --version
# run tests
- npm test
# Don't actually build.
build: off

3
bin/probot-receive.js Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env node
require("../lib/bin/probot-receive");

View File

@ -1,44 +1,3 @@
#!/usr/bin/env node
require('dotenv').config()
const pkgConf = require('pkg-conf')
const program = require('commander')
const {findPrivateKey} = require('../lib/private-key')
program
.usage('[options] <apps...>')
.option('-p, --port <n>', 'Port to start the server on', process.env.PORT || 3000)
.option('-W, --webhook-proxy <url>', 'URL of the webhook proxy service.`', process.env.WEBHOOK_PROXY_URL)
.option('-w, --webhook-path <path>', 'URL path which receives webhooks. Ex: `/webhook`', process.env.WEBHOOK_PATH)
.option('-a, --app <id>', 'ID of the GitHub App', process.env.APP_ID)
.option('-s, --secret <secret>', 'Webhook secret of the GitHub App', process.env.WEBHOOK_SECRET)
.option('-P, --private-key <file>', 'Path to certificate of the GitHub App', findPrivateKey)
.parse(process.argv)
program.privateKey = findPrivateKey()
const {createProbot} = require('../')
const probot = createProbot({
id: program.app,
secret: program.secret,
cert: program.privateKey,
port: program.port,
webhookPath: program.webhookPath,
webhookProxy: program.webhookProxy
})
const setupMode = !program.app || !program.privateKey
if (setupMode) {
const setupApp = require('../lib/apps/setup')
probot.load(setupApp)
probot.start()
} else {
pkgConf('probot').then(pkg => {
probot.setup(program.args.concat(pkg.apps || pkg.plugins || []))
probot.start()
})
}
require("../lib/bin/probot-run");

View File

@ -1,35 +0,0 @@
#!/usr/bin/env node
// Usage: bin/simulate issues path/to/payload app.js
require('dotenv').config({silent: true})
const path = require('path')
const program = require('commander')
const {findPrivateKey} = require('../lib/private-key')
program
.usage('[options] <event-name> <path/to/payload.json> [path/to/app.js...]')
.option('-a, --app <id>', 'ID of the GitHub App', process.env.APP_ID)
.option('-P, --private-key <file>', 'Path to certificate of the GitHub App', findPrivateKey)
.parse(process.argv)
const eventName = program.args[0]
const payloadPath = program.args[1]
if (!eventName || !payloadPath) {
program.help()
}
const payload = require(path.join(process.cwd(), payloadPath))
const {createProbot} = require('../')
const probot = createProbot({
id: program.app,
cert: findPrivateKey()
})
probot.setup(program.args.slice(2))
probot.logger.debug('Simulating event', eventName)
probot.receive({name: eventName, payload})

View File

@ -1,16 +1,3 @@
#!/usr/bin/env node
const semver = require('semver')
const version = require('../package').engines.node
if (!semver.satisfies(process.version, version)) {
console.log(`Node.js version ${version} is required. You have ${process.version}.`)
process.exit(1)
}
require('commander')
.version(require('../package').version)
.usage('<command> [options]')
.command('run', 'run the bot')
.command('simulate', 'simulate a webhook being delivered')
.parse(process.argv)
require("../lib/bin/probot");

View File

@ -1,17 +1,18 @@
---
next: docs/hello-world.md
permalink: /docs/
next: hello-world
title: Introduction
layout: docs.liquid
---
# Introduction
If you've ever thought, "wouldn't it be cool if GitHub could…"; imma stop you right there. Most features can actually be added via [GitHub Apps](https://developer.github.com/apps/), which extend GitHub and can be installed directly on organizations and user accounts and granted access to specific repositories. They come with granular permissions and built-in webhooks. Apps are first class actors within GitHub.
If you've ever thought, "wouldn't it be cool if GitHub could…"; imma stop you right there. Most features can actually be added via [GitHub Apps](https://docs.github.com/apps/), which extend GitHub and can be installed directly on organizations and user accounts and granted access to specific repositories. They come with granular permissions and built-in webhooks. Apps are first class actors within GitHub.
**Probot is a framework for building [GitHub Apps](http://developer.github.com/apps) in [Node.js](https://nodejs.org/)**. It aims to eliminate all the drudgerylike receiving and validating webhooks, and doing authentication handstandsso you can focus on the features you want to build.
**Probot is a framework for building [GitHub Apps](http://docs.github.com/apps) in [Node.js](https://nodejs.org/)**. It aims to eliminate all the drudgerylike receiving and validating webhooks, and doing authentication handstandsso you can focus on the features you want to build.
Probot apps are easy to write, deploy, and share. Many of the most popular Probot apps are hosted, so there's nothing for you to deploy and manage. Here are just a few examples of things that have been built with Probot:
{% include docs/examples.html %}
{% include "docs/examples" %}
Check out the [featured apps](https://probot.github.io/apps/) or [browse more examples on GitHub](https://github.com/search?q=topic%3Aprobot-app&type=Repositories)

View File

@ -1,4 +1,8 @@
# Best Practices
---
title: Best practices
---
# Best practices
First and foremost, your app must obey the [The Three Laws of Robotics](https://en.wikipedia.org/wiki/Three_Laws_of_Robotics):
@ -11,9 +15,19 @@ Now that we agree that nobody will get hurt, here are some tips to make your app
**Contents:**
<!-- toc -->
- [Empathy](#empathy)
- [Avoid the uncanny valley](#avoid-the-uncanny-valley)
- [Autonomy](#autonomy)
- [Never take bulk actions without explicit permission](#never-take-bulk-actions-without-explicit-permission)
- [Include "dry run" functionality](#include-dry-run-functionality)
- [Configuration](#configuration)
- [Require minimal configuration](#require-minimal-configuration)
- [Provide full configuration](#provide-full-configuration)
- [Store configuration in the repository](#store-configuration-in-the-repository)
<!-- tocstop -->
## Empathy
@ -29,8 +43,8 @@ The [uncanny valley](https://en.wikipedia.org/wiki/Uncanny_valley) is the hypoth
Your app should be empathetic, but it shouldn't pretend to be human. It is an app and everyone that interacts with it knows that.
> - :smile: "Latest build failures: _{listing of build failures}_…"
> - :cry: "Hey there! You asked for the build failures, so I went and dug them up for you: _{listing of build failures}_ … Have a fantastic day!"
> - 😄 "Latest build failures: _{listing of build failures}_…"
> - 😢 "Hey there! You asked for the build failures, so I went and dug them up for you: _{listing of build failures}_ … Have a fantastic day!"
## Autonomy
@ -58,4 +72,52 @@ Apps _should_ allow all settings to be customized for each installation.
### Store configuration in the repository
Any configuration _should_ be stored in the target repository. Unless the app is using files from an established convention, the configuration _should_ be stored in the `.github` directory. See the [API docs for `context.config`](https://probot.github.io/api/latest/classes/context.html#config).
Any configuration _should_ be stored in the target repository. Unless the app is using files from an established convention, the configuration _should_ be stored in the `.github` directory. See the [API docs for `context.config`](https://probot.github.io/api/latest/classes/context.Context.html#config).
`context.config` supports sharing configs between repositories. If configuration for your app is not available in the target repository, it will be loaded from the `.github` directory of the target organization's `.github` repository.
User's can also choose their own shared location. Use the `_extends` option in the configuration file to extend settings from another repository.
For example, given `.github/test.yml`:
```yaml
_extends: github-settings
# Override values from the extended config or define new values
name: myrepo
```
This configuration will be merged with the `.github/test.yml` file from the `github-settings` repository, which might look like this:
```yaml
shared1: will be merged
shared2: will also be merged
```
Just put common configuration keys in a repository within your organization. Then reference this repository from config files with the same name.
You can also reference configurations from other organizations:
```yaml
_extends: other/probot-settings
other: DDD
```
Additionally, you can specify a specific path for the configuration by
appending a colon after the project.
```yaml
_extends: probot-settings:.github/other_test.yaml
other: FFF
```
Inherited configurations are in the **exact same location** within the
repositories.
```yaml
# octocat/repo1:.github/test.yaml
_extends: .github
other: GGG
# octocat/.github:test.yaml
other: HHH
```

View File

@ -1,35 +1,37 @@
---
next: docs/deployment.md
next: testing
title: Configuration
---
# Environment Configuration
# Configuration
When developing a Probot App, you will need to have several different fields in a `.env` file which specify environment variables. Here are some common use cases:
Variable | Description
---|---
`APP_ID` | The App ID assigned to your GitHub App. **Required** <p>_(Example: `1234`)_</p>
**Private Key Options** | One of the following is **Required** if there is no `.pem` file in your project's root directory
`PRIVATE_KEY_PATH` | The path to the `.pem` file for your GitHub App. <p>_(Example: `path/to/key.pem`)_</p>
`PRIVATE_KEY` | The contents of the private key for your GitHub App. If you're unable to use multiline environment variables, use base64 encoding to convert the key to a single line string. See the [Deployment](deployment.md) docs for provider specific usage. |
**Webhook Options** |
`WEBHOOK_PROXY_URL` | Allows your local development environment to receive GitHub webhook events. Go to https://smee.io/new to get started. <p>_(Example: `https://smee.io/your-custom-url`)_</p>
`WEBHOOK_SECRET` | The webhook secret used when creating a GitHub App. 'development' is used as a default, but the value in `.env` needs to match the value configured in your App settings on GitHub. Note: GitHub marks this value as optional, but for optimal security it's required for Probot apps. **Required** <p>_(Example: `development`)_</p>
| Environment Variable | [Programmatic Argument](/docs/development/#run-probot-programmatically) | Description |
| ----------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `APP_ID` | `new Probot({ appId })` | The App ID assigned to your GitHub App. **Required** <p>_(Example: `1234`)_</p> |
| `PRIVATE_KEY` or `PRIVATE_KEY_PATH` | `new Probot({ privateKey })` | The contents of the private key for your GitHub App. If you're unable to use multiline environment variables, use base64 encoding to convert the key to a single line string. See the [Deployment](/docs/deployment) docs for provider specific usage. When using the `PRIVATE_KEY_PATH` environment variable, set it to the path of the `.pem` file that you downloaded from your GitHub App registration. <p>_(Example: `path/to/key.pem`)_</p> |
| **Webhook options** | |
| `WEBHOOK_PROXY_URL` | `new Server({ webhookProxy })` | Allows your local development environment to receive GitHub webhook events. Go to https://smee.io/new to get started. <p>_(Example: `https://smee.io/your-custom-url`)_</p> |
| `WEBHOOK_SECRET` | `new Probot({ secret })` | The webhook secret used when creating a GitHub App. 'development' is used as a default, but the value in `.env` needs to match the value configured in your App settings on GitHub. Note: GitHub marks this value as optional, but for optimal security it's required for Probot apps. **Required** <p>_(Example: `development`)_</p> |
For more on the set up of these items, check out [Configuring a GitHub App](https://probot.github.io/docs/development/#configuring-a-github-app).
For more on the set up of these items, check out [Configuring a GitHub App](/docs/development/#configuring-a-github-app).
Some less common environment variables are:
Variable | Description
---|---
`DISABLE_STATS` | Set to `true` to disable the the `/probot/stats` endpoint, which gathers data about each app. Recommend for apps with a lot of installations. <p>_(Example: `true`)_</p>
`GHE_HOST` | The hostname of your GitHub Enterprise instance. <p>_(Example: `github.mycompany.com`)_</p>
`IGNORED_ACCOUNTS` | A comma-separated list of GitHub account names to ignore. This is currently used by the `/probot/stats`. By marking an account as ignored, that account will not be included in data collected on the website. The primary use case for this is spammy or abusive users that the GitHub API sends us but who 404. <p>_(Example: `spammyPerson,abusiveAccount`)_</p>
`LOG_FORMAT` | By default, logs are formatted for readability in development. You can set this to `short`, `long`, `simple`, `json`, `bunyan`. Default: `short`
`LOG_LEVEL` | The verbosity of logs to show when running your app, which can be `trace`, `debug`, `info`, `warn`, `error`, or `fatal`. Default: `info`
`PORT` | The port to start the local server on. Default: `3000`.
`SENTRY_DSN` | Set to a [Sentry](https://sentry.io/) DSN to report all errors thrown by your app. <p>_(Example: `https://1234abcd@sentry.io/12345`)_</p>
`WEBHOOK_PATH` | The URL path which will recieve webhooks. Default: `/`.
`INSTALLATION_TOKEN_TTL` | The length of time installation access tokens are cached by Probot. This may be useful if your app is running long processes before accessing `context.github`. Default: `3540` (59 minutes) <p>_(Example: `3300` or 55 minutes)_</p>
| Environment Variable | [Programmatic Argument](/docs/development/#run-probot-programmatically) | Description |
| --------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GHE_HOST` | `new Probot({ Octokit: ProbotOctokit.defaults({ baseUrl }) })` | The hostname of your GitHub Enterprise instance. <p>_(Example: `github.mycompany.com`)_</p> |
| `GHE_PROTOCOL` | `new Probot({ Octokit: ProbotOctokit.defaults({ baseUrl }) })` | The protocol of your GitHub Enterprise instance. Defaults to HTTPS. Do not change unless you are certain. <p>_(Example: `https`)_</p> |
| `GH_ORG` | - | The organization where you want to register the app in the app creation manifest flow. If set, the app is registered for an organization (https://github.com/organizations/ORGANIZATION/settings/apps/new), if not set, the GitHub app would be registered for the user account (https://github.com/settings/apps/new). |
| `LOG_FORMAT` | - | By default, logs are formatted for readability in development. You can set this to `json` in order to disable the formatting |
| `LOG_LEVEL` | `const log = require('pino')({ level })` | The verbosity of logs to show when running your app, which can be `fatal`, `error`, `warn`, `info`, `debug`, `trace` or `silent`. Default: `info` |
| `LOG_LEVEL_IN_STRING` | - | By default, when using the `json` format, the level printed in the log records is an int (`10`, `20`, ..). This option tells the logger to print level as a string: `{"level": "info"}`. Default `false` |
| `LOG_MESSAGE_KEY` | `const log = require('pino')({ messageKey: 'msg' })` | Only relevant when `LOG_FORMAT` is set to `json`. Sets the json key for the log message. Default: `msg` |
| `SENTRY_DSN` | - | Set to a [Sentry](https://sentry.io/) DSN to report all errors thrown by your app. <p>_(Example: `https://1234abcd@sentry.io/12345`)_</p> |
| `PORT` | `new Server({ port })` | The port to start the local server on. Default: `3000` |
| `HOST` | `new Server({ host })` | The host to start the local server on. |
| `WEBHOOK_PATH` | `new Server({ webhookPath })` | The URL path which will receive webhooks. Default: `/api/github/webhooks` |
| `REDIS_URL` | `new Probot({ redisConfig })` | Set to a `redis://` url as connection option for [ioredis](https://github.com/luin/ioredis#connect-to-redis) in order to enable [cluster support for request throttling](https://github.com/octokit/plugin-throttling.js#clustering). <p>_(Example: `redis://:secret@redis-123.redislabs.com:12345/0`)_</p> |
For more information on the formatting conventions and rules of `.env` files, check out [the npm `dotenv` module's documentation](https://www.npmjs.com/package/dotenv#rules).

View File

@ -1,5 +1,6 @@
---
next: docs/persistence.md
next: http
title: Deployment
---
# Deployment
@ -10,23 +11,36 @@ Every app can either be deployed stand-alone, or combined with other apps in one
**Contents:**
1. [Create the GitHub App](#create-the-github-app)
1. [Deploy the app](#deploy-the-app)
1. [Glitch](#glitch)
1. [Heroku](#heroku)
1. [Now](#now)
1. [Share the app](#share-the-app)
1. [Combining apps](#combining-apps)
1. [Error tracking](#error-tracking)
<!-- toc -->
## Create the GitHub App
- [Register the GitHub App](#register-the-github-app)
- [Deploy the app](#deploy-the-app)
- [As node app](#as-node-app)
- [Glitch](#glitch)
- [Heroku](#heroku)
- [As serverless function](#as-serverless-function)
- [AWS Lambda](#aws-lambda)
- [Azure Functions](#azure-functions)
- [Google Cloud Functions](#google-cloud-functions)
- [GitHub Actions](#github-actions)
- [Begin](#begin)
- [Vercel](#vercel)
- [Netlify Functions](#netlify-functions)
- [Share the app](#share-the-app)
- [Combining apps](#combining-apps)
- [Error tracking](#error-tracking)
Every deployment will need an [App](https://developer.github.com/apps/).
<!-- tocstop -->
1. [Create a new GitHub App](https://github.com/settings/apps/new) with:
- **Homepage URL**: the URL to the GitHub repository for your app
- **Webhook URL**: Use `https://example.com/` for now, we'll come back in a minute to update this with the URL of your deployed app.
- **Webhook Secret**: Generate a unique secret with `openssl rand -base64 32` and save it because you'll need it in a minute to configure your deployed app.
## Register the GitHub App
Every deployment will need a [GitHub App registration](https://docs.github.com/apps).
1. [Register a new GitHub App](https://github.com/settings/apps/new) with:
- **Homepage URL**: the URL to the GitHub repository for your app
- **Webhook URL**: Use `https://example.com/` for now, we'll come back in a minute to update this with the URL of your deployed app.
- **Webhook Secret**: Generate a unique secret with (e.g. with `openssl rand -base64 32`) and save it because you'll need it in a minute to configure your Probot app.
1. Download the private key from the app.
@ -46,7 +60,11 @@ And one of:
`PRIVATE_KEY` takes precedence over `PRIVATE_KEY_PATH`.
### Glitch
### As node app
Probot can run your app function using the `probot` binary. If your app function lives in `./app.js`, you can start it as node process using `probot run ./app.js`
#### Glitch
Glitch lets you host node applications for free and edit them directly in your browser. Its great for experimentation and entirely sufficient for simple apps.
@ -65,77 +83,246 @@ Glitch lets you host node applications for free and edit them directly in your b
Enjoy!
**Bonus:** You can deploy your app using [glitch-deploy](https://github.com/gr2m/glitch-deploy) directly from your terminal or as [continuous deployment](https://github.com/gr2m/glitch-deploy#deploy-from-ci).
#### Heroku
### Heroku
Probot runs like [any other Node app](https://devcenter.heroku.com/articles/deploying-nodejs) on Heroku. After [creating the GitHub App](#register-the-github-app):
Probot runs like [any other Node app](https://devcenter.heroku.com/articles/deploying-nodejs) on Heroku. After [creating the GitHub App](#create-the-github-app):
1. Make sure you have the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli) client installed.
1. Make sure you have the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli) client installed.
1. Clone the app that you want to deploy. e.g. `git clone https://github.com/probot/stale`
1. Clone the app that you want to deploy. e.g. `git clone https://github.com/probot/stale`
1. Create the Heroku app with the `heroku create` command:
1. Create the Heroku app with the `heroku create` command:
$ heroku create
Creating arcane-lowlands-8408... done, stack is cedar
http://arcane-lowlands-8408.herokuapp.com/ | git@heroku.com:arcane-lowlands-8408.git
Git remote heroku added
1. Go back to your [app settings page](https://github.com/settings/apps) and update the **Webhook URL** to the URL of your deployment, e.g. `http://arcane-lowlands-8408.herokuapp.com/`.
1. Go back to your [app settings page](https://github.com/settings/apps) and update the **Webhook URL** to the URL of your deployment, e.g. `http://arcane-lowlands-8408.herokuapp.com/`.
1. Configure the Heroku app, replacing the `APP_ID` and `WEBHOOK_SECRET` with the values for those variables, and setting the path for the `PRIVATE_KEY`:
1. Configure the Heroku app, replacing the `APP_ID` and `WEBHOOK_SECRET` with the values for those variables, and setting the path for the `PRIVATE_KEY`:
$ heroku config:set APP_ID=aaa \
WEBHOOK_SECRET=bbb \
PRIVATE_KEY="$(cat ~/Downloads/*.private-key.pem)"
1. Deploy the app to heroku with `git push`:
1. Deploy the app to heroku with `git push`:
$ git push heroku master
$ git push heroku main
...
-----> Node.js app detected
...
-----> Launching... done
http://arcane-lowlands-8408.herokuapp.com deployed to Heroku
1. Your app should be up and running! To verify that your app
is receiving webhook data, you can tail your app's logs:
1. Your app should be up and running! To verify that your app
is receiving webhook data, you can tail your app's logs:
$ heroku config:set LOG_LEVEL=trace
$ heroku logs --tail
$ heroku config:set LOG_LEVEL=trace
$ heroku logs --tail
### Now
### As serverless function
Zeit [Now](http://zeit.co/now) is a great service for running Probot apps. After [creating the GitHub App](#create-the-github-app):
When deploying your Probot app to a serverless/function environment, you don't need to worry about handling the http webhook requests coming from GitHub, the platform takes care of that. In many cases you can use [`createNodeMiddleware`](/docs/development/#use-createNodeMiddleware) directly, e.g. for Vercel or Google Cloud Function.
1. Install the now CLI with `npm i -g now`
```js
import { Probot, createProbot } from "probot";
import { createMyMiddleware } from "my-probot-middleware";
import myApp from "./my-app.js";
1. Clone the app that you want to deploy. e.g. `git clone https://github.com/probot/stale`
export default createMyMiddleware(myApp, { probot: createProbot() });
```
1. Run `now` to deploy, replacing the `APP_ID` and `WEBHOOK_SECRET` with the values for those variables, and setting the `PRIVATE_KEY`:
For other environments such as AWS Lambda, Netlify Functions or GitHub Actions, you can use one of [Probot's adapters](https://github.com/probot/?q=adapter).
$ now -e APP_ID=aaa \
-e WEBHOOK_SECRET=bbb \
-e NODE_ENV=production \
-e PRIVATE_KEY="$(cat ~/Downloads/*.private-key.pem | base64)"
#### AWS Lambda
**NOTE**: Add `-e LOG_LEVEL=trace` to get verbose logging, or add `-e LOG_LEVEL=info` instead to show less details.
```js
// handler.js
import {
createLambdaFunction,
createProbot,
} from "@probot/adapter-aws-lambda-serverless";
import appFn from "./app.js";
1. Once the deploy is started, go back to your [app settings page](https://github.com/settings/apps) and update the **Webhook URL** to the URL of your deployment (which `now` has kindly copied to your clipboard).
export const webhooks = createLambdaFunction(appFn, {
probot: createProbot(),
});
```
1. Your app should be up and running! For long term use, create an alias for your app. After making an alias, you can swap to new deploy URLs with no downtime.
Learn more
$ now alias set https://your-generated-url.now.sh https://a-fancier-url.now.sh
- Probot's official adapter for AWS Lambda using the Serverless framework: [@probot/adapter-aws-lambda-serverless](https://github.com/probot/adapter-aws-lambda-serverless#readme)
1. You can also keep your app running forever, with instant response to webhooks with:
Examples
$ now scale https://a-fancier-url.now.sh 1
- Probot's "Hello, world!" example deployed to AWS Lambda: [probot/example-aws-lambda-serverless](https://github.com/probot/example-aws-lambda-serverless/#readme)
- Issue labeler bot deployed to AWS Lambda: [riyadhalnur/issuelabeler](https://github.com/riyadhalnur/issuelabeler#issuelabeler)
- Auto-Me-Bot is deployed to AWS Lambda without using the _serverless_ framework and adapter: [TomerFi/auto-me-bot](https://github.com/TomerFi/auto-me-bot)
Please add yours!
#### Azure Functions
```js
// ProbotFunction/index.js
import {
createProbot,
createAzureFunction,
} from "@probot/adapter-azure-functions";
import app from "../app.js";
export default createAzureFunction(app, { probot: createProbot() });
```
Learn more
- Probot's official adapter for Azure functions: [@probot/adapter-azure-functions](https://github.com/probot/adapter-azure-functions#readme)
Examples
- Probot's "Hello, world!" example deployed to Azure functions: [probot/example-azure-function](https://github.com/probot/example-azure-function/#readme)
Please add yours!
#### Google Cloud Functions
```js
// function.js
import { createNodeMiddleware, createProbot } from "probot";
import app from "./app.js";
exports.probotApp = createNodeMiddleware(app, { probot: createProbot() });
```
Examples
- Probot's "Hello, world!" example deployed to Google Cloud Functions: [probot/example-google-cloud-function](https://github.com/probot/example-google-cloud-function#readme)
Please add yours!
#### GitHub Actions
```js
import { run } from "@probot/adapter-github-actions";
import app from "./app.js";
run(app);
```
Learn more
- Probot's official adapter for GitHub Actions: [@probot/adapter-github-actions](https://github.com/probot/adapter-github-actions#readme)
Examples
- Probot's "Hello, world!" example deployed as a GitHub Action: [probot/example-github-action](https://github.com/probot/example-github-action/#readme)
Please add yours!
#### Begin
[Begin](https://begin.com/) is a service to deploy serverless applications build using the [Architect](https://arc.codes/) to AWS.
1. Add the `@http` pragma to your `app.arc` file
```
@app
my-app-name
@http
post /api/github/webhooks
```
2. Make sure to [configure your app](../confinguration) using environment variables
3. Create the `src/http/post-api-github-webhooks` folder with the following files
```js
{
"name": "http-post-api-github-webhooks",
"dependencies": {}
}
```
in the new directory, install the `probot` and `@architect/functions`
```
cd src/http/post-api-github-webhooks
npm install probot @architect/functions
```
4. Create `src/http/post-api-github-webhooks/app.js` with your Probot application function, e.g.
```
/**
* @param {import('probot').Probot} app
*/
export default (app) => {
app.log("Yay! The app was loaded!");
app.on("issues.opened", async (context) => {
return context.octokit.issues.createComment(
context.issue({ body: "Hello, World!" })
);
});
};
```
5. Create `src/http/post-api-github-webhooks/index.js` with the request handler. See [/probot/example-begin/src/http/post-api-github-webhooks/index.js](https://github.com/probot/example-begin/blob/main/src/http/post-api-github-webhooks/index.js) for an example.
Examples
- [probot/example-begin](https://github.com/probot/example-begin#readme)
Please add yours!
#### Vercel
```js
// api/github/webhooks/index.js
import { createNodeMiddleware, createProbot } from "probot";
import app from "../../../app.js";
export default createNodeMiddleware(app, {
probot: createProbot(),
webhooksPath: "/api/github/webhooks",
});
```
**Important:** Set `NODEJS_HELPERS` environment variable to `0` in order to prevent Vercel from parsing the response body.
See [Disable Helpers](https://vercel.com/docs/functions/runtimes/node-js#disabling-helpers-for-node.js) for detail.
Examples
- [probot/example-vercel](https://github.com/probot/example-vercel#readme)
- [wip/app](https://github.com/wip/app#readme)
- [all-contributors/app](https://github.com/all-contributors/app#readme)
- [probot-nextjs-starter](https://github.com/maximousblk/probot-nextjs-starter#readme)
Please add yours!
#### Netlify Functions
[Netlify Functions](https://www.netlify.com/products/functions/) are deployed on AWS by Netlify itself. So we can use `@probot/adapter-aws-lambda-serverless` adapter for Netlify Functions as well.
```js
// functions/index.js
import {
createLambdaFunction,
createProbot,
} from "@probot/adapter-aws-lambda-serverless";
import appFn from "../src/app";
export const handler = createLambdaFunction(appFn, {
probot: createProbot(),
});
```
## Share the app
The Probot website includes a list of [featured apps](https://probot.github.io/apps). Consider [adding your app to the website](https://github.com/probot/probot.github.io/blob/master/CONTRIBUTING.md#adding-your-app
) so others can discover and use it.
The Probot website includes a list of [featured apps](https://probot.github.io/apps). Consider [adding your app to the website](https://github.com/probot/probot.github.io/blob/master/CONTRIBUTING.md#adding-your-app) so others can discover and use it.
## Combining apps
@ -151,20 +338,28 @@ To deploy multiple apps in one instance, create a new app that has the existing
},
"scripts": {
"start": "probot run"
},
"probot": {
"apps": [
"probot-autoresponder",
"probot-settings"
]
}
},
"probot": {
"apps": ["probot-autoresponder", "probot-settings"]
}
}
```
Note that this feature is only supported when [run as Node app](#as-node-app). For serverless/function deployments, create a new Probot app that combines others programmatically
```js
// app.js
import autoresponder from "probot-autoresponder";
import settings from "probot-settings";
export default async (app, options) => {
await autoresponder(app, options);
await settings(app, options);
};
```
## Error tracking
Probot comes bundled with a client for the [Sentry](https://github.com/getsentry/sentry) exception tracking platform. To enable Sentry:
Probot logs messages using [pino](https://getpino.io/). There is a growing number of tools that consume these logs and send them to error tracking services: https://getpino.io/#/docs/transports.
1. [Install Sentry from Marketplace](https://github.com/marketplace/sentry) (with [10k events/month free](https://github.com/marketplace/sentry/plan/MDIyOk1hcmtldHBsYWNlTGlzdGluZ1BsYW40Nw==#pricing-and-setup)) or [host your own instance](https://github.com/getsentry/sentry) (Students can get [extra Sentry credit](https://education.github.com/pack))
2. Follow the setup instructions to find your DSN.
3. Set the `SENTRY_DSN` environment variable with the DSN you retrieved.
By default, Probot can send errors to [Sentry](https://sentry.io/) using its own transport [`@probot/pino`](https://github.com/probot/pino/#readme). Set the `SENTRY_DSN` environment variable to enable it.

View File

@ -1,44 +1,86 @@
---
next: docs/webhooks.md
next: webhooks
title: Developing an app
---
# Developing an app
To develop a Probot app, you will first need a recent version of [Node.js](https://nodejs.org/) installed. Open a terminal and run `node -v` to verify that it is installed and is at least 8.3.0 or later. Otherwise, [install the latest version](https://nodejs.org/).
To develop a Probot app, you will first need a recent version of [Node.js](https://nodejs.org/) installed. Open a terminal and run `node -v` to verify that it is installed and is at least 18.0.0 or later. Otherwise, [install the latest version](https://nodejs.org/).
**Contents:**
<!-- toc -->
- [Generating a new app](#generating-a-new-app)
- [Running the app locally](#running-the-app-locally)
- [Configuring a GitHub App](#configuring-a-github-app)
- [Manually Configuring a GitHub App](#manually-configuring-a-github-app)
- [Installing the app on a repository](#installing-the-app-on-a-repository)
- [Debugging](#debugging)
- [Run Probot programmatically](#run-probot-programmatically)
- [Use run](#use-run)
- [Use server](#use-server)
- [Use createNodeMiddleware](#use-createnodemiddleware)
- [Use probot](#use-probot)
- [Use Probot's Octokit](#use-probots-octokit)
<!-- tocstop -->
## Generating a new app
[create-probot-app](https://github.com/probot/create-probot-app) is the best way to start building a new app. It will generate a new app with everything you need to get started and run your app in production.
To get started, run one of these commands:
To get started, run:
- If you're using npm: `$ npx create-probot-app [--typescript] my-first-app`
- or, if you're using Yarn: `$ yarn create probot-app [--typescript] my-first-app`
```
npx create-probot-app my-first-app
```
This will ask you a series of questions about your app, which should look something like this:
```
Let's create a Probot app!
? App name: my-first-app
? Description of app: A "Hello World" GitHub App built with Probot
? Description of app: A 'Hello World' GitHub App built with Probot.
? Author's full name: Katie Horne
? Author's email address: katie@auth0.com
? Homepage:
? GitHub user or org name: khorne3
? Repository name: my-first-app
created files...
? Which template would you like to use? (Use arrow keys)
basic-js
basic-ts (use this one for TypeScript support)
checks-js
git-data-js
deploy-js
Finished scaffolding files!
Installing Node dependencies!
Installing dependencies. This may take a few minutes...
Done! Enjoy building your Probot app!
Successfully created my-first-app.
Begin using your app with:
cd my-first-app
npm start
View your app's README for more usage instructions.
Visit the Probot docs:
https://probot.github.io/docs/
Get help from the community:
https://probot.github.io/community/
Enjoy building your Probot app!
```
The most important files note here are `index.js`, which is where the code for your app will go, and `package.json`, which makes this a standard [npm module](https://docs.npmjs.com/files/package.json).
The most important files created are `index.js`, which is where the code for your app will go, and `package.json`, which makes the app a standard [npm module](https://docs.npmjs.com/files/package.json).
## Running the app locally
Now you're ready to run the app on your local machine. Run `npm run dev` to start the server:
Now you're ready to run the app on your local machine. Run `npm start` to start the server:
> Note: If you're building a TypeScript app, be sure to run `npm run build` first!
```
> testerino@1.0.0 dev /Users/hiimbex/Desktop/testerino
@ -50,58 +92,204 @@ Now you're ready to run the app on your local machine. Run `npm run dev` to star
[nodemon] starting `npm start`
> testerino@1.0.0 start /Users/hiimbex/Desktop/testerino
> my-first-app@1.0.0 start /private/var/folders/hs/x9qtfmvn1lz1sgml9q21h7k80000gn/T/tmp.bgzYr6j1/my-first-app
> probot run ./index.js
01:57:22.365Z INFO probot:
Welcome to Probot! Go to http://localhost:3000 to get started.
01:57:22.428Z INFO probot: Forwarding https://smee.io/1S10MsoRSjZKWKMt to http://localhost:3000/
01:57:22.431Z INFO probot: Listening on http://localhost:3000
01:57:22.564Z INFO probot: Connected https://smee.io/1S10MsoRSjZKWKMt
INFO (probot):
INFO (probot): Welcome to Probot!
INFO (probot): Probot is in setup mode, webhooks cannot be received and
INFO (probot): custom routes will not work until APP_ID and PRIVATE_KEY
INFO (probot): are configured in .env.
INFO (probot): Please follow the instructions at http://localhost:3000 to configure .env.
INFO (probot): Once you are done, restart the server.
INFO (probot):
INFO (server): Running Probot v11.0.5 (Node.js: v15.5.1)
INFO (server): Listening on http://localhost:3000
```
The `dev` script will start your app using [nodemon](https://github.com/remy/nodemon#nodemon), which will watch for any files changes in your local development environment and automatically restart the server.
## Configuring a GitHub App
To automatically configure your GitHub App, follow these steps:
1. Run the app locally by running `npm run dev`.
1. Next follow instructions to visit localhost:3000 (or your custom Glitch URL).
1. You should see something like this: <img width="625" alt="screen shot 2018-09-25 at 10 01 28 pm" src="https://user-images.githubusercontent.com/13410355/46052950-a19e2900-c10e-11e8-9e7e-0c803b8ca35c.png">
1. Run the app locally by running `npm start` in your terminal.
1. Next follow instructions to visit [http://localhost:3000](http://localhost:3000) (or your custom Glitch URL).
1. You should see something like this: ![Screenshot of Probot's setup wizard](/assets/img/probot-setup-wizard.png)
1. Go ahead and click the **Register a GitHub App** button.
1. Next you'll get to decide on an app name that isn't already taken.
1. After registering your GitHub App, you'll be redirected to install the app on any repos. At the same time, you can check your local `.env` and notice it will be populated with values GitHub sends us in the course of that redirect.
1. Install the app on a test repo and try triggering a webhook to activate the bot!
1. Next, you'll get to decide on an app name that isn't already taken. Note: if you see a message "Name is already in use" although no such app exists, it means that a GitHub organization with that name exists and cannot be used as an app name.
1. After registering your GitHub App, you'll be redirected to install the app on any repositories. At the same time, you can check your local `.env` and notice it will be populated with values GitHub sends us in the course of that redirect.
1. Restart the server in your terminal (press <kbd>ctrl</kbd> + <kbd>c</kbd> to stop the server)
1. Install the app on a test repository.
1. Try triggering a webhook to activate the bot!
1. You're all set! Head down to [Debugging](#debugging) to learn more about developing your Probot App.
GitHub App Manifests--otherwise known as easy app creation--make it simple to generate all the settings necessary for a GitHub App. This process abstracts the [Configuring a GitHub App](#configuring-a-github-app) section. You can learn more about how GitHub App Manifests work and how to change your settings for one via the [GitHub Developer Docs](https://developer.github.com/apps/building-github-apps/creating-github-apps-from-a-manifest/).
GitHub App Manifests--otherwise known as easy app creation--make it simple to generate all the settings necessary for a GitHub App. This process abstracts the [Configuring a GitHub App](#configuring-a-github-app) section. You can learn more about how GitHub App Manifests work and how to change your settings for one via the [GitHub Developer Docs](https://docs.github.com/en/developers/apps/creating-a-github-app-from-a-manifest/).
## Manually Configuring a GitHub App
> If you created an App with a manifest, you can skip this section; your app is already configured! If you ever need to edit those settings, you can visit `https://github.com/settings/apps/your-app-name`
> If you created an App with a manifest, you can skip this section; your app is already configured! If you ever need to edit those settings, you can visit `https://github.com/settings/apps/[your-app-name]`
To run your app in development, you will need to configure a GitHub App to deliver webhooks to your local machine.
To run your app in development, you will need to configure a GitHub App's `APP_ID`, `PRIVATE_KEY`, `WEBHOOK_SECRET`, and `WEBHOOK_PROXY_URL` in order to receive webhooks to your local machine.
1. On your local machine, copy `.env.example` to `.env`.
1. Go to [smee.io](https://smee.io) and click **Start a new channel**. Set `WEBHOOK_PROXY_URL` in `.env` to the URL that you are redirected to.
1. On your local machine, copy `.env.example` to `.env` in the same directory. We're going to be changing a few things in this new file.
1. Go to [smee.io](https://smee.io) and click **Start a new channel**. Set `WEBHOOK_PROXY_URL` to the URL that you are redirected to.<br/>
E.g. `https://smee.io/AbCd1234EfGh5678`
1. [Create a new GitHub App](https://github.com/settings/apps/new) with:
- **Webhook URL**: Use your `WEBHOOK_PROXY_URL` from the previous step.
- **Webhook Secret:** `development` (Note: For optimal security, Probot apps **require** this secret be set, even though it's optional on GitHub.).
- **Permissions & events** is located lower down the page and will depend on what data you want your app to have access to. Note: if, for example, you only enable issue events, you will not be able to listen on pull request webhooks with your app. However, for development we recommend enabling everything.
1. Download the private key and move it to your project's directory. As long as it's in the root of your project, Probot will find it automatically regardless of the filename.
1. Edit `.env` and set `APP_ID` to the ID of the app you just created. The App ID can be found in your app settings page here <img width="1048" alt="screen shot 2017-08-20 at 8 31 31 am" src="https://user-images.githubusercontent.com/5713670/42248717-f6bf4f10-7edb-11e8-8dd5-387181c771bc.png">
- **Webhook URL**: Use the same `WEBHOOK_PROXY_URL` from the previous step.
- **Webhook Secret:** `development`, or whatever you set for this in your `.env` file. (Note: For optimal security, Probot apps **require** this secret be set, even though it's optional on GitHub.).
- **Permissions & events** is located lower down the page and will depend on what data you want your app to have access to. Note: if, for example, you only enable issue events, you will not be able to listen on pull request webhooks with your app. However, for development, we recommend enabling everything.
1. You must now set `APP_ID` in your `.env` to the ID of the app you just created. The App ID can be found in on top of your apps settings page.
1. Finally, generate and download a private key file (using the button seen in the image above), then move it to your project's directory. As long as it's in the root of your project, Probot will find it automatically regardless of the filename.
## Installing the app on a repo
For more information about these and other available keys, head over to the [environmental configuration documentation](https://probot.github.io/docs/configuration/).
## Installing the app on a repository
You'll need to create a test repository and install your app by clicking the "Install" button on the settings page of your app, e.g. `https://github.com/apps/your-app`
**Other available scripts**
* `$ npm start` to start your app without watching files.
* `$ npm run lint` to lint your code using [standard](https://www.npmjs.com/package/standard).
## Debugging
1. Always run `$ npm install` and restart the server if `package.json` has changed.
1. To turn on verbose logging, start server by running: `$ LOG_LEVEL=trace npm start`
1. To turn on verbose logging, start the server by running: `$ LOG_LEVEL=trace npm start`
## Run Probot programmatically
### Use run
If you take a look to the `npm start` script, this is what it runs: `probot run ./index.js`. This is nice, but you sometimes need more control over how your Node.js application is executed. For example, if you want to use custom V8 flags, `ts-node`, etc. you need more flexibility. In those cases there's a simple way of executing your probot application programmatically:
```js
// main.js
import { run } from "probot";
import app from "./index.js";
// pass a probot app function
run(app);
```
You can also set your own [`Octokit` constructor](https://github.com/octokit/core.js) with custom plugins and a custom logger while still reading default options from `.env`:
```js
// main.js
import { run } from "probot";
import app from "./index.js";
// pass a probot app function by overriding Probot options
run(app, {
Octokit: ProbotOctokit.plugin(myPlugin),
log: pino(),
});
```
Now you can run `main.js` however you want.
### Use server
The [`run`](https://github.com/probot/probot/blob/master/src/run.ts) function that gets executed when running `probot run ./index.js` does two main things
1. Read configuration from environment variables and local files
2. Start a [`Server`](https://probot.github.io/api/latest/classes/server_server.Server.html) instance
If you need more control over the Server instance, you can use the `Server` class directly:
```js
import { Server, Probot } from "probot";
import app from "./index.js";
async function startServer() {
const server = new Server({
Probot: Probot.defaults({
appId: 123,
privateKey: "content of your *.pem file here",
secret: "webhooksecret123",
}),
});
await server.load(app);
server.start();
}
```
The `server` instance gives you access to the express app instance (`server.expressApp`) as well as the [`Probot`](https://probot.github.io/api/latest/classes/probot.Probot.html) instance (`server.probotApp`).
### Use createNodeMiddleware
If you have your own server or deploy to a serverless environment that supports loading [Express-style middleware](https://expressjs.com/en/guide/using-middleware.html) or Node's http middleware (`(request, response) => { ... }`), you can use `createNodeMiddleware`.
```js
import { createNodeMiddleware, Probot } from "probot";
import app from "./index.js";
const probot = new Probot({
appId: 123,
privateKey: "content of your *.pem file here",
secret: "webhooksecret123",
});
export default createNodeMiddleware(app, { probot });
```
If you want to read probot's configuration from the same environment variables as [`run`](#run), use the [`createProbot`](https://probot.github.io/api/latest/index.html#createprobot) export
```js
import { createNodeMiddleware, createProbot } from "probot";
import app from "./index.js";
export default createNodeMiddleware(app, { probot: createProbot() });
```
By default, `createNodeMiddleware()` uses `/api/github/webhooks` as the webhook endpoint. To customize this behaviour, you can use the `webhooksPath` option.
```js
export default createNodeMiddleware(app, {
probot: createProbot(),
webhooksPath: "/path/to/webhook/endpoint",
});
```
### Use probot
If you don't use Probot's http handling in order to receive and verify events from GitHub via webhook requests, you can use the [`Probot`](https://probot.github.io/api/latest/classes/probot.Probot.html) class directly.
```js
import { Probot } from "probot";
import app from "./index.js";
async function example() {
const probot = new Probot({
appId: 123,
privateKey: "content of your *.pem file here",
secret: "webhooksecret123",
});
await probot.load(app);
// https://github.com/octokit/webhooks.js/#webhooksreceive
probot.webhooks.receive({
id: '123',
name: 'issues',
payload: { ... }
})
}
```
Using the `Probot` class directly is great for [writing tests](/docs/testing) for your Probot app function. It permits you to pass a custom logger to test for log output, disable throttling, request retries, and much more.
### Use Probot's Octokit
Sometimes you may need to create your own instance of Probot's internally used [Octokit](https://github.com/octokit/rest.js/#readme) class, for example when using the [OAuth user authorization flow](https://docs.github.com/en/developers/apps/identifying-and-authorizing-users-for-github-apps). You may access the class by importing `ProbotOctokit`:
```js
import { ProbotOctokit } from "probot";
function myProbotApp(app) {
const octokit = new ProbotOctokit({
// any options you'd pass to Octokit
auth: {
token: "yourToken",
},
// and a logger
log: app.log.child({ name: "my-octokit" }),
});
}
```

View File

@ -1,47 +1,19 @@
---
next: docs/configuration.md
next: persistence
title: Extensions
---
# Extensions
While Probot doesn't have an official extension API (yet), there are a handful of reusable utilities that have been extracted from existing apps.
While Probot doesn't have an official extension API, there are a handful of reusable utilities that have been extracted from existing apps.
## Config
<!-- toc -->
[probot-config](https://github.com/getsentry/probot-config) is an extension for sharing configs between repositories.
- [Commands](#commands)
- [Metadata](#metadata)
- [Attachments](#attachments)
```js
const getConfig = require('probot-config')
module.exports = app => {
app.on('push', async context => {
// Will look for 'test.yml' inside the '.github' folder
const config = await getConfig(context, 'test.yml')
context.log(config, 'Loaded config')
})
}
```
Use the `_extends` option in your configuration file to extend settings from another repository.
For example, given `.github/test.yml`:
```yaml
_extends: github-settings
# Override values from the extended config or define new values
name: myrepo
```
This configuration will be merged with the `.github/test.yml` file from the `github-settings` repository, which might look like this:
```yaml
shared1: will be merged
shared2: will also be merged
```
Just put common configuration keys in a repository within your organization. Then reference this repository from config files with the same name.
<!-- tocstop -->
## Commands
@ -50,15 +22,15 @@ Just put common configuration keys in a repository within your organization. The
For example, users could add labels from comments by typing `/label in-progress`.
```js
const commands = require('probot-commands')
import commands from "probot-commands";
module.exports = app => {
export default (app) => {
// Type `/label foo, bar` in a comment box for an Issue or Pull Request
commands(app, 'label', (context, command) => {
const labels = command.arguments.split(/, */)
return context.github.issues.addLabels(context.issue({labels}))
})
}
commands(app, "label", (context, command) => {
const labels = command.arguments.split(/, */);
return context.octokit.issues.addLabels(context.issue({ labels }));
});
};
```
## Metadata
@ -68,60 +40,40 @@ module.exports = app => {
For example, here is a contrived app that stores the number of times that comments were edited in a discussion and comments with the edit count when the issue is closed.
```js
const metadata = require('probot-metadata')
import metadata from "probot-metadata";
module.exports = app => {
app.on(['issues.edited', 'issue_comment.edited'], async context => {
const kv = await metadata(context)
await kv.set('edits', await kv.get('edits') || 1)
})
export default (app) => {
app.on(["issues.edited", "issue_comment.edited"], async (context) => {
const kv = await metadata(context);
await kv.set("edits", (await kv.get("edits")) || 1);
});
app.on('issues.closed', async context => {
const edits = await metadata(context).get('edits')
context.github.issues.createComment(context.issue({
body: `There were ${edits} edits to issues in this thread.`
}))
})
}
app.on("issues.closed", async (context) => {
const edits = await metadata(context).get("edits");
context.octokit.issues.createComment(
context.issue({
body: `There were ${edits} edits to issues in this thread.`,
}),
);
});
};
```
## Scheduler
[probot-scheduler](https://github.com/probot/scheduler) is an extension to trigger events on a periodic schedule. It triggers a `schedule.repository` event every hour for each repository it has access to.
```js
const createScheduler = require('probot-scheduler')
module.exports = app => {
createScheduler(app)
app.on('schedule.repository', context => {
// this event is triggered on an interval, which is 1 hr by default
})
}
```
Check out [stale](https://github.com/probot/stale) to see it in action.
## Attachments
[probot-attachments](https://github.com/probot/attachments) adds message attachments to comments on GitHub. This extension should be used any time an app is appending content to user comments.
```js
const attachments = require('probot-attachments')
import attachments from "probot-attachments";
module.exports = app => {
app.on('issue_comment.created', context => {
export default (app) => {
app.on("issue_comment.created", (context) => {
return attachments(context).add({
'title': 'Hello World',
'title_link': 'https://example.com/hello'
})
})
}
title: "Hello World",
title_link: "https://example.com/hello",
});
});
};
```
Check out [probot/unfurl](https://github.com/probot/unfurl) to see it in action.
## Community Created Extensions
[probot-messages](https://github.com/dessant/probot-messages) was created by [@dessant](https://github.com/dessant) to deliver messages that require user action to ensure the correct operation of the app.

View File

@ -1,40 +1,39 @@
---
next: docs/http.md
next: configuration
title: Interacting with GitHub
---
# Interacting with GitHub
Probot uses [GitHub Apps](https://developer.github.com/apps/). An app is a first-class actor on GitHub, like a user (e.g. [@defunkt](https://github.com/defunkt)) or an organization (e.g. [@github](https://github.com/github)). The app is given access to a repository or repositories by being "installed" on a user or organization account and can perform actions through the API like [commenting on an issue](https://developer.github.com/v3/issues/comments/#create-a-comment) or [creating a status](https://developer.github.com/v3/repos/statuses/#create-a-status).
Probot uses [GitHub Apps](https://docs.github.com/developers/apps/) for authorizing requests to GitHub's APIs. A registered GitHub App is a first-class actor on GitHub, like a user (e.g. [@bkeepers](https://github.com/bkeepers)) or an organization (e.g. [@github](https://github.com/github)). The GitHub App is granted access to all or selected repositories by being "installed" on a user or organization account and can perform actions through the API like [commenting on an issue](https://docs.github.com/rest/reference/issues#create-an-issue-comment) or [creating a status](https://docs.github.com/rest/reference/repos#create-a-commit-status).
Your app has access to an authenticated GitHub client that can be used to make API calls. It supports both the [GitHub REST API](https://developer.github.com/v3/), and the [GitHub GraphQL API](https://developer.github.com/v4/).
Your Probot app has access to an authenticated [Octokit client](https://octokit.github.io/rest.js/) that can be used to make API calls. It supports both the [GitHub REST API](https://docs.github.com/rest), and the [GitHub GraphQL API](https://docs.github.com/graphql).
## REST API
`context.github` is an instance of the [`@octokit/rest` Node.js module](https://github.com/octokit/rest.js), which wraps the [GitHub REST API](https://developer.github.com/v3/) and allows you to do almost anything programmatically that you can do through a web browser.
`context.octokit` is an instance of the [`@octokit/rest` Node.js module](https://github.com/octokit/rest.js#readme), and allows you to do almost anything programmatically that you can do through a web browser.
Here is an example of an autoresponder app that comments on opened issues:
```js
module.exports = app => {
app.on('issues.opened', async context => {
export default (app) => {
app.on("issues.opened", async (context) => {
// `context` extracts information from the event, which can be passed to
// GitHub API calls. This will return:
// {owner: 'yourname', repo: 'yourrepo', number: 123, body: 'Hello World!}
const params = context.issue({body: 'Hello World!'})
// { owner: 'yourname', repo: 'yourrepo', number: 123, body: 'Hello World! }
const params = context.issue({ body: "Hello World!" });
// Post a comment on the issue
return context.github.issues.createComment(params)
})
}
return context.octokit.issues.createComment(params);
});
};
```
See the [full API docs](https://octokit.github.io/rest.js/) to see all the ways you can interact with GitHub. Some API endpoints are not available on GitHub Apps yet, so check [which ones are available](https://developer.github.com/v3/apps/available-endpoints/) first.
See the [full API docs](https://octokit.github.io/rest.js/) to see all the ways you can interact with GitHub. Some API endpoints are not available on GitHub Apps yet, so check [which ones are available](https://docs.github.com/en/rest/overview/endpoints-available-for-github-apps) first.
## GraphQL API
> **Heads Up!** GraphQL support in Probot is currently a preview feature and this interface could change in a future releases without notice. [Let us know](https://github.com/probot/probot/issues/476) if you have any feedback or ideas to improve GraphQL support.
Use `context.github.query` to make requests to the [GitHub GraphQL API](https://developer.github.com/v4/).
Use `context.octokit.graphql` to make requests to the [GitHub GraphQL API](https://docs.github.com/en/graphql).
Here is an example of the same autoresponder app from above that comments on opened issues, but this time with GraphQL:
@ -46,30 +45,54 @@ const addComment = `
clientMutationId
}
}
`
`;
module.exports = app => {
app.on('issues.opened', async context => {
export default (app) => {
app.on("issues.opened", async (context) => {
// Post a comment on the issue
context.github.query(addComment, {
context.octokit.graphql(addComment, {
id: context.payload.issue.node_id,
body: 'Hello World'
})
})
}
body: "Hello World",
});
});
};
```
Check out the [GitHub GraphQL API docs](https://developer.github.com/v4/) to learn more.
The options in the 2nd argument will be passed as variables to the query. You can pass custom headers by using the `headers` key:
```js
// GraphQL query to pin an issue
const pinIssue = `
mutation comment($id: ID!) {
pinIssue(input: {subjectId: $id}) {
clientMutationId
}
}
`;
export default (app) => {
app.on("issues.opened", async (context) => {
context.octokit.graphql(pinIssue, {
id: context.payload.issue.node_id,
headers: {
accept: "application/vnd.github.elektra-preview+json",
},
});
});
};
```
Check out the [GitHub GraphQL API docs](https://docs.github.com/en/graphql) to learn more.
## Unauthenticated Events
When [receiving webhook events](./webhooks.md), `context.github` is _usually_ an authenticated client, but there are a few events that are exceptions:
When [receiving webhook events](/docs/webhooks), `context.octokit` is _usually_ an authenticated client, but there are a few events that are exceptions:
- [`installation.deleted`](https://developer.github.com/v3/activity/events/types/#installationevent) - The installation was _just_ deleted, so we can't authenticate as the installation.
- [`installation.deleted` & `installation.suspend`](https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads#installation) - The installation was _just_ deleted or suspended, so we can't authenticate as the installation.
- [`marketplace_purchase`](https://developer.github.com/v3/activity/events/types/#marketplacepurchaseevent) - The purchase happens before the app is installed on an account.
- [`marketplace_purchase`](https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads#marketplace_purchase) - The purchase happens before the app is installed on an account.
For these events, `context.github` will be [authenticated as the GitHub App](https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-a-github-app) instead of as a specific installation.
For these events, `context.octokit` will be unauthenticated. Attemts to send any requests will fail with an error explaining the circumstances.
## GitHub Enterprise
@ -79,4 +102,10 @@ If you want to run a Probot App against a GitHub Enterprise instance, you'll nee
GHE_HOST=fake.github-enterprise.com
```
> GitHub Apps are enabled in GitHub Enterprise 2.12 as an [early access technical preview](https://developer.github.com/enterprise/2.12/apps/) but are generally available in GitHub Enterprise 2.13 and above.
When [using Probot programmatically](/docs/development/#run-probot-programmatically), set the `baseUrl` option for the [`Probot`](https://probot.github.io/api/latest/classes/probot.Probot.html) constructor to the full base Url of the REST API
```js
const MyProbot = Probot.defaults({
baseUrl: "https://fake.github-enterprise.com/api/v3",
});
```

View File

@ -1,49 +1,50 @@
---
next: docs/development.md
next: development
title: Hello World
---
# Hello World
# Hello world
A Probot app is just a [Node.js module](https://nodejs.org/api/modules.html) that exports a function:
```js
module.exports = app => {
export default (app) => {
// your code here
}
};
```
The `app` parameter is an instance of [`Application`](https://probot.github.io/api/latest/classes/application.html) and gives you access to all of the GitHub goodness.
The `app` parameter is an instance of [`Probot`](https://probot.github.io/api/latest/classes/probot.Probot.html) and gives you access to all of the GitHub goodness.
`app.on` will listen for any [webhook events triggered by GitHub](./webhooks.md), which will notify you when anything interesting happens on GitHub that your app wants to know about.
`app.on` will listen for any [webhook events triggered by GitHub](/docs/webhooks/), which will notify you when anything interesting happens on GitHub that your app wants to know about.
```js
module.exports = app => {
app.on('issues.opened', async context => {
export default (app) => {
app.on("issues.opened", async (context) => {
// A new issue was opened, what should we do with it?
context.log(context.payload)
})
}
context.log.info(context.payload);
});
};
```
The `context` passed to the event handler includes everything about the event that was triggered, as well as some helpful properties for doing something useful in response to the event. `context.github` is an authenticated GitHub client that can be used to [make API calls](./github-api.md), and allows you to do almost anything programmatically that you can do through a web browser on GitHub.
The [`context`](https://probot.github.io/api/latest/classes/context.Context.html) passed to the event handler includes everything about the event that was triggered, as well as some helpful properties for doing something useful in response to the event. `context.octokit` is an authenticated GitHub client that can be used to [make REST API and GraphQL calls](/docs/github-api), and allows you to do almost anything programmatically that you can do through a web browser on GitHub.
Here is an example of an autoresponder app that comments on opened issues:
```js
module.exports = app => {
app.on('issues.opened', async context => {
export default (app) => {
app.on("issues.opened", async (context) => {
// `context` extracts information from the event, which can be passed to
// GitHub API calls. This will return:
// {owner: 'yourname', repo: 'yourrepo', number: 123, body: 'Hello World!}
const params = context.issue({body: 'Hello World!'})
// { owner: 'yourname', repo: 'yourrepo', number: 123, body: 'Hello World !}
const params = context.issue({ body: "Hello World!" });
// Post a comment on the issue
return context.github.issues.createComment(params)
})
}
return context.octokit.issues.createComment(params);
});
};
```
To get started, you can use the instructions for [Developing an App](https://probot.github.io/docs/development/) or remix this 'Hello World' project on Glitch:
To get started, you can use the instructions for [Developing an App](/docs/development/) or remix this 'Hello World' project on Glitch:
[![Remix on Glitch](https://cdn.glitch.com/2703baf2-b643-4da7-ab91-7ee2a2d00b5b%2Fremix-button.svg)](https://glitch.com/edit/#!/remix/probot-hello-world)

View File

@ -1,28 +1,33 @@
---
next: docs/simulating-webhooks.md
next: pagination
title: HTTP routes
---
# HTTP Routes
# HTTP routes
Calling `app.route('/my-app')` will return an [express](http://expressjs.com/) router that you can use to expose HTTP endpoints from your app.
When starting your app using `probot run ./app.js` or using the [`Server`](/docs/development/#use-server) class, your Probot app function will receive the `options.getRouter` function as its 2nd argument.
Calling `getRouter('/my-app')` will return an [express](http://expressjs.com/) router that you can use to expose custom HTTP endpoints from your app.
```js
module.exports = app => {
import * as express from "express";
export default (app, { getRouter }) => {
// Get an express router to expose new HTTP endpoints
const router = app.route('/my-app')
const router = getRouter("/my-app");
// Use any middleware
router.use(require('express').static('public'))
router.use(express.static("public"));
// Add a new route
router.get('/hello-world', (req, res) => {
res.send('Hello World')
})
}
router.get("/hello-world", (req, res) => {
res.send("Hello World");
});
};
```
Visit https://localhost:3000/my-app/hello-world to access the endpoint.
Visit http://localhost:3000/my-app/hello-world to access the endpoint.
It is strongly encouraged to use the name of your package as the prefix so none of your routes or middleware conflict with other apps. For example, if [`probot/owners`](https://github.com/probot/owners) exposed an endpoint, the app would call `app.route('/owners')` to prefix all endpoints with `/owners`.
It is strongly encouraged to use the name of your package as the prefix so none of your routes or middleware conflict with other apps. For example, if [`probot/owners`](https://github.com/probot/owners) exposed an endpoint, the app would call `getRouter('/owners')` to prefix all endpoints with `/owners`.
See the [express documentation](http://expressjs.com/en/guide/routing.html) for more information.

View File

@ -1,78 +1,79 @@
---
next: docs/pagination.md
next: deployment
title: Logging
---
# Logging
A good logger is a good developer's secret weapon. Probot comes with [bunyan](https://github.com/trentm/node-bunyan), which is a simple and fast logging library that supports some pretty sophisticated logging if you need it (hint: you will).
Probot comes with [`pino`](https://getpino.io), a minimal logging library that outputs [newline delimited JSON](http://ndjson.org/). Probot uses [`pino-pretty`](https://github.com/pinojs/pino-pretty) for more readable formatting during development and [`@probot/pino`](https://github.com/probot/pino/) which supports error reporting to Sentry by configuring the `SENTRY_DSN` environment variable.
`app.log`, `context.log` in an event handler, and `req.log` in an HTTP request are all loggers that you can use to get more information about what your app is doing.
```js
module.exports = app => {
app.log('Yay, my app is loaded')
export default (app, { getRouter }) => {
app.log.info("Yay, my app is loaded");
app.on('issues.opened', context => {
app.on("issues.opened", (context) => {
if (context.payload.issue.body.match(/bacon/)) {
context.log('This issue is about bacon')
context.log.info("This issue is about bacon");
} else {
context.log('Sadly, this issue is not about bacon')
context.log.info("Sadly, this issue is not about bacon");
}
})
});
app.route().get('/hello-world', (req, res) => {
req.log('Someone is saying hello')
})
}
const router = getRouter("/my-app");
router.get("/hello-world", (req, res) => {
req.log.info("Someone is saying hello");
res.send("Hello World");
});
};
```
When you start up your app with `npm start`, You should see your log message appear in your terminal.
When you start up your Probot app you should see your log message appear in your terminal.
<img width="753" alt="" src="https://user-images.githubusercontent.com/173/33234904-d43e7f14-d1f3-11e7-8dcb-6c47e58bd56b.png">
`app.log` will log messages at the `info` level, which is what your app should use for most relevant messages. Occasionally you will want to log more detailed information that is useful for debugging, but you might not want to see it all the time.
Occasionally you will want to log more detailed information that is useful for debugging, but you might not want to see it all the time.
```js
module.exports = app => {
export default (app) => {
// …
app.log.trace('Really low-level logging')
app.log.debug({data: 'here'}, 'End-line specs on the rotary girder')
app.log.info('Same as using `app.log`')
app.log.trace("Really low-level logging");
app.log.debug({ data: "here" }, "End-line specs on the rotary girder");
app.log.info("Same as using `app.log`");
const err = new Error('Some error')
app.log.warn(err, 'Uh-oh, this may not be good')
app.log.error(err, 'Yeah, it was bad')
app.log.fatal(err, 'Goodbye, cruel world!')
}
const err = new Error("Some error");
app.log.warn(err, "Uh-oh, this may not be good");
app.log.error(err, "Yeah, it was bad");
app.log.fatal(err, "Goodbye, cruel world!");
};
```
By default, messages that are `info` and above will show in your logs, but you can change it by setting the
`LOG_LEVEL` environment variable to `trace`, `debug`, `info`, `warn`, `error`, or `fatal` in `.env` or on the comand line.
`LOG_LEVEL` environment variable to `trace`, `debug`, `info`, `warn`, `error`, or `fatal` in `.env` or on the command line.
```
$ LOG_LEVEL=debug npm start
```
### Log formats
## Log formats
In development, it's nice to see simple, colorized, pretty log messages. But those pretty messages don't do you any good when you have 2TB of log files and you're trying to track down why that one-in-a-million bug is happening in production.
Set `LOG_FORMAT=json` to show log messages as structured JSON, which can then be drained to a logging service that allows querying by various attributes.
When `NODE_ENV` is set (as it should be in production), the log output is structured JSON, which can then be drained to a logging service that allows querying by various attributes.
For example, given this log:
```js
module.exports = app => {
app.on('issue_comment.created', context => {
context.log('Comment created')
})
}
export default (app) => {
app.on("issue_comment.created", (context) => {
context.log.info("Comment created");
});
};
```
You'll see this output:
```
{"name":"Probot","hostname":"Brandons-MacBook-Pro-3.local","pid":96993,"event":{"id":"afdcb370-c57d-11e7-9b26-0f31120e45b8","event":"issue_comment","action":"created","repository":"robotland/test","installation":13055},"level":20,"msg":"Comment created","time":"2017-11-09T18:42:07.312Z","v":0}
{"name":"Probot","hostname":"Brandons-MacBook-Pro-3.local","pid":96993,"event":{"id":"afdcb370-c57d-11e7-9b26-0f31120e45b8","event":"issue_comment","action":"created","repository":"robotland/test","installation":13055},"level":30,"msg":"Comment created","time":"2017-11-09T18:42:07.312Z","v":0}
```
For more about bunyan, check out [this talk about logging in production](http://trentm.com/talk-bunyan-in-prod/).
The output can then be piped to one of [pino's transport tools](https://getpino.io/#/docs/transports), or you can build your own.

View File

@ -1,61 +1,86 @@
---
next: docs/extensions.md
next: extensions
title: Pagination
---
# Pagination
Many GitHub API endpoints are paginated. The `github.paginate` method can be used to get each page of the results.
Many GitHub API endpoints are paginated. The [`octokit.paginate` method](https://github.com/octokit/plugin-paginate-rest.js/#readme) can be used to get each page of the results.
```js
module.exports = app => {
app.on('issues.opened', context => {
context.github.paginate(
context.github.issues.getAll(context.repo()),
res => {
res.data.issues.forEach(issue => {
context.log('Issue: %s', issue.title)
})
}
)
})
}
export default (app) => {
app.on("issues.opened", (context) => {
context.octokit.paginate(
context.octokit.issues.list,
context.repo(),
(response) => {
response.data.issues.forEach((issue) => {
context.log.info("Issue: %s", issue.title);
});
},
);
});
};
```
## Accumulating pages
The return value of the `github.paginate` callback will be used to accumulate results.
The return value of the `octokit.paginate` callback will be used to accumulate results.
```js
module.exports = app => {
app.on('issues.opened', async context => {
const allIssues = await context.github.paginate(
context.github.issues.getAll(context.repo()),
res => res.data
)
console.log(allIssues)
})
}
export default (app) => {
app.on("issues.opened", async (context) => {
const allIssues = await context.octokit.paginate(
context.octokit.issues.list,
context.repo(),
(response) => response.data,
);
console.log(allIssues);
});
};
```
## Early exit
Sometimes it is desirable to stop fetching pages after a certain condition has been satisfied. A second argument, `done`, is provided to the callback and can be used to stop pagination. After `done` is invoked, no additional pages will be fetched.
Sometimes it is desirable to stop fetching pages after a certain condition has been satisfied. A second argument, `done`, is provided to the callback and can be used to stop pagination. After `done` is invoked, no additional pages will be fetched, but you still need to return the mapped value for the current page request.
```js
module.exports = app => {
app.on('issues.opened', context => {
context.github.paginate(
context.github.issues.getAll(context.repo()),
export default (app) => {
app.on("issues.opened", (context) => {
context.octokit.paginate(
context.octokit.issues.list,
context.repo(),
(res, done) => {
for (let issue of res.data) {
if (issue.body.includes('something')) {
console.log('found it:', issue)
done()
break
for (const issue of res.data) {
if (issue.body.includes("something")) {
console.log("found it:", issue);
done();
break;
}
}
}
)
})
}
},
);
});
};
```
## Async iterators
Using async iterators you can iterate through each response
```js
export default (app) => {
app.on("issues.opened", async (context) => {
for await (const response of octokit.paginate.iterator(
context.octokit.issues.list,
context.repo(),
)) {
for (const issue of response.data) {
if (issue.body.includes("something")) {
return console.log("found it:", issue);
}
}
}
});
};
```

View File

@ -1,22 +1,36 @@
---
next: docs/best-practices.md
next: best-practices
title: Persistence
---
# Persistence
Generally speaking, adding database storage or persistence to your Probot App will greatly complicate your life. It's perfectly doable, but for most use-cases you'll be able to manage relevant data within GitHub issues, pull requests and your app's configuration file.
**Contents:**
<!-- toc -->
- [Using GitHub for persistent data](#using-github-for-persistent-data)
- [Using a Database](#using-a-database)
- [MongoDB (with Mongoose)](#mongodb-with-mongoose)
- [MySQL](#mysql)
- [Postgres](#postgres)
- [Firebase](#firebase)
<!-- tocstop -->
## Using GitHub for persistent data
Probot includes a wrapper for the GitHub API which can enable you to store and manipulate data in the GitHub environment. Since your Probot App will usually be running to supplement work done on GitHub, it makes sense to try to keep everything in one place and avoid extra complications.
- *Comments:* The API can read, write and delete comments on issues and pull requests.
- *Status:* The API can read and change the status of an issue or pull request.
- *Search:* GitHub has a powerful search API that can be used
- *Repository:* Built-in `context.config()` allows storing configuration in the repository.
- *Labels:* The API can read labels, and add or remove them from issues and pull requests.
- _Comments:_ The API can read, write and delete comments on issues and pull requests.
- _Status:_ The API can read and change the status of an issue or pull request.
- _Search:_ GitHub has a powerful search API that can be used
- _Repository:_ Built-in `context.config()` allows storing configuration in the repository or the organization's `.github` repository.
- _Labels:_ The API can read labels, and add or remove them from issues and pull requests.
If your Probot App needs to store more data than Issues and Pull Requests store normally, you can use the [`probot-metadata` extension](https://probot.github.io/docs/extensions/#metadata) to hide data in comments. It isn't meant to be super secure or scalable, but it's an easy way to manage some data without a full database.
If your Probot App needs to store more data than Issues and Pull Requests store normally, you can use the [`probot-metadata` extension](/docs/extensions#metadata) to hide data in comments. It isn't meant to be super secure or scalable, but it's an easy way to manage some data without a full database.
There are even more APIs that you can use to increase the functionality of your Probot App. You can read about all of the ones available in Probot on the [`@octokit/rest` documentation](http://octokit.github.io/rest.js/).
@ -31,147 +45,186 @@ For when you absolutely do need external data storage, here are some examples us
```js
// PeopleSchema.js
const mongoose = require('mongoose')
import * as mongoose from "mongoose";
const Schema = mongoose.Schema
const Schema = mongoose.Schema;
const PeopleSchema = new Schema({
name: {
type: String,
required: true
}
})
required: true,
},
});
module.exports = mongoose.model('People', PeopleSchema)
export default mongoose.model("People", PeopleSchema);
```
```js
// index.js
const mongoose = require('mongoose')
import * as mongoose from "mongoose";
// Connect to the Mongo database using credentials
// in your environment variables
const mongoUri = `mongodb://${process.env.DB_HOST}`
const mongoUri = `mongodb://${process.env.DB_HOST}`;
mongoose.connect(mongoUri, {
user: process.env.DB_USER,
pass: process.env.DB_PASS,
useMongoClient: true
})
useMongoClient: true,
});
// Register the mongoose model
const People = require('./PeopleSchema')
import People from "./PeopleSchema.js";
module.exports = app => {
app.on('issues.opened', async context => {
export default (app) => {
app.on("issues.opened", async (context) => {
// Find all the people in the database
const people = await People.find().exec()
const people = await People.find().exec();
// Generate a string using all the peoples' names.
// It would look like: 'Jason, Jane, James, Jennifer'
const peoplesNames = people.map(person => person.name).join(', ')
const peoplesNames = people.map((person) => person.name).join(", ");
// `context` extracts information from the event, which can be passed to
// GitHub API calls. This will return:
// {owner: 'yourname', repo: 'yourrepo', number: 123, body: 'The following people are in the database: Jason, Jane, James, Jennifer'}
const params = context.issue({body: `The following people are in the database: ${peoplesNames}`})
// { owner: 'yourname', repo: 'yourrepo', number: 123, body: 'The following people are in the database: Jason, Jane, James, Jennifer' }
const params = context.issue({
body: `The following people are in the database: ${peoplesNames}`,
});
// Post a comment on the issue
return context.github.issues.createComment(params)
})
}
return context.octokit.issues.createComment(params);
});
};
```
### MySQL
Using the [`mysql`](https://github.com/mysqljs/mysql) module, we can connect to our MySQL database and perform queries.
Using the [`@databases/mysql`](https://www.atdatabases.org/docs/mysql.html) module, we can connect to our MySQL database and perform queries.
```js
// connection.js
const mysql = require('mysql')
import { connect } from "@databases/mysql";
const connection = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_DATABASE
})
// DATABASE_URL = mysql://my-user:my-password@localhost/my-db
const connection = connect(process.env.DATABASE_URL);
connection.connect()
module.exports = connection
export default connection;
```
```js
// index.js
const connection = require('./connection')
import { sql } from "@databases/mysql";
import connection from "./connection.js";
function performQuery (query) {
return new Promise((resolve, reject) => {
connection.query(query, function (error, results, fields) {
if (error) reject(new Error(error))
resolve(results)
})
})
}
module.exports = app => {
app.on('issues.opened', async context => {
export default (app) => {
app.on("issues.opened", async (context) => {
// Find all the people in the database
const people = await performQuery('SELECT * FROM `people`')
const people = await connection.query(sql`SELECT * FROM people`);
// Generate a string using all the peoples' names.
// It would look like: 'Jason, Jane, James, Jennifer'
const peoplesNames = people.map(key => people[key].name).join(', ')
const peoplesNames = people.map((key) => people[key].name).join(", ");
// `context` extracts information from the event, which can be passed to
// GitHub API calls. This will return:
// {owner: 'yourname', repo: 'yourrepo', number: 123, body: 'The following people are in the database: Jason, Jane, James, Jennifer'}
const params = context.issue({body: `The following people are in the database: ${peoplesNames}`})
// { owner: 'yourname', repo: 'yourrepo', number: 123, body: 'The following people are in the database: Jason, Jane, James, Jennifer' }
const params = context.issue({
body: `The following people are in the database: ${peoplesNames}`,
});
// Post a comment on the issue
return context.github.issues.createComment(params)
})
}
return context.octokit.issues.createComment(params);
});
};
```
### Postgres
Using the [`@databases/pg`](https://www.atdatabases.org/docs/pg.html) module, we can connect to our Postgres database and perform queries.
```js
// connection.js
import { connect } from "@databases/pg";
// DATABASE_URL = postgresql://my-user:my-password@localhost/my-db
const connection = connect(process.env.DATABASE_URL);
export default connection;
```
```js
// index.js
import { sql } from "@databases/pg";
import connection from "./connection.js";
export default (app) => {
app.on("issues.opened", async (context) => {
// Find all the people in the database
const people = await connection.query(sql`SELECT * FROM people`);
// Generate a string using all the peoples' names.
// It would look like: 'Jason, Jane, James, Jennifer'
const peoplesNames = people.map((key) => people[key].name).join(", ");
// `context` extracts information from the event, which can be passed to
// GitHub API calls. This will return:
// { owner: 'yourname', repo: 'yourrepo', number: 123, body: 'The following people are in the database: Jason, Jane, James, Jennifer' }
const params = context.issue({
body: `The following people are in the database: ${peoplesNames}`,
});
// Post a comment on the issue
return context.octokit.issues.createComment(params);
});
};
```
### Firebase
[Firebase](https://firebase.google.com/) is Google's services-as-a-service that includes a simple JSON database. You can learn more about dealing with the Javascript API [here](https://firebase.google.com/docs/database/web/start). Note that for security purposes, you may also want to look into the [Admin API](https://firebase.google.com/docs/database/admin/start).
[Firebase](https://firebase.google.com/) is Google's services-as-a-service that includes a simple JSON database. You can learn more about dealing with the JavaScript API [here](https://firebase.google.com/docs/database/web/start). Note that for security purposes, you may also want to look into the [Admin API](https://firebase.google.com/docs/database/admin/start).
```js
// index.js
const firebase = require('firebase')
import * as firebase from "firebase";
// Set the configuration for your app
// TODO: Replace with your project's config object
const config = {
apiKey: 'apiKey',
authDomain: 'projectId.firebaseapp.com',
databaseURL: 'https://databaseName.firebaseio.com'
}
firebase.initializeApp(config)
apiKey: "apiKey",
authDomain: "projectId.firebaseapp.com",
databaseURL: "https://databaseName.firebaseio.com",
};
firebase.initializeApp(config);
const database = firebase.database()
const database = firebase.database();
module.exports = app => {
app.on('issues.opened', async context => {
export default (app) => {
app.on("issues.opened", async (context) => {
// Find all the people in the database
const people = await database.ref('/people').once('value').then((snapshot) => {
return snapshot.val()
})
const people = await database
.ref("/people")
.once("value")
.then((snapshot) => {
return snapshot.val();
});
// Generate a string using all the peoples' names.
// It would look like: 'Jason, Jane, James, Jennifer'
const peoplesNames = Object.keys(people).map(key => people[key].name).join(', ')
const peoplesNames = Object.keys(people)
.map((key) => people[key].name)
.join(", ");
// `context` extracts information from the event, which can be passed to
// GitHub API calls. This will return:
// {owner: 'yourname', repo: 'yourrepo', number: 123, body: 'The following people are in the database: Jason, Jane, James, Jennifer'}
const params = context.issue({body: `The following people are in the database: ${peoplesNames}`})
// { owner: 'yourname', repo: 'yourrepo', number: 123, body: 'The following people are in the database: Jason, Jane, James, Jennifer' }
const params = context.issue({
body: `The following people are in the database: ${peoplesNames}`,
});
// Post a comment on the issue
return context.github.issues.createComment(params)
})
}
return context.octokit.issues.createComment(params);
});
};
```

View File

@ -1,24 +1,28 @@
---
next: docs/testing.md
next: logging
title: Simulate receiving webhooks
---
# Simulating Webhooks
# Simulate receiving webhooks
As you are developing your app, you will likely want to test it by repeatedly triggering the same webhook. You can simulate a webhook being delivered by saving the payload to a file, and then calling `probot simulate` from the command line.
As you are developing your app, you will likely want to test it by repeatedly triggering the same webhook. You can simulate a webhook being delivered by saving the payload to a file, and then calling `probot receive` from the command line.
To save a copy of the payload, go to the [settings](https://github.com/settings/apps) page for your App, and go to the **Advanced** tab. Click on one of the **Recent Deliveries** to expand it and see the details of the webhook event. Copy the JSON from the the **Payload** and save it to a new file. (`test/fixtures/issues.labeled.json` in this example).
To save a copy of the payload, go to the [settings](https://github.com/settings/apps) page for your App, and go to the **Advanced** tab. Click on one of the **Recent Deliveries** to expand it and see the details of the webhook event. Copy the JSON from the **Payload** and save it to a new file. (`test/fixtures/issues.labeled.json` in this example).
**Note**: This will only simulate the JSON payload, not the headers that are also sent by GitHub webhooks.
**Note**: This will only receive the JSON payload, not the headers that are also sent by GitHub webhooks.
![](https://user-images.githubusercontent.com/173/28491924-e03e91f2-6ebe-11e7-9570-6d48da68c6ca.png)
Next, simulate this event being delivered by running:
Next, simulate receiving this event being delivered by running:
$ node_modules/.bin/probot simulate <event-name> <path-to-fixture> <path-to-app>
```bash
$ node_modules/.bin/probot receive -e <event-name> -p <path-to-fixture> <path-to-app>
```
Note that `event-name` here is just the name of the event (like pull_request or issues) and not the action (like labeled). You can find it in the GitHub deliveries history under the `X-GitHub-Event` header.
Note that `event-name` here is just the name of the event (like pull_request or issues) and not the action (like labeled). You can find it in the GitHub deliveries history under the `X-GitHub-Event` header.
For example, to simulate the pull_request.labeled event, run:
For example, to simulate receiving the `pull_request.labeled` event, run:
$ node_modules/.bin/probot simulate pull_request test/fixtures/pull_request.labeled.json ./index.js
```bash
$ node_modules/.bin/probot receive -e pull_request -p test/fixtures/pull_request.labeled.json ./index.js
```

View File

@ -1,57 +0,0 @@
# Welcome to Probot's Summer of Code!
In 2018, Probot is participating in Google's Summer of Code and Rails Girls Summer of Code, two amazing programs that help students connect with open source projects.
## FAQ
#### How do I get started with Probot?
We suggest that you build our ["Hello World" app](https://probot.github.io/docs/hello-world/) to get the hang of registering a GitHub App and the base setup of a Probot App. In your application, include a link to a comment that your Hello World app created on a test repository.
You can also [join our Slack](https://probot-slackin.herokuapp.com/) and join the **#summer-of-code** channel.
#### Can I claim this cool idea?
We're excited that you're excited, but the Summer of Code ideas in our [ideas repo](https://github.com/probot/ideas) are for the students that are accepted to work on Probot over the summer. Claims to those ideas will be ignored until April (when the submission deadline has passed).
#### Someone has claimed this idea, can I still have it in my application?
Absolutely! See the above answer.
#### I have another cool idea for an app!
Amazing! Be sure to include it in your application.
## Mentors
<table>
<tbody>
<tr>
<td align="center" valign="top">
<img width="150" height="150" src="https://github.com/bkeepers.png?s=150">
<br>
<a href="https://github.com/bkeepers">Brandon Keepers</a>
</td>
<td align="center" valign="top">
<img width="150" height="150" src="https://github.com/gr2m.png?s=150">
<br>
<a href="https://github.com/gr2m">Gregor Martynus</a>
</td>
<td align="center" valign="top">
<img width="150" height="150" src="https://github.com/JasonEtco.png?s=150">
<br>
<a href="https://github.com/JasonEtco">Jason Etcovitch</a>
</td>
<td align="center" valign="top">
<img width="150" height="150" src="https://github.com/tcbyrd.png?s=150">
<br>
<a href="https://github.com/tcbyrd">Tommy Byrd</a>
<br>
</td>
</tr>
</tbody>
</table>
## Application Requirements
* You must have created the ["Hello World" app](https://probot.github.io/docs/hello-world/), and provided a link to a comment that it has posted.

View File

@ -1,48 +1,105 @@
---
next: docs/logging.md
next: simulating-webhooks
title: Testing
---
# Testing
We highly recommend working in the style of [test-driven development](http://agiledata.org/essays/tdd.html) when creating probot apps. It is frustrating to constantly create real GitHub events in order to test an app. Redelivering webhooks is possible and can be accessed in your app's [settings](https://github.com/settings/apps) page under the **Advanced** tab. We do offer the above documented `simulate` method to help make this easier; however, by writing your tests first, you can avoid repeatedly recreating actual events from GitHub to check if your code is working.
We highly recommend working in the style of [test-driven development](http://agiledata.org/essays/tdd.html) when creating probot apps. It is frustrating to constantly create real GitHub events in order to test an app. Redelivering webhooks is possible and can be accessed in your app's [settings](https://github.com/settings/apps) page under the **Advanced** tab. We do offer the above documented `receive` method to help make this easier; however, by writing your tests first, you can avoid repeatedly recreating actual events from GitHub to check if your code is working.
For our testing examples, we use [jest](https://facebook.github.io/jest/), but there are other options that can perform similar operations. Here's an example of creating an app instance and mocking out the GitHub API:
For our testing examples, we use [jest](https://facebook.github.io/jest/), but there are other options that can perform similar operations. We also recommend using [nock](https://github.com/nock/nock), a tool for mocking HTTP requests, which is often crucial to testing in Probot, considering how much of Probot depends on GitHub's APIs. Here's an example of creating an app instance and using nock to test that we correctly hit the GitHub API:
```js
const { Application } = require('probot')
import nock from "nock";
// Requiring our app implementation
const myProbotApp = require('..')
import myProbotApp from "../index.js";
import { Probot, ProbotOctokit } from "probot";
// Requiring our fixtures
import payload from "./fixtures/issues.opened.json" with { type: "json" };
const issueCreatedBody = { body: "Thanks for opening this issue!" };
const issuesOpenedPayload = require('./fixtures/issues.opened.json')
describe('My Probot app', () => {
let app, github
describe("My Probot app", () => {
let probot;
beforeEach(() => {
app = new Application()
// Initialize the app based on the code from index.js
app.load(myProbotApp)
// This is an easy way to mock out the GitHub API
github = {
issues: {
createComment: jest.fn().mockReturnValue(Promise.resolve({}))
}
}
// Passes the mocked out GitHub API into out app instance
app.auth = () => Promise.resolve(github)
})
nock.disableNetConnect();
probot = new Probot({
githubToken: "test",
// Disable throttling & retrying requests for easier testing
Octokit: ProbotOctokit.defaults({
retry: { enabled: false },
throttle: { enabled: false },
}),
});
myProbotApp(probot);
});
test('creates a comment when an issue is opened', async () => {
// Simulates delivery of an issues.opened webhook
await app.receive({
name: 'issues.opened',
payload: issuesOpenedPayload
})
test("creates a passing check", async () => {
// Test that we correctly return a test token
nock("https://api.github.com")
.post("/app/installations/2/access_tokens")
.reply(200, { token: "test" });
// This test passes if the code in your index.js file calls `context.github.issues.createComment`
expect(github.issues.createComment).toHaveBeenCalled()
})
})
// Test that a comment is posted
nock("https://api.github.com")
.post("/repos/hiimbex/testing-things/issues/1/comments", (body) => {
expect(body).toMatchObject(issueCreatedBody);
return true;
})
.reply(200);
// Receive a webhook event
await probot.receive({ name: "issues", payload });
});
afterEach(() => {
nock.cleanAll();
nock.enableNetConnect();
});
});
```
A good testing example from [update-docs](https://github.com/behaviorbot/update-docs) can be found [here](https://github.com/behaviorbot/update-docs/blob/master/test/index.js), and another one from [owners](https://github.com/probot/owners) can be found [here](https://github.com/probot/owners/blob/master/test/owner-notifier.js).
## Testing log output
Probot is using [pino](https://getpino.io/) for logging. A custom `pino` instance can be passed to the `Probot` constructor. You can write all log output into an array by passing a custom transport function:
```js
import pino from "pino";
import Stream from "stream";
const streamLogsToOutput = new Stream.Writable({ objectMode: true });
streamLogsToOutput._write = (object, encoding, done) => {
output.push(JSON.parse(object));
done();
};
const probot = new Probot({
id: 1,
githubToken: "test",
// Disable throttling & retrying requests for easier testing
Octokit: ProbotOctokit.defaults({
retry: { enabled: false },
throttle: { enabled: false },
}),
log: pino(streamLogsToOutput),
});
probot.log.info("test");
// output is now:
// [ { level: 30, time: 1600619283012, pid: 44071, hostname: 'Gregors-MacBook-Pro.local', msg: 'test' } ]
```
## Examples:
Using Jest
- [Settings](https://github.com/probot/settings): [probot/settings/test/integration/plugins/repository.test.js](https://github.com/probot/settings/blob/master/test/integration/plugins/repository.test.js)
- [DCO](https://github.com/probot/dco): [probot/dco/test/index.test.js](https://github.com/probot/dco/blob/master/test/index.test.js)
Using Tap
- [WIP](https://github.com/apps/wip/): [wip/app/test/integration](https://github.com/wip/app/tree/master/test/integration)
Using Mocha and Sinon
- [Auto-Me-Bot](https://github.com/TomerFi/auto-me-bot): [tests](https://github.com/TomerFi/auto-me-bot/tree/main/tests)

View File

@ -1,53 +1,54 @@
---
next: docs/github-api.md
next: github-api
title: Receiving webhooks
---
# Receiving Webhooks
# Receiving webhooks
[GitHub webhooks](https://developer.github.com/webhooks/) are fired for almost every significant action that users take on GitHub, whether it's pushes to code, opening or closing issues, opening or merging pull requests, or commenting on a discussion.
[GitHub webhooks](https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/about-webhooks) are fired for almost every significant action that users or other apps take on GitHub, whether it's pushing to code, opening or closing issues, opening or merging pull requests, or commenting on a discussion.
Many apps will spend their entire day responding to these actions. `app.on` will listen for any GitHub webhook events:
```js
module.exports = app => {
app.on('push', async context => {
export default (app) => {
app.on("push", async (context) => {
// Code was pushed to the repo, what should we do with it?
app.log(context)
})
}
app.log.info(context);
});
};
```
The app can listen to any of the [GitHub webhook events](https://developer.github.com/webhooks/#events). The `context` object includes everything about the event that was triggered, and `context.payload` has the payload delivered by GitHub.
The app can listen to any of the [GitHub webhook events](https://docs.github.com/en/developers/webhooks-and-events). The `context` object includes everything about the event that was triggered, and `context.payload` has the payload delivered by GitHub.
Most events also include an "action". For example, the [`issues`](https://developer.github.com/v3/activity/events/types/#issuesevent) event has actions of `assigned`, `unassigned`, `labeled`, `unlabeled`, `opened`, `edited`, `milestoned`, `demilestoned`, `closed`, and `reopened`. Often, your app will only care about one type of action, so you can append it to the event name with a `.`:
Most events also include an "action". For example, the [`issues`](https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads#issues) event has actions of `assigned`, `unassigned`, `labeled`, `unlabeled`, `opened`, `edited`, `milestoned`, `demilestoned`, `closed`, and `reopened`. Often, your app will only care about one type of action, so you can append it to the event name with a `.`:
```js
module.exports = app => {
app.on('issues.opened', async context => {
export default (app) => {
app.on("issues.opened", async (context) => {
// An issue was just opened.
})
}
});
};
```
Sometimes you want to handle multiple webhook events the same way. `app.on` can listen to a list of events and run the same callback:
```js
module.exports = app => {
app.on(['issues.opened', 'issues.edited'], async context => {
export default (app) => {
app.on(["issues.opened", "issues.edited"], async (context) => {
// An issue was opened or edited, what should we do with it?
app.log(context)
})
}
app.log.info(context);
});
};
```
You can also use the wildcard event (`*`) to listen for any event that your app is subscribed to:
You can also use `app.onAny()` to listen for any event that your app is subscribed to:
```js
module.exports = app => {
app.on(`*`, async context => {
context.log({event: context.event, action: context.payload.action})
})
}
export default (app) => {
app.onAny(async (context) => {
app.log.info({ event: context.name, action: context.payload.action });
});
};
```
Explore the [GitHub webhook documentation](https://developer.github.com/webhooks/#events) to see what events are available to use in your app.
For more details, explore the [GitHub webhook documentation](https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/about-webhooks#events) or see a list of all the named events in the [@octokit/webhooks.js](https://github.com/octokit/webhooks.js/#webhook-events) npm module.

View File

@ -0,0 +1,70 @@
'use strict';
const { createServer } = require("http");
const { createNodeMiddleware } = require('../lib/create-node-middleware');
const { createProbot } = require('../lib/create-probot');
const { sign } = require("@octokit/webhooks-methods");
const WebhookExamples = require("@octokit/webhooks-examples");
process.env.APP_ID = "123";
process.env.PRIVATE_KEY = `-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA1c7+9z5Pad7OejecsQ0bu3aozN3tihPmljnnudb9G3HECdnH
lWu2/a1gB9JW5TBQ+AVpum9Okx7KfqkfBKL9mcHgSL0yWMdjMfNOqNtrQqKlN4kE
p6RD++7sGbzbfZ9arwrlD/HSDAWGdGGJTSOBM6pHehyLmSC3DJoR/CTu0vTGTWXQ
rO64Z8tyXQPtVPb/YXrcUhbBp8i72b9Xky0fD6PkEebOy0Ip58XVAn2UPNlNOSPS
ye+Qjtius0Md4Nie4+X8kwVI2Qjk3dSm0sw/720KJkdVDmrayeljtKBx6AtNQsSX
gzQbeMmiqFFkwrG1+zx6E7H7jqIQ9B6bvWKXGwIDAQABAoIBAD8kBBPL6PPhAqUB
K1r1/gycfDkUCQRP4DbZHt+458JlFHm8QL6VstKzkrp8mYDRhffY0WJnYJL98tr4
4tohsDbqFGwmw2mIaHjl24LuWXyyP4xpAGDpl9IcusjXBxLQLp2m4AKXbWpzb0OL
Ulrfc1ZooPck2uz7xlMIZOtLlOPjLz2DuejVe24JcwwHzrQWKOfA11R/9e50DVse
hnSH/w46Q763y4I0E3BIoUMsolEKzh2ydAAyzkgabGQBUuamZotNfvJoDXeCi1LD
8yNCWyTlYpJZJDDXooBU5EAsCvhN1sSRoaXWrlMSDB7r/E+aQyKua4KONqvmoJuC
21vSKeECgYEA7yW6wBkVoNhgXnk8XSZv3W+Q0xtdVpidJeNGBWnczlZrummt4xw3
xs6zV+rGUDy59yDkKwBKjMMa42Mni7T9Fx8+EKUuhVK3PVQyajoyQqFwT1GORJNz
c/eYQ6VYOCSC8OyZmsBM2p+0D4FF2/abwSPMmy0NgyFLCUFVc3OECpkCgYEA5OAm
I3wt5s+clg18qS7BKR2DuOFWrzNVcHYXhjx8vOSWV033Oy3yvdUBAhu9A1LUqpwy
Ma+unIgxmvmUMQEdyHQMcgBsVs10dR/g2xGjMLcwj6kn+xr3JVIZnbRT50YuPhf+
ns1ScdhP6upo9I0/sRsIuN96Gb65JJx94gQ4k9MCgYBO5V6gA2aMQvZAFLUicgzT
u/vGea+oYv7tQfaW0J8E/6PYwwaX93Y7Q3QNXCoCzJX5fsNnoFf36mIThGHGiHY6
y5bZPPWFDI3hUMa1Hu/35XS85kYOP6sGJjf4kTLyirEcNKJUWH7CXY+00cwvTkOC
S4Iz64Aas8AilIhRZ1m3eQKBgQCUW1s9azQRxgeZGFrzC3R340LL530aCeta/6FW
CQVOJ9nv84DLYohTVqvVowdNDTb+9Epw/JDxtDJ7Y0YU0cVtdxPOHcocJgdUGHrX
ZcJjRIt8w8g/s4X6MhKasBYm9s3owALzCuJjGzUKcDHiO2DKu1xXAb0SzRcTzUCn
7daCswKBgQDOYPZ2JGmhibqKjjLFm0qzpcQ6RPvPK1/7g0NInmjPMebP0K6eSPx0
9/49J6WTD++EajN7FhktUSYxukdWaCocAQJTDNYP0K88G4rtC2IYy5JFn9SWz5oh
x//0u+zd/R/QRUzLOw4N72/Hu+UG6MNt5iDZFCtapRaKt6OvSBwy8w==
-----END RSA PRIVATE KEY-----`;
process.env.WEBHOOK_SECRET = "secret";
process.env.WEBHOOK_PATH = "/";
const pushEvent = JSON.stringify((
WebhookExamples.filter(
(event) => event.name === "push",
)[0]
).examples[0]);
const appFn = (app) => {
app.on("issues.opened", async (context) => {
const issueComment = context.issue({
body: "Thanks for opening this issue!",
});
return context.octokit.issues.createComment(issueComment);
});
app.onAny(async (context) => {
context.log.info({ event: context.name, action: context.payload.action });
});
app.onError(async (error) => {
app.log.error(error);
});
};
const middleware = createNodeMiddleware(appFn, { probot: createProbot() });
const server = createServer(middleware);
server.listen(3000, async () => {
console.log("Probot started http://localhost:3000/")
console.log(`autocannon -m POST -b '${pushEvent}' -H content-type=application/json -H x-github-event=push -H x-github-delivery=1 -H x-hub-signature-256=${await sign("secret", pushEvent)} http://127.0.0.1:3000/`)
});

13946
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
{
"name": "probot",
"version": "7.2.0",
"description": "🤖 A framework for building GitHub Apps to automate and improve your workflow",
"repository": "https://github.com/probot/probot",
"version": "0.0.0-development",
"description": "A framework for building GitHub Apps to automate and improve your workflow",
"repository": "github:probot/probot",
"main": "lib/index.js",
"types": "./lib/index.d.ts",
"bin": {
@ -10,14 +10,13 @@
},
"scripts": {
"build": "rimraf lib && tsc -p tsconfig.json",
"start": "node ./bin/probot run",
"lint": "tslint --project test",
"test": "tsc --noEmit -p test && jest --coverage && npm run lint && npm run doc-lint",
"doc-lint": "standard-markdown docs/",
"doc": "typedoc --options .typedoc.json",
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
"postsemantic-release": "script/publish-docs",
"prepare": "npm run build"
"lint": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\" \"docs/*.md\" *.md tsconfig.json package.json --end-of-line auto",
"lint:fix": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"docs/*.md\" *.md tsconfig.json package.json --end-of-line auto",
"pretest": "npm run build && tsc --noEmit -p test",
"test": "vitest run",
"test:coverage": "vitest run --coverage",
"test:dev": "vitest --ui --coverage",
"doc": "typedoc --options .typedoc.json"
},
"files": [
"lib",
@ -25,32 +24,6 @@
"static",
"views"
],
"jest": {
"setupFiles": [
"<rootDir>/test/setup.js"
],
"coveragePathIgnorePatterns": [
"<rootDir>/lib/"
],
"moduleFileExtensions": [
"ts",
"js",
"json",
"node"
],
"transform": {
".+\\.tsx?$": "ts-jest"
},
"testMatch": [
"<rootDir>/test/**/*.test.(ts|js)"
],
"globals": {
"ts-jest": {
"skipBabel": true
}
},
"testURL": "http://localhost/"
},
"keywords": [
"probot",
"github-apps",
@ -59,81 +32,87 @@
"robots",
"workflow"
],
"bugs": "https://github.com/probot/probot/issues",
"homepage": "https://probot.github.io",
"author": "Brandon Keepers",
"license": "ISC",
"dependencies": {
"@octokit/rest": "^15.13.0",
"@octokit/webhooks": "^5.0.2",
"@types/supports-color": "^5.3.0",
"bottleneck": "^2.8.0",
"bunyan": "^1.8.12",
"bunyan-format": "^0.2.1",
"bunyan-sentry-stream": "^1.1.0",
"cache-manager": "^2.4.0",
"commander": "^2.11.0",
"dotenv": "~6.0.0",
"express": "^4.16.2",
"express-async-errors": "^3.0.0",
"hbs": "^4.0.1",
"is-base64": "0.0.5",
"js-yaml": "^3.9.1",
"jsonwebtoken": "^8.1.0",
"pkg-conf": "^2.0.0",
"promise-events": "^0.1.3",
"qs": "^6.5.2",
"raven": "^2.4.2",
"resolve": "^1.4.0",
"semver": "^5.5.0",
"supports-color": "^5.5.0",
"update-dotenv": "^1.1.0",
"uuid": "^3.2.1"
"@octokit/core": "^5.0.2",
"@octokit/plugin-enterprise-compatibility": "^4.0.1",
"@octokit/plugin-paginate-rest": "^9.1.4",
"@octokit/plugin-rest-endpoint-methods": "^10.1.5",
"@octokit/plugin-retry": "^6.0.1",
"@octokit/plugin-throttling": "^8.1.3",
"@octokit/request": "^8.1.6",
"@octokit/types": "^12.3.0",
"@octokit/webhooks": "^12.0.10",
"@probot/get-private-key": "^1.1.2",
"@probot/octokit-plugin-config": "^2.0.1",
"@probot/pino": "^2.3.5",
"@types/express": "^4.17.21",
"bottleneck": "^2.19.5",
"commander": "^12.0.0",
"deepmerge": "^4.3.1",
"dotenv": "^16.3.1",
"eventsource": "^2.0.2",
"express": "^4.18.2",
"ioredis": "^5.3.2",
"js-yaml": "^4.1.0",
"lru-cache": "^10.0.3",
"octokit-auth-probot": "^2.0.0",
"pino": "^9.0.0",
"pino-http": "^10.0.0",
"pkg-conf": "^3.1.0",
"resolve": "^1.22.8",
"update-dotenv": "^1.1.1"
},
"devDependencies": {
"@types/bunyan": "^1.8.4",
"@types/bunyan-format": "^0.2.0",
"@types/cache-manager": "^1.2.6",
"@types/dotenv": "^4.0.2",
"@types/express": "^4.0.39",
"@types/jest": "^23.1.3",
"@types/js-yaml": "^3.10.1",
"@types/jsonwebtoken": "^7.2.5",
"@types/nock": "^9.1.0",
"@types/node": "^10.7.0",
"@types/qs": "^6.5.1",
"@types/raven": "^2.1.5",
"@types/resolve": "^0.0.4",
"@types/semver": "^5.4.0",
"@types/supertest": "^2.0.4",
"@types/uuid": "^3.4.3",
"@octokit/tsconfig": "^3.0.0",
"@octokit/webhooks-examples": "^7.3.1",
"@octokit/webhooks-methods": "^4.0.0",
"@octokit/webhooks-types": "^7.3.1",
"@types/eventsource": "^1.1.15",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.0.0",
"@types/resolve": "^1.20.5",
"@types/supertest": "^6.0.0",
"@vitest/coverage-v8": "^1.0.0",
"@vitest/ui": "^1.0.0",
"connect-sse": "^1.2.0",
"eslint": "^5.0.1",
"eslint-plugin-markdown": "^1.0.0-beta.8",
"jest": "^23.6.0",
"minami": "^1.1.1",
"nock": "^10.0.0",
"semantic-release": "^15.0.0",
"smee-client": "^1.0.1",
"standard": "^12.0.0",
"standard-markdown": "^4.0.2",
"supertest": "^3.0.0",
"ts-jest": "^23.1.4",
"tslint": "^5.9.1",
"tslint-config-prettier": "^1.10.0",
"tslint-config-standard": "^8.0.0",
"typedoc": "^0.12.0",
"typescript": "^2.7.2"
},
"standard": {
"env": [
"jest"
],
"plugins": [
"markdown"
]
"execa": "^5.0.0",
"fetch-mock": "npm:@gr2m/fetch-mock@9.11.0-pull-request-644.1",
"get-port": "^5.1.1",
"prettier": "^3.0.3",
"rimraf": "^5.0.5",
"semantic-release": "^23.0.0",
"semantic-release-plugin-update-version-in-files": "^1.1.0",
"smee-client": "^2.0.0",
"sonic-boom": "^4.0.0",
"supertest": "^7.0.0",
"tsd": "^0.31.0",
"typedoc": "^0.25.3",
"typescript": "^5.2.2",
"vitest": "^1.0.0"
},
"engines": {
"node": ">=7.7"
"node": ">=18"
},
"release": {
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/github",
"@semantic-release/npm",
[
"semantic-release-plugin-update-version-in-files",
{
"files": [
"lib/version.*"
]
}
]
]
},
"publishConfig": {
"provenance": true
}
}

View File

@ -1,25 +1,23 @@
#!/bin/sh
#!/bin/bash
# Exit if a command returns a non-zero code
set -e
# Store the versions
VERSION=$(node -e "process.stdout.write(require('./package.json').version)")
VERSION=$(git describe --tags $(git rev-list --tags --max-count=1) | sed 's/^v//')
HEAD=$(git rev-parse HEAD)
REPO=https://${OCTOKITBOT_PAT}@github.com/probot/probot.github.io.git
echo "Version is $VERSION"
echo "Head is $HEAD"
# Generate docs
npm run doc
# Checkout the website into the tmp directory
[ -d tmp/website ] || {
REPO=https://github.com/probot/probot.github.io
# If GH_TOKEN environment variable is present, use that to push.
if [[ -n "$GH_TOKEN" ]]; then
REPO=${REPO/https:\/\//https://${GH_TOKEN}@}
fi
mkdir -p tmp
git clone $REPO tmp/website
git clone "$REPO" tmp/website
}
# Update the website checkout
@ -27,23 +25,24 @@ cd tmp/website
git pull origin master
# Delete previously generated docs for this version
rm -rf api/$VERSION
rm -rf "api/$VERSION"
# Copy over the new docs
mv ../../out api/$VERSION
mv ../../out "api/$VERSION"
# Update the /api/latest link to point to this version
ln -sfn $VERSION api/latest
ln -sfn "$VERSION" api/latest
# Update the submodule
git submodule update --init
cd _submodules/probot
git fetch
git checkout $HEAD
git checkout "$HEAD"
cd ../..
# Commit and publish
# Configure Git and commit, and publish
git config --global user.email "action@github.com"
git config --global user.name "GitHub Action"
git add .
git commit -m "Update docs for v$VERSION"
git tag -f v$VERSION
git push --tags origin master
git commit -m "Update docs from Probot v$VERSION" -m "via $GITHUB_REPOSITORY@$HEAD"
git push "$REPO" master

View File

@ -1,68 +0,0 @@
declare module '@octokit/webhooks' {
import { Application } from 'express'
interface Options {
path: string,
secret: string
}
declare class Webhooks {
public middleware: Application
constructor (Options)
public on (event: string, callback: (event: WebhookEvent | Error) => void)
public sign (data: WebhookPayloadWithRepository)
}
declare namespace Webhooks {
export interface WebhookEvent {
id: string
name: string
payload: WebhookPayloadWithRepository
protocol?: 'http' | 'https'
host?: string
url?: string
}
export interface PayloadRepository {
[key: string]: any
full_name?: string
name: string
owner: {
[key: string]: any
login: string
name?: string
}
html_url?: string
}
export interface WebhookPayloadWithRepository {
[key: string]: any
repository?: PayloadRepository
issue?: {
[key: string]: any
number: number
html_url?: string
body?: string
}
pull_request?: {
[key: string]: any
number: number
html_url?: string
body?: string
}
sender?: {
[key: string]: any
type: string
}
action?: string
installation?: {
id: number
[key: string]: any
}
}
}
export = Webhooks
}

View File

@ -1,38 +0,0 @@
/** Declaration file generated by dts-gen */
declare module 'bunyan-sentry-stream' {
export = bunyan_sentry_stream;
declare function bunyan_sentry_stream(client: any): any;
declare namespace bunyan_sentry_stream {
class SentryStream {
constructor(...args: any[]);
deserializeError(...args: any[]): void;
getSentryLevel(...args: any[]): void;
write(...args: any[]): void;
}
const prototype: {
};
namespace SentryStream {
namespace prototype {
function deserializeError(...args: any[]): void;
function getSentryLevel(...args: any[]): void;
function write(...args: any[]): void;
}
}
}
}

View File

@ -1,6 +0,0 @@
declare module "promise-events" {
class EventEmitter {
emit<T = void>(event: string | symbol, ...args: any[]): Promise<T>;
on<T = void>(event: string | symbol, handler: (...args: any[]) => Promise<T>): void;
}
}

View File

@ -1,227 +0,0 @@
import { WebhookEvent } from '@octokit/webhooks'
import express from 'express'
import { EventEmitter } from 'promise-events'
import { ApplicationFunction } from '.'
import { Context } from './context'
import { GitHubAPI } from './github'
import { logger } from './logger'
import { LoggerWithTarget, wrapLogger } from './wrap-logger'
// Some events can't get an authenticated client (#382):
function isUnauthenticatedEvent (event: WebhookEvent) {
return !event.payload.installation ||
(event.name === 'installation' && event.payload.action === 'deleted')
}
/**
* The `app` parameter available to `ApplicationFunction`s
*
* @property {logger} log - A logger
*/
export class Application {
public events: EventEmitter
public app: () => string
public cache: Cache
public router: express.Router
public catchErrors: boolean
public log: LoggerWithTarget
constructor (options?: Options) {
const opts = options || {} as any
this.events = new EventEmitter()
this.log = wrapLogger(logger, logger)
this.app = opts.app
this.cache = opts.cache
this.catchErrors = opts.catchErrors || false
this.router = opts.router || express.Router() // you can do this?
}
/**
* Loads an ApplicationFunction into the current Application
* @param appFn - Probot application function to load
*/
public load (appFn: ApplicationFunction | ApplicationFunction[]): Application {
if (Array.isArray(appFn)) {
appFn.forEach(a => this.load(a))
} else {
appFn(this)
}
return this
}
public async receive (event: WebhookEvent) {
if ((event as any).event) {
// tslint:disable-next-line:no-console
console.warn(new Error('Propery `event` is deprecated, use `name`'))
event = { name: (event as any).event, ...event }
}
return Promise.all([
this.events.emit('*', event),
this.events.emit(event.name, event),
this.events.emit(`${ event.name }.${ event.payload.action }`, event)
])
}
/**
* Get an {@link http://expressjs.com|express} router that can be used to
* expose HTTP endpoints
*
* ```
* module.exports = app => {
* // Get an express router to expose new HTTP endpoints
* const route = app.route('/my-app');
*
* // Use any middleware
* route.use(require('express').static(__dirname + '/public'));
*
* // Add a new route
* route.get('/hello-world', (req, res) => {
* res.end('Hello World');
* });
* };
* ```
*
* @param path - the prefix for the routes
* @returns an [express.Router](http://expressjs.com/en/4x/api.html#router)
*/
public route (path?: string): express.Router {
if (path) {
const router = express.Router()
this.router.use(path, router)
return router
} else {
return this.router
}
}
/**
* Listen for [GitHub webhooks](https://developer.github.com/webhooks/),
* which are fired for almost every significant action that users take on
* GitHub.
*
* @param event - the name of the [GitHub webhook
* event](https://developer.github.com/webhooks/#events). Most events also
* include an "action". For example, the * [`issues`](
* https://developer.github.com/v3/activity/events/types/#issuesevent)
* event has actions of `assigned`, `unassigned`, `labeled`, `unlabeled`,
* `opened`, `edited`, `milestoned`, `demilestoned`, `closed`, and `reopened`.
* Often, your bot will only care about one type of action, so you can append
* it to the event name with a `.`, like `issues.closed`.
*
* ```js
* app.on('push', context => {
* // Code was just pushed.
* });
*
* app.on('issues.opened', context => {
* // An issue was just opened.
* });
* ```
*
* @param callback - a function to call when the
* webhook is received.
*/
public on (eventName: string | string[], callback: (context: Context) => Promise<void>) {
if (typeof eventName === 'string') {
return this.events.on(eventName, async (event: WebhookEvent) => {
const log = this.log.child({ name: 'event', id: event.id })
try {
let github
if (isUnauthenticatedEvent(event)) {
github = await this.auth()
log.debug('`context.github` is unauthenticated. See https://probot.github.io/docs/github-api/#unauthenticated-events')
} else {
github = await this.auth(event.payload.installation!.id, log)
}
const context = new Context(event, github, log)
await callback(context)
} catch (err) {
log.error({ err, event })
if (!this.catchErrors) {
throw err
}
}
})
} else {
eventName.forEach(e => this.on(e, callback))
}
}
/**
* Authenticate and get a GitHub client that can be used to make API calls.
*
* You'll probably want to use `context.github` instead.
*
* **Note**: `app.auth` is asynchronous, so it needs to be prefixed with a
* [`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await)
* to wait for the magic to happen.
*
* ```js
* module.exports = (app) => {
* app.on('issues.opened', async context => {
* const github = await app.auth();
* });
* };
* ```
*
* @param id - ID of the installation, which can be extracted from
* `context.payload.installation.id`. If called without this parameter, the
* client wil authenticate [as the app](https://developer.github.com/apps/building-integrations/setting-up-and-registering-github-apps/about-authentication-options-for-github-apps/#authenticating-as-a-github-app)
* instead of as a specific installation, which means it can only be used for
* [app APIs](https://developer.github.com/v3/apps/).
*
* @returns An authenticated GitHub API client
* @private
*/
public async auth (id?: number, log = this.log): Promise<GitHubAPI> {
if (process.env.GHE_HOST && /^https?:\/\//.test(process.env.GHE_HOST)) {
throw new Error('Your \`GHE_HOST\` environment variable should not begin with https:// or http://')
}
const github = GitHubAPI({
baseUrl: process.env.GHE_HOST && `https://${process.env.GHE_HOST}/api/v3`,
debug: process.env.LOG_LEVEL === 'trace',
logger: log.child({ name: 'github', installation: String(id) })
})
// Cache for 1 minute less than GitHub expiry
const installationTokenTTL = parseInt(process.env.INSTALLATION_TOKEN_TTL || '3540', 10)
if (id) {
const res = await this.cache.wrap(`app:${id}:token`, () => {
log.trace(`creating token for installation`)
github.authenticate({ type: 'app', token: this.app() })
return github.apps.createInstallationToken({ installation_id: id })
}, { ttl: installationTokenTTL })
github.authenticate({ type: 'token', token: res.data.token })
} else {
github.authenticate({ type: 'app', token: this.app() })
}
return github
}
}
// The TypeScript definition for cache-manager does not export the Cache interface so we recreate it here
export interface Cache {
wrap<T> (key: string, wrapper: (callback: (error: any, result: T) => void) => any, options: CacheConfig): Promise<any>
}
export interface CacheConfig {
ttl: number
}
export interface Options {
app: () => string
cache: Cache
router?: express.Router
catchErrors?: boolean
}

View File

@ -1,18 +1,28 @@
import path from 'path'
import { Application } from '../application'
import { resolve } from "node:path";
export = (app: Application) => {
const route = app.route()
import type { ApplicationFunctionOptions, Probot } from "../index.js";
import { loadPackageJson } from "../helpers/load-package-json.js";
import { probotView } from "../views/probot.js";
route.get('/probot', (req, res) => {
let pkg
try {
pkg = require(path.join(process.cwd(), 'package.json'))
} catch (e) {
pkg = {}
}
export function defaultApp(
_app: Probot,
{ getRouter, cwd = process.cwd() }: ApplicationFunctionOptions,
) {
if (!getRouter) {
throw new Error("getRouter() is required for defaultApp");
}
res.render('probot.hbs', pkg)
})
route.get('/', (req, res, next) => res.redirect('/probot'))
const pkg = loadPackageJson(resolve(cwd, "package.json"));
const probotViewRendered = probotView({
name: pkg.name,
version: pkg.version,
description: pkg.description,
});
const router = getRouter();
router.get("/probot", (_req, res) => {
res.send(probotViewRendered);
});
router.get("/", (_req, res) => res.redirect("/probot"));
}

View File

@ -1,16 +0,0 @@
import sentryStream from 'bunyan-sentry-stream'
import Raven from 'raven'
import { Application } from '../application'
export = (app: Application) => {
// If sentry is configured, report all logged errors
if (process.env.SENTRY_DSN) {
app.log.debug(process.env.SENTRY_DSN, 'Errors will be reported to Sentry')
Raven.disableConsoleAlerts()
Raven.config(process.env.SENTRY_DSN, {
autoBreadcrumbs: true
}).install()
app.log.target.addStream(sentryStream(Raven))
}
}

View File

@ -1,55 +1,210 @@
import { exec } from 'child_process'
import { Request, Response } from 'express'
import { Application } from '../application'
import { ManifestCreation } from '../manifest-creation'
import { exec } from "node:child_process";
// use glitch env to get correct domain welcome message
// https://glitch.com/help/project/
const domain = process.env.PROJECT_DOMAIN || `http://localhost:${process.env.PORT || 3000}`
const welcomeMessage = `\nWelcome to Probot! Go to ${domain} to get started.\n`
import type { IncomingMessage, ServerResponse } from "http";
import { parse as parseQuery } from "querystring";
import express from "express";
import updateDotenv from "update-dotenv";
export = async (app: Application, setup: ManifestCreation = new ManifestCreation()) => {
// If not on Glitch or Production, create a smee URL
if (process.env.NODE_ENV !== 'production' && !(process.env.PROJECT_DOMAIN || process.env.WEBHOOK_PROXY_URL)) {
await setup.createWebhookChannel()
}
import { Probot } from "../probot.js";
import { ManifestCreation } from "../manifest-creation.js";
import { getLoggingMiddleware } from "../server/logging-middleware.js";
import type { ApplicationFunctionOptions } from "../types.js";
import { isProduction } from "../helpers/is-production.js";
const route = app.route()
import { importView } from "../views/import.js";
import { setupView } from "../views/setup.js";
import { successView } from "../views/success.js";
app.log.info(welcomeMessage)
route.get('/probot', async (req, res) => {
const protocols = req.headers['x-forwarded-proto'] || req.protocol
const protocol = typeof protocols === 'string' ? protocols.split(',')[0] : protocols[0]
const host = req.headers['x-forwarded-host'] || req.get('host')
const baseUrl = `${protocol}://${host}`
const pkg = setup.pkg
const manifest = setup.getManifest(pkg, baseUrl)
const createAppUrl = setup.createAppUrl
// Pass the manifest to be POST'd
res.render('setup.hbs', { pkg, createAppUrl, manifest })
})
route.get('/probot/setup', async (req: Request, res: Response) => {
const { code } = req.query
const response = await setup.createAppFromCode(code)
// If using glitch, restart the app
if (process.env.PROJECT_DOMAIN) {
exec('refresh', (err, stdout, stderr) => {
if (err) {
app.log.error(err, stderr)
}
})
export const setupAppFactory = (
host: string | undefined,
port: number | undefined,
) =>
async function setupApp(
app: Probot,
{ getRouter }: ApplicationFunctionOptions,
) {
if (!getRouter) {
throw new Error("getRouter is required to use the setup app");
}
res.redirect(`${response}/installations/new`)
})
const setup: ManifestCreation = new ManifestCreation();
const pkg = setup.pkg;
route.get('/probot/success', async (req, res) => {
res.render('success.hbs')
})
// If not on Glitch or Production, create a smee URL
if (
!isProduction() &&
!(
process.env.PROJECT_DOMAIN ||
process.env.WEBHOOK_PROXY_URL ||
process.env.NO_SMEE_SETUP === "true"
)
) {
await setup.createWebhookChannel();
}
route.get('/', (req, res, next) => res.redirect('/probot'))
const route = getRouter();
route.use(getLoggingMiddleware(app.log));
printWelcomeMessage(app, host, port);
route.get("/probot", async (req: IncomingMessage, res: ServerResponse) => {
const baseUrl = getBaseUrl(req);
const manifest = setup.getManifest(pkg, baseUrl);
const createAppUrl = setup.createAppUrl;
// Pass the manifest to be POST'd
res.writeHead(200, { "content-type": "text/html" }).end(
setupView({
name: pkg.name,
version: pkg.version,
description: pkg.description,
createAppUrl,
manifest,
}),
);
});
route.get(
"/probot/setup",
async (req: IncomingMessage, res: ServerResponse) => {
// @ts-expect-error query could be set by a framework, e.g. express
const { code } = req.query || parseQuery(req.url?.split("?")[1] || "");
if (!code || typeof code !== "string" || code.length === 0) {
res
.writeHead(400, { "content-type": "text/plain" })
.end("code missing or invalid");
return;
}
const response = await setup.createAppFromCode(code, {
// @ts-expect-error
request: app.state.request,
});
// If using glitch, restart the app
if (process.env.PROJECT_DOMAIN) {
exec("refresh", (error) => {
if (error) {
app.log.error(error);
}
});
} else {
printRestartMessage(app);
}
res
.writeHead(302, {
"content-type": "text/plain",
location: `${response}/installations/new`,
})
.end(`Found. Redirecting to ${response}/installations/new`);
},
);
const { WEBHOOK_PROXY_URL, GHE_HOST } = process.env;
const GH_HOST = `https://${GHE_HOST ?? "github.com"}`;
const importViewRendered = importView({
name: pkg.name,
WEBHOOK_PROXY_URL,
GH_HOST,
});
route.get(
"/probot/import",
(_req: IncomingMessage, res: ServerResponse) => {
res
.writeHead(200, {
"content-type": "text/html",
})
.end(importViewRendered);
},
);
route.post(
"/probot/import",
express.json(),
(req: IncomingMessage, res: ServerResponse) => {
const { appId, pem, webhook_secret } = (req as unknown as { body: any })
.body;
if (!appId || !pem || !webhook_secret) {
res
.writeHead(400, {
"content-type": "text/plain",
})
.end("appId and/or pem and/or webhook_secret missing");
return;
}
updateDotenv({
APP_ID: appId,
PRIVATE_KEY: `"${pem}"`,
WEBHOOK_SECRET: webhook_secret,
});
res.end();
printRestartMessage(app);
},
);
const successViewRendered = successView({ name: pkg.name });
route.get(
"/probot/success",
(_req: IncomingMessage, res: ServerResponse) => {
res
.writeHead(200, { "content-type": "text/html" })
.end(successViewRendered);
},
);
route.get("/", (_req, res: ServerResponse) =>
res
.writeHead(302, { "content-type": "text/plain", location: `/probot` })
.end(`Found. Redirecting to /probot`),
);
};
function printWelcomeMessage(
app: Probot,
host: string | undefined,
port: number | undefined,
) {
// use glitch env to get correct domain welcome message
// https://glitch.com/help/project/
const domain =
process.env.PROJECT_DOMAIN ||
`http://${host ?? "localhost"}:${port || 3000}`;
[
``,
`Welcome to Probot!`,
`Probot is in setup mode, webhooks cannot be received and`,
`custom routes will not work until APP_ID and PRIVATE_KEY`,
`are configured in .env.`,
`Please follow the instructions at ${domain} to configure .env.`,
`Once you are done, restart the server.`,
``,
].forEach((line) => {
app.log.info(line);
});
}
function printRestartMessage(app: Probot) {
app.log.info("");
app.log.info("Probot has been set up, please restart the server!");
app.log.info("");
}
function getBaseUrl(req: IncomingMessage): string {
const protocols =
req.headers["x-forwarded-proto"] ||
// @ts-expect-error based on the functionality of express
req.socket?.encrypted
? "https"
: "http";
const protocol =
typeof protocols === "string" ? protocols.split(",")[0] : protocols[0];
const host = req.headers["x-forwarded-host"] || req.headers.host;
const baseUrl = `${protocol}://${host}`;
return baseUrl;
}

View File

@ -1,86 +0,0 @@
import { AnyResponse } from '@octokit/rest'
import { Request,Response } from 'express'
// Built-in app to expose stats about the deployment
module.exports = async (app: any): Promise<void> => {
if (process.env.DISABLE_STATS) {
return
}
const REFRESH_INTERVAL = 60 * 60 * 1000
// Cache of stats that get reported
const stats = { installations: 0, popular: [{}] }
// Refresh the stats when the ApplicationFunction is loaded
const initializing = refresh()
// Refresh the stats on an interval
setInterval(refresh, REFRESH_INTERVAL)
// Check for accounts (typically spammy or abusive) to ignore
const ignoredAccounts = (process.env.IGNORED_ACCOUNTS || '').toLowerCase().split(',')
// Setup /probot/stats endpoint to return cached stats
app.router.get('/probot/stats', async (req: Request, res: Response) => {
// ensure stats are loaded
await initializing
res.json(stats)
})
async function refresh () {
const installations = await getInstallations()
stats.installations = installations.length
stats.popular = await popularInstallations(installations)
}
async function getInstallations (): Promise<Installation[]> {
const github = await app.auth()
const req = github.apps.getInstallations({ per_page: 100 })
return github.paginate(req, (res: AnyResponse) => res.data)
}
async function popularInstallations (installations: Installation[]): Promise<Account[]> {
let popular = await Promise.all(installations.map(async (installation) => {
const { account } = installation
if (ignoredAccounts.includes(account.login.toLowerCase())) {
account.stars = 0
app.log.debug({ installation }, 'Installation is ignored')
return account
}
const github = await app.auth(installation.id)
const req = github.apps.getInstallationRepositories({ per_page: 100 })
const repositories: Repository[] = await github.paginate(req, (res: AnyResponse) => {
return res.data.repositories.filter((repository: Repository) => !repository.private)
})
account.stars = repositories.reduce((stars, repository) => {
return stars + repository.stargazers_count
}, 0)
return account
}))
popular = popular.filter(installation => installation.stars > 0)
return popular.sort((a, b) => b.stars - a.stars).slice(0, 10)
}
}
interface Installation {
id: number
account: Account
}
interface Account {
stars: number
login: string
}
interface Repository {
private: boolean
stargazers_count: number
}

36
src/auth.ts Normal file
View File

@ -0,0 +1,36 @@
import { getAuthenticatedOctokit } from "./octokit/get-authenticated-octokit.js";
import { ProbotOctokit } from "./octokit/probot-octokit.js";
import type { State } from "./types.js";
/**
* Authenticate and get a GitHub client that can be used to make API calls.
*
* You'll probably want to use `context.octokit` instead.
*
* **Note**: `app.auth` is asynchronous, so it needs to be prefixed with a
* [`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await)
* to wait for the magic to happen.
*
* ```js
* export default (app) => {
* app.on('issues.opened', async context => {
* const octokit = await app.auth();
* });
* };
* ```
* @param state - Probot application instance state, which is used to persist
*
* @param id - ID of the installation, which can be extracted from
* `context.payload.installation.id`. If called without this parameter, the
* client wil authenticate [as the app](https://docs.github.com/en/developers/apps/authenticating-with-github-apps#authenticating-as-a-github-app)
* instead of as a specific installation, which means it can only be used for
* [app APIs](https://docs.github.com/apps/).
*
* @returns An authenticated GitHub API client
*/
export async function auth(
state: State,
installationId?: number,
): Promise<InstanceType<typeof ProbotOctokit>> {
return getAuthenticatedOctokit(Object.assign({}, state), installationId);
}

144
src/bin/probot-receive.ts Normal file
View File

@ -0,0 +1,144 @@
// Usage: probot receive -e push -p path/to/payload app.js
import fs from "node:fs";
import path from "node:path";
import { randomUUID as uuidv4 } from "node:crypto";
import express, { Router } from "express";
import { config as dotenvConfig } from "dotenv";
dotenvConfig();
import { program } from "commander";
import { getPrivateKey } from "@probot/get-private-key";
import { getLog } from "../helpers/get-log.js";
import { Probot, type ApplicationFunctionOptions } from "../index.js";
import { resolveAppFunction } from "../helpers/resolve-app-function.js";
async function main() {
program
.usage("[options] [path/to/app.js...]")
.option(
"-e, --event <event-name>",
"Event name",
process.env.GITHUB_EVENT_NAME,
)
.option(
"-p, --payload-path <payload-path>",
"Path to the event payload",
process.env.GITHUB_EVENT_PATH,
)
.option(
"-t, --token <access-token>",
"Access token",
process.env.GITHUB_TOKEN,
)
.option("-a, --app <id>", "ID of the GitHub App", process.env.APP_ID)
.option(
"-P, --private-key <file>",
"Path to private key file (.pem) for the GitHub App",
process.env.PRIVATE_KEY_PATH,
)
.option(
"-L, --log-level <level>",
'One of: "trace" | "debug" | "info" | "warn" | "error" | "fatal"',
process.env.LOG_LEVEL,
)
.option(
"--log-format <format>",
'One of: "pretty", "json"',
process.env.LOG_LEVEL || "pretty",
)
.option(
"--log-level-in-string",
"Set to log levels (trace, debug, info, ...) as words instead of numbers (10, 20, 30, ...)",
process.env.LOG_LEVEL_IN_STRING === "true",
)
.option(
"--log-message-key",
"Set to the string key for the 'message' in the log JSON object",
process.env.LOG_MESSAGE_KEY || "msg",
)
.option(
"--sentry-dsn <dsn>",
'Set to your Sentry DSN, e.g. "https://1234abcd@sentry.io/12345"',
process.env.SENTRY_DSN,
)
.option(
"--base-url <url>",
'GitHub API base URL. If you use GitHub Enterprise Server, and your hostname is "https://github.acme-inc.com", then the root URL is "https://github.acme-inc.com/api/v3"',
process.env.GHE_HOST
? `${process.env.GHE_PROTOCOL || "https"}://${
process.env.GHE_HOST
}/api/v3`
: "https://api.github.com",
)
.parse(process.argv);
const {
app: appId,
baseUrl,
token: githubToken,
event,
payloadPath,
logLevel,
logFormat,
logLevelInString,
logMessageKey,
sentryDsn,
} = program.opts();
if (!event || !payloadPath) {
program.help();
}
const privateKey = getPrivateKey();
if (!githubToken && (!appId || !privateKey)) {
console.warn(
"No token specified and no certificate found, which means you will not be able to do authenticated requests to GitHub",
);
}
const payload = JSON.parse(
fs.readFileSync(path.resolve(payloadPath), "utf8"),
);
const log = getLog({
level: logLevel,
logFormat,
logLevelInString,
logMessageKey,
sentryDsn,
});
const probot = new Probot({
appId,
privateKey: String(privateKey),
githubToken: githubToken,
log,
baseUrl: baseUrl,
});
const expressApp = express();
const options: ApplicationFunctionOptions = {
getRouter: (path: string = "/") => {
const newRouter = Router();
expressApp.use(path, newRouter);
return newRouter;
},
};
const appFn = await resolveAppFunction(
path.resolve(process.cwd(), program.args[0]),
);
await probot.load(appFn, options);
probot.log.debug("Receiving event", event);
probot.receive({ name: event, payload, id: uuidv4() }).catch(() => {
// Process must exist non-zero to indicate that the action failed to run
process.exit(1);
});
}
main().catch((error) => {
console.error(error);
process.exit(1);
});

3
src/bin/probot-run.ts Normal file
View File

@ -0,0 +1,3 @@
import { run } from "../index.js";
run(process.argv);

34
src/bin/probot.ts Normal file
View File

@ -0,0 +1,34 @@
import { resolve } from "node:path";
import { program } from "commander";
import { config as dotenvConfig } from "dotenv";
import { isSupportedNodeVersion } from "../helpers/is-supported-node-version.js";
import { loadPackageJson } from "../helpers/load-package-json.js";
/*import { dirname } from 'path';
import { fileURLToPath } from 'url';*/
dotenvConfig();
//const __dirname = dirname(fileURLToPath(import.meta.url));
const pkg = loadPackageJson(resolve(__dirname, "package.json"));
if (!isSupportedNodeVersion()) {
console.log(`Node.js version 18 is required. You have ${process.version}.`);
process.exit(1);
}
program
.version(pkg.version || "0.0.0-dev")
.usage("<command> [options]")
.command("run", "run the bot")
.command("receive", "Receive a single event and payload")
.on("command:*", (cmd) => {
if (!program.commands.find((c) => c.name() == cmd[0])) {
console.error(`Invalid command: ${program.args.join(" ")}\n`);
program.outputHelp();
process.exit(1);
}
});
program.parse(process.argv);

View File

@ -0,0 +1,89 @@
import { program } from "commander";
import { getPrivateKey } from "@probot/get-private-key";
import type { Options as PinoOptions } from "@probot/pino";
import type { Options } from "../types.js";
export function readCliOptions(
argv: string[],
): Options & PinoOptions & { args: string[] } {
program
.usage("[options] <apps...>")
.option(
"-p, --port <n>",
"Port to start the server on",
String(process.env.PORT || 3000),
)
.option("-H --host <host>", "Host to start the server on", process.env.HOST)
.option(
"-W, --webhook-proxy <url>",
"URL of the webhook proxy service.`",
process.env.WEBHOOK_PROXY_URL,
)
.option(
"-w, --webhook-path <path>",
"URL path which receives webhooks. Ex: `/webhook`",
process.env.WEBHOOK_PATH,
)
.option("-a, --app <id>", "ID of the GitHub App", process.env.APP_ID)
.option(
"-s, --secret <secret>",
"Webhook secret of the GitHub App",
process.env.WEBHOOK_SECRET,
)
.option(
"-P, --private-key <file>",
"Path to private key file (.pem) for the GitHub App",
process.env.PRIVATE_KEY_PATH,
)
.option(
"-L, --log-level <level>",
'One of: "trace" | "debug" | "info" | "warn" | "error" | "fatal"',
process.env.LOG_LEVEL || "info",
)
.option(
"--log-format <format>",
'One of: "pretty", "json"',
process.env.LOG_FORMAT,
)
.option(
"--log-level-in-string",
"Set to log levels (trace, debug, info, ...) as words instead of numbers (10, 20, 30, ...)",
process.env.LOG_LEVEL_IN_STRING === "true",
)
.option(
"--sentry-dsn <dsn>",
'Set to your Sentry DSN, e.g. "https://1234abcd@sentry.io/12345"',
process.env.SENTRY_DSN,
)
.option(
"--redis-url <url>",
'Set to a "redis://" url in order to enable cluster support for request throttling. Example: "redis://:secret@redis-123.redislabs.com:12345/0"',
process.env.REDIS_URL,
)
.option(
"--base-url <url>",
'GitHub API base URL. If you use GitHub Enterprise Server, and your hostname is "https://github.acme-inc.com", then the root URL is "https://github.acme-inc.com/api/v3"',
process.env.GHE_HOST
? `${process.env.GHE_PROTOCOL || "https"}://${
process.env.GHE_HOST
}/api/v3`
: "https://api.github.com",
)
.parse(argv);
const {
app: appId,
privateKey: privateKeyPath,
redisUrl,
...options
} = program.opts();
return {
privateKey: getPrivateKey({ filepath: privateKeyPath }) || undefined,
appId,
redisConfig: redisUrl,
args: program.args,
...options,
};
}

View File

@ -0,0 +1,35 @@
import { getPrivateKey } from "@probot/get-private-key";
import type { Options as PinoOptions, LogLevel } from "@probot/pino";
export function readEnvOptions(env = process.env) {
const privateKey = getPrivateKey({ env });
const logFormat: PinoOptions["logFormat"] =
env.LOG_FORMAT && env.LOG_FORMAT.length !== 0
? env.LOG_FORMAT === "pretty"
? "pretty"
: "json"
: env.NODE_ENV === "production"
? "json"
: "pretty";
return {
args: [],
privateKey: (privateKey && privateKey.toString()) || undefined,
appId: Number(env.APP_ID),
port: Number(env.PORT) || 3000,
host: env.HOST,
secret: env.WEBHOOK_SECRET,
webhookPath: env.WEBHOOK_PATH,
webhookProxy: env.WEBHOOK_PROXY_URL,
logLevel: env.LOG_LEVEL as LogLevel,
logFormat: logFormat,
logLevelInString: env.LOG_LEVEL_IN_STRING === "true",
logMessageKey: env.LOG_MESSAGE_KEY,
sentryDsn: env.SENTRY_DSN,
redisConfig: env.REDIS_URL,
baseUrl: env.GHE_HOST
? `${env.GHE_PROTOCOL || "https"}://${env.GHE_HOST}/api/v3`
: "https://api.github.com",
};
}

View File

@ -1,51 +1,78 @@
import { WebhookEvent, WebhookPayloadWithRepository } from '@octokit/webhooks'
import yaml from 'js-yaml'
import path from 'path'
import { GitHubAPI } from './github'
import { LoggerWithTarget } from './wrap-logger'
import path from "node:path";
import merge from "deepmerge";
import type {
EmitterWebhookEvent as WebhookEvent,
EmitterWebhookEventName as WebhookEvents,
} from "@octokit/webhooks";
import type { Logger } from "pino";
import type { ProbotOctokit } from "./octokit/probot-octokit.js";
export type MergeOptions = merge.Options;
/** Repo owner type, either string or never depending on the context */
type RepoOwnerType<T extends WebhookEvents> =
WebhookEvent<T>["payload"] extends {
repository: { owner: { login: string } };
}
? string
: never;
/** Repo name type, either string or never depending on the context */
type RepoNameType<T extends WebhookEvents> =
WebhookEvent<T>["payload"] extends { repository: { name: string } }
? string
: never;
/** Issue type (also pull request number), either number or never depending on the context */
type RepoIssueNumberType<T extends WebhookEvents> =
WebhookEvent<T>["payload"] extends { issue: { number: number } }
? number
: never | WebhookEvent<T>["payload"] extends {
pull_request: { number: number };
}
? number
: never | WebhookEvent<T>["payload"] extends { number: number }
? number
: never;
/** Context.repo return type */
type RepoResultType<E extends WebhookEvents> = {
owner: RepoOwnerType<E>;
repo: RepoNameType<E>;
};
/**
* The context of the event that was triggered, including the payload and
* helpers for extracting information can be passed to GitHub API calls.
*
* ```js
* module.exports = app => {
* export default app => {
* app.on('push', context => {
* context.log('Code was pushed to the repo, what should we do with it?');
* context.log.info('Code was pushed to the repo, what should we do with it?');
* });
* };
* ```
*
* @property {github} github - A GitHub API client
* @property {octokit} octokit - An Octokit instance
* @property {payload} payload - The webhook event payload
* @property {logger} log - A logger
* @property {log} log - A pino instance
*/
export class Context<E extends WebhookEvents = WebhookEvents> {
public name: WebhookEvents;
public id: string;
public payload: WebhookEvent<E>["payload"];
export class Context implements WebhookEvent {
public name: string
public id: string
public payload: WebhookPayloadWithRepository
public protocol?: 'http' | 'https'
public host?: string
public url?: string
public octokit: ProbotOctokit;
public log: Logger;
public github: GitHubAPI
public log: LoggerWithTarget
constructor(event: WebhookEvent<E>, octokit: ProbotOctokit, log: Logger) {
this.name = event.name;
this.id = event.id;
this.payload = event.payload;
constructor (event: WebhookEvent, github: GitHubAPI, log: LoggerWithTarget) {
this.name = event.name
this.id = event.id
this.payload = event.payload
this.protocol = event.protocol
this.host = event.host
this.url = event.url
this.github = github
this.log = log
}
// Maintain backward compatability
public get event (): string {
return this.name
this.octokit = octokit;
this.log = log;
}
/**
@ -60,44 +87,84 @@ export class Context implements WebhookEvent {
* @param object - Params to be merged with the repo params.
*
*/
public repo<T> (object?: T) {
const repo = this.payload.repository
public repo<T>(object?: T): RepoResultType<E> & T {
// @ts-expect-error `repository` is not always present in this.payload
const repo = this.payload.repository;
if (!repo) {
throw new Error('context.repo() is not supported for this webhook event.')
throw new Error(
"context.repo() is not supported for this webhook event.",
);
}
return Object.assign({
owner: repo.owner.login || repo.owner.name!,
repo: repo.name
}, object)
return Object.assign(
{
owner: repo.owner.login,
repo: repo.name,
},
object,
);
}
/**
* Return the `owner`, `repo`, and `number` params for making API requests
* against an issue or pull request. The object passed in will be merged with
* the repo params.
* Return the `owner`, `repo`, and `issue_number` params for making API requests
* against an issue. The object passed in will be merged with the repo params.
*
*
* ```js
* const params = context.issue({body: 'Hello World!'})
* // Returns: {owner: 'username', repo: 'reponame', number: 123, body: 'Hello World!'}
* // Returns: {owner: 'username', repo: 'reponame', issue_number: 123, body: 'Hello World!'}
* ```
*
* @param object - Params to be merged with the issue params.
*/
public issue<T> (object?: T) {
const payload = this.payload
return Object.assign({
number: (payload.issue || payload.pull_request || payload).number
}, this.repo(object))
public issue<T>(
object?: T,
): RepoResultType<E> & { issue_number: RepoIssueNumberType<E> } & T {
return Object.assign(
{
issue_number:
// @ts-expect-error - this.payload may not have `issue` or `pull_request` keys
(this.payload.issue || this.payload.pull_request || this.payload)
.number,
},
this.repo(object),
);
}
/**
* Return the `owner`, `repo`, and `pull_number` params for making API requests
* against a pull request. The object passed in will be merged with the repo params.
*
*
* ```js
* const params = context.pullRequest({body: 'Hello World!'})
* // Returns: {owner: 'username', repo: 'reponame', pull_number: 123, body: 'Hello World!'}
* ```
*
* @param object - Params to be merged with the pull request params.
*/
public pullRequest<T>(
object?: T,
): RepoResultType<E> & { pull_number: RepoIssueNumberType<E> } & T {
const payload = this.payload;
return Object.assign(
{
// @ts-expect-error - this.payload may not have `issue` or `pull_request` keys
pull_number: (payload.issue || payload.pull_request || payload).number,
},
this.repo(object),
);
}
/**
* Returns a boolean if the actor on the event was a bot.
* @type {boolean}
*/
get isBot () {
return this.payload.sender!.type === 'Bot'
get isBot() {
// @ts-expect-error - `sender` key is currently not present in all events
// see https://github.com/octokit/webhooks/issues/510
return this.payload.sender.type === "Bot";
}
/**
@ -111,15 +178,15 @@ export class Context implements WebhookEvent {
* comment: Check the specs on the rotary girder.
* ```
*
* You app can read that file from the target repository:
* Your app can read that file from the target repository:
*
* ```js
* // Load config from .github/config.yml in the repository
* const config = await context.config('config.yml')
*
* if (config.close) {
* context.github.issues.comment(context.issue({body: config.comment}))
* context.github.issues.edit(context.issue({state: 'closed'}))
* context.octokit.issues.comment(context.issue({body: config.comment}))
* context.octokit.issues.edit(context.issue({state: 'closed'}))
* }
* ```
*
@ -130,31 +197,50 @@ export class Context implements WebhookEvent {
* const config = await context.config('config.yml', {comment: 'Make sure to check all the specs.'})
*
* if (config.close) {
* context.github.issues.comment(context.issue({body: config.comment}));
* context.github.issues.edit(context.issue({state: 'closed'}))
* context.octokit.issues.comment(context.issue({body: config.comment}));
* context.octokit.issues.edit(context.issue({state: 'closed'}))
* }
* ```
*
* Config files can also specify a base that they extend. `deepMergeOptions` can be used
* to configure how the target config, extended base, and default configs are merged.
*
* For security reasons, configuration is only loaded from the repository's default branch,
* changes made in pull requests from different branches or forks are ignored.
*
* If you need more lower-level control over reading and merging configuration files,
* you can `context.octokit.config.get(options)`, see https://github.com/probot/octokit-plugin-config.
*
* @param fileName - Name of the YAML file in the `.github` directory
* @param defaultConfig - An object of default config options
* @param deepMergeOptions - Controls merging configs (from the [deepmerge](https://github.com/TehShrike/deepmerge) module)
* @return Configuration object read from the file
*/
public async config<T> (fileName: string, defaultConfig?: T) {
const params = this.repo({ path: path.posix.join('.github', fileName) })
public async config<T>(
fileName: string,
defaultConfig?: T,
deepMergeOptions?: MergeOptions,
): Promise<T | null> {
const params = this.repo({
path: path.posix.join(".github", fileName),
defaults(configs: object[]) {
const result = merge.all(
[defaultConfig || {}, ...configs],
deepMergeOptions,
);
try {
const res = await this.github.repos.getContent(params)
const config = yaml.safeLoad(Buffer.from(res.data.content, 'base64').toString()) || {}
return Object.assign({}, defaultConfig, config)
} catch (err) {
if (err.code === 404) {
if (defaultConfig) {
return defaultConfig
}
return null
} else {
throw err
}
return result;
},
});
// @ts-expect-error
const { config, files } = await this.octokit.config.get(params);
// if no default config is set, and no config files are found, return null
if (!defaultConfig && !files.find((file) => file.config !== null)) {
return null;
}
return config as T;
}
}

View File

@ -0,0 +1,17 @@
import type { RequestListener } from "http";
import { createNodeMiddleware as createWebhooksMiddleware } from "@octokit/webhooks";
import type { ApplicationFunction, MiddlewareOptions } from "./types.js";
import { defaultWebhooksPath } from "./server/server.js";
import { createProbot } from "./create-probot.js";
export function createNodeMiddleware(
appFn: ApplicationFunction,
{ probot = createProbot(), webhooksPath } = {} as MiddlewareOptions,
): RequestListener {
probot.load(appFn);
return createWebhooksMiddleware(probot.webhooks, {
path: webhooksPath || probot.webhookPath || defaultWebhooksPath,
});
}

78
src/create-probot.ts Normal file
View File

@ -0,0 +1,78 @@
import type { LogLevel, Options as PinoOptions } from "@probot/pino";
import { getPrivateKey } from "@probot/get-private-key";
import { getLog } from "./helpers/get-log.js";
import type { Options } from "./types.js";
import { Probot } from "./probot.js";
import { defaultWebhooksPath } from "./server/server.js";
type CreateProbotOptions = {
overrides?: Options;
defaults?: Options;
env?: Partial<NodeJS.ProcessEnv>;
};
const DEFAULTS: Partial<NodeJS.ProcessEnv> = {
APP_ID: "",
WEBHOOK_SECRET: "",
WEBHOOK_PATH: defaultWebhooksPath,
GHE_HOST: "",
GHE_PROTOCOL: "https",
LOG_FORMAT: undefined,
LOG_LEVEL: "warn",
LOG_LEVEL_IN_STRING: "false",
LOG_MESSAGE_KEY: "msg",
REDIS_URL: "",
SENTRY_DSN: "",
};
/**
* Merges configuration from defaults/environment variables/overrides and returns
* a Probot instance. Finds private key using [`@probot/get-private-key`](https://github.com/probot/get-private-key).
*
* @see https://probot.github.io/docs/configuration/
* @param defaults default Options, will be overwritten if according environment variable is set
* @param overrides overwrites defaults and according environment variables
* @param env defaults to process.env
*/
export function createProbot({
overrides = {},
defaults = {},
env = process.env,
}: CreateProbotOptions = {}) {
const privateKey = getPrivateKey({ env });
const envWithDefaults = { ...DEFAULTS, ...env };
const envOptions: Options = {
logLevel: envWithDefaults.LOG_LEVEL as LogLevel,
appId: Number(envWithDefaults.APP_ID),
privateKey: (privateKey && privateKey.toString()) || undefined,
secret: envWithDefaults.WEBHOOK_SECRET,
redisConfig: envWithDefaults.REDIS_URL,
webhookPath: envWithDefaults.WEBHOOK_PATH,
baseUrl: envWithDefaults.GHE_HOST
? `${envWithDefaults.GHE_PROTOCOL || "https"}://${
envWithDefaults.GHE_HOST
}/api/v3`
: "https://api.github.com",
};
const probotOptions = {
...defaults,
...envOptions,
...overrides,
};
const log = getLog({
level: probotOptions.logLevel,
logFormat: envWithDefaults.LOG_FORMAT as PinoOptions["logFormat"],
logLevelInString: envWithDefaults.LOG_LEVEL_IN_STRING === "true",
logMessageKey: envWithDefaults.LOG_MESSAGE_KEY,
sentryDsn: envWithDefaults.SENTRY_DSN,
}).child({ name: "server" });
return new Probot({
log: log.child({ name: "probot" }),
...probotOptions,
});
}

18
src/get-router.ts Normal file
View File

@ -0,0 +1,18 @@
import { Router } from "express";
/**
* Get an {@link http://expressjs.com|express} router that can be used to
* expose HTTP endpoints
*
* @param path - the prefix for the routes
* @returns an [express.Router](http://expressjs.com/en/4x/api.html#router)
*/
export function getRouter(router: Router, path?: string): Router {
if (path) {
const newRouter = Router();
router.use(path, newRouter);
return newRouter;
}
return router;
}

View File

@ -1,19 +0,0 @@
import jwt from 'jsonwebtoken'
export const createApp = (options: AppOptions) => {
return () => {
const payload = {
exp: Math.floor(Date.now() / 1000) + 60, // JWT expiration time
iat: Math.floor(Date.now() / 1000), // Issued at time
iss: options.id // GitHub App ID
}
// Sign with RSA SHA256
return jwt.sign(payload, options.cert, { algorithm: 'RS256' })
}
}
export interface AppOptions {
id: number
cert: string
}

View File

@ -1,43 +0,0 @@
import { GitHubAPI, Headers, Variables } from './'
class GraphQLError extends Error {
public query: string
public variables: Variables
constructor (errors: Error[], query: string, variables: Variables) {
super(JSON.stringify(errors))
this.name = 'GraphQLError'
this.query = query
this.variables = variables
if (Error.captureStackTrace) {
Error.captureStackTrace(this, GraphQLError)
}
}
}
export function addGraphQL (client: GitHubAPI) {
client.query = graphql.bind(null, client)
}
async function graphql (client: GitHubAPI, query: string, variables: Variables, headers: Headers = {}) {
const res = await client.request({
baseUrl: process.env.GHE_HOST && `https://${process.env.GHE_HOST}/api`,
headers: {
'accept': 'application/json',
'content-type': 'application/json',
...headers
},
method: 'POST',
url: '/graphql',
query,
variables
})
if (res.data.errors) {
throw new GraphQLError(res.data.errors, query, variables)
}
return res.data.data
}

View File

@ -1,72 +0,0 @@
import Octokit from '@octokit/rest'
import { addGraphQL } from './graphql'
import { addLogging, Logger } from './logging'
import { addPagination } from './pagination'
import { addRateLimiting } from './rate-limiting'
/**
* the [@octokit/rest Node.js module](https://github.com/octokit/rest.js),
* which wraps the [GitHub API](https://developer.github.com/v3/) and allows
* you to do almost anything programmatically that you can do through a web
* browser.
* @see {@link https://github.com/octokit/rest.js}
*/
export function GitHubAPI (options: Options = {} as any) {
const octokit = new Octokit(options) as GitHubAPI
addRateLimiting(octokit, options.limiter)
addLogging(octokit, options.logger)
addPagination(octokit)
addGraphQL(octokit)
return octokit
}
export interface Options extends Octokit.Options {
debug?: boolean
logger: Logger
limiter?: any
}
export interface RequestOptions {
baseUrl?: string
method: string
url: string
headers: any
query?: string
variables?: Variables
}
export interface Result {
headers: {
status: string
}
}
export interface OctokitError extends Error {
code: number
status: string
}
export interface GitHubAPI extends Octokit {
paginate: (res: Promise<Octokit.AnyResponse>, callback: (results: Octokit.AnyResponse) => void) => Promise<any[]>
// The following are added because Octokit does not expose the hook.error, hook.before, and hook.after methods
hook: {
error: (when: 'request', callback: (error: OctokitError, options: RequestOptions) => void) => void
before: (when: 'request', callback: (result: Result, options: RequestOptions) => void) => void
after: (when: 'request', callback: (result: Result, options: RequestOptions) => void) => void
}
request: (RequestOptions: RequestOptions) => Promise<Octokit.AnyResponse>
query: (query: string, variables?: Variables, headers?: Headers) => Promise<GraphQLResponse>
}
export interface Headers {
[key: string]: string
}
export interface Variables { [key: string]: any }
export interface GraphQLResponse {
data: any
}

View File

@ -1,22 +0,0 @@
import Logger from 'bunyan'
import { GitHubAPI } from './'
export function addLogging (client: GitHubAPI, logger: Logger) {
if (!logger) {
return
}
client.hook.error('request', (error, options) => {
const { method, url, headers, ...params } = options
const msg = `GitHub request: ${method} ${url} - ${error.code} ${error.status}`
logger.debug({ params }, msg)
throw error
})
client.hook.after('request', (result, options) => {
const { method, url, headers, ...params } = options
const msg = `GitHub request: ${method} ${url} - ${result.headers.status}`
logger.debug({ params }, msg)
})
}
export { Logger }

View File

@ -1,28 +0,0 @@
import { AnyResponse } from '@octokit/rest'
import { GitHubAPI } from './'
export function addPagination (octokit: GitHubAPI) {
octokit.paginate = paginate.bind(null, octokit)
}
const defaultCallback = (response: Promise<AnyResponse>, done?: () => void) => response
async function paginate (octokit: GitHubAPI, responsePromise: any, callback = defaultCallback) {
let collection: any[] = []
let getNextPage = true
const done = () => {
getNextPage = false
}
let response = await responsePromise
collection = collection.concat(await callback(response, done))
// eslint-disable-next-line no-unmodified-loop-condition
while (getNextPage && octokit.hasNextPage(response)) {
response = await octokit.getNextPage(response)
collection = collection.concat(await callback(response, done))
}
return collection
}

View File

@ -1,14 +0,0 @@
import Bottleneck from 'bottleneck'
import { GitHubAPI } from './'
export function addRateLimiting (octokit: GitHubAPI, limiter: Bottleneck) {
if (!limiter) {
limiter = new Bottleneck({
maxConcurrent: 1,
minTime: 1000
})
}
const noop = () => Promise.resolve()
octokit.hook.before('request', limiter.schedule.bind(limiter, noop))
}

View File

@ -0,0 +1,42 @@
import type { Logger } from "pino";
import type {
WebhookError,
EmitterWebhookEvent as WebhookEvent,
} from "@octokit/webhooks";
export function getErrorHandler(log: Logger) {
return (error: Error & { event?: WebhookEvent }) => {
const errors = (
error.name === "AggregateError" ? error : [error]
) as WebhookError[];
const event = error.event;
for (const error of errors) {
const errMessage = (error.message || "").toLowerCase();
if (errMessage.includes("x-hub-signature-256")) {
log.error(
error,
"Go to https://github.com/settings/apps/YOUR_APP and verify that the Webhook secret matches the value of the WEBHOOK_SECRET environment variable.",
);
continue;
}
if (errMessage.includes("pem") || errMessage.includes("json web token")) {
log.error(
error,
"Your private key (a .pem file or PRIVATE_KEY environment variable) or APP_ID is incorrect. Go to https://github.com/settings/apps/YOUR_APP, verify that APP_ID is set correctly, and generate a new PEM file if necessary.",
);
continue;
}
log
.child({
name: "event",
id: event?.id,
})
.error(error);
}
};
}

39
src/helpers/get-log.ts Normal file
View File

@ -0,0 +1,39 @@
/**
* A logger backed by [pino](https://getpino.io/)
*
* The default log level is `info`, but you can change it passing a level
* string set to one of: `"trace"`, `"debug"`, `"info"`, `"warn"`,
* `"error"`, or `"fatal"`.
*
* ```js
* app.log.debug("…so is this");
* app.log.trace("Now we're talking");
* app.log.info("I thought you should know…");
* app.log.warn("Woah there");
* app.log.error("ETOOMANYLOGS");
* app.log.fatal("Goodbye, cruel world!");
* ```
*/
import { pino } from "pino";
import type { Logger, LoggerOptions } from "pino";
import { getTransformStream, type Options, type LogLevel } from "@probot/pino";
import { rebindLog } from "./rebind-log";
export type GetLogOptions = {
level?: LogLevel;
logMessageKey?: string;
} & Options;
export function getLog(options: GetLogOptions = {}): Logger {
const { level, logMessageKey, ...getTransformStreamOptions } = options;
const pinoOptions: LoggerOptions = {
level: level || "info",
name: "probot",
messageKey: logMessageKey || "msg",
};
const transform = getTransformStream(getTransformStreamOptions);
transform.pipe(pino.destination(1));
return rebindLog(pino(pinoOptions, transform));
}

View File

@ -0,0 +1,3 @@
export function isProduction() {
return process.env.NODE_ENV === "production";
}

View File

@ -0,0 +1,3 @@
export function isSupportedNodeVersion(nodeVersion = process.versions.node) {
return Number(nodeVersion.split(".", 10)[0]) >= 18;
}

View File

@ -0,0 +1,24 @@
import fs from "node:fs";
import path from "node:path";
import type { PackageJson } from "../types.js";
export function loadPackageJson(
filepath = path.join(process.cwd(), "package.json"),
): PackageJson {
let pkgContent;
try {
pkgContent = fs.readFileSync(filepath, "utf8");
} catch {
return {};
}
try {
const pkg = pkgContent && JSON.parse(pkgContent);
if (pkg && typeof pkg === "object") {
return pkg;
}
return {};
} catch {
return {};
}
}

17
src/helpers/rebind-log.ts Normal file
View File

@ -0,0 +1,17 @@
import type { Logger } from "pino";
const kIsBound = Symbol("is-bound");
export function rebindLog(log: Logger): Logger {
// @ts-ignore
if (log[kIsBound]) return log;
for (const key in log) {
// @ts-ignore
if (typeof log[key] !== "function") continue;
// @ts-ignore
log[key] = log[key].bind(log);
}
// @ts-ignore
log[kIsBound] = true;
return log;
}

View File

@ -0,0 +1,30 @@
import resolveModule from "resolve";
const defaultOptions: ResolveOptions = {};
export const resolveAppFunction = async (
appFnId: string,
opts?: ResolveOptions,
) => {
opts = opts || defaultOptions;
// These are mostly to ease testing
const basedir = opts.basedir || process.cwd();
const resolver: Resolver = opts.resolver || resolveModule.sync;
const appFnPath = resolver(appFnId, { basedir });
// On windows, an absolute path may start with a drive letter, e.g. C:/path/to/file.js
// This can be interpreted as a protocol, so ensure it's prefixed with file://
const appFnPathWithFileProtocol = appFnPath.replace(
/^([a-zA-Z]:)/,
"file://$1",
);
const { default: mod } = await import(appFnPathWithFileProtocol);
// mod.default gets exported by transpiled TypeScript code
return mod.__esModule && mod.default ? mod.default : mod;
};
export type Resolver = (appFnId: string, opts: { basedir: string }) => string;
export interface ResolveOptions {
basedir?: string;
resolver?: Resolver;
}

View File

@ -0,0 +1,31 @@
import EventSource from "eventsource";
import type { Logger } from "pino";
export const createWebhookProxy = async (
opts: WebhookProxyOptions,
): Promise<EventSource | undefined> => {
try {
const SmeeClient = (await import("smee-client")).default;
const smee = new SmeeClient({
logger: opts.logger,
source: opts.url,
target: `http://localhost:${opts.port}${opts.path}`,
fetch: opts.fetch,
});
return smee.start();
} catch (error) {
opts.logger.warn(
"Run `npm install --save-dev smee-client` to proxy webhooks to localhost.",
);
return;
}
};
export interface WebhookProxyOptions {
url: string;
port?: number;
path?: string;
logger: Logger;
fetch?: Function;
}

View File

@ -1,129 +1,154 @@
import Webhooks, { WebhookEvent } from '@octokit/webhooks'
import Logger from 'bunyan'
import cacheManager from 'cache-manager'
import express from 'express'
import { Application } from './application'
import { Context } from './context'
import { createApp } from './github-app'
import { logger } from './logger'
import { resolve } from './resolver'
import { createServer } from './server'
import { createWebhookProxy } from './webhook-proxy'
export type { Logger } from "pino";
// tslint:disable:no-var-requires
// These needs types
const logRequestErrors = require('./middleware/log-request-errors')
export { Context } from "./context.js";
const cache = cacheManager.caching({
store: 'memory',
ttl: 60 * 60 // 1 hour
})
export { Probot } from "./probot.js";
export { Server } from "./server/server.js";
export { ProbotOctokit } from "./octokit/probot-octokit.js";
export { run } from "./run.js";
export { createNodeMiddleware } from "./create-node-middleware.js";
export { createProbot } from "./create-probot.js";
const defaultAppFns: ApplicationFunction[] = [
require('./apps/default'),
require('./apps/sentry'),
require('./apps/stats')
]
// tslint:enable:no-var-requires
/** NOTE: exported types might change at any point in time */
export type {
Options,
ApplicationFunction,
ApplicationFunctionOptions,
} from "./types.js";
export class Probot {
public server: express.Application
public webhook: Webhooks
public logger: Logger
declare global {
namespace NodeJS {
interface ProcessEnv {
/**
* The App ID assigned to your GitHub App.
* @example '1234'
*/
APP_ID?: string;
private options: Options
private apps: Application[]
private app: () => string
/**
* By default, logs are formatted for readability in development. You can
* set this to `json` in order to disable the formatting.
*/
LOG_FORMAT?: "json" | "pretty";
constructor (options: Options) {
options.webhookPath = options.webhookPath || '/'
options.secret = options.secret || 'development'
this.options = options
this.logger = logger
this.apps = []
this.webhook = new Webhooks({ path: options.webhookPath, secret: options.secret })
this.app = createApp({ id: options.id, cert: options.cert })
this.server = createServer({ webhook: this.webhook.middleware, logger })
/**
* The verbosity of logs to show when running your app, which can be
* `fatal`, `error`, `warn`, `info`, `debug`, `trace` or `silent`.
* @default 'info'
*/
LOG_LEVEL?:
| "trace"
| "debug"
| "info"
| "warn"
| "error"
| "fatal"
| "silent";
// Log all received webhooks
this.webhook.on('*', (event: WebhookEvent) => {
return this.receive(event)
})
/**
* By default, when using the `json` format, the level printed in the log
* records is an int (`10`, `20`, ..). This option tells the logger to
* print level as a string: `{"level": "info"}`. Default `false`
*/
LOG_LEVEL_IN_STRING?: "true" | "false";
// Log all webhook errors
this.webhook.on('error', this.errorHandler)
}
/**
* Only relevant when `LOG_FORMAT` is set to `json`. Sets the json key for the log message.
* @default 'msg'
*/
LOG_MESSAGE_KEY?: string;
public errorHandler (err: Error) {
const errMessage = err.message.toLowerCase()
if (errMessage.includes('x-hub-signature')) {
logger.error({ err }, 'Go to https://github.com/settings/apps/YOUR_APP and verify that the Webhook secret matches the value of the WEBHOOK_SECRET environment variable.')
} else if (errMessage.includes('pem') || errMessage.includes('json web token')) {
logger.error({ err }, 'Your private key (usually a .pem file) is not correct. Go to https://github.com/settings/apps/YOUR_APP and generate a new PEM file. If you\'re deploying to Now, visit https://probot.github.io/docs/deployment/#now.')
} else {
logger.error(err)
/**
* The organization where you want to register the app in the app
* creation manifest flow. If set, the app is registered for an
* organization
* (https://github.com/organizations/ORGANIZATION/settings/apps/new), if
* not set, the GitHub app would be registered for the user account
* (https://github.com/settings/apps/new).
*/
GH_ORG?: string;
/**
* The hostname of your GitHub Enterprise instance.
* @example github.mycompany.com
*/
GHE_HOST?: string;
/**
* The protocol of your GitHub Enterprise instance. Defaults to HTTPS.
* Do not change unless you are certain.
* @default 'https'
*/
GHE_PROTOCOL?: string;
/**
* The contents of the private key for your GitHub App. If you're unable
* to use multiline environment variables, use base64 encoding to
* convert the key to a single line string. See the Deployment docs for
* provider specific usage.
*/
PRIVATE_KEY?: string;
/**
* When using the `PRIVATE_KEY_PATH` environment variable, set it to the
* path of the `.pem` file that you downloaded from your GitHub App registration.
* @example 'path/to/key.pem'
*/
PRIVATE_KEY_PATH?: string;
/**
* The port to start the local server on.
* @default '3000'
*/
PORT?: string;
/**
* The host to start the local server on.
*/
HOST?: string;
/**
* Set to a `redis://` url as connection option for
* [ioredis](https://github.com/luin/ioredis#connect-to-redis) in order
* to enable
* [cluster support for request throttling](https://github.com/octokit/plugin-throttling.js#clustering).
* @example 'redis://:secret@redis-123.redislabs.com:12345/0'
*/
REDIS_URL?: string;
/**
* Set to a [Sentry](https://sentry.io/) DSN to report all errors thrown
* by your app.
* @example 'https://1234abcd@sentry.io/12345'
*/
SENTRY_DSN?: string;
/**
* The URL path which will receive webhooks.
* @default '/api/github/webhooks'
*/
WEBHOOK_PATH?: string;
/**
* Allows your local development environment to receive GitHub webhook
* events. Go to https://smee.io/new to get started.
* @example 'https://smee.io/your-custom-url'
*/
WEBHOOK_PROXY_URL?: string;
/**
* **Required**
* The webhook secret used when creating a GitHub App. 'development' is
* used as a default, but the value in `.env` needs to match the value
* configured in your App settings on GitHub. Note: GitHub marks this
* value as optional, but for optimal security it's required for Probot
* apps.
*
* @example 'development'
* @default 'development'
*/
WEBHOOK_SECRET?: string;
NODE_ENV?: string;
}
}
public receive (event: WebhookEvent) {
this.logger.debug({ event }, 'Webhook received')
return Promise.all(this.apps.map(app => app.receive(event)))
}
public load (appFn: string | ApplicationFunction) {
if (typeof appFn === 'string') {
appFn = resolve(appFn) as ApplicationFunction
}
const app = new Application({ app: this.app, cache, catchErrors: true })
// Connect the router from the app to the server
this.server.use(app.router)
// Initialize the ApplicationFunction
app.load(appFn)
this.apps.push(app)
return app
}
public setup (appFns: Array<string | ApplicationFunction>) {
// Log all unhandled rejections
process.on('unhandledRejection', this.errorHandler)
// Load the given appFns along with the default ones
appFns.concat(defaultAppFns).forEach(appFn => this.load(appFn))
// Register error handler as the last middleware
this.server.use(logRequestErrors)
}
public start () {
if (this.options.webhookProxy) {
createWebhookProxy({
logger,
path: this.options.webhookPath,
port: this.options.port,
url: this.options.webhookProxy
})
}
this.server.listen(this.options.port)
logger.info('Listening on http://localhost:' + this.options.port)
}
}
export const createProbot = (options: Options) => new Probot(options)
export type ApplicationFunction = (app: Application) => void
export interface Options {
webhookPath?: string
secret?: string,
id: number,
cert: string,
webhookProxy?: string,
port?: number
}
export { Logger, Context, Application }

View File

@ -1,65 +0,0 @@
/**
* A logger backed by [bunyan](https://github.com/trentm/node-bunyan)
*
* The default log level is `info`, but you can change it by setting the
* `LOG_LEVEL` environment variable to `trace`, `debug`, `info`, `warn`,
* `error`, or `fatal`.
*
* By default, logs are formatted for readability in development. If you intend
* to drain logs to a logging service, set `LOG_FORMAT=json`.
*
* **Note**: All execptions reported with `logger.error` will be forwarded to
* [sentry](https://github.com/getsentry/sentry) if the `SENTRY_DSN` environment
* variable is set.
*
* ```js
* app.log("This is an info message");
* app.log.debug("…so is this");
* app.log.trace("Now we're talking");
* app.log.info("I thought you should know…");
* app.log.warn("Woah there");
* app.log.error("ETOOMANYLOGS");
* app.log.fatal("Goodbye, cruel world!");
* ```
*/
import Logger from 'bunyan'
import bunyanFormat from 'bunyan-format'
import supportsColor from 'supports-color'
import { serializers } from './serializers'
function toBunyanLogLevel (level: string) {
switch (level) {
case 'info':
case 'trace':
case 'debug':
case 'warn':
case 'error':
case 'fatal':
case undefined:
return level
default:
throw new Error('Invalid log level')
}
}
function toBunyanFormat (format: string) {
switch (format) {
case 'short':
case 'long':
case 'simple':
case 'json':
case 'bunyan':
case undefined:
return format
default:
throw new Error('Invalid log format')
}
}
export const logger = new Logger({
level: toBunyanLogLevel(process.env.LOG_LEVEL || 'info'),
name: 'probot',
serializers,
stream: new bunyanFormat({ outputMode: toBunyanFormat(process.env.LOG_FORMAT || 'short'), color: supportsColor.stdout })
})

View File

@ -1,84 +1,105 @@
import fs from 'fs'
import yaml from 'js-yaml'
import path from 'path'
import updateDotenv from 'update-dotenv'
import { GitHubAPI } from './github'
import fs from "node:fs";
import path from "node:path";
import yaml from "js-yaml";
import updateDotenv from "update-dotenv";
import type { RequestParameters } from "@octokit/types";
import { ProbotOctokit } from "./octokit/probot-octokit.js";
import { loadPackageJson } from "./helpers/load-package-json.js";
import type { Env, Manifest, OctokitOptions, PackageJson } from "./types.js";
export class ManifestCreation {
get pkg () {
let pkg: any
try {
pkg = require(path.join(process.cwd(), 'package.json'))
} catch (e) {
pkg = {}
}
return pkg
get pkg() {
return loadPackageJson();
}
public async createWebhookChannel () {
public async createWebhookChannel(): Promise<string | undefined> {
try {
// tslint:disable:no-var-requires
const SmeeClient = require('smee-client')
await this.updateEnv({ WEBHOOK_PROXY_URL: await SmeeClient.createChannel() })
} catch (err) {
const SmeeClient = (await import("smee-client")).default;
const WEBHOOK_PROXY_URL = await SmeeClient.createChannel();
await this.updateEnv({
WEBHOOK_PROXY_URL,
});
return WEBHOOK_PROXY_URL;
} catch (error) {
// Smee is not available, so we'll just move on
// tslint:disable:no-console
console.warn('Unable to connect to smee.io, try restarting your server.')
console.warn("Unable to connect to smee.io, try restarting your server.");
return void 0;
}
}
public getManifest (pkg: any, baseUrl: any) {
let manifest: any = {}
public getManifest(pkg: PackageJson, baseUrl: string) {
let manifest: Partial<Manifest> = {};
try {
const file = fs.readFileSync(path.join(process.cwd(), 'app.yml'), 'utf8')
manifest = yaml.safeLoad(file)
} catch (err) {
const file = fs.readFileSync(path.join(process.cwd(), "app.yml"), "utf8");
manifest = yaml.load(file) as Manifest;
} catch (error) {
// App config does not exist, which is ok.
if (err.code !== 'ENOENT') {
throw err
if ((error as Error & { code?: string }).code !== "ENOENT") {
throw error;
}
}
const generatedManifest = JSON.stringify(Object.assign({
const generatedManifest = JSON.stringify({
description: manifest.description || pkg.description,
hook_attributes: {
url: process.env.WEBHOOK_PROXY_URL || `${baseUrl}/`
url: process.env.WEBHOOK_PROXY_URL || `${baseUrl}/`,
},
name: manifest.name || pkg.name,
name: process.env.PROJECT_DOMAIN || manifest.name || pkg.name,
public: manifest.public || true,
redirect_url: `${baseUrl}/probot/setup`,
// TODO: add setup url
// setup_url:`${baseUrl}/probot/success`,
url: manifest.url || pkg.homepage || pkg.repository
}, manifest))
url: manifest.url || pkg.homepage || pkg.repository,
version: "v1",
...manifest,
});
return generatedManifest
return generatedManifest;
}
public async createAppFromCode (code: any) {
const github = GitHubAPI()
const response = await github.request({
headers: { accept: 'application/vnd.github.fury-preview+json' },
method: 'POST',
url: `/app-manifests/${code}/conversions`
})
public async createAppFromCode(code: string, probotOptions?: OctokitOptions) {
const octokit = new ProbotOctokit(probotOptions);
const options: RequestParameters = {
...probotOptions,
code,
mediaType: {
previews: ["fury"], // needed for GHES 2.20 and older
},
...(process.env.GHE_HOST && {
baseUrl: `${process.env.GHE_PROTOCOL || "https"}://${
process.env.GHE_HOST
}/api/v3`,
}),
};
const response = await octokit.request(
"POST /app-manifests/:code/conversions",
options,
);
const { id, webhook_secret, pem } = response.data
const { id, client_id, client_secret, webhook_secret, pem } = response.data;
await this.updateEnv({
APP_ID: id.toString(),
PRIVATE_KEY: pem,
WEBHOOK_SECRET: webhook_secret
})
PRIVATE_KEY: `"${pem}"`,
WEBHOOK_SECRET: webhook_secret,
GITHUB_CLIENT_ID: client_id,
GITHUB_CLIENT_SECRET: client_secret,
});
return response.data.html_url
return response.data.html_url;
}
private async updateEnv (env: any) {
return updateDotenv(env)
public async updateEnv(env: Env) {
// Needs to be public due to tests
return updateDotenv(env);
}
get createAppUrl () {
const githubHost = process.env.GHE_HOST || `github.com`
return `https://${githubHost}/settings/apps/new`
get createAppUrl() {
const githubHost = process.env.GHE_HOST || `github.com`;
return `${process.env.GHE_PROTOCOL || "https"}://${githubHost}${
process.env.GH_ORG ? `/organizations/${process.env.GH_ORG}` : ""
}/settings/apps/new`;
}
}

View File

@ -1,8 +0,0 @@
import { NextFunction, Request, Response } from './logging'
module.exports = (err: Error, req: Request, res: Response, next: NextFunction) => {
if (req.log) {
req.log.error(err)
}
next()
}

View File

@ -1,53 +0,0 @@
// Borrowed from https://github.com/vvo/bunyan-request
// Copyright (c) Christian Tellnes <christian@tellnes.no>
// tslint:disable
import {wrapLogger} from '../wrap-logger'
import uuid from 'uuid'
import express from 'express'
import Logger from 'bunyan'
export const logRequest = function ({logger}: any): express.RequestHandler {
return function (req: Request, res: Response, next: NextFunction) {
// Use X-Request-ID from request if it is set, otherwise generate a uuid
req.id = req.headers['x-request-id'] ||
req.headers['x-github-delivery'] ||
uuid.v4()
res.setHeader('x-request-id', req.id)
// Make a logger available on the request
req.log = wrapLogger(logger, logger.target).child({name: 'http', id: req.id})
// Request started
req.log.trace({req}, `${req.method} ${req.url}`)
// Start the request timer
const time = process.hrtime()
res.on('finish', () => {
// Calculate how long the request took
const [seconds, nanoseconds] = process.hrtime(time)
res.duration = (seconds * 1e3 + nanoseconds * 1e-6).toFixed(2)
const message = `${req.method} ${req.url} ${res.statusCode} - ${res.duration} ms`
if (req.log) {
req.log.info(message)
req.log.trace({res})
}
})
next()
}
}
export interface Request extends express.Request {
id?: string | number | string[]
log?: Logger
}
export interface Response extends express.Response {
duration?: string
log?: Logger
}
export interface NextFunction extends express.NextFunction { }

View File

@ -0,0 +1,56 @@
import type { State } from "../types.js";
import type { ProbotOctokit } from "./probot-octokit.js";
import type { OctokitOptions } from "../types.js";
import type { LogFn, Level } from "pino";
type FactoryOptions = {
octokit: ProbotOctokit;
octokitOptions: OctokitOptions;
[key: string]: unknown;
};
export async function getAuthenticatedOctokit(
state: State,
installationId?: number,
) {
const { log, octokit } = state;
if (!installationId) return octokit;
return octokit.auth({
type: "installation",
installationId,
factory: ({ octokit, octokitOptions, ...otherOptions }: FactoryOptions) => {
const pinoLog = log.child({ name: "github" });
const options: ConstructorParameters<typeof ProbotOctokit>[0] & {
log: Record<Level, LogFn>;
} = {
...octokitOptions,
log: {
fatal: pinoLog.fatal.bind(pinoLog),
error: pinoLog.error.bind(pinoLog),
warn: pinoLog.warn.bind(pinoLog),
info: pinoLog.info.bind(pinoLog),
debug: pinoLog.debug.bind(pinoLog),
trace: pinoLog.trace.bind(pinoLog),
},
throttle: octokitOptions.throttle?.enabled
? {
...octokitOptions.throttle,
id: String(installationId),
}
: { enabled: false },
auth: {
...octokitOptions.auth,
otherOptions,
installationId,
},
};
const Octokit = octokit.constructor as typeof ProbotOctokit;
return new Octokit(options);
},
}) as Promise<ProbotOctokit>;
}

View File

@ -0,0 +1,52 @@
import Bottleneck from "bottleneck";
import { Redis, type RedisOptions } from "ioredis";
import type { Logger } from "pino";
import type { ThrottlingOptions } from "@octokit/plugin-throttling";
type Options = {
log: Logger;
redisConfig?: RedisOptions | string;
};
export function getOctokitThrottleOptions(options: Options) {
let { log, redisConfig } = options;
const throttlingOptions: ThrottlingOptions = {
onRateLimit: (retryAfter, options: { [key: string]: any }) => {
log.warn(
`Request quota exhausted for request ${options.method} ${options.url}`,
);
// Retry twice after hitting a rate limit error, then give up
if (options.request.retryCount <= 2) {
log.info(`Retrying after ${retryAfter} seconds!`);
return true;
}
return false;
},
onSecondaryRateLimit: (_retryAfter, options: { [key: string]: any }) => {
// does not retry, only logs a warning
log.warn(
`Secondary quota detected for request ${options.method} ${options.url}`,
);
},
};
if (!redisConfig) return throttlingOptions;
const connection = new Bottleneck.IORedisConnection({
client: getRedisClient(options),
});
connection.on("error", (error) => {
log.error(Object.assign(error, { source: "bottleneck" }));
});
throttlingOptions.Bottleneck = Bottleneck;
throttlingOptions.connection = connection;
return throttlingOptions;
}
function getRedisClient({ redisConfig }: Options): Redis | void {
if (redisConfig) return new Redis(redisConfig as RedisOptions);
}

View File

@ -0,0 +1,93 @@
import type { LRUCache } from "lru-cache";
import { ProbotOctokit } from "./probot-octokit.js";
import type { RedisOptions } from "ioredis";
import { request } from "@octokit/request";
import { getOctokitThrottleOptions } from "./get-octokit-throttle-options.js";
import type { Logger } from "pino";
import type { RequestRequestOptions } from "@octokit/types";
import type { OctokitOptions } from "../types.js";
type Options = {
cache: LRUCache<number, string>;
Octokit: typeof ProbotOctokit;
log: Logger;
githubToken?: string;
appId?: number;
privateKey?: string;
redisConfig?: RedisOptions | string;
webhookPath?: string;
baseUrl?: string;
request?: RequestRequestOptions;
};
/**
* Returns an Octokit instance with default settings for authentication. If
* a `githubToken` is passed explicitly, the Octokit instance will be
* pre-authenticated with that token when instantiated. Otherwise Octokit's
* app authentication strategy is used, and `options.auth` options are merged
* deeply when instantiated.
*
* Besides the authentication, the Octokit's baseUrl is set as well when run
* against a GitHub Enterprise Server with a custom domain.
*/
export function getProbotOctokitWithDefaults(options: Options) {
const authOptions = options.githubToken
? {
token: options.githubToken,
request: request.defaults({
request: {
fetch: options.request?.fetch,
},
}),
}
: {
cache: options.cache,
appId: options.appId,
privateKey: options.privateKey,
request: request.defaults({
request: {
fetch: options.request?.fetch,
},
}),
};
const octokitThrottleOptions = getOctokitThrottleOptions({
log: options.log,
redisConfig: options.redisConfig,
});
let defaultOptions: Partial<OctokitOptions> = {
auth: authOptions,
log: options.log.child
? options.log.child({ name: "octokit" })
: options.log,
};
if (options.baseUrl) {
defaultOptions.baseUrl = options.baseUrl;
}
if (octokitThrottleOptions) {
defaultOptions.throttle = octokitThrottleOptions;
}
return options.Octokit.defaults((instanceOptions: OctokitOptions) => {
const options = Object.assign({}, defaultOptions, instanceOptions, {
auth: instanceOptions.auth
? Object.assign({}, defaultOptions.auth, instanceOptions.auth)
: defaultOptions.auth,
});
if (instanceOptions.throttle) {
options.throttle = Object.assign(
{},
defaultOptions.throttle,
instanceOptions.throttle,
);
}
return options;
});
}

View File

@ -0,0 +1,15 @@
import { Webhooks } from "@octokit/webhooks";
import type { State } from "../types.js";
import { getErrorHandler } from "../helpers/get-error-handler.js";
import { webhookTransform } from "./octokit-webhooks-transform.js";
export function getWebhooks(state: State) {
const webhooks = new Webhooks({
log: state.log,
secret: state.webhooks.secret!,
transform: (hook) => webhookTransform(state, hook),
});
webhooks.onError(getErrorHandler(state.log));
return webhooks;
}

View File

@ -0,0 +1,25 @@
import type { Octokit } from "@octokit/core";
export function probotRequestLogging(octokit: Octokit) {
octokit.hook.error("request", (error, options) => {
if ("status" in error) {
const { method, url, request, ...params } =
octokit.request.endpoint.parse(options);
const msg = `GitHub request: ${method} ${url} - ${error.status}`;
// @ts-expect-error log.debug is a pino log method and accepts a fields object
octokit.log.debug(params.body || {}, msg);
}
throw error;
});
octokit.hook.after("request", (result, options) => {
const { method, url, request, ...params } =
octokit.request.endpoint.parse(options);
const msg = `GitHub request: ${method} ${url} - ${result.status}`;
// @ts-expect-error log.debug is a pino log method and accepts a fields object
octokit.log.debug(params.body || {}, msg);
});
}

View File

@ -0,0 +1,18 @@
import type { EmitterWebhookEvent as WebhookEvent } from "@octokit/webhooks";
import { Context } from "../context.js";
import type { State } from "../types.js";
/**
* Probot's transform option, which extends the `event` object that is passed
* to webhook event handlers by `@octokit/webhooks`
* @see https://github.com/octokit/webhooks.js/#constructor
*/
export async function webhookTransform(state: State, event: WebhookEvent) {
const log = state.log.child({ name: "event", id: event.id });
const octokit = (await state.octokit.auth({
type: "event-octokit",
event,
})) as typeof state.octokit;
return new Context(event, octokit, log);
}

View File

@ -0,0 +1,63 @@
import { Octokit } from "@octokit/core";
import { enterpriseCompatibility } from "@octokit/plugin-enterprise-compatibility";
import type { RequestOptions } from "@octokit/types";
import { paginateRest } from "@octokit/plugin-paginate-rest";
import { legacyRestEndpointMethods } from "@octokit/plugin-rest-endpoint-methods";
import { retry } from "@octokit/plugin-retry";
import { throttling } from "@octokit/plugin-throttling";
import { config } from "@probot/octokit-plugin-config";
import { createProbotAuth } from "octokit-auth-probot";
import { probotRequestLogging } from "./octokit-plugin-probot-request-logging.js";
import { VERSION } from "../version.js";
const defaultOptions = {
authStrategy: createProbotAuth,
throttle: {
enabled: true,
onSecondaryRateLimit: (
retryAfter: number,
options: RequestOptions,
octokit: Octokit,
) => {
octokit.log.warn(
`Secondary Rate limit hit with "${options.method} ${options.url}", retrying in ${retryAfter} seconds.`,
);
return true;
},
onRateLimit: (
retryAfter: number,
options: RequestOptions,
octokit: Octokit,
) => {
octokit.log.warn(
`Rate limit hit with "${options.method} ${options.url}", retrying in ${retryAfter} seconds.`,
);
return true;
},
},
userAgent: `probot/${VERSION}`,
};
export const ProbotOctokit = Octokit.plugin(
throttling,
retry,
paginateRest,
legacyRestEndpointMethods,
enterpriseCompatibility,
probotRequestLogging,
config,
).defaults((instanceOptions: any) => {
// merge throttle options deeply
const options = {
...defaultOptions,
...instanceOptions,
...{
throttle: { ...defaultOptions.throttle, ...instanceOptions?.throttle },
},
};
return options;
});
export type ProbotOctokit = InstanceType<typeof ProbotOctokit>;

View File

@ -1,58 +0,0 @@
import fs from 'fs'
// tslint:disable-next-line:no-var-requires
const isBase64 = require('is-base64')
const hint = `please use:
* \`--private-key=/path/to/private-key\` flag, or
* \`PRIVATE_KEY\` environment variable, or
* \`PRIVATE_KEY_PATH\` environment variable
`
/**
* Finds a private key through various user-(un)specified methods.
* Order of precedence:
* 1. Explicit path (CLI option)
* 2. `PRIVATE_KEY` env var
* 3. `PRIVATE_KEY_PATH` env var
* 4. Any file w/ `.pem` extension in current working dir
* @param filepath - Explicit, user-defined path to keyfile
* @returns Private key
* @private
*/
export function findPrivateKey (filepath?: string): Buffer | string | null {
if (filepath) {
return fs.readFileSync(filepath)
}
if (process.env.PRIVATE_KEY) {
let cert = process.env.PRIVATE_KEY
if (isBase64(cert)) {
// Decode base64-encoded certificate
cert = Buffer.from(cert, 'base64').toString()
}
const begin = '-----BEGIN RSA PRIVATE KEY-----'
const end = '-----END RSA PRIVATE KEY-----'
if (cert.includes(begin) && cert.includes(end)) {
// Full key with new lines
return cert.replace(/\\n/g, '\n')
}
throw new Error('The contents of \`PRIVATE_KEY\` could not be validated. Please check to ensure you have copied the contents of the .pem file correctly.')
}
if (process.env.PRIVATE_KEY_PATH) {
return fs.readFileSync(process.env.PRIVATE_KEY_PATH)
}
const pemFiles = fs.readdirSync(process.cwd())
.filter(path => path.endsWith('.pem'))
if (pemFiles.length > 1) {
throw new Error(
`Found several private keys: ${pemFiles.join(', ')}. ` +
`To avoid ambiguity ${hint}`
)
} else if (pemFiles[0]) {
return findPrivateKey(pemFiles[0])
}
return null
}

136
src/probot.ts Normal file
View File

@ -0,0 +1,136 @@
import { LRUCache } from "lru-cache";
import type { Logger } from "pino";
import type { EmitterWebhookEvent as WebhookEvent } from "@octokit/webhooks";
import { auth } from "./auth.js";
import { getLog } from "./helpers/get-log.js";
import { getProbotOctokitWithDefaults } from "./octokit/get-probot-octokit-with-defaults.js";
import { getWebhooks } from "./octokit/get-webhooks.js";
import { ProbotOctokit } from "./octokit/probot-octokit.js";
import { VERSION } from "./version.js";
import type {
ApplicationFunction,
ApplicationFunctionOptions,
Options,
ProbotWebhooks,
State,
} from "./types.js";
import { defaultWebhooksPath } from "./server/server.js";
import { rebindLog } from "./helpers/rebind-log.js";
export type Constructor<T = any> = new (...args: any[]) => T;
export class Probot {
static version = VERSION;
static defaults<S extends Constructor>(this: S, defaults: Options) {
const ProbotWithDefaults = class extends this {
constructor(...args: any[]) {
const options = args[0] || {};
super(Object.assign({}, defaults, options));
}
};
return ProbotWithDefaults;
}
public webhooks: ProbotWebhooks;
public webhookPath: string;
public log: Logger;
public version: String;
public on: ProbotWebhooks["on"];
public onAny: ProbotWebhooks["onAny"];
public onError: ProbotWebhooks["onError"];
public auth: (
installationId?: number,
log?: Logger,
) => Promise<ProbotOctokit>;
private state: State;
constructor(options: Options = {}) {
options.secret = options.secret || "development";
let level = options.logLevel;
const logMessageKey = options.logMessageKey;
this.log = options.log
? rebindLog(options.log)
: getLog({ level, logMessageKey });
// TODO: support redis backend for access token cache if `options.redisConfig`
const cache = new LRUCache<number, string>({
// cache max. 15000 tokens, that will use less than 10mb memory
max: 15000,
// Cache for 1 minute less than GitHub expiry
ttl: 1000 * 60 * 59,
});
const Octokit = getProbotOctokitWithDefaults({
githubToken: options.githubToken,
Octokit: options.Octokit || ProbotOctokit,
appId: Number(options.appId),
privateKey: options.privateKey,
cache,
log: rebindLog(this.log),
redisConfig: options.redisConfig,
baseUrl: options.baseUrl,
});
const octokitLogger = rebindLog(this.log.child({ name: "octokit" }));
const octokit = new Octokit({
request: options.request,
log: {
debug: octokitLogger.debug.bind(octokitLogger),
info: octokitLogger.info.bind(octokitLogger),
warn: octokitLogger.warn.bind(octokitLogger),
error: octokitLogger.error.bind(octokitLogger),
},
});
this.state = {
cache,
githubToken: options.githubToken,
log: rebindLog(this.log),
Octokit,
octokit,
webhooks: {
secret: options.secret,
},
appId: Number(options.appId),
privateKey: options.privateKey,
host: options.host,
port: options.port,
webhookPath: options.webhookPath || defaultWebhooksPath,
request: options.request,
};
this.auth = auth.bind(null, this.state);
this.webhooks = getWebhooks(this.state);
this.webhookPath = this.state.webhookPath;
this.on = this.webhooks.on;
this.onAny = this.webhooks.onAny;
this.onError = this.webhooks.onError;
this.version = VERSION;
}
public receive(event: WebhookEvent) {
this.log.debug({ event }, "Webhook received");
return this.webhooks.receive(event);
}
public async load(
appFn: ApplicationFunction | ApplicationFunction[],
options: ApplicationFunctionOptions = {},
) {
if (Array.isArray(appFn)) {
for (const fn of appFn) {
await this.load(fn);
}
return;
}
return appFn(this, options);
}
}

View File

@ -1,18 +0,0 @@
import { sync } from 'resolve'
const defaultOptions: ResolveOptions = {}
export const resolve = (appFnId: string, opts?: ResolveOptions) => {
opts = opts || defaultOptions
// These are mostly to ease testing
const basedir = opts.basedir || process.cwd()
const resolver: Resolver = opts.resolver || sync
return require(resolver(appFnId, { basedir }))
}
export type Resolver = (appFnId: string, opts: {basedir: string}) => string
export interface ResolveOptions {
basedir?: string
resolver?: Resolver
}

153
src/run.ts Normal file
View File

@ -0,0 +1,153 @@
import pkgConf from "pkg-conf";
import type { ApplicationFunction, Options, ServerOptions } from "./types.js";
import { Logger, Probot, ProbotOctokit } from "./index.js";
import { setupAppFactory } from "./apps/setup.js";
import { getLog } from "./helpers/get-log.js";
import { readCliOptions } from "./bin/read-cli-options.js";
import { readEnvOptions } from "./bin/read-env-options.js";
import { Server } from "./server/server.js";
import { defaultApp } from "./apps/default.js";
import { resolveAppFunction } from "./helpers/resolve-app-function.js";
import { isProduction } from "./helpers/is-production.js";
import { config as dotenvConfig } from "dotenv";
type AdditionalOptions = {
env?: NodeJS.ProcessEnv;
Octokit?: typeof ProbotOctokit;
log?: Logger;
};
/**
*
* @param appFnOrArgv set to either a probot application function: `(app) => { ... }` or to process.argv
*/
export async function run(
appFnOrArgv: ApplicationFunction | string[],
additionalOptions?: AdditionalOptions,
) {
dotenvConfig();
const envOptions = readEnvOptions(additionalOptions?.env);
const cliOptions = Array.isArray(appFnOrArgv)
? readCliOptions(appFnOrArgv)
: {};
const {
// log options
logLevel: level,
logFormat,
logLevelInString,
logMessageKey,
sentryDsn,
// server options
host,
port,
webhookPath,
webhookProxy,
// probot options
appId,
privateKey,
redisConfig,
secret,
baseUrl,
// others
args,
} = { ...envOptions, ...cliOptions };
const log = getLog({
level,
logFormat,
logLevelInString,
logMessageKey,
sentryDsn,
});
const probotOptions: Options = {
appId,
privateKey,
redisConfig,
secret,
baseUrl,
log: additionalOptions?.log || log.child({ name: "probot" }),
Octokit: additionalOptions?.Octokit || undefined,
};
const serverOptions: ServerOptions = {
host,
port,
webhookPath,
webhookProxy,
log: log.child({ name: "server" }),
Probot: Probot.defaults(probotOptions),
};
let server: Server;
if (!appId || !privateKey) {
if (isProduction()) {
if (!appId) {
throw new Error(
"App ID is missing, and is required to run in production mode. " +
"To resolve, ensure the APP_ID environment variable is set.",
);
} else if (!privateKey) {
throw new Error(
"Certificate is missing, and is required to run in production mode. " +
"To resolve, ensure either the PRIVATE_KEY or PRIVATE_KEY_PATH environment variable is set and contains a valid certificate",
);
}
}
// Workaround for setup (#1512)
// When probot is started for the first time, it gets into a setup mode
// where `appId` and `privateKey` are not present. The setup mode gets
// these credentials. In order to not throw an error, we set the values
// to anything, as the Probot instance is not used in setup it makes no
// difference anyway.
server = new Server({
...serverOptions,
Probot: Probot.defaults({
...probotOptions,
appId: 1,
privateKey: "dummy value for setup, see #1512",
}),
});
await server.load(setupAppFactory(host, port));
await server.start();
return server;
}
if (Array.isArray(appFnOrArgv)) {
const pkg = await pkgConf("probot");
const combinedApps: ApplicationFunction = async (_app) => {
await server.load(defaultApp);
if (Array.isArray(pkg.apps)) {
for (const appPath of pkg.apps) {
const appFn = await resolveAppFunction(appPath);
await server.load(appFn);
}
}
const [appPath] = args;
const appFn = await resolveAppFunction(appPath);
await server.load(appFn);
};
server = new Server(serverOptions);
await server.load(combinedApps);
await server.start();
return server;
}
server = new Server(serverOptions);
await server.load(appFnOrArgv);
await server.start();
return server;
}

View File

@ -1,62 +0,0 @@
import { PayloadRepository, WebhookEvent } from '@octokit/webhooks'
import bunyan from 'bunyan'
import express from 'express'
export const serializers: bunyan.StdSerializers = {
event: (event: WebhookEvent | any) => {
if (typeof event !== 'object' || !event.payload) {
return event
} else {
let name = event.name
if (event.payload && event.payload.action) {
name = `${name}.${event.payload.action}`
}
return {
event: name,
id: event.id,
installation: event.payload.installation && event.payload.installation.id,
repository: event.payload.repository && event.payload.repository.full_name
}
}
},
installation: (installation: Installation) => {
if (installation.account) {
return installation.account.login
} else {
return installation
}
},
err: bunyan.stdSerializers.err,
repository: (repository: PayloadRepository) => repository.full_name,
req: bunyan.stdSerializers.req,
// Same as buyan's standard serializers, but gets headers as an object
// instead of a string.
// https://github.com/trentm/node-bunyan/blob/fe31b83e42d9c7f784e83fdcc528a7c76e0dacae/lib/bunyan.js#L1105-L1113
res: (res: ExpressResponseWithDuration) => {
if (!res || !res.statusCode) {
return res
} else {
return {
duration: res.duration,
headers: res.getHeaders(),
statusCode: res.statusCode
}
}
}
}
interface Installation {
account: {
login: string
}
}
interface ExpressResponseWithDuration extends express.Response {
duration: any
}

View File

@ -1,27 +0,0 @@
import Logger from 'bunyan'
import express from 'express'
import path from 'path'
// Teach express to properly handle async errors
// tslint:disable-next-line:no-var-requires
require('express-async-errors')
import { logRequest } from './middleware/logging'
export const createServer = (args: ServerArgs) => {
const app: express.Application = express()
app.use(logRequest({ logger: args.logger }))
app.use('/probot/static/', express.static(path.join(__dirname, '..', 'static')))
app.use(args.webhook)
app.set('view engine', 'hbs')
app.set('views', path.join(__dirname, '..', 'views'))
app.get('/ping', (req, res) => res.end('PONG'))
return app
}
export interface ServerArgs {
webhook: express.Application
logger: Logger
}

View File

@ -0,0 +1,26 @@
import { randomUUID as uuidv4 } from "node:crypto";
import { pinoHttp, startTime, type Options, type HttpLogger } from "pino-http";
import type { Logger } from "pino";
export function getLoggingMiddleware(
logger: Logger,
options?: Options,
): HttpLogger {
return pinoHttp({
...options,
logger: logger.child({ name: "http" }),
customSuccessMessage(_req, res) {
const responseTime = Date.now() - res[startTime];
return `${res.req.method} ${res.req.url} ${res.statusCode} - ${responseTime}ms`;
},
customErrorMessage(_err, res) {
const responseTime = Date.now() - res[startTime];
return `${res.req.method} ${res.req.url} ${res.statusCode} - ${responseTime}ms`;
},
genReqId: (req) =>
req.headers["x-request-id"] ||
req.headers["x-github-delivery"] ||
uuidv4(),
});
}

130
src/server/server.ts Normal file
View File

@ -0,0 +1,130 @@
import type { Server as HttpServer } from "node:http";
import { join } from "node:path";
import express, { Router, type Application } from "express";
import type { Logger } from "pino";
import { createNodeMiddleware as createWebhooksMiddleware } from "@octokit/webhooks";
import { getLoggingMiddleware } from "./logging-middleware.js";
import { createWebhookProxy } from "../helpers/webhook-proxy.js";
import { VERSION } from "../version.js";
import type { ApplicationFunction, ServerOptions } from "../types.js";
import type { Probot } from "../index.js";
import type EventSource from "eventsource";
import { rebindLog } from "../helpers/rebind-log.js";
// the default path as defined in @octokit/webhooks
export const defaultWebhooksPath = "/api/github/webhooks";
type State = {
cwd?: string;
httpServer?: HttpServer;
port?: number;
host?: string;
webhookPath: string;
webhookProxy?: string;
eventSource?: EventSource;
};
export class Server {
static version = VERSION;
public expressApp: Application;
public log: Logger;
public version = VERSION;
public probotApp: Probot;
private state: State;
constructor(options: ServerOptions = {} as ServerOptions) {
this.expressApp = express();
this.probotApp = new options.Probot({
request: options.request,
});
this.log = options.log
? rebindLog(options.log)
: rebindLog(this.probotApp.log.child({ name: "server" }));
this.state = {
cwd: options.cwd || process.cwd(),
port: options.port,
host: options.host,
webhookPath: options.webhookPath || defaultWebhooksPath,
webhookProxy: options.webhookProxy,
};
this.expressApp.use(getLoggingMiddleware(this.log, options.loggingOptions));
this.expressApp.use(
"/probot/static/",
express.static(join(__dirname, "..", "..", "static")),
);
this.expressApp.use(
createWebhooksMiddleware(this.probotApp.webhooks, {
path: this.state.webhookPath,
}),
);
this.expressApp.get("/ping", (_req, res) => res.end("PONG"));
}
public async load(appFn: ApplicationFunction) {
await appFn(this.probotApp, {
cwd: this.state.cwd,
getRouter: (path) => this.router(path),
});
}
public async start() {
this.log.info(
`Running Probot v${this.version} (Node.js: ${process.version})`,
);
const port = this.state.port || 3000;
const { host, webhookPath, webhookProxy } = this.state;
const printableHost = host ?? "localhost";
this.state.httpServer = await new Promise((resolve, reject) => {
const server = this.expressApp.listen(
port,
...((host ? [host] : []) as any),
async () => {
if (webhookProxy) {
this.state.eventSource = await createWebhookProxy({
logger: this.log,
path: webhookPath,
port: port,
url: webhookProxy,
});
}
this.log.info(`Listening on http://${printableHost}:${port}`);
resolve(server);
},
);
server.on("error", (error: NodeJS.ErrnoException) => {
if (error.code === "EADDRINUSE") {
error = Object.assign(error, {
message: `Port ${port} is already in use. You can define the PORT environment variable to use a different port.`,
});
}
this.log.error(error);
reject(error);
});
});
return this.state.httpServer;
}
public async stop() {
if (this.state.eventSource) this.state.eventSource.close();
if (!this.state.httpServer) return;
const server = this.state.httpServer;
return new Promise((resolve) => server.close(resolve));
}
public router(path: string = "/") {
const newRouter = Router();
this.expressApp.use(path, newRouter);
return newRouter;
}
}

184
src/types.ts Normal file
View File

@ -0,0 +1,184 @@
import express from "express";
import type {
EmitterWebhookEvent as WebhookEvent,
Webhooks,
} from "@octokit/webhooks";
import type { LRUCache } from "lru-cache";
import type { RedisOptions } from "ioredis";
import type { Options as LoggingOptions } from "pino-http";
import { Probot } from "./index.js";
import { Context } from "./context.js";
import { ProbotOctokit } from "./octokit/probot-octokit.js";
import type { Logger } from "pino";
import type { RequestRequestOptions } from "@octokit/types";
export interface Options {
privateKey?: string;
githubToken?: string;
appId?: number | string;
Octokit?: typeof ProbotOctokit;
log?: Logger;
redisConfig?: RedisOptions | string;
secret?: string;
logLevel?: "trace" | "debug" | "info" | "warn" | "error" | "fatal";
logMessageKey?: string;
port?: number;
host?: string;
baseUrl?: string;
request?: RequestRequestOptions;
webhookPath?: string;
}
export type State = {
appId?: number;
privateKey?: string;
githubToken?: string;
log: Logger;
Octokit: typeof ProbotOctokit;
octokit: ProbotOctokit;
cache?: LRUCache<number, string>;
webhooks: {
secret?: string;
};
port?: number;
host?: string;
baseUrl?: string;
webhookPath: string;
request?: RequestRequestOptions;
};
// Omit the `payload`, `id`,`name` properties from the `Context` class as they are already present in the types of `WebhookEvent`
// The `Webhooks` class accepts a type parameter (`TTransformed`) that is used to transform the event payload in the form of
// WebhookEvent["payload"] & T
// Simply passing `Context` as `TTransformed` would result in the payload types being too complex for TypeScript to infer
// See https://github.com/probot/probot/issues/1388
// See https://github.com/probot/probot/issues/1815 as for why this is in a seperate type, and not directly passed to `Webhooks`
type SimplifiedObject = Omit<Context, keyof WebhookEvent>;
export type ProbotWebhooks = Webhooks<SimplifiedObject>;
export type ApplicationFunctionOptions = {
getRouter?: (path?: string) => express.Router;
cwd?: string;
[key: string]: unknown;
};
export type ApplicationFunction = (
app: Probot,
options: ApplicationFunctionOptions,
) => void | Promise<void>;
export type ServerOptions = {
cwd?: string;
log?: Logger;
port?: number;
host?: string;
webhookPath?: string;
webhookProxy?: string;
Probot: typeof Probot;
loggingOptions?: LoggingOptions;
request?: RequestRequestOptions;
};
export type MiddlewareOptions = {
probot: Probot;
webhooksPath?: string;
[key: string]: unknown;
};
export type OctokitOptions = NonNullable<
ConstructorParameters<typeof ProbotOctokit>[0]
>;
export type PackageJson = {
name?: string;
version?: string;
description?: string;
homepage?: string;
repository?: string;
engines?: {
[key: string]: string;
};
};
export type Env = Record<Uppercase<string>, string>;
type ManifestPermissionValue = "read" | "write" | "none";
type ManifestPermissionScope =
| "actions"
| "checks"
| "contents"
| "deployments"
| "id-token"
| "issues"
| "discussions"
| "packages"
| "pages"
| "pull-requests"
| "repository-projects"
| "security-events"
| "statuses";
export type Manifest = {
/**
* The name of the GitHub App.
*/
name?: string;
/**
* __Required.__ The homepage of your GitHub App.
*/
url: string;
/**
* The configuration of the GitHub App's webhook.
*/
hook_attributes?: {
/*
* __Required.__ The URL of the server that will receive the webhook POST requests.
*/
url: string;
/*
* Deliver event details when this hook is triggered, defaults to true.
*/
active?: boolean;
};
/**
* The full URL to redirect to after a user initiates the registration of a GitHub App from a manifest.
*/
redirect_url?: string;
/**
* A full URL to redirect to after someone authorizes an installation. You can provide up to 10 callback URLs.
*/
callback_urls?: string[];
/**
* A full URL to redirect users to after they install your GitHub App if additional setup is required.
*/
setup_url?: string;
/**
* A description of the GitHub App.
*/
description?: string;
/**
* Set to `true` when your GitHub App is available to the public or `false` when it is only accessible to the owner of the app.
*/
public?: boolean;
/**
* The list of events the GitHub App subscribes to.
*/
default_events?: WebhookEvent[];
/**
* The set of permissions needed by the GitHub App. The format of the object uses the permission name for the key (for example, `issues`) and the access type for the value (for example, `write`).
*/
default_permissions?:
| "read-all"
| "write-all"
| Record<ManifestPermissionScope, ManifestPermissionValue>;
/**
* Set to `true` to request the user to authorize the GitHub App, after the GitHub App is installed.
*/
request_oauth_on_install?: boolean;
/**
* Set to `true` to redirect users to the setup_url after they update your GitHub App installation.
*/
setup_on_update?: boolean;
};

2
src/version.ts Normal file
View File

@ -0,0 +1,2 @@
// The version is set automatically before publish to npm
export const VERSION = "0.0.0-development";

94
src/views/import.ts Normal file
View File

@ -0,0 +1,94 @@
export function importView({
name,
GH_HOST,
WEBHOOK_PROXY_URL = "",
}: {
name?: string;
GH_HOST: string;
WEBHOOK_PROXY_URL?: string;
}): string {
return `<!DOCTYPE html>
<html lang="en" class="height-full" data-color-mode="auto" data-light-theme="light" data-dark-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Import ${name || "Your App"} | built with Probot</title>
<link rel="icon" href="/probot/static/probot-head.png">
<link rel="stylesheet" href="/probot/static/primer.css">
</head>
<body class="bg-gray-light">
<div class="d-flex flex-column flex-justify-center flex-items-center text-center height-full py-6">
<a href="/probot"><img src="/probot/static/robot.svg" alt="Probot Logo" width="100" class="mb-6"></a>
<div class="box-shadow rounded-2 border p-6 bg-white">
<h2>Use existing Github App</h2>
<br>
<h3>Step 1:</h3>
<p class="d-block mt-2">
Replace your app's Webhook URL with <br>
<b>${WEBHOOK_PROXY_URL}</b>
</p>
<a class="d-block mt-2" href="${GH_HOST}/settings/apps" target="__blank" rel="noreferrer">
You can do it here
</a>
<br>
<h3>Step 2:</h3>
<p class="mt-2">Fill out this form</p>
<form onsubmit="return onSubmit(event) || false">
<label class="d-block mt-2" for="appId">App Id</label>
<input class="form-control width-full" type="text" required="true" id="appId" name="appId"><br>
<label class="d-block mt-3" for="whs">Webhook secret (required!)</label>
<input class="form-control width-full" type="password" required="true" id="whs" name="whs"><br>
<label class="d-block mt-3" for="pem">Private Key</label>
<input class="form-control width-full m-2" type="file" accept=".pem" required="true" id="pem"
name="pem">
<br>
<button class="btn btn-outline m-2" type="submit">Submit</button>
</form>
</div>
<div class="mt-4">
<h4 class="alt-h4 text-gray-light">Need help?</h4>
<div class="d-flex flex-justify-center mt-2">
<a href="https://probot.github.io/docs/" class="btn btn-outline mr-2">Documentation</a>
<a href="https://github.com/probot/probot/discussions" class="btn btn-outline">Discuss on GitHub</a>
</div>
</div>
</div>
<script>
function onSubmit(e) {
e.preventDefault();
const idEl = document.getElementById('appId');
const appId = idEl.value;
const secretEl = document.getElementById('whs');
const webhook_secret = secretEl.value;
const fileEl = document.getElementById('pem');
const file = fileEl.files[0];
file.text().then((text) => fetch('', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ appId, pem: text, webhook_secret })
})).then((r) => {
if (r.ok) {
location.replace('/probot/success');
}
}).catch((e) => alert(e));
return false;
}
</script>
</body>
</html>`;
}

View File

@ -1,10 +1,19 @@
<!DOCTYPE html>
<html lang="en" class="height-full">
export function probotView({
name,
description,
version,
}: {
name?: string;
description?: string;
version?: string;
}): string {
return `<!DOCTYPE html>
<html lang="en" class="height-full" data-color-mode="auto" data-light-theme="light" data-dark-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{{#if name }}{{ name }}{{else}}Your App{{/if}} | built with Probot</title>
<title>${name || "Your App"} | built with Probot</title>
<link rel="icon" href="/probot/static/probot-head.png">
<link rel="stylesheet" href="/probot/static/primer.css">
</head>
@ -13,26 +22,28 @@
<img src="/probot/static/robot.svg" alt="Probot Logo" width="100" class="mb-6">
<div class="box-shadow rounded-2 border p-6 bg-white">
<h1>
Welcome to {{#if name }}{{ name }}{{else}}your Probot App{{/if}}
{{#if version}}
<span class="Label Label--outline v-align-middle ml-2 text-gray-light">v{{ version }}</span>
{{/if}}
</h1>
Welcome to ${name || "your Probot App"}
${
version
? ` <span class="Label Label--outline v-align-middle ml-2 text-gray-light">v${version}</span>\n`
: ""
} </h1>
{{#if description }}
<p>{{ description }}</p>
{{else}}
<p>This bot was built using <a href="https://github.com/probot/probot">Probot</a>, a framework for building GitHub Apps.</p>
{{/if}}
<p>${
description
? description
: 'This bot was built using <a href="https://github.com/probot/probot">Probot</a>, a framework for building GitHub Apps.'
}</p>
</div>
<div class="mt-4">
<h4 class="alt-h4 text-gray-light">Need help?</h4>
<div class="d-flex flex-justify-center mt-2">
<a href="https://probot.github.io/docs/" class="btn btn-outline mr-2">Documentation</a>
<a href="https://probot-slackin.herokuapp.com/" class="btn btn-outline">Chat on Slack</a>
<a href="https://github.com/probot/probot/discussions" class="btn btn-outline">Discuss on GitHub</a>
</div>
</div>
</div>
</body>
</html>
</html>`;
}

View File

@ -1,10 +1,24 @@
export function setupView({
name,
description,
version,
createAppUrl,
manifest,
}: {
name?: string;
description?: string;
version?: string;
createAppUrl: string;
manifest: string;
}): string {
return `<!DOCTYPE html>
<!DOCTYPE html>
<html lang="en" class="height-full">
<html lang="en" class="height-full" data-color-mode="auto" data-light-theme="light" data-dark-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Setup {{#if pkg.name }}{{ pkg.name }}{{else}}Your App{{/if}} | built with Probot</title>
<title>Setup ${name || "Your App"} | built with Probot</title>
<link rel="icon" href="/probot/static/probot-head.png">
<link rel="stylesheet" href="/probot/static/primer.css">
</head>
@ -13,17 +27,19 @@
<img src="/probot/static/robot.svg" alt="Probot Logo" width="100" class="mb-6">
<div class="box-shadow rounded-2 border p-6 bg-white">
<h1>
Welcome to {{#if pkg.name }}{{ pkg.name }}{{else}}your Probot App{{/if}}
{{#if version}}
<span class="Label Label--outline v-align-middle ml-2 text-gray-light">v{{ pkg.version }}</span>
{{/if}}
Welcome to ${name || "your Probot App"}
${
version
? `<span class="Label Label--outline v-align-middle ml-2 text-gray-light">v${version}</span>`
: ""
}
</h1>
{{#if pkg.description }}
<p>{{ pkg.description }}</p>
{{else}}
<p>This app was built using <a href="https://github.com/probot/probot">Probot</a>, a framework for building GitHub Apps.</p>
{{/if}}
<p>${
description
? description
: 'This app was built using <a href="https://github.com/probot/probot">Probot</a>, a framework for building GitHub Apps.'
}</p>
<div class="text-left mt-6">
<h2 class="alt-h3 mb-2">Getting Started</h2>
@ -31,8 +47,9 @@
<p>To start building a GitHub App, you'll need to register a new app on GitHub.</p>
<br>
<form action="{{ createAppUrl }}" method="post" target="_blank">
<button class="btn btn-outline" name="manifest" id="manifest" value='{{{ manifest }}}' >Register GitHub App</button>
<form action="${createAppUrl}" method="post" target="_blank" class="d-flex flex-items-center">
<button class="btn btn-outline" name="manifest" id="manifest" value='${manifest}' >Register GitHub App</button>
<a href="/probot/import" class="ml-2">or use an existing Github App</a>
</form>
</div>
</div>
@ -41,9 +58,10 @@
<h4 class="alt-h4 text-gray-light">Need help?</h4>
<div class="d-flex flex-justify-center mt-2">
<a href="https://probot.github.io/docs/" class="btn btn-outline mr-2">Documentation</a>
<a href="https://probot-slackin.herokuapp.com/" class="btn btn-outline">Chat on Slack</a>
<a href="https://github.com/probot/probot/discussions" class="btn btn-outline">Discuss on GitHub</a>
</div>
</div>
</div>
</body>
</html>
</html>`;
}

Some files were not shown because too many files have changed in this diff Show More