├── .all-contributorsrc
├── .eslintrc.js
├── .github
├── FUNDING.yml
└── workflows
│ ├── greetings.yml
│ └── nodejs.yml
├── .gitignore
├── .vscode
├── extensions.json
├── launch.json
└── settings.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── app.yml
├── package.json
├── prettier.config.js
├── shrinkwrap.yaml
├── src
├── config.js
├── diff.js
├── events.js
├── index.js
├── size.js
└── utils
│ ├── api.js
│ ├── emoji.js
│ ├── github.js
│ ├── keep-alive.js
│ ├── template.js
│ └── utils.js
├── static
└── images
│ ├── icon.png
│ ├── robot.svg
│ └── sample.png
├── test
├── fixtures
│ └── issues.opened.json
└── index.test.js
├── tools
└── scripts
│ ├── cmds.txt
│ ├── deploy.js
│ └── secret.js
└── yarn.lock
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "contributors": [
8 | {
9 | "login": "kuldeepkeshwar",
10 | "name": "anotherjsguy",
11 | "avatar_url": "https://avatars1.githubusercontent.com/u/10448534?v=4",
12 | "profile": "https://in.linkedin.com/in/kuldeepkeshwar",
13 | "contributions": [
14 | "code"
15 | ]
16 | }
17 | ],
18 | "contributorsPerLine": 7,
19 | "projectName": "size-plugin-bot",
20 | "projectOwner": "kuldeepkeshwar",
21 | "repoType": "github",
22 | "repoHost": "https://github.com"
23 | }
24 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: 'airbnb-base',
3 | }
4 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | custom: https://www.paypal.me/kuldeepkeshwar
9 |
--------------------------------------------------------------------------------
/.github/workflows/greetings.yml:
--------------------------------------------------------------------------------
1 | name: Greetings
2 |
3 | on: [pull_request, issues]
4 |
5 | jobs:
6 | greeting:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/first-interaction@v1
10 | with:
11 | repo-token: ${{ secrets.GITHUB_TOKEN }}
12 | issue-message: 'Thanks for your contribution 👍'' first issue'
13 | pr-message: 'Thanks for your contribution 👍'' first pr'
14 |
--------------------------------------------------------------------------------
/.github/workflows/nodejs.yml:
--------------------------------------------------------------------------------
1 | name: Node CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | strategy:
11 | matrix:
12 | node-version: [8.x, 10.x, 12.x]
13 |
14 | steps:
15 | - uses: actions/checkout@v1
16 | - name: Use Node.js ${{ matrix.node-version }}
17 | uses: actions/setup-node@v1
18 | with:
19 | node-version: ${{ matrix.node-version }}
20 | - name: npm install, build, and test
21 | run: |
22 | npm install
23 | npm run build --if-present
24 | env:
25 | CI: true
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.env
3 | *.key
4 | *.pem
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "editorconfig.editorconfig",
4 | "dbaeumer.vscode-eslint",
5 | "esbenp.prettier-vscode"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "attach",
10 | "name": "Attach by Process ID",
11 | "processId": "${command:PickProcess}"
12 | }
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.minimap.enabled": false,
3 | "editor.wordWrap": "off",
4 | // Turns on auto format on save for all files types.
5 | // This is also required to allow Prettier to auto format files on save.
6 | "editor.formatOnSave": true,
7 | // Turns it off for JavaScript since we use Prettier to auto format on save.
8 | "javascript.format.enable": false,
9 | "javascript.validate.enable": true,
10 | "json.format.enable": false,
11 | // Required to allow auto format on save with Prettier + ESLint + Vetur.
12 | "eslint.autoFixOnSave": true,
13 | "eslint.alwaysShowStatus": true,
14 | "prettier.eslintIntegration": true,
15 | "prettier.stylelintIntegration": true,
16 | "sort-imports.suppress-warnings": true,
17 | "search.exclude": {
18 | "**/node_modules*": true
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | education, socio-economic status, nationality, personal appearance, race,
10 | religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | - Using welcoming and inclusive language
18 | - Being respectful of differing viewpoints and experiences
19 | - Gracefully accepting constructive criticism
20 | - Focusing on what is best for the community
21 | - Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | - The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | - Trolling, insulting/derogatory comments, and personal or political attacks
28 | - Public or private harassment
29 | - Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | - Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at kuldeepkeshwar@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing
2 |
3 | [fork]: /fork
4 | [pr]: /compare
5 | [style]: https://standardjs.com/
6 | [code-of-conduct]: CODE_OF_CONDUCT.md
7 |
8 | Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
9 |
10 | Please note that this project is released with a [Contributor Code of Conduct][code-of-conduct]. By participating in this project you agree to abide by its terms.
11 |
12 | ## Issues and PRs
13 |
14 | If you have suggestions for how this project could be improved, or want to report a bug, open an issue! We'd love all and any contributions. If you have questions, too, we'd love to hear them.
15 |
16 | We'd also love PRs. If you're thinking of a large PR, we advise opening up an issue first to talk about it, though! Look at the links below if you're not sure how to open a PR.
17 |
18 | ## Submitting a pull request
19 |
20 | 1. [Fork][fork] and clone the repository.
21 | 1. Configure and install the dependencies: `npm install`.
22 | 1. Make sure the tests pass on your machine: `npm test`, note: these tests also apply the linter, so there's no need to lint separately.
23 | 1. Create a new branch: `git checkout -b my-branch-name`.
24 | 1. Make your change, add tests, and make sure the tests still pass.
25 | 1. Push to your fork and [submit a pull request][pr].
26 | 1. Pat your self on the back and wait for your pull request to be reviewed and merged.
27 |
28 | Here are a few things you can do that will increase the likelihood of your pull request being accepted:
29 |
30 | - Follow the [style guide][style] which is using standard. Any linting errors should be shown when running `npm test`.
31 | - Write and update tests.
32 | - Keep your changes 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.
33 | - Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
34 |
35 | Work in Progress pull requests are also welcome to get feedback early on, or if there is something blocked you.
36 |
37 | ## Resources
38 |
39 | - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
40 | - [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
41 | - [GitHub Help](https://help.github.com)
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 kuldeepkeshwar
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](#contributors)
2 |
3 |
4 |
5 |
6 |
7 |
size-plugin
8 |
9 |
10 | > A GitHub 🤖 built with Probot that helps you to keep an 👁️ on static asset 📦 sizes of your application and gives instant feedback 📝 for developer whenever they make change.
11 |
12 | ##### 🤖 comments the gzipped sizes of your webpack assets and the changes since the last build into the relevant PR
13 |
14 | ## Usage
15 |
16 | First add an instance of the [size-plugin](https://github.com/GoogleChromeLabs/size-plugin) to your webpack configuration:
17 |
18 | > using rollup ? use [rollup-plugin-size](https://github.com/luwes/rollup-plugin-size)
19 |
20 | ```diff
21 | // webpack.config.js
22 | + const SizePlugin = require('size-plugin');
23 |
24 | module.exports = {
25 | plugins: [
26 | + new SizePlugin({publish:true})
27 | ]
28 | }
29 | ```
30 |
31 | Second Simply [install the app](https://github.com/apps/size-plugin) and make some changes, open a pr and watch the magic happen 😊
32 |
33 |
34 |
35 |
36 |
37 | Currently works with [Travis CI](https://travis-ci.org), [CircleCI](https://circleci.com/), [Wercker](http://www.wercker.com), and [Drone](http://readme.drone.io/).
38 |
39 | Using a different CI? Under the hood, [size-plugin](https://github.com/GoogleChromeLabs/size-plugin) uses [ci-env](https://github.com/siddharthkp/ci-env) to extract meta information which works perfectly with Custom CI 🙃
40 |
41 | ## Configure Bot
42 |
43 | Create a file `.github/size-plugin.yml`.
44 |
45 | example 👇
46 |
47 | ```yml
48 | base-branches: # base branches against which bot can open a pull request.
49 | - master
50 | - next
51 | size-files: # list(string/object) of size*.json files
52 | ## In case of multiple builds.
53 | - sizes-browser.json
54 | - sizes-server.json
55 | ## In case of multiple packages in a single repo or mono repo
56 | - dir: packages/a
57 | filename: sizes-a.json
58 | - dir: packages/b
59 | filename: sizes-b.json
60 | - dir: packages/c
61 | filename: sizes-c-browser.json
62 | - dir: packages/c
63 | filename: sizes-c-server.json
64 | ```
65 |
66 | > Note: `filename` must be unique
67 |
68 | ## Contributing
69 |
70 | If you have suggestions for how size-plugin could be improved, or want to report a bug, open an issue! We'd love all and any contributions.
71 |
72 | For more, check out the [Contributing Guide](CONTRIBUTING.md).
73 |
74 | #### like it?
75 |
76 | ⭐️ this repo
77 |
78 |
79 |
80 | ## License
81 |
82 | [ISC](LICENSE) © 2019 kuldeepkeshwar
83 |
84 | ## Contributors ✨
85 |
86 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
87 |
88 |
89 |
90 |
95 |
96 |
97 |
98 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
99 |
--------------------------------------------------------------------------------
/app.yml:
--------------------------------------------------------------------------------
1 | # This is a GitHub App Manifest. These settings will be used by default when
2 | # initially configuring your GitHub App.
3 | #
4 | # NOTE: changing this file will not update your GitHub App settings.
5 | # You must visit github.com/settings/apps/your-app-name to edit them.
6 | #
7 | # Read more about configuring your GitHub App:
8 | # https://probot.github.io/docs/development/#configuring-a-github-app
9 | #
10 | # Read more about GitHub App Manifests:
11 | # https://developer.github.com/apps/building-github-apps/creating-github-apps-from-a-manifest/
12 |
13 | # The list of events the GitHub App subscribes to.
14 | # Uncomment the event names below to enable them.
15 | default_events:
16 | # - check_run
17 | # - check_suite
18 | # - commit_comment
19 | # - create
20 | # - delete
21 | # - deployment
22 | # - deployment_status
23 | # - fork
24 | # - gollum
25 | # - issue_comment
26 | - issues
27 | # - label
28 | # - milestone
29 | # - member
30 | # - membership
31 | # - org_block
32 | # - organization
33 | # - page_build
34 | # - project
35 | # - project_card
36 | # - project_column
37 | # - public
38 | - pull_request
39 | - pull_request_review
40 | - pull_request_review_comment
41 | - push
42 | # - release
43 | # - repository
44 | # - repository_import
45 | # - status
46 | # - team
47 | # - team_add
48 | # - watch
49 |
50 | # The set of permissions needed by the GitHub App. The format of the object uses
51 | # the permission name for the key (for example, issues) and the access type for
52 | # the value (for example, write).
53 | # Valid values are `read`, `write`, and `none`
54 | default_permissions:
55 | # Repository creation, deletion, settings, teams, and collaborators.
56 | # https://developer.github.com/v3/apps/permissions/#permission-on-administration
57 | # administration: read
58 |
59 | # Checks on code.
60 | # https://developer.github.com/v3/apps/permissions/#permission-on-checks
61 | # checks: read
62 |
63 | # Repository contents, commits, branches, downloads, releases, and merges.
64 | # https://developer.github.com/v3/apps/permissions/#permission-on-contents
65 | contents: write
66 |
67 | # Deployments and deployment statuses.
68 | # https://developer.github.com/v3/apps/permissions/#permission-on-deployments
69 | # deployments: read
70 |
71 | # Issues and related comments, assignees, labels, and milestones.
72 | # https://developer.github.com/v3/apps/permissions/#permission-on-issues
73 | issues: write
74 |
75 | # Search repositories, list collaborators, and access repository metadata.
76 | # https://developer.github.com/v3/apps/permissions/#metadata-permissions
77 | metadata: read
78 |
79 | # Retrieve Pages statuses, configuration, and builds, as well as create new builds.
80 | # https://developer.github.com/v3/apps/permissions/#permission-on-pages
81 | # pages: read
82 |
83 | # Pull requests and related comments, assignees, labels, milestones, and merges.
84 | # https://developer.github.com/v3/apps/permissions/#permission-on-pull-requests
85 | pull_requests: write
86 |
87 | # Manage the post-receive hooks for a repository.
88 | # https://developer.github.com/v3/apps/permissions/#permission-on-repository-hooks
89 | # repository_hooks: read
90 | # Manage repository projects, columns, and cards.
91 | # https://developer.github.com/v3/apps/permissions/#permission-on-repository-projects
92 | # repository_projects: read
93 | # Retrieve security vulnerability alerts.
94 | # https://developer.github.com/v4/object/repositoryvulnerabilityalert/
95 | # vulnerability_alerts: read
96 | # Commit statuses.
97 | # https://developer.github.com/v3/apps/permissions/#permission-on-statuses
98 | # statuses: read
99 | # Organization members and teams.
100 | # https://developer.github.com/v3/apps/permissions/#permission-on-members
101 | # members: read
102 | # View and manage users blocked by the organization.
103 | # https://developer.github.com/v3/apps/permissions/#permission-on-organization-user-blocking
104 | # organization_user_blocking: read
105 | # Manage organization projects, columns, and cards.
106 | # https://developer.github.com/v3/apps/permissions/#permission-on-organization-projects
107 | # organization_projects: read
108 | # Manage team discussions and related comments.
109 | # https://developer.github.com/v3/apps/permissions/#permission-on-team-discussions
110 | # team_discussions: read
111 | # Manage the post-receive hooks for an organization.
112 | # https://developer.github.com/v3/apps/permissions/#permission-on-organization-hooks
113 | # organization_hooks: read
114 | # Get notified of, and update, content references.
115 | # https://developer.github.com/v3/apps/permissions/
116 | # organization_administration: read
117 |
118 | # The name of the GitHub App. Defaults to the name specified in package.json
119 | name: Size Plugin
120 |
121 | # The homepage of your GitHub App.
122 | url: https://github.com/kuldeepkeshwar/size-plugin-bot
123 |
124 | # A description of the GitHub App.
125 | # description: A description of my awesome app
126 |
127 | # 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.
128 | # Default: true
129 | public: true
130 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "size-plugin-bot",
3 | "version": "1.0.0",
4 | "description": "A Github bot for size-plugin",
5 | "author": "kuldeepkeshwar ",
6 | "license": "ISC",
7 | "repository": "https://github.com/kuldeepkeshwar/size-plugin-bot.git",
8 | "homepage": "https://github.com/kuldeepkeshwar/size-plugin-bot",
9 | "bugs": "https://github.com/kuldeepkeshwar/size-plugin-bot/issues",
10 | "keywords": [
11 | "size-plugin",
12 | "size-plugin-bot"
13 | ],
14 | "scripts": {
15 | "dev": "nodemon",
16 | "start": "probot run ./src/index.js",
17 | "lint": "eslint src --fix",
18 | "format": "prettier --write '**/*.{js,scss,jsx,md}'",
19 | "test": "jest",
20 | "test:watch": "jest --watch --notify --notifyMode=change --coverage"
21 | },
22 | "dependencies": {
23 | "axios": "^0.19.0",
24 | "emojis-list": "^3.0.0",
25 | "fs-extra": "^8.0.1",
26 | "pretty-bytes": "^5.2.0",
27 | "probot": "^9.2.20",
28 | "probot-config": "^1.1.0",
29 | "simple-github-db": "^1.0.0"
30 | },
31 | "devDependencies": {
32 | "dotenv": "^8.0.0",
33 | "eslint": "^5.16.0",
34 | "eslint-config-airbnb-base": "^13.2.0",
35 | "eslint-plugin-import": "^2.18.0",
36 | "execa": "^1.0.0",
37 | "jest": "^24.0.0",
38 | "lint-staged": "^9.2.4",
39 | "nock": "^10.0.0",
40 | "nodemon": "^1.17.2",
41 | "prettier": "^1.18.2",
42 | "smee-client": "^1.0.2"
43 | },
44 | "engines": {
45 | "node": ">= 8.3.0"
46 | },
47 | "lint-staged": {
48 | "linters": {
49 | "*.{js,jsx}": [
50 | "eslint --fix",
51 | "prettier --write",
52 | "git add"
53 | ]
54 | },
55 | "ignore": [
56 | "**/dist/*",
57 | "**/public/*"
58 | ]
59 | },
60 | "nodemonConfig": {
61 | "exec": "npm start",
62 | "watch": [
63 | ".env",
64 | "."
65 | ]
66 | },
67 | "jest": {
68 | "testEnvironment": "node"
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | trailingComma: 'es5',
3 | tabWidth: 4,
4 | semi: false,
5 | singleQuote: true,
6 | }
7 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | const MAX_RETRY = 20;
2 | const RETRY_INTERVAL = 30 * 1000;
3 |
4 | const SIZE_STORE_ENDPOINT = process.env.SIZE_STORE_ENDPOINT || 'https://size-store.now.sh';
5 |
6 | const BOT = 'size-plugin[bot]';
7 |
8 | const STAR_REPO_MESSAGE = `
9 | #### like it?
10 | ⭐️ [me](https://github.com/kuldeepkeshwar/size-plugin-bot) 😊
11 | `;
12 |
13 | const WARNGING_MESSAGE = '⚠️ [size-plugin bot](https://github.com/apps/size-plugin) automatically updates the stats file(s), avoid manually committing the stats file(s)';
14 |
15 | module.exports = {
16 | MAX_RETRY,
17 | RETRY_INTERVAL,
18 | SIZE_STORE_ENDPOINT,
19 | STAR_REPO_MESSAGE,
20 | WARNGING_MESSAGE,
21 | BOT,
22 | };
23 |
--------------------------------------------------------------------------------
/src/diff.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-restricted-syntax */
2 | /* eslint-disable camelcase */
3 | /* eslint-disable no-console */
4 |
5 | const axios = require('axios');
6 | const GithubDb = require('simple-github-db');
7 | const { getBotConfig } = require('./utils/utils');
8 | const { SIZE_STORE_ENDPOINT, STAR_REPO_MESSAGE, WARNGING_MESSAGE } = require('./config');
9 | const { decorateComment, decorateHeading } = require('./utils/template');
10 | const { fetchWithRetry } = require('./utils/api');
11 | const { isPullRequestOpenedByMe } = require('./utils/github');
12 |
13 | const url = `${SIZE_STORE_ENDPOINT}/diff`;
14 | const Database = GithubDb({ db: process.env.DATABASE_NAME, token: process.env.DATABASE_TOKEN });
15 | const DOCUMENT = 'pull_requests';
16 |
17 | function createIdentifier(repo) {
18 | return `${repo}`;
19 | }
20 |
21 | async function warnForSizeFiles(context, fullRepositoryName, pull_request_number, sizefiles) {
22 | const [owner, repo] = fullRepositoryName.split('/');
23 | const sizeFilePathMap = sizefiles.reduce((acc, item) => {
24 | const { dir, filename = item } = item;
25 | if (dir) {
26 | acc[`${dir}/${filename}`] = true;
27 | } else {
28 | acc[filename] = true;
29 | }
30 | return acc;
31 | }, {});
32 | const { data: files } = await context.github.pulls.listFiles({
33 | owner,
34 | repo,
35 | pull_number: pull_request_number,
36 | });
37 | const updatedFiles = files
38 | .filter(file => sizeFilePathMap[file.filename])
39 | .map(file => file.filename);
40 | if (updatedFiles.length) {
41 | const REASON = `👉 \`${updatedFiles.join('`,`')}\` ${
42 | updatedFiles.length === 1 ? 'was' : 'were'
43 | } updated`;
44 | const body = `${WARNGING_MESSAGE} \n \n ${REASON}`;
45 | const identifier = `${fullRepositoryName}/warning`;
46 | context.log(`Adding warning: ${fullRepositoryName}/pull/${pull_request_number}`);
47 |
48 | try {
49 | const pullRequestMap = await Database.fetchOne({ document: DOCUMENT, identifier });
50 | const comment_id = pullRequestMap[pull_request_number];
51 | if (comment_id) {
52 | await context.github.issues.updateComment({
53 | owner,
54 | repo,
55 | comment_id,
56 | body,
57 | });
58 | } else {
59 | const issueComment = context.issue({ body });
60 | const {
61 | data: { id },
62 | } = await context.github.issues.createComment(issueComment);
63 | await Database.update(
64 | { document: DOCUMENT, identifier },
65 | { ...pullRequestMap, [pull_request_number]: id },
66 | );
67 | }
68 | } catch (error) {
69 | const issueComment = context.issue({ body });
70 | const {
71 | data: { id },
72 | } = await context.github.issues.createComment(issueComment);
73 | await Database.add({ document: DOCUMENT, identifier }, { [pull_request_number]: id });
74 | }
75 | }
76 | }
77 |
78 | async function commentPullRequest(context, fullRepositoryName, pull_request_number, body) {
79 | const identifier = createIdentifier(fullRepositoryName);
80 | try {
81 | const pullRequestMap = await Database.fetchOne({ document: DOCUMENT, identifier });
82 | const comment_id = pullRequestMap[pull_request_number];
83 | if (comment_id) {
84 | const [owner, repo] = fullRepositoryName.split('/');
85 | await context.github.issues.updateComment({
86 | owner,
87 | repo,
88 | comment_id,
89 | body,
90 | });
91 | } else {
92 | const issueComment = context.issue({ body });
93 | const {
94 | data: { id },
95 | } = await context.github.issues.createComment(issueComment);
96 | await Database.update(
97 | { document: DOCUMENT, identifier },
98 | { ...pullRequestMap, [pull_request_number]: id },
99 | );
100 | }
101 | } catch (error) {
102 | const issueComment = context.issue({ body });
103 | const {
104 | data: { id },
105 | } = await context.github.issues.createComment(issueComment);
106 | await Database.add({ document: DOCUMENT, identifier }, { [pull_request_number]: id });
107 | }
108 | }
109 | function sizeCommentTemplate(item) {
110 | const {
111 | filename,
112 | diff: { files },
113 | } = item;
114 | return `
115 | ${decorateHeading(filename, files)}
116 |
117 | \`\`\`
118 | ${decorateComment(files)}
119 | \`\`\`
120 | `;
121 | }
122 | function combinedCommentMessageTemplate(items, sha) {
123 | const sizes = items.reduce(
124 | (agg, item) => `${agg}
125 | ${sizeCommentTemplate(item)}
126 | `,
127 | '',
128 | );
129 |
130 | return `
131 | Size report for the changes in this PR:
132 | ${sizes}
133 |
134 | commit: ${sha}
135 |
136 | ${STAR_REPO_MESSAGE}
137 | `;
138 | }
139 |
140 | async function fetchSizes(repo, sha, pull_request_number, sizefiles) {
141 | let sizes = null;
142 | await fetchWithRetry(() => {
143 | const params = {
144 | repo,
145 | sha,
146 | pull_request_number,
147 | };
148 | return axios.get(url, { params }).then(({ data }) => {
149 | const values = Object.values(data);
150 | sizes = values;
151 | if (values.length < sizefiles.length) {
152 | throw Error('waiting for all file sizes');
153 | }
154 | });
155 | });
156 | return sizes;
157 | }
158 | // eslint-disable-next-line consistent-return
159 | async function get(context) {
160 | try {
161 | const {
162 | pull_request: {
163 | number: pull_request_number,
164 | head: { sha },
165 | user,
166 | },
167 | } = context.payload;
168 | const {
169 | repository: { full_name },
170 | } = context.payload;
171 | const fullRepositoryName = full_name.toLowerCase();
172 | if (!isPullRequestOpenedByMe(user)) {
173 | const config = await getBotConfig(context);
174 | const sizefiles = config['size-files'];
175 | warnForSizeFiles(context, fullRepositoryName, pull_request_number, sizefiles);
176 | context.log(`fetching diff for: ${fullRepositoryName}/pull/${pull_request_number}`);
177 | const sizes = await fetchSizes(fullRepositoryName, sha, pull_request_number, sizefiles);
178 | const message = combinedCommentMessageTemplate(sizes, sha);
179 | commentPullRequest(context, fullRepositoryName, pull_request_number, message).then(
180 | () => {
181 | context.log(
182 | `commented sizes for: ${fullRepositoryName}/pull/${pull_request_number}`,
183 | );
184 | },
185 | console.error,
186 | );
187 | }
188 | } catch (err) {
189 | console.error(err);
190 | }
191 | }
192 | module.exports = { get };
193 |
--------------------------------------------------------------------------------
/src/events.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | const diff = require('./diff');
4 | const size = require('./size');
5 |
6 | function register(app) {
7 | app.on(['push'], async (context) => {
8 | setTimeout(size.get, 0, context);
9 | });
10 | app.on(
11 | ['pull_request.opened', 'pull_request.synchronize', 'pull_request.reopened'],
12 | async (context) => {
13 | setTimeout(diff.get, 0, context);
14 | },
15 | );
16 | }
17 | module.exports = { register };
18 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | const { register } = require('./events');
4 | const keepAlive = require('./utils/keep-alive');
5 |
6 | module.exports = (app) => {
7 | console.log('Yay, the app was loaded!');
8 | const router = app.route('/size-plugin');
9 | router.use(
10 | keepAlive({
11 | path: '/size-plugin/_keepalive',
12 | handler(req, res, next, options) {
13 | if (req.path === options.path.replace('/size-plugin', '')) {
14 | res.send("I'm alive");
15 | } else {
16 | next();
17 | }
18 | },
19 | }),
20 | );
21 | router.get('/ping', (req, res) => {
22 | res.send('pong!!');
23 | });
24 | router.get('/', (req, res) => {
25 | res.send('Hello Dolores !!');
26 | });
27 | register(app);
28 | };
29 |
--------------------------------------------------------------------------------
/src/size.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable consistent-return */
2 | /* eslint-disable no-await-in-loop */
3 | /* eslint-disable no-restricted-syntax */
4 | /* eslint-disable camelcase */
5 | /* eslint-disable no-console */
6 |
7 | const axios = require('axios');
8 | const emoji = require('./utils/emoji');
9 | const { toMap, getBotConfig } = require('./utils/utils');
10 | const {
11 | isCommitedByMe,
12 | updateFile,
13 | createPullRequest,
14 | createReviewRequest,
15 | updatePullRequest,
16 | listPullRequest,
17 | } = require('./utils/github');
18 | const { SIZE_STORE_ENDPOINT, STAR_REPO_MESSAGE, BOT } = require('./config');
19 | const { fetchWithRetry } = require('./utils/api');
20 |
21 | const url = `${SIZE_STORE_ENDPOINT}/size`;
22 |
23 | async function fetchSizes(params, sizefiles) {
24 | const sizes = {};
25 | const sizeFilePathMap = sizefiles.reduce((acc, item) => {
26 | const { dir, filename = item } = item;
27 | if (dir) {
28 | acc[filename] = `${dir}/${filename}`;
29 | } else {
30 | acc[filename] = filename;
31 | }
32 | return acc;
33 | }, {});
34 | try {
35 | await fetchWithRetry(() => axios.get(url, { params }).then((resp) => {
36 | const { data } = resp;
37 | const values = Object.values(data);
38 | if (values.length < sizefiles.length) {
39 | for (const { filename, size } of values) {
40 | sizes[filename] = {
41 | filename: sizeFilePathMap[filename],
42 | content: size,
43 | };
44 | }
45 | throw Error('waiting for all file sizes');
46 | } else {
47 | Object.values(data).forEach(({ filename, size }) => {
48 | sizes[filename] = {
49 | filename: sizeFilePathMap[filename],
50 | content: size,
51 | };
52 | });
53 | }
54 | }));
55 | } catch (error) {
56 | console.error(error);
57 | }
58 | return Object.values(sizes);
59 | }
60 |
61 | async function cleanUp(context, owner, repo, branch, number) {
62 | // close stale pull request
63 | console.log('cleaning stale pr');
64 | const allPullRequests = await listPullRequest(context.github, {
65 | owner: owner.name,
66 | repo,
67 | state: 'open',
68 | });
69 | const pullRequestsByBot = allPullRequests.filter((pr) => {
70 | const user = pr.user.login;
71 | const base = pr.base.ref;
72 | if (base !== branch) {
73 | return false;
74 | }
75 | if (pr.number === number) {
76 | return false;
77 | }
78 | if (BOT === user) {
79 | return true;
80 | }
81 | return false;
82 | });
83 | console.log('pr to close ', pullRequestsByBot.length);
84 |
85 | pullRequestsByBot.forEach(pr => updatePullRequest(context.github, {
86 | owner: owner.name,
87 | repo,
88 | pull_number: pr.number,
89 | state: 'closed',
90 | })
91 | .then(({ html_url }) => console.log('closed : ', html_url))
92 | .catch(console.error));
93 | }
94 | // eslint-disable-next-line consistent-return
95 | async function get(context) {
96 | try {
97 | const {
98 | ref,
99 | repository: { name, full_name, owner },
100 | after: sha,
101 | head_commit,
102 | commits,
103 | } = context.payload;
104 |
105 | const fullRepositoryName = full_name.toLowerCase();
106 | const branch = ref.replace('refs/heads/', '');
107 | const config = await getBotConfig(context);
108 | const baseBranches = config['base-branches'];
109 | const sizefiles = config['size-files'];
110 | if (baseBranches.includes(branch) && !isCommitedByMe(commits)) {
111 | const params = { repo: fullRepositoryName, sha };
112 | context.log(`fetching sizes for: ${fullRepositoryName}`);
113 | const files = await fetchSizes(params, sizefiles);
114 |
115 | const changedFiles = files.reduce((agg, file) => {
116 | const { content: stats } = file;
117 | const [recent] = stats;
118 | const { files } = recent;
119 | if (files.some(({ delta, diff }) => (delta || diff) !== 0)) {
120 | const content = [recent];
121 | agg.push({ ...file, content });
122 | }
123 | return agg;
124 | }, []);
125 | console.log({ changedFiles:JSON.stringify(changedFiles),content:JSON.stringify(files[0].content),fullRepositoryName });
126 | if (changedFiles.length) {
127 | try {
128 | for (const file of changedFiles) {
129 | await updateFile(context.github, {
130 | owner: owner.name,
131 | repo: name,
132 | branch,
133 | file,
134 | });
135 | }
136 | } catch (err) {
137 | const title = `${emoji.random().join(' ')} update sizes`;
138 | const body = `👇
139 | ${head_commit.id} : ${head_commit.message || ''}
140 |
141 | ${STAR_REPO_MESSAGE}
142 | `;
143 | const { number } = await createPullRequest(context.github, {
144 | owner: owner.name,
145 | repo: name,
146 | base: branch,
147 | head: `size-plugin-${Date.now()}`,
148 | title,
149 | body,
150 | files:changedFiles,
151 | });
152 | await createReviewRequest(context.github, {
153 | owner: owner.name,
154 | repo: name,
155 | pull_number: number,
156 | reviewers: commits
157 | .map(({ author: { username } }) => username)
158 | .filter(Boolean),
159 | });
160 | await cleanUp(context, owner, name, branch, number);
161 | }
162 | }
163 | }
164 | } catch (err) {
165 | console.error(err);
166 | }
167 | }
168 | module.exports = { get };
169 |
--------------------------------------------------------------------------------
/src/utils/api.js:
--------------------------------------------------------------------------------
1 | const { MAX_RETRY, RETRY_INTERVAL } = require('./../config');
2 |
3 | function logError(error) {
4 | if (error.response) {
5 | console.error({
6 | data: error.response.data,
7 | status: error.response.status,
8 | });
9 | } else if (error.request) {
10 | console.error(error.request);
11 | } else {
12 | console.error('Error', error.message);
13 | }
14 | }
15 | function parseError(error) {
16 | if (error.response) {
17 | return { message: error.response.data, status: error.response.status };
18 | }
19 | return error;
20 | }
21 |
22 | function fetchWithRetry(makeRequest, options = { max: MAX_RETRY, interval: RETRY_INTERVAL }) {
23 | return new Promise((resolve, reject) => {
24 | let retry = 0;
25 | const id = setInterval(() => {
26 | (async function poll() {
27 | try {
28 | retry += 1;
29 | const response = await makeRequest();
30 | clearInterval(id);
31 | resolve(response && response.data);
32 | } catch (error) {
33 | if (retry === options.max) {
34 | clearInterval(id);
35 | reject(parseError(error));
36 | }
37 | }
38 | }());
39 | }, options.interval);
40 | });
41 | }
42 | module.exports = { logError, parseError, fetchWithRetry };
43 |
--------------------------------------------------------------------------------
/src/utils/emoji.js:
--------------------------------------------------------------------------------
1 | const list = require('emojis-list');
2 |
3 | const total = list.length;
4 |
5 | function random(n = 2) {
6 | const items = [];
7 | for (let index = 0; index < n; index += 1) {
8 | const i = Math.floor(Math.random() * total);
9 | items.push(list[i]);
10 | }
11 | return items;
12 | }
13 | module.exports = { random };
14 |
--------------------------------------------------------------------------------
/src/utils/github.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable camelcase */
2 | /* eslint-disable no-console */
3 | /* eslint-disable no-await-in-loop */
4 | /* eslint-disable no-restricted-syntax */
5 | const { BOT } = require('./../config');
6 |
7 | const emoji = require('./emoji');
8 |
9 | async function createBranch(github, {
10 | owner, repo, base = 'master', branch,
11 | }) {
12 | const {
13 | data: {
14 | object: { sha },
15 | },
16 | } = await github.git.getRef({
17 | owner,
18 | repo,
19 | ref: `/heads/${base}`,
20 | });
21 | await github.git.createRef({
22 | owner,
23 | repo,
24 | ref: `refs/heads/${branch}`,
25 | sha,
26 | });
27 | }
28 | async function getFile(github, {
29 | owner, repo, branch, filename,
30 | }) {
31 | try {
32 | const { data } = await github.repos.getContents({
33 | owner,
34 | repo,
35 | branch,
36 | path: filename,
37 | });
38 | return data;
39 | } catch (err) {
40 | console.error('getFile:', owner, repo, branch, filename, err.message || err.status);
41 | }
42 | return null;
43 | }
44 | async function updateFile(github, {
45 | owner, repo, branch, file,
46 | }) {
47 | const { filename, content } = file;
48 | const data = Buffer.from(JSON.stringify(content, null, 2)).toString('base64');
49 | const oldFile = await getFile(github, {
50 | owner,
51 | repo,
52 | branch,
53 | filename,
54 | });
55 | if (oldFile && oldFile.content !== data) {
56 | await github.repos.createOrUpdateFile({
57 | owner,
58 | repo,
59 | branch,
60 | path: filename,
61 | message: `${emoji.random().join(' ')} update ${filename} 👍`,
62 | content: data,
63 | sha: oldFile.sha,
64 | });
65 | } else if (!oldFile) {
66 | await github.repos.createOrUpdateFile({
67 | owner,
68 | repo,
69 | branch,
70 | path: filename,
71 | message: `${emoji.random().join(' ')} create ${filename} 👍`,
72 | content: data,
73 | });
74 | }
75 | }
76 | async function createPullRequest(github, {
77 | owner, repo, base, head, title, body, files,
78 | }) {
79 | console.log('create branch:', head);
80 |
81 | await createBranch(github, {
82 | owner,
83 | repo,
84 | base,
85 | branch: head,
86 | });
87 | for (const file of files) {
88 | console.log('update file', head, file.filename);
89 | await updateFile(github, {
90 | owner,
91 | repo,
92 | branch: head,
93 | file,
94 | });
95 | }
96 | const { data } = await github.pulls.create({
97 | owner,
98 | repo,
99 | title,
100 | body,
101 | head,
102 | base,
103 | });
104 | console.log('opened pull request', data.number);
105 | return data;
106 | }
107 | async function createReviewRequest(github, {
108 | owner, repo, pull_number, reviewers,
109 | }) {
110 | console.log('createReviewRequest', owner, repo, pull_number, reviewers);
111 | github.pulls.createReviewRequest({
112 | owner,
113 | repo,
114 | pull_number,
115 | reviewers,
116 | });
117 | }
118 | async function listPullRequest(
119 | github,
120 | {
121 | owner,
122 | repo,
123 | state, // head, base,
124 | },
125 | ) {
126 | const { data } = await github.pulls.list({
127 | owner,
128 | repo,
129 | state,
130 | // head,
131 | // base,
132 | });
133 | return data;
134 | }
135 | async function updatePullRequest(github, {
136 | owner, repo, pull_number, state,
137 | }) {
138 | const { data } = await github.pulls.update({
139 | owner,
140 | repo,
141 | pull_number,
142 | state,
143 | });
144 | return data;
145 | }
146 | function isCommitedByMe(commits = []) {
147 | return !commits.some((commit) => {
148 | const {
149 | author: { name },
150 | } = commit;
151 | if (name !== BOT) {
152 | return true;
153 | }
154 | return false;
155 | });
156 | }
157 | function isPullRequestOpenedByMe(user) {
158 | return user.login === BOT;
159 | }
160 | module.exports = {
161 | isCommitedByMe,
162 | isPullRequestOpenedByMe,
163 | createReviewRequest,
164 | getFile,
165 | updateFile,
166 | createPullRequest,
167 | listPullRequest,
168 | updatePullRequest,
169 | createBranch,
170 | };
171 |
--------------------------------------------------------------------------------
/src/utils/keep-alive.js:
--------------------------------------------------------------------------------
1 | const http = require('http');
2 |
3 | function createOptions(options) {
4 | const opt = {
5 | path: options.path || '/_keepalive',
6 | delay: options.delay || 3,
7 | handler: options.handler,
8 | };
9 | opt.url = `http://${process.env.PROJECT_DOMAIN}.glitch.me${opt.path}`;
10 | return opt;
11 | }
12 | module.exports = function keepAlive(options) {
13 | const opt = createOptions(options);
14 | setInterval(() => http.get(opt.url), opt.delay * 60 * 1000);
15 | return (req, res, next) => {
16 | if (opt.handler) {
17 | opt.handler(req, res, next, opt);
18 | } else if (req.path === opt.path) {
19 | res.send("I'm alive");
20 | } else {
21 | next();
22 | }
23 | };
24 | };
25 |
--------------------------------------------------------------------------------
/src/utils/template.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-underscore-dangle */
2 | /* eslint-disable no-param-reassign */
3 | /* eslint-disable consistent-return */
4 | /* eslint-disable no-nested-ternary */
5 | const prettyBytes = require('pretty-bytes');
6 |
7 | function textWithEmoji(color, text) {
8 | const emoji = { red: '🚫', green: '✅' };
9 | return `${text} ${emoji[color]}`;
10 | }
11 | function colorText(color, text) {
12 | return text;
13 | // return `${text} `;
14 | }
15 | function getColorCode(size) {
16 | return size > 100 * 1024
17 | ? 'red'
18 | : size > 40 * 1024
19 | ? 'yellow'
20 | : size > 20 * 1024
21 | ? 'cyan'
22 | : 'green';
23 | }
24 | function decorateDelta(delta) {
25 | let deltaText = (delta > 0 ? '+' : '') + prettyBytes(delta);
26 | if (delta > 1024) {
27 | deltaText = textWithEmoji('red', deltaText);
28 | } else if (delta < -10) {
29 | deltaText = textWithEmoji('green', deltaText);
30 | }
31 | return deltaText;
32 | }
33 | function decorateHeading(name, files) {
34 | try {
35 | const result = files.reduce(
36 | (agg, item) => {
37 | agg.total = agg.total - 0 + item.size;
38 | agg.delta = agg.delta - 0 + (item.diff || item.delta);
39 | return agg;
40 | },
41 | {
42 | total: 0,
43 | delta: 0,
44 | },
45 | );
46 |
47 | const total = `Overall size: ${prettyBytes(result.total)}`;
48 | const delta = result.delta && Math.abs(result.delta) > 1 ? ` (${decorateDelta(result.delta)})` : '';
49 | return `${name.replace('.json', '')} ${total} ${delta}`;
50 | } catch (err) {
51 | console.log(err);
52 | }
53 | }
54 | function decorateComment(files) {
55 | const width = Math.max(...files.map(file => file.filename.length));
56 | let output = '';
57 | // eslint-disable-next-line no-restricted-syntax
58 | for (const file of files) {
59 | const {
60 | filename: name, size, diff, delta,
61 | } = file;
62 | const _delta = diff || delta;
63 | const msg = `${new Array(width - name.length + 2).join(' ') + name} ⏤ `;
64 | const color = getColorCode(size);
65 | let sizeText = colorText(color, prettyBytes(size));
66 | if (_delta && Math.abs(_delta) > 1) {
67 | sizeText += ` (${decorateDelta(_delta)})`;
68 | }
69 | const text = `${msg + sizeText}\n`;
70 | output += text;
71 | }
72 | return output;
73 | }
74 | module.exports = { decorateComment, decorateHeading };
75 |
--------------------------------------------------------------------------------
/src/utils/utils.js:
--------------------------------------------------------------------------------
1 | const getConfig = require('probot-config');
2 |
3 | function toMap(arr, propertyname) {
4 | return arr.reduce((agg, item) => {
5 | const property = item[propertyname];
6 | // eslint-disable-next-line no-param-reassign
7 | agg[property] = item;
8 | return agg;
9 | }, {});
10 | }
11 | async function getBotConfig(context) {
12 | const botConfig = await getConfig(context, 'size-plugin.yml');
13 | return {
14 | 'size-files': ['size-plugin.json'],
15 | 'base-branches': ['master'],
16 | ...botConfig,
17 | };
18 | }
19 |
20 | module.exports = { toMap, getBotConfig };
21 |
--------------------------------------------------------------------------------
/static/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kuldeepkeshwar/size-plugin-bot/39da3f755665827f8180011b955e9024e70e6c88/static/images/icon.png
--------------------------------------------------------------------------------
/static/images/sample.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kuldeepkeshwar/size-plugin-bot/39da3f755665827f8180011b955e9024e70e6c88/static/images/sample.png
--------------------------------------------------------------------------------
/test/fixtures/issues.opened.json:
--------------------------------------------------------------------------------
1 | {
2 | "action": "opened",
3 | "issue": {
4 | "number": 1,
5 | "user": {
6 | "login": "hiimbex"
7 | }
8 | },
9 | "repository": {
10 | "name": "testing-things",
11 | "owner": {
12 | "login": "hiimbex"
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test/index.test.js:
--------------------------------------------------------------------------------
1 | const nock = require('nock')
2 | // Requiring our app implementation
3 | const myProbotApp = require('..')
4 | const { Probot } = require('probot')
5 | // Requiring our fixtures
6 | const payload = require('./fixtures/issues.opened')
7 | const issueCreatedBody = { body: 'Thanks for opening this issue!' }
8 |
9 | nock.disableNetConnect()
10 |
11 | describe('My Probot app', () => {
12 | let probot
13 |
14 | beforeEach(() => {
15 | probot = new Probot({})
16 | // Load our app into probot
17 | const app = probot.load(myProbotApp)
18 |
19 | // just return a test token
20 | app.app = () => 'test'
21 | })
22 |
23 | test('creates a comment when an issue is opened', async () => {
24 | // Test that we correctly return a test token
25 | nock('https://api.github.com')
26 | .post('/app/installations/2/access_tokens')
27 | .reply(200, { token: 'test' })
28 |
29 | // Test that a comment is posted
30 | nock('https://api.github.com')
31 | .post('/repos/hiimbex/testing-things/issues/1/comments', body => {
32 | expect(body).toMatchObject(issueCreatedBody)
33 | return true
34 | })
35 | .reply(200)
36 |
37 | // Receive a webhook event
38 | await probot.receive({ name: 'issues', payload })
39 | })
40 | })
41 |
42 | // For more information about testing with Jest see:
43 | // https://facebook.github.io/jest/
44 |
45 | // For more information about testing with Nock see:
46 | // https://github.com/nock/nock
47 |
--------------------------------------------------------------------------------
/tools/scripts/cmds.txt:
--------------------------------------------------------------------------------
1 | --- pem to single line
2 | awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' key.pem
3 |
4 | --- add secret from .key
5 | now secret add size-plugin-bot-private-key-base64-encoded -- "`< .key`"
--------------------------------------------------------------------------------
/tools/scripts/deploy.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | require('dotenv').config();
3 |
4 | const execa = require('execa');
5 | const { alias } = require('../../now.json');
6 |
7 | (async () => {
8 | try {
9 | const { stdout } = await execa('now');
10 | const alisaExec = execa('now', ['alias', stdout, `${alias[0]}.now.sh`]);
11 | alisaExec.stdout.pipe(process.stdout);
12 | alisaExec.stderr.pipe(process.stderr);
13 | } catch (e) {
14 | console.log(e.stderr);
15 | }
16 | })();
17 |
--------------------------------------------------------------------------------
/tools/scripts/secret.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | require('dotenv').config();
3 |
4 | const execa = require('execa');
5 |
6 | const secrets = {
7 | 'size-plugin-bot-app-id': process.env.APP_ID,
8 | 'size-plugin-bot-webhook-secret': process.env.WEBHOOK_SECRET,
9 | 'size-plugin-bot-private-key-base64-encoded': process.env.PRIVATE_KEY,
10 | };
11 | (async () => {
12 | try {
13 | const keys = Object.keys(secrets);
14 | for (const key of keys) {
15 | const cmd = `now secret add ${key} ${secrets[key]}`;
16 | console.log(cmd);
17 | await execa.shell(cmd);
18 | }
19 | } catch (e) {
20 | console.log(e.stderr);
21 | }
22 | })();
23 |
--------------------------------------------------------------------------------