├── .eslintrc
├── .gitignore
├── .taskcluster.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── Procfile
├── README.md
├── app.json
├── assets
├── error.svg
├── failure.svg
├── newrepo.svg
├── pending.svg
└── success.svg
├── config.yml
├── docs
├── intro.md
├── taskcluster-yml-v0.md
└── taskcluster-yml-v1.md
├── package.json
├── schemas
├── constants.yml
└── v1
│ ├── build-list.yml
│ ├── create-comment.yml
│ ├── create-status.yml
│ ├── github-pull-request-message.yml
│ ├── github-push-message.yml
│ ├── github-release-message.yml
│ ├── repository.yml
│ ├── task-group-creation-requested.yml
│ ├── taskcluster-github-config.v1.yml
│ └── taskcluster-github-config.yml
├── src
├── api.js
├── data.js
├── exchanges.js
├── github-auth.js
├── handlers.js
├── intree.js
├── main.js
├── pr-allowed.js
└── tc-yaml.js
├── test
├── api_test.js
├── checkStaging.js
├── data
│ ├── configs
│ │ ├── taskcluster.branchlimited.v0.yml
│ │ ├── taskcluster.branchlimited.v1.yml
│ │ ├── taskcluster.exclude-error.yml
│ │ ├── taskcluster.exclude.yml
│ │ ├── taskcluster.non-github.v0.yml
│ │ ├── taskcluster.non-github.v1.yml
│ │ ├── taskcluster.pull_with_exclude.yml
│ │ ├── taskcluster.push_pull_release.v0.yml
│ │ ├── taskcluster.push_pull_release.v1.yml
│ │ ├── taskcluster.release_single.v0.yml
│ │ ├── taskcluster.release_single.v1.yml
│ │ ├── taskcluster.single.v0.yml
│ │ ├── taskcluster.single.v1.yml
│ │ ├── taskcluster.star.yml
│ │ ├── taskcluster.tag.branchlimited.v0.yml
│ │ ├── taskcluster.tag.branchlimited.v1.yml
│ │ ├── taskcluster.tag_single.v0.yml
│ │ └── taskcluster.tag_single.v1.yml
│ └── webhooks
│ │ ├── webhook.pull_request.close.json
│ │ ├── webhook.pull_request.open.json
│ │ ├── webhook.push.bad_secret.json
│ │ ├── webhook.push.json
│ │ ├── webhook.push.no_secret.json
│ │ ├── webhook.push.offbranch.json
│ │ ├── webhook.push.unicode.json
│ │ ├── webhook.release.bad_secret.json
│ │ ├── webhook.release.json
│ │ ├── webhook.tag_push.json
│ │ └── webhook.unknown_event.json
├── fake_github_test.js
├── github-auth.js
├── handler_test.js
├── helper.js
├── intree_test.js
├── invalid-yaml.json
├── mocha.opts
├── pr-allowed_test.js
├── pulse_test.js
├── references_test.js
├── sync_test.js
├── tc-yaml_test.js
├── valid-yaml.json
└── webhook_test.js
├── user-config-example.yml
└── yarn.lock
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | 'extends': 'eslint-config-taskcluster'
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | config/development.js
3 | *.sw[a-z]
4 | taskcluster-github.conf.json
5 | npm-debug.log
6 | .test/
7 | lib/
8 | user-config.yml
9 | yarn-error.log
10 | package-lock.json
11 |
--------------------------------------------------------------------------------
/.taskcluster.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | policy:
3 | pullRequests: public
4 | tasks:
5 | - $if: 'tasks_for == "github-pull-request" && event["action"] in ["opened", "reopened", "synchronize"]'
6 | then:
7 | taskId: {$eval: as_slugid("pr_task")}
8 | created: {$fromNow: ''}
9 | deadline: {$fromNow: '1 hour'}
10 | provisionerId: aws-provisioner-v1
11 | workerType: github-worker
12 | scopes:
13 | - secrets:get:project/taskcluster/testing/taskcluster-github
14 | payload:
15 | maxRunTime: 600
16 | image: "node:8"
17 | env:
18 | DEBUG: "* -mocha* -nock* -express* -body-parser* -eslint*"
19 | features:
20 | taskclusterProxy: true
21 | command:
22 | - "/bin/bash"
23 | - "-lc"
24 | - "git clone ${event.pull_request.head.repo.git_url} repo && cd repo && git checkout ${event.pull_request.head.sha} && yarn && yarn test"
25 | metadata:
26 | name: "Taskcluster GitHub Tests"
27 | description: "All tests"
28 | owner: ${event.pull_request.user.login}@users.noreply.github.com
29 | source: ${event.repository.url}
30 | - $if: 'tasks_for == "github-push"'
31 | then:
32 | taskId: {$eval: as_slugid("push_task")}
33 | created: {$fromNow: ''}
34 | deadline: {$fromNow: '1 hour'}
35 | provisionerId: aws-provisioner-v1
36 | workerType: github-worker
37 | scopes:
38 | - secrets:get:project/taskcluster/testing/taskcluster-github
39 | payload:
40 | maxRunTime: 600
41 | image: "node:8"
42 | env:
43 | DEBUG: "* -mocha* -nock* -express* -body-parser* -eslint*"
44 | NO_TEST_SKIP: "true"
45 | features:
46 | taskclusterProxy: true
47 | command:
48 | - "/bin/bash"
49 | - "-lc"
50 | - "git clone ${event.repository.url} repo && cd repo && git checkout ${event.after} && yarn && yarn test"
51 | metadata:
52 | name: "Taskcluster GitHub Tests"
53 | description: "All tests"
54 | owner: ${event.pusher.name}@users.noreply.github.com
55 | source: ${event.repository.url}
56 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Mozilla Community Participation Guidelines
2 |
3 | The most recent version of the Mozilla Community Participation Guideline can always be found here: https://www.mozilla.org/en-US/about/governance/policies/participation/
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We welcome pull requests from everyone. We do expect everyone to adhere to the [Mozilla Community Participation Guidelines][participation].
4 |
5 | If you're trying to figure out what to work on, here are some places to find suitable projects:
6 | * [Good first bugs][goodfirstbug]: these are scoped to make it easy for first-time contributors to get their feet wet with Taskcluster code.
7 | * [Mentored bugs][bugsahoy]: these are slightly more involved projects that may require insight or guidance from someone on the Taskcluster team.
8 | * [Full list of open issues][issues]: everything else
9 |
10 | If the project you're interested in working on isn't covered by a bug or issue, or you're unsure about how to proceed on an existing issue, it's a good idea to talk to someone on the Taskcluster team before you go too far down a particular path. You can find us in the #taskcluster channel on [Mozilla's IRC server][irc] to discuss. You can also simply add a comment to the issue or bug.
11 |
12 | Once you've found an issue to work on and written a patch, submit a pull request. Some things that will increase the chance that your pull request is accepted:
13 |
14 | * Follow our [best practices][bestpractices].
15 | * This includes [writing or updating tests][testing].
16 | * Write a [good commit message][commit].
17 |
18 | Welcome to the team!
19 |
20 | [participation]: https://www.mozilla.org/en-US/about/governance/policies/participation/
21 | [issues]: ../../issues
22 | [bugsahoy]: https://www.joshmatthews.net/bugsahoy/?taskcluster=1
23 | [goodfirstbug]: http://www.joshmatthews.net/bugsahoy/?taskcluster=1&simple=1
24 | [irc]: https://wiki.mozilla.org/IRC
25 | [bestpractices]: https://docs.taskcluster.net/docs/manual/design/devel/best-practices
26 | [testing]: https://docs.taskcluster.net/docs/manual/design/devel/best-practices/testing
27 | [commit]: https://docs.taskcluster.net/docs/manual/design/devel/best-practices/commits
28 |
29 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: node src/main server
2 | worker: node src/main worker
3 | sync: node src/main syncInstallations
4 | write-docs: node src/main writeDocs
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Moved to Monorepo
2 |
3 | This project has been moved into the Taskcluster "monorepo" at https://github.com/taskcluster/taskcluster.
4 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "taskcluster-github",
3 | "scripts": {
4 | },
5 | "env": {
6 | "AZURE_ACCOUNT_NAME": {
7 | "required": true
8 | },
9 | "DEBUG": {
10 | "required": true
11 | },
12 | "GITHUB_OAUTH_TOKEN": {
13 | "required": true
14 | },
15 | "NODE_ENV": {
16 | "required": true
17 | },
18 | "PULSE_PASSWORD": {
19 | "required": true
20 | },
21 | "PULSE_USERNAME": {
22 | "required": true
23 | },
24 | "TASKCLUSTER_ACCESS_TOKEN": {
25 | "required": true
26 | },
27 | "TASKCLUSTER_CLIENT_ID": {
28 | "required": true
29 | },
30 | "WEBHOOK_SECRET": {
31 | "required": true
32 | }
33 | },
34 | "formation": {
35 | },
36 | "addons": [
37 |
38 | ],
39 | "buildpacks": [
40 | {
41 | "url": "heroku/nodejs"
42 | }
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------
/assets/error.svg:
--------------------------------------------------------------------------------
1 |
2 |
72 |
--------------------------------------------------------------------------------
/assets/failure.svg:
--------------------------------------------------------------------------------
1 |
2 |
105 |
--------------------------------------------------------------------------------
/assets/newrepo.svg:
--------------------------------------------------------------------------------
1 |
2 |
116 |
--------------------------------------------------------------------------------
/assets/success.svg:
--------------------------------------------------------------------------------
1 |
2 |
96 |
--------------------------------------------------------------------------------
/config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | defaults:
3 | app:
4 | jobQueue: null
5 | deprecatedResultStatusQueue: null
6 | deprecatedInitialStatusQueue: null
7 | resultStatusQueue: null
8 | initialStatusQueue: null
9 | buildsTableName: 'TaskclusterGithubBuilds'
10 | ownersDirectoryTableName: 'TaskclusterIntegrationOwners'
11 | checkRunsTableName: 'TaskclusterCheckRuns'
12 | checkTaskRoute: 'checks'
13 | statusTaskRoute: 'statuses'
14 | publishMetaData: !env:bool PUBLISH_METADATA
15 | statusContext: 'Taskcluster'
16 | botName: !env BOT_USERNAME
17 |
18 | taskcluster:
19 | rootUrl: !env TASKCLUSTER_ROOT_URL
20 | credentials:
21 | clientId: !env TASKCLUSTER_CLIENT_ID
22 | accessToken: !env TASKCLUSTER_ACCESS_TOKEN
23 |
24 | github:
25 | credentials:
26 | privatePEM: !env GITHUB_PRIVATE_PEM
27 | integrationId: !env GITHUB_INTEGRATION_ID
28 |
29 | webhook:
30 | secret: !env:list WEBHOOK_SECRET
31 |
32 | intree:
33 | provisionerId: !env PROVISIONER_ID
34 | workerType: !env WORKER_TYPE
35 |
36 | azure:
37 | accountId: !env AZURE_ACCOUNT_NAME
38 |
39 | monitoring:
40 | project: !env MONITORING_PROJECT
41 | enable: !env:bool MONITORING_ENABLE
42 |
43 | server:
44 | port: !env:number PORT
45 | env: 'development'
46 | forceSSL: false
47 | trustProxy: false
48 |
49 | pulse:
50 | hostname: !env PULSE_HOSTNAME
51 | username: !env PULSE_USERNAME
52 | password: !env PULSE_PASSWORD
53 | vhost: !env PULSE_VHOST
54 | namespace: !env PULSE_NAMESPACE
55 |
56 | aws:
57 | accessKeyId: !env AWS_ACCESS_KEY
58 | secretAccessKey: !env AWS_SECRET_KEY
59 | region: 'us-west-2'
60 | apiVersion: '2014-01-01'
61 |
62 | production:
63 | app:
64 | jobQueue: 'jobs'
65 | deprecatedResultStatusQueue: 'stat-result'
66 | deprecatedInitialStatusQueue: 'stat-init'
67 | resultStatusQueue: 'ch-result'
68 | initialStatusQueue: 'ch-init'
69 | botName: 'taskcluster[bot]'
70 |
71 | intree:
72 | provisionerId: 'aws-provisioner-v1'
73 | workerType: 'github-worker'
74 |
75 | server:
76 | env: 'production'
77 | forceSSL: !env:bool FORCE_SSL
78 | trustProxy: !env:bool TRUST_PROXY
79 |
80 | taskcluster:
81 | schedulerId: taskcluster-github
82 |
83 | pulse:
84 | namespace: taskcluster-github
85 |
86 | staging:
87 | app:
88 | jobQueue: 'jobs'
89 | deprecatedResultStatusQueue: 'deprecated-statuses'
90 | deprecatedInitialStatusQueue: 'deprecated-tasks'
91 | resultStatusQueue: 'checks-statuses'
92 | initialStatusQueue: 'checks-tasks'
93 | buildsTableName: 'TaskclusterGithubBuildsStaging'
94 | ownersDirectoryTableName: 'TaskclusterIntegrationOwnersStaging'
95 | name: 'tc-github-staging'
96 | statusContext: 'Taskcluster-Staging'
97 | botName: 'taskcluster-staging[bot]'
98 |
99 | intree:
100 | provisionerId: 'aws-provisioner-v1'
101 | workerType: 'github-worker'
102 |
103 | server:
104 | env: 'production'
105 | forceSSL: true
106 | # We trust the proxy on heroku, as the SSL end-point provided by heroku
107 | # is a proxy, so we have to trust it.
108 | trustProxy: true
109 |
110 | taskcluster:
111 | schedulerId: tc-gh-staging
112 |
113 | test:
114 | app:
115 | botName: 'magicalTCspirit'
116 | jobQueue: 'test-jobs'
117 | deprecatedResultStatusQueue: 'deprecated-statuses'
118 | deprecatedInitialStatusQueue: 'deprecated-tasks'
119 | resultStatusQueue: 'checks-statuses'
120 | initialStatusQueue: 'checks-tasks'
121 |
122 | webhook:
123 | secret: ['abc123', 'def456']
124 |
125 | azure:
126 | accountId: 'jungle'
127 |
128 | monitoring:
129 | enable: false
130 |
131 | server:
132 | port: 60415
133 |
134 | intree:
135 | provisionerId: 'dummy-provisioner'
136 | workerType: 'dummy-worker'
137 |
138 | taskcluster:
139 | schedulerId: tc-gh-devel
140 |
141 | pulse:
142 | namespace: 'taskcluster-fake'
143 | hostname: 'test-hostname'
144 | username: 'username'
145 | password: 'password'
146 | vhost: 'test-vhost'
147 |
--------------------------------------------------------------------------------
/docs/intro.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Using Taskcluster for Github Projects
3 | order: 10
4 | ---
5 |
6 | Taskcluster is easy to set up for simple CI cases and very expressive
7 | and powerful for more complex cases. It should fit just about any
8 | use case you can think of for CI on a Github project. It is used for
9 | projects as simple to test as calling `npm test` all the way up to
10 | the very complex set of tasks to perform in order to test and build
11 | the Firefox browser.
12 |
13 | The syntax offers an enormous amount of flexibility. [The quickstart tool](https://tools.taskcluster.net/quickstart/) should get you going quickly.
14 |
15 | The eventual goal of this project is to support all platforms and allow users to define workflows for testing, shipping, and landing patches from within their configurations. Currently, we offer mostly Linux support and have Windows available.
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "taskcluster-github",
3 | "version": "1.0.0",
4 | "private": true,
5 | "scripts": {
6 | "heroku-prebuild": "echo $SOURCE_VERSION > .git-version",
7 | "lint": "eslint src/*.js test/*.js",
8 | "test": "mocha test/*_test.js",
9 | "pretest": "yarn lint",
10 | "checkStaging": "node test/checkStaging.js"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/taskcluster/taskcluster-github.git"
15 | },
16 | "dependencies": {
17 | "@octokit/rest": "^16.0.5",
18 | "ajv": "^6.5.0",
19 | "azure-entities": "^5.1.0",
20 | "bluebird": "^3.5.1",
21 | "debug": "^3.1.0",
22 | "eslint-config-taskcluster": "^3.2.0",
23 | "js-yaml": "^3.10.0",
24 | "json-e": "^2.5.0",
25 | "json-parameterization": "^0.2.0",
26 | "jsonwebtoken": "^8.1.0",
27 | "lodash": "^4.11.1",
28 | "slugid": "^1.1.0",
29 | "taskcluster-client": "^11.0.0",
30 | "taskcluster-lib-api": "12.6.0",
31 | "taskcluster-lib-app": "^10.0.0",
32 | "taskcluster-lib-azure": "^10.0.0",
33 | "taskcluster-lib-docs": "^10.0.0",
34 | "taskcluster-lib-loader": "^10.0.0",
35 | "taskcluster-lib-monitor": "^11.1.1",
36 | "taskcluster-lib-pulse": "^11.1.0",
37 | "taskcluster-lib-urls": "^12.0.0",
38 | "taskcluster-lib-validate": "^12.0.0",
39 | "typed-env-config": "^2.0.0"
40 | },
41 | "engine-strict": true,
42 | "engines": {
43 | "node": "^8.0.0",
44 | "yarn": "^1.0.0"
45 | },
46 | "devDependencies": {
47 | "fs-extra": "^4.0.2",
48 | "got": "^8.0.0",
49 | "mocha": "^4.0.1",
50 | "sinon": "^4.1.2",
51 | "taskcluster-lib-references": "^1.3.0",
52 | "taskcluster-lib-testing": "^12.1.2"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/schemas/constants.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Identifier patterns, min and max length, these limitations are applied to
3 | # all common identifiers. It's not personal, it's just that without these
4 | # limitation, the identifiers won't be useful as routing keys in RabbitMQ
5 | # topic exchanges. Specifically, the length limitation and the fact that
6 | # identifiers can't contain dots `.` is critical.
7 | github-identifier-pattern: "^([a-zA-Z0-9-_%]*)$"
8 | github-identifier-min-length: 1
9 | github-identifier-max-length: 100
10 | github-guid-pattern: "^[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}$"
11 |
12 | identifier-pattern: "^([a-zA-Z0-9-_]*)$"
13 | identifier-min-length: 1
14 | identifier-max-length: 22
15 |
16 | github-installation-minimum: 0
17 | github-installation-maximum: 10000000000
18 |
19 | # Slugid pattern, for when-ever that is useful
20 | # Currently allow all v4 slugs, although we only generate nice slugs
21 | # See https://www.npmjs.com/package/slugid for more info
22 | slugid-pattern: "^[A-Za-z0-9_-]{8}[Q-T][A-Za-z0-9_-][CGKOSWaeimquy26-][A-Za-z0-9_-]{10}[AQgw]$"
23 |
24 | # Message version numbers
25 | message-version:
26 | description: "Message version"
27 | enum: [1]
28 |
29 | # Creation time of tasks
30 | created:
31 | title: "Created"
32 | description: "Creation time of task"
33 | type: "string"
34 | default: "{{ $fromNow }}"
35 |
36 | # Deadline of task
37 | deadline:
38 | title: "Deadline"
39 | description: "Deadline of the task, `pending` and `running` runs are resolved as **failed** if not resolved by other means before the deadline"
40 | type: "string"
41 | default: "{{ '1 day' | $fromNow }}"
42 |
--------------------------------------------------------------------------------
/schemas/v1/build-list.yml:
--------------------------------------------------------------------------------
1 | $schema: http://json-schema.org/draft-06/schema#
2 | title: "Builds Response"
3 | description: |
4 | A paginated list of builds
5 | type: object
6 | properties:
7 | continuationToken:
8 | type: string
9 | description: Passed back from Azure to allow us to page through long result sets.
10 | builds:
11 | type: array
12 | description: |
13 | A simple list of builds.
14 | items:
15 | title: Build
16 | type: object
17 | properties:
18 | organization:
19 | type: string
20 | minLength: {$const: github-identifier-min-length}
21 | maxLength: {$const: github-identifier-max-length}
22 | pattern: {$const: github-identifier-pattern}
23 | description: Github organization associated with the build.
24 | repository:
25 | type: string
26 | minLength: {$const: github-identifier-min-length}
27 | maxLength: {$const: github-identifier-max-length}
28 | pattern: {$const: github-identifier-pattern}
29 | description: Github repository associated with the build.
30 | sha:
31 | type: string
32 | minLength: 40
33 | maxLength: 40
34 | description: Github revision associated with the build.
35 | state:
36 | type: string
37 | enum: ['pending', 'success', 'error', 'failure']
38 | description: Github status associated with the build.
39 | taskGroupId:
40 | type: string
41 | pattern: {$const: slugid-pattern}
42 | description: Taskcluster task-group associated with the build.
43 | eventType:
44 | type: string
45 | description: Type of Github event that triggered the build (i.e. push, pull_request.opened).
46 | eventId:
47 | type: string
48 | description: |
49 | The GitHub webhook deliveryId. Extracted from the header 'X-GitHub-Delivery'
50 | oneOf:
51 | - pattern: {$const: github-guid-pattern}
52 | type: string
53 | title: Github GUID
54 | - enum: [Unknown]
55 | type: string
56 | title: Unknown Github GUID
57 | created:
58 | type: string
59 | format: date-time
60 | description: |
61 | The initial creation time of the build. This is when it became pending.
62 | updated:
63 | type: string
64 | format: date-time
65 | description: |
66 | The last updated of the build. If it is done, this is when it finished.
67 | additionalProperties: false
68 | required:
69 | - organization
70 | - repository
71 | - sha
72 | - state
73 | - taskGroupId
74 | - eventType
75 | - eventId
76 | - created
77 | - updated
78 | additionalProperties: false
79 | required:
80 | - builds
81 |
--------------------------------------------------------------------------------
/schemas/v1/create-comment.yml:
--------------------------------------------------------------------------------
1 | $schema: http://json-schema.org/draft-06/schema#
2 | title: "Create Comment Request"
3 | description: |
4 | Write a new comment on a GitHub Issue or Pull Request.
5 | Full specification on [GitHub docs](https://developer.github.com/v3/issues/comments/#create-a-comment)
6 | type: object
7 | properties:
8 | body:
9 | type: string
10 | description: The contents of the comment.
11 | additionalProperties: false
12 | required:
13 | - body
14 |
--------------------------------------------------------------------------------
/schemas/v1/create-status.yml:
--------------------------------------------------------------------------------
1 | $schema: http://json-schema.org/draft-06/schema#
2 | title: "Create Status Request"
3 | description: |
4 | Create a commit status on GitHub.
5 | Full specification on [GitHub docs](https://developer.github.com/v3/repos/statuses/#create-a-status)
6 | type: object
7 | properties:
8 | state:
9 | type: string
10 | enum: ['pending', 'success', 'error', 'failure']
11 | description: The state of the status.
12 | target_url:
13 | type: string
14 | description: The target URL to associate with this status. This URL will be linked from the GitHub UI to allow users to easily see the 'source' of the Status.
15 | description:
16 | type: string
17 | description: A short description of the status.
18 | context:
19 | type: string
20 | description: A string label to differentiate this status from the status of other systems.
21 | additionalProperties: false
22 | required:
23 | - state
24 |
--------------------------------------------------------------------------------
/schemas/v1/github-pull-request-message.yml:
--------------------------------------------------------------------------------
1 | $schema: http://json-schema.org/draft-06/schema#
2 | title: "GitHub Pull Request Message"
3 | description: |
4 | Message reporting that a GitHub pull request has occurred
5 | type: object
6 | properties:
7 | version: {$const: message-version}
8 | organization:
9 | description: |
10 | The GitHub `organization` which had an event.
11 | type: string
12 | minLength: {$const: github-identifier-min-length}
13 | maxLength: {$const: github-identifier-max-length}
14 | pattern: {$const: github-identifier-pattern}
15 | repository:
16 | description: |
17 | The GitHub `repository` which had an event.
18 | type: string
19 | minLength: {$const: github-identifier-min-length}
20 | maxLength: {$const: github-identifier-max-length}
21 | pattern: {$const: github-identifier-pattern}
22 | installationId:
23 | description: |
24 | The installation which had an event.
25 | type: integer
26 | minimum: {$const: github-installation-minimum}
27 | maximum: {$const: github-installation-maximum}
28 | action:
29 | description: |
30 | The GitHub `action` which triggered an event.
31 | enum: [assigned, unassigned, labeled, unlabeled, opened, edited, closed, reopened, synchronize, review_requested, review_request_removed]
32 | eventId:
33 | type: string
34 | description: |
35 | The GitHub webhook deliveryId. Extracted from the header 'X-GitHub-Delivery'
36 | pattern: {$const: github-guid-pattern}
37 | details:
38 | type: object
39 | description: |
40 | Metadata describing the pull request (for version 0)
41 | body:
42 | type: object
43 | description: |
44 | The raw body of github event (for version 1)
45 | tasks_for:
46 | type: string
47 | description: |
48 | The type of the event (for version 1)
49 | branch:
50 | type: string
51 | description: |
52 | The head ref of the event (for version 1)
53 | additionalProperties: false
54 | required:
55 | - version
56 | - organization
57 | - repository
58 | - action
59 | - installationId
60 | - eventId
61 | - body
62 | - tasks_for
63 | - branch
64 |
--------------------------------------------------------------------------------
/schemas/v1/github-push-message.yml:
--------------------------------------------------------------------------------
1 | $schema: http://json-schema.org/draft-06/schema#
2 | title: "GitHub Push Message"
3 | description: |
4 | Message reporting that a GitHub push has occurred
5 | type: object
6 | properties:
7 | version: {$const: message-version}
8 | organization:
9 | description: |
10 | The GitHub `organization` which had an event.
11 | type: string
12 | minLength: {$const: github-identifier-min-length}
13 | maxLength: {$const: github-identifier-max-length}
14 | pattern: {$const: github-identifier-pattern}
15 | repository:
16 | description: |
17 | The GitHub `repository` which had an event.
18 | type: string
19 | minLength: {$const: github-identifier-min-length}
20 | maxLength: {$const: github-identifier-max-length}
21 | pattern: {$const: github-identifier-pattern}
22 | installationId:
23 | description: |
24 | The installation which had an event.
25 | type: integer
26 | minLength: {$const: github-installation-minimum}
27 | maxLength: {$const: github-installation-maximum}
28 | eventId:
29 | type: string
30 | description: |
31 | The GitHub webhook deliveryId. Extracted from the header 'X-GitHub-Delivery'
32 | pattern: {$const: github-guid-pattern}
33 | details:
34 | type: object
35 | description: |
36 | Metadata describing the push (for version 0)
37 | body:
38 | type: object
39 | description: |
40 | The raw body of github event (for version 1)
41 | tasks_for:
42 | type: string
43 | description: |
44 | The type of the event (for version 1)
45 | branch:
46 | type: string
47 | description: |
48 | The head ref of the event (for version 1)
49 | additionalProperties: false
50 | required:
51 | - version
52 | - organization
53 | - repository
54 | - installationId
55 | - eventId
56 | - body
57 | - tasks_for
58 | - branch
59 |
--------------------------------------------------------------------------------
/schemas/v1/github-release-message.yml:
--------------------------------------------------------------------------------
1 | $schema: http://json-schema.org/draft-06/schema#
2 | title: "GitHub Release Message"
3 | description: |
4 | Message reporting that a GitHub release has occurred
5 | type: object
6 | properties:
7 | version: {$const: message-version}
8 | organization:
9 | description: |
10 | The GitHub `organization` which had an event.
11 | type: string
12 | minLength: {$const: github-identifier-min-length}
13 | maxLength: {$const: github-identifier-max-length}
14 | pattern: {$const: github-identifier-pattern}
15 | repository:
16 | description: |
17 | The GitHub `repository` which had an event.
18 | type: string
19 | minLength: {$const: github-identifier-min-length}
20 | maxLength: {$const: github-identifier-max-length}
21 | pattern: {$const: github-identifier-pattern}
22 | installationId:
23 | description: |
24 | The installation which had an event.
25 | type: integer
26 | minimum: {$const: github-installation-minimum}
27 | maximum: {$const: github-installation-maximum}
28 | eventId:
29 | type: string
30 | description: |
31 | The GitHub webhook deliveryId. Extracted from the header 'X-GitHub-Delivery'
32 | pattern: {$const: github-guid-pattern}
33 | details:
34 | type: object
35 | description: |
36 | Metadata describing the release (for version 0)
37 | body:
38 | type: object
39 | description: |
40 | The raw body of github event (for version 1)
41 | tasks_for:
42 | type: string
43 | description: |
44 | The type of the event (for version 1)
45 | branch:
46 | type: string
47 | description: |
48 | The head ref of the event (for version 1)
49 | additionalProperties: false
50 | required:
51 | - version
52 | - organization
53 | - repository
54 | - installationId
55 | - eventId
56 | - body
57 | - tasks_for
58 | - branch
--------------------------------------------------------------------------------
/schemas/v1/repository.yml:
--------------------------------------------------------------------------------
1 | $schema: http://json-schema.org/draft-06/schema#
2 | title: "Repository Response"
3 | description: |
4 | Any Taskcluster-specific Github repository information.
5 | type: object
6 | properties:
7 | installed:
8 | type: boolean
9 | description: |
10 | True if integration is installed, False otherwise.
11 | additionalProperties: false
12 | required:
13 | - installed
14 |
--------------------------------------------------------------------------------
/schemas/v1/task-group-creation-requested.yml:
--------------------------------------------------------------------------------
1 | $schema: http://json-schema.org/draft-06/schema#
2 | title: "Task Group Defined - Create Status"
3 | description: |
4 | Indicates that this service has created a new task group in response to a GitHub event.
5 | This message is for internal use only and should not be relied on for other purposes.
6 | Full specification on [GitHub docs](https://developer.github.com/v3/repos/statuses/#create-a-status)
7 | type: object
8 | properties:
9 | version: {$const: message-version}
10 | organization:
11 | description: |
12 | The GitHub `organization` which had an event.
13 | type: string
14 | minLength: {$const: github-identifier-min-length}
15 | maxLength: {$const: github-identifier-max-length}
16 | pattern: {$const: github-identifier-pattern}
17 | repository:
18 | description: |
19 | The GitHub `repository` which had an event.
20 | type: string
21 | minLength: {$const: github-identifier-min-length}
22 | maxLength: {$const: github-identifier-max-length}
23 | pattern: {$const: github-identifier-pattern}
24 | taskGroupId:
25 | type: string
26 | description: The id of the taskGroup that had been created.
27 | additionalProperties: false
28 | required:
29 | - taskGroupId
30 | - organization
31 | - repository
32 | - version
33 |
--------------------------------------------------------------------------------
/schemas/v1/taskcluster-github-config.v1.yml:
--------------------------------------------------------------------------------
1 | $schema: http://json-schema.org/draft-06/schema#
2 | title: ".taskcluster.yml format"
3 | description: |
4 | Description of a taskcluster.yml file v1, which may be used to generate a taskgraph
5 | and tasks.
6 | type: object
7 | properties:
8 | version:
9 | description: Version of the format of this file; must be 1
10 | enum: [1]
11 | type: integer
12 | policy:
13 | pullRequests:
14 | description: |
15 | Policy for creating tasks for pull requests. The effective policy is found in this property
16 | in the `.taskcluster.yml` file in the repository's default branch. See the documentation for
17 | detailed definition of the options.
18 | type: string
19 | enum:
20 | - public
21 | - collaborators
22 | reporting:
23 | description: Policy for reporting status of PR or a commit. If absent, Github Statuses API is used
24 | type: string
25 | enum:
26 | - checks-v1
27 | tasks:
28 | title: Task definition template"
29 | description: |
30 | Definitions of tasks that can be scheduled. Rendered with JSON-e
31 | default: []
32 | oneOf:
33 | - type: array
34 | description: Each element of this should evaluate to a task definition via json-e
35 | items:
36 | type: object
37 | additionalProperties: true
38 | - type: object
39 | description: This must evaluate to an array via json-e i.e. `$flatten`
40 | additionalProperties: true
41 | additionalProperties: false
42 | required:
43 | - version
44 |
--------------------------------------------------------------------------------
/schemas/v1/taskcluster-github-config.yml:
--------------------------------------------------------------------------------
1 | $schema: http://json-schema.org/draft-06/schema#
2 | title: ".taskcluster.yml format"
3 | description: |
4 | Description of a taskcluster.yml file v0, which may be used to generate a taskgraph
5 | and tasks.
6 | type: object
7 | properties:
8 | version:
9 | description: ".taskcluster.yml version"
10 | enum: [0]
11 | type: integer
12 | metadata:
13 | description: "Required task graph metadata"
14 | type: object
15 | properties:
16 | name:
17 | title: "Name"
18 | description: |
19 | Human readable name of task, used to very briefly given an idea about
20 | what the task does.
21 | type: string
22 | maxLength: 255
23 | description:
24 | title: "Description"
25 | description: |
26 | Human readable description of the task, please **explain** what the
27 | task does. A few lines of documentation is not going to hurt you.
28 | type: string
29 | maxLength: 32768
30 | owner:
31 | title: "Owner"
32 | description: |
33 | E-mail of person who caused this task, e.g. the person who did
34 | `hg push`. The person we should contact to ask why this task is here.
35 | type: string
36 | maxLength: 255
37 | source:
38 | title: "Source"
39 | description: |
40 | Link to source of this task, should specify a file, revision and
41 | repository. This should be place someone can go an do a git/hg blame
42 | to who came up with recipe for this task.
43 | type: string
44 | maxLength: 4096
45 | additionalProperties: false
46 | required:
47 | - name
48 | - description
49 | - owner
50 | - source
51 | allowPullRequests:
52 | description: |
53 | Policy for creating tasks for pull requests. The effective policy is found in this property
54 | in the `.taskcluster.yml` file in the repository's default branch. See the documentation for
55 | detailed definition of the options.
56 | type: string
57 | enum:
58 | - public
59 | - collaborators
60 | tasks:
61 | type: array
62 | default: []
63 | items:
64 | title: "Task Definition"
65 | description: |
66 | Definition of a task that can be scheduled
67 | type: object
68 | properties:
69 | created: {$const: created}
70 | deadline: {$const: deadline}
71 | additionalProperties: true
72 | additionalProperties: false
73 | required:
74 | - version
75 |
--------------------------------------------------------------------------------
/src/data.js:
--------------------------------------------------------------------------------
1 | const Entity = require('azure-entities');
2 |
3 | /**
4 | * Entity for tracking which task-groups are associated
5 | * with which org/repo/sha, etc.
6 | *
7 | */
8 | module.exports.Builds = Entity.configure({
9 | version: 1,
10 | partitionKey: Entity.keys.StringKey('taskGroupId'),
11 | rowKey: Entity.keys.ConstantKey('taskGroupId'),
12 | properties: {
13 | organization: Entity.types.String,
14 | repository: Entity.types.String,
15 | sha: Entity.types.String,
16 | taskGroupId: Entity.types.String,
17 | state: Entity.types.String,
18 | created: Entity.types.Date,
19 | updated: Entity.types.Date,
20 | },
21 | }).configure({
22 | version: 2,
23 | properties: {
24 | organization: Entity.types.String,
25 | repository: Entity.types.String,
26 | sha: Entity.types.String,
27 | taskGroupId: Entity.types.String,
28 | state: Entity.types.String,
29 | created: Entity.types.Date,
30 | updated: Entity.types.Date,
31 | // GitHub installation ID that comes from the webhook
32 | // Needed for authentication in statusHandler
33 | installationId: Entity.types.Number,
34 | },
35 | migrate: function(item) {
36 | item.installationId = 0;
37 | return item;
38 | },
39 | }).configure({
40 | version: 3,
41 | properties: {
42 | organization: Entity.types.String,
43 | repository: Entity.types.String,
44 | sha: Entity.types.String,
45 | taskGroupId: Entity.types.String,
46 | state: Entity.types.String,
47 | created: Entity.types.Date,
48 | updated: Entity.types.Date,
49 | installationId: Entity.types.Number,
50 | eventType: Entity.types.String,
51 | },
52 | migrate: function(item) {
53 | item.eventType = 'Unknown Event';
54 | return item;
55 | },
56 | }).configure({
57 | version: 4,
58 | properties: {
59 | organization: Entity.types.String,
60 | repository: Entity.types.String,
61 | sha: Entity.types.String,
62 | taskGroupId: Entity.types.String,
63 | state: Entity.types.String,
64 | created: Entity.types.Date,
65 | updated: Entity.types.Date,
66 | installationId: Entity.types.Number,
67 | eventType: Entity.types.String,
68 | eventId: Entity.types.String,
69 | },
70 | migrate: function(item) {
71 | item.Id = 'Unknown';
72 | return item;
73 | },
74 | }).configure({
75 | version: 5,
76 | properties: {
77 | organization: Entity.types.String,
78 | repository: Entity.types.String,
79 | sha: Entity.types.String,
80 | taskGroupId: Entity.types.String,
81 | state: Entity.types.String,
82 | created: Entity.types.Date,
83 | updated: Entity.types.Date,
84 | installationId: Entity.types.Number,
85 | eventType: Entity.types.String,
86 | eventId: Entity.types.String,
87 | },
88 | migrate: function(item) {
89 | // Delete Id because it was mistakenly set in the last migration
90 | delete item.Id; // Just in case the migrations are chained
91 | item.eventId = item.eventId || 'Unknown';
92 | return item;
93 | },
94 | });
95 |
96 | module.exports.OwnersDirectory = Entity.configure({
97 | version: 1,
98 | partitionKey: Entity.keys.StringKey('owner'),
99 | rowKey: Entity.keys.ConstantKey('someConstant'),
100 | properties: {
101 | installationId: Entity.types.Number,
102 | owner: Entity.types.String,
103 | },
104 | });
105 |
106 | module.exports.CheckRuns = Entity.configure({
107 | version: 1,
108 | partitionKey: Entity.keys.StringKey('taskGroupId'),
109 | rowKey: Entity.keys.StringKey('taskId'),
110 | properties: {
111 | taskGroupId: Entity.types.String,
112 | taskId: Entity.types.String,
113 | checkSuiteId: Entity.types.String,
114 | checkRunId: Entity.types.String,
115 | },
116 | });
117 |
--------------------------------------------------------------------------------
/src/exchanges.js:
--------------------------------------------------------------------------------
1 | const {Exchanges} = require('taskcluster-lib-pulse');
2 | const _ = require('lodash');
3 | const assert = require('assert');
4 |
5 | /** Build common routing key construct for `exchanges.declare` */
6 | const commonRoutingKey = function(options) {
7 | options = options || {};
8 | let routingKey = [
9 | {
10 | name: 'routingKeyKind',
11 | summary: 'Identifier for the routing-key kind. This is ' +
12 | 'always `"primary"` for the formalized routing key.',
13 | constant: 'primary',
14 | required: true,
15 | }, {
16 | name: 'organization',
17 | summary: 'The GitHub `organization` which had an event. ' +
18 | 'All periods have been replaced by % - such that ' +
19 | 'foo.bar becomes foo%bar - and all other special ' +
20 | 'characters aside from - and _ have been stripped.',
21 | maxSize: 100,
22 | required: true,
23 | }, {
24 | name: 'repository',
25 | summary: 'The GitHub `repository` which had an event.' +
26 | 'All periods have been replaced by % - such that ' +
27 | 'foo.bar becomes foo%bar - and all other special ' +
28 | 'characters aside from - and _ have been stripped.',
29 | maxSize: 100,
30 | required: true,
31 | },
32 | ];
33 | if (options.hasActions) {
34 | routingKey.push({
35 | name: 'action',
36 | summary: 'The GitHub `action` which triggered an event. ' +
37 | 'See for possible values see the payload actions ' +
38 | 'property.',
39 | maxSize: 22,
40 | required: true,
41 | });
42 | }
43 | return routingKey;
44 | };
45 |
46 | const commonMessageBuilder = function(msg) {
47 | msg.version = 1;
48 | return msg;
49 | };
50 |
51 | /** Build list of routing keys to CC */
52 | const commonCCBuilder = (message, routes) => {
53 | assert(Array.isArray(routes), 'Routes must be an array');
54 | return routes.map(route => 'route.' + route);
55 | };
56 |
57 | /** Declaration of exchanges offered by the github */
58 | let exchanges = new Exchanges({
59 | serviceName: 'github',
60 | projectName: 'taskcluster-github',
61 | apiVersion: 'v1',
62 | title: 'Taskcluster-Github Exchanges',
63 | description: [
64 | 'The github service publishes a pulse',
65 | 'message for supported github events, translating Github webhook',
66 | 'events into pulse messages.',
67 | '',
68 | 'This document describes the exchange offered by the taskcluster',
69 | 'github service',
70 | ].join('\n'),
71 | });
72 |
73 | /** pull request exchange */
74 | exchanges.declare({
75 | exchange: 'pull-request',
76 | name: 'pullRequest',
77 | title: 'GitHub Pull Request Event',
78 | description: [
79 | 'When a GitHub pull request event is posted it will be broadcast on this',
80 | 'exchange with the designated `organization` and `repository`',
81 | 'in the routing-key along with event specific metadata in the payload.',
82 | ].join('\n'),
83 | routingKey: commonRoutingKey({hasActions: true}),
84 | schema: 'github-pull-request-message.yml',
85 | messageBuilder: commonMessageBuilder,
86 | routingKeyBuilder: msg => _.pick(msg, 'organization', 'repository', 'action'),
87 | CCBuilder: () => [],
88 | });
89 |
90 | /** push exchange */
91 | exchanges.declare({
92 | exchange: 'push',
93 | name: 'push',
94 | title: 'GitHub push Event',
95 | description: [
96 | 'When a GitHub push event is posted it will be broadcast on this',
97 | 'exchange with the designated `organization` and `repository`',
98 | 'in the routing-key along with event specific metadata in the payload.',
99 | ].join('\n'),
100 | routingKey: commonRoutingKey(),
101 | schema: 'github-push-message.yml',
102 | messageBuilder: commonMessageBuilder,
103 | routingKeyBuilder: msg => _.pick(msg, 'organization', 'repository'),
104 | CCBuilder: () => [],
105 | });
106 |
107 | /** release exchange */
108 | exchanges.declare({
109 | exchange: 'release',
110 | name: 'release',
111 | title: 'GitHub release Event',
112 | description: [
113 | 'When a GitHub release event is posted it will be broadcast on this',
114 | 'exchange with the designated `organization` and `repository`',
115 | 'in the routing-key along with event specific metadata in the payload.',
116 | ].join('\n'),
117 | routingKey: commonRoutingKey(),
118 | schema: 'github-release-message.yml',
119 | messageBuilder: commonMessageBuilder,
120 | routingKeyBuilder: msg => _.pick(msg, 'organization', 'repository'),
121 | CCBuilder: () => [],
122 | });
123 |
124 | /** task group exchange */
125 | exchanges.declare({
126 | exchange: 'task-group-creation-requested',
127 | name: 'taskGroupCreationRequested',
128 | title: 'tc-gh requested the Queue service to create all the tasks in a group',
129 | description: [
130 | 'supposed to signal that taskCreate API has been called for every task in the task group',
131 | 'for this particular repo and this particular organization',
132 | 'currently used for creating initial status indicators in GitHub UI using Statuses API.',
133 | 'This particular exchange can also be bound to RabbitMQ queues by custom routes - for that,',
134 | 'Pass in the array of routes as a second argument to the publish method. Currently, we do',
135 | 'use the statuses routes to bind the handler that creates the initial status.',
136 | ].join('\n'),
137 | routingKey: commonRoutingKey(),
138 | schema: 'task-group-creation-requested.yml',
139 | messageBuilder: commonMessageBuilder,
140 | routingKeyBuilder: msg => _.pick(msg, 'organization', 'repository'),
141 | CCBuilder: commonCCBuilder,
142 | });
143 |
144 | // Export exchanges
145 | module.exports = exchanges;
146 |
--------------------------------------------------------------------------------
/src/github-auth.js:
--------------------------------------------------------------------------------
1 | const debug = require('debug')('taskcluster-github:github-auth');
2 | const Promise = require('bluebird');
3 | const jwt = require('jsonwebtoken');
4 |
5 | const retryPlugin = (octokit, options) => {
6 |
7 | const retries = 7;
8 | const baseBackoff = 100;
9 | const sleep = timeout => new Promise(resolve => setTimeout(resolve, timeout));
10 |
11 | octokit.hook.wrap('request', async (request, options) => {
12 | let response;
13 | for (let attempt = 1; attempt <= retries; attempt++) {
14 | try {
15 | return await request(options);
16 | } catch (err) {
17 | if (attempt === retries || err.name !== 'HttpError' || err.status !== 404) {
18 | throw err;
19 | }
20 | debug(`404 getting retried for eventual consistency. attempt: ${attempt}`);
21 | await sleep(baseBackoff * Math.pow(2, attempt));
22 | }
23 | }
24 | });
25 | };
26 | const Github = require('@octokit/rest').plugin([retryPlugin]);
27 |
28 | module.exports = async ({cfg}) => {
29 | let github = new Github({
30 | promise: Promise,
31 | });
32 |
33 | let setupToken = _ => {
34 | let inteToken = jwt.sign(
35 | {iss: cfg.github.credentials.integrationId},
36 | cfg.github.credentials.privatePEM,
37 | {algorithm: 'RS256', expiresIn: '1m'},
38 | );
39 | try {
40 | github.authenticate({type: 'app', token: inteToken});
41 | } catch (e) {
42 | debug('Authentication as app failed!');
43 | throw e;
44 | }
45 | return github;
46 | };
47 |
48 | // This object insures that the authentication is delayed until we need it.
49 | // Also, the authentication happens not just once in the beginning, but for each request.
50 | return {
51 | getIntegrationGithub: async _ => {
52 | setupToken();
53 | return github;
54 | },
55 | getInstallationGithub: async (inst_id) => {
56 | setupToken();
57 | // Authenticating as installation
58 | var instaToken = (await github.apps.createInstallationToken({
59 | installation_id: inst_id,
60 | })).data;
61 | let gh = new Github({promise: Promise});
62 | try {
63 | gh.authenticate({type: 'token', token: instaToken.token});
64 | debug(`Authenticated as installation: ${inst_id}`);
65 | } catch (e) {
66 | debug('Authentication as app failed!');
67 | throw e;
68 | }
69 | return gh;
70 | },
71 | };
72 | };
73 |
--------------------------------------------------------------------------------
/src/intree.js:
--------------------------------------------------------------------------------
1 | const debug = require('debug')('taskcluster-github:intree');
2 | const yaml = require('js-yaml');
3 | const TcYaml = require('./tc-yaml');
4 |
5 | module.exports = {};
6 |
7 | /**
8 | * Returns a function that merges an existing taskcluster github config with
9 | * a pull request message's payload to generate a full task graph config.
10 | * params {
11 | * config: '...', A yaml string
12 | * payload: {}, GitHub WebHook message payload
13 | * schema: url, Url to the taskcluster config schema
14 | * }
15 | **/
16 | module.exports.setup = async function({cfg, schemaset}) {
17 | const validate = await schemaset.validator(cfg.taskcluster.rootUrl);
18 |
19 | return function({config, payload, schema}) {
20 | config = yaml.safeLoad(config);
21 | const version = config.version;
22 |
23 | const errors = validate(config, schema[version]);
24 | if (errors) {
25 | throw new Error(errors);
26 | }
27 | debug(`intree config for ${payload.organization}/${payload.repository} matches valid schema.`);
28 |
29 | // We need to toss out the config version number; it's the only
30 | // field that's not also in graph/task definitions
31 | delete config.version;
32 |
33 | const tcyaml = TcYaml.instantiate(version);
34 |
35 | // Perform parameter substitutions. This happens after verification
36 | // because templating may change with schema version, and parameter
37 | // functions are used as default values for some fields.
38 | config = tcyaml.substituteParameters(config, cfg, payload);
39 |
40 | try {
41 | // Compile individual tasks, filtering any that are not intended
42 | // for the current github event type. Append taskGroupId while
43 | // we're at it.
44 | return tcyaml.compileTasks(config, cfg, payload, new Date().toJSON());
45 | } catch (e) {
46 | debug('Error processing tasks!');
47 | throw e;
48 | }
49 | };
50 | };
51 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | const builder = require('./api');
2 | const exchanges = require('./exchanges');
3 | const Handlers = require('./handlers');
4 | const Intree = require('./intree');
5 | const data = require('./data');
6 | const Promise = require('bluebird');
7 | const Ajv = require('ajv');
8 | const config = require('typed-env-config');
9 | const monitor = require('taskcluster-lib-monitor');
10 | const SchemaSet = require('taskcluster-lib-validate');
11 | const loader = require('taskcluster-lib-loader');
12 | const docs = require('taskcluster-lib-docs');
13 | const App = require('taskcluster-lib-app');
14 | const {sasCredentials} = require('taskcluster-lib-azure');
15 | const githubAuth = require('./github-auth');
16 | const {Client, pulseCredentials} = require('taskcluster-lib-pulse');
17 |
18 | const load = loader({
19 | cfg: {
20 | requires: ['profile'],
21 | setup: ({profile}) => config({profile}),
22 | },
23 |
24 | monitor: {
25 | requires: ['process', 'profile', 'cfg'],
26 | setup: ({process, profile, cfg}) => monitor({
27 | rootUrl: cfg.taskcluster.rootUrl,
28 | projectName: cfg.monitoring.project || 'taskcluster-github',
29 | enable: cfg.monitoring.enable,
30 | credentials: cfg.taskcluster.credentials,
31 | mock: profile === 'test',
32 | process,
33 | }),
34 | },
35 |
36 | schemaset: {
37 | requires: ['cfg'],
38 | setup: ({cfg}) => new SchemaSet({
39 | serviceName: 'github',
40 | publish: cfg.app.publishMetaData,
41 | aws: cfg.aws,
42 | }),
43 | },
44 |
45 | reference: {
46 | requires: [],
47 | setup: () => exchanges.reference(),
48 | },
49 |
50 | ajv: {
51 | requires: [],
52 | setup: () => new Ajv(),
53 | },
54 |
55 | docs: {
56 | requires: ['cfg', 'schemaset', 'reference'],
57 | setup: ({cfg, schemaset, reference}) => docs.documenter({
58 | credentials: cfg.taskcluster.credentials,
59 | tier: 'integrations',
60 | schemaset: schemaset,
61 | publish: cfg.app.publishMetaData,
62 | references: [
63 | {name: 'api', reference: builder.reference()},
64 | {name: 'events', reference: reference},
65 | ],
66 | }),
67 | },
68 |
69 | writeDocs: {
70 | requires: ['docs'],
71 | setup: ({docs}) => docs.write({docsDir: process.env['DOCS_OUTPUT_DIR']}),
72 | },
73 |
74 | pulseClient: {
75 | requires: ['cfg', 'monitor'],
76 | setup: ({cfg, monitor}) => {
77 | return new Client({
78 | namespace: cfg.pulse.namespace,
79 | monitor,
80 | credentials: pulseCredentials(cfg.pulse),
81 | });
82 | },
83 | },
84 |
85 | publisher: {
86 | requires: ['cfg', 'schemaset', 'pulseClient'],
87 | setup: async ({cfg, pulseClient, schemaset}) => await exchanges.publisher({
88 | rootUrl: cfg.taskcluster.rootUrl,
89 | schemaset,
90 | client: pulseClient,
91 | publish: cfg.app.publishMetaData,
92 | aws: cfg.aws,
93 | }),
94 | },
95 |
96 | github: {
97 | requires: ['cfg'],
98 | setup: ({cfg}) => githubAuth({cfg}),
99 | },
100 |
101 | intree: {
102 | requires: ['cfg', 'schemaset'],
103 | setup: ({cfg, schemaset}) => Intree.setup({cfg, schemaset}),
104 | },
105 |
106 | Builds: {
107 | requires: ['cfg', 'monitor'],
108 | setup: async ({cfg, monitor}) => data.Builds.setup({
109 | tableName: cfg.app.buildsTableName,
110 | credentials: sasCredentials({
111 | accountId: cfg.azure.accountId,
112 | tableName: cfg.app.buildsTableName,
113 | rootUrl: cfg.taskcluster.rootUrl,
114 | credentials: cfg.taskcluster.credentials,
115 | }),
116 | monitor: monitor.prefix('table.builds'),
117 | }),
118 | },
119 |
120 | OwnersDirectory: {
121 | requires: ['cfg', 'monitor'],
122 | setup: async ({cfg, monitor}) => data.OwnersDirectory.setup({
123 | tableName: cfg.app.ownersDirectoryTableName,
124 | credentials: sasCredentials({
125 | accountId: cfg.azure.accountId,
126 | tableName: cfg.app.ownersDirectoryTableName,
127 | rootUrl: cfg.taskcluster.rootUrl,
128 | credentials: cfg.taskcluster.credentials,
129 | }),
130 | monitor: monitor.prefix('table.ownersdirectory'),
131 | }),
132 | },
133 |
134 | CheckRuns: {
135 | requires: ['cfg', 'monitor'],
136 | setup: async ({cfg, monitor}) => data.CheckRuns.setup({
137 | tableName: cfg.app.checkRunsTableName,
138 | credentials: sasCredentials({
139 | accountId: cfg.azure.accountId,
140 | tableName: cfg.app.checkRunsTableName,
141 | rootUrl: cfg.taskcluster.rootUrl,
142 | credentials: cfg.taskcluster.credentials,
143 | }),
144 | monitor: monitor.prefix('table.checkruns'),
145 | }),
146 | },
147 |
148 | api: {
149 | requires: [
150 | 'cfg', 'monitor', 'schemaset', 'github', 'publisher', 'Builds',
151 | 'OwnersDirectory', 'ajv'],
152 | setup: ({cfg, monitor, schemaset, github, publisher, Builds,
153 | OwnersDirectory, ajv}) => builder.build({
154 | rootUrl: cfg.taskcluster.rootUrl,
155 | context: {
156 | publisher,
157 | cfg,
158 | github,
159 | Builds,
160 | OwnersDirectory,
161 | ajv,
162 | monitor: monitor.prefix('api-context'),
163 | },
164 | publish: cfg.app.publishMetaData,
165 | aws: cfg.aws,
166 | monitor: monitor.prefix('api'),
167 | schemaset,
168 | }),
169 | },
170 |
171 | server: {
172 | requires: ['cfg', 'api', 'docs'],
173 | setup: ({cfg, api, docs}) => App({
174 | port: cfg.server.port,
175 | env: cfg.server.env,
176 | forceSSL: cfg.server.forceSSL,
177 | trustProxy: cfg.server.trustProxy,
178 | apis: [api],
179 | }),
180 | },
181 |
182 | syncInstallations: {
183 | requires: ['github', 'OwnersDirectory', 'monitor'],
184 | setup: ({github, OwnersDirectory, monitor}) => {
185 | return monitor.oneShot('syncInstallations', async () => {
186 | const gh = await github.getIntegrationGithub();
187 | const installations = (await gh.apps.getInstallations({})).data;
188 | await Promise.map(installations, inst => {
189 | return OwnersDirectory.create({
190 | installationId: inst.id,
191 | owner: inst.account.login,
192 | }, true);
193 | });
194 | });
195 | },
196 | },
197 |
198 | handlers: {
199 | requires: [
200 | 'cfg',
201 | 'github',
202 | 'monitor',
203 | 'intree',
204 | 'schemaset',
205 | 'reference',
206 | 'Builds',
207 | 'pulseClient',
208 | 'publisher',
209 | 'CheckRuns',
210 | ],
211 | setup: async ({cfg, github, monitor, intree, schemaset, reference, Builds, pulseClient, publisher, CheckRuns}) =>
212 | new Handlers({
213 | rootUrl: cfg.taskcluster.rootUrl,
214 | credentials: cfg.pulse,
215 | monitor: monitor.prefix('handlers'),
216 | intree,
217 | reference,
218 | jobQueueName: cfg.app.jobQueue,
219 | deprecatedResultStatusQueueName: cfg.app.deprecatedResultStatusQueue,
220 | deprecatedInitialStatusQueueName: cfg.app.deprecatedInitialStatusQueue,
221 | resultStatusQueueName: cfg.app.resultStatusQueue,
222 | initialStatusQueueName: cfg.app.initialStatusQueue,
223 | context: {cfg, github, schemaset, Builds, CheckRuns, publisher},
224 | pulseClient,
225 | }),
226 | },
227 |
228 | worker: {
229 | requires: ['handlers'],
230 | setup: async ({handlers}) => handlers.setup(),
231 | },
232 | }, ['profile', 'process']);
233 |
234 | if (!module.parent) {
235 | load(process.argv[2], {
236 | process: process.argv[2],
237 | profile: process.env.NODE_ENV,
238 | }).catch(err => {
239 | console.log(err.stack || err);
240 | process.exit(1);
241 | });
242 | }
243 |
244 | // Export load for tests
245 | module.exports = load;
246 |
--------------------------------------------------------------------------------
/src/pr-allowed.js:
--------------------------------------------------------------------------------
1 | const yaml = require('js-yaml');
2 |
3 | const DEFAULT_POLICY = 'collaborators';
4 |
5 | async function prAllowed(options) {
6 | switch (await getRepoPolicy(options)) {
7 | case 'collaborators':
8 | return await isCollaborator(options);
9 |
10 | case 'public':
11 | return true;
12 |
13 | default:
14 | return false;
15 | }
16 | }
17 |
18 | /**
19 | * Get the repo's "policy" on pull requests, by fetching .taskcluster.yml from the default
20 | * branch, parsing it, and looking at its `allowPullRequests`.
21 | */
22 | async function getRepoPolicy({login, organization, repository, instGithub, debug}) {
23 | // first, get the repository's default branch
24 | let repoInfo = (await instGithub.repos.get({owner: organization, repo: repository})).data;
25 | let branch = repoInfo.default_branch;
26 |
27 | // load .taskcluster.yml from that branch
28 | let taskclusterYml;
29 | try {
30 | let content = await instGithub.repos.getContents({
31 | owner: organization,
32 | repo: repository,
33 | path: '.taskcluster.yml',
34 | ref: branch,
35 | });
36 | taskclusterYml = yaml.safeLoad(new Buffer(content.data.content, 'base64').toString());
37 | } catch (e) {
38 | if (e.code === 404) {
39 | return DEFAULT_POLICY;
40 | }
41 | throw e;
42 | }
43 |
44 | if (!taskclusterYml.version || taskclusterYml.version === 0) {
45 | // consult its `allowPullRequests` field
46 | return taskclusterYml['allowPullRequests'] || DEFAULT_POLICY;
47 | } else if (taskclusterYml.version === 1) {
48 | if (taskclusterYml.policy) {
49 | return taskclusterYml.policy.pullRequests || DEFAULT_POLICY;
50 | }
51 | }
52 |
53 | return DEFAULT_POLICY;
54 | }
55 |
56 | async function isCollaborator({login, organization, repository, sha, instGithub, debug}) {
57 | // GithubAPI's collaborator check returns an error if a user isn't
58 | // listed as a collaborator.
59 | try {
60 | await instGithub.repos.checkCollaborator({
61 | owner: organization,
62 | repo: repository,
63 | username: login,
64 | });
65 | // No error, the user is a collaborator
66 | debug(`Checking collaborator: ${login} is a collaborator on ${organization}/${repository}: True!`);
67 | return true;
68 | } catch (e) {
69 | // a 404 means the user is not a collaborator
70 | if (e.code !== 404) {
71 | throw e;
72 | }
73 | }
74 | return false;
75 | }
76 |
77 | module.exports = prAllowed;
78 |
79 | // for testing..
80 | module.exports.getRepoPolicy = getRepoPolicy;
81 | module.exports.isCollaborator = isCollaborator;
82 |
--------------------------------------------------------------------------------
/test/api_test.js:
--------------------------------------------------------------------------------
1 | const helper = require('./helper');
2 | const assert = require('assert');
3 | const _ = require('lodash');
4 | const got = require('got');
5 |
6 | /**
7 | * Tests of endpoints in the api _other than_
8 | * the github webhook endpoint which is tested
9 | * in webhook_test.js
10 | */
11 | helper.secrets.mockSuite('api_test.js', ['taskcluster'], function(mock, skipping) {
12 | helper.withEntities(mock, skipping);
13 | helper.withFakeGithub(mock, skipping);
14 | helper.withServer(mock, skipping);
15 |
16 | suiteSetup(async function() {
17 | await helper.Builds.create({
18 | organization: 'abc123',
19 | repository: 'def456',
20 | sha: '7650871208002a13ba35cf232c0e30d2c3d64783',
21 | state: 'pending',
22 | taskGroupId: 'biizERCQQwi9ZS_WkCSjXQ',
23 | created: new Date(),
24 | updated: new Date(),
25 | installationId: 1,
26 | eventType: 'push',
27 | eventId: '26370a80-ed65-11e6-8f4c-80082678482d',
28 | });
29 | await helper.Builds.create({
30 | organization: 'ghi789',
31 | repository: 'jkl101112',
32 | sha: '8650871208002a13ba35cf232c0e30d2c3d64783',
33 | state: 'success',
34 | taskGroupId: 'aiizERCQQwi9ZS_WkCSjXQ',
35 | created: new Date(),
36 | updated: new Date(),
37 | installationId: 1,
38 | eventType: 'push',
39 | eventId: '26370a80-ed65-11e6-8f4c-80082678482d',
40 | });
41 | await helper.Builds.create({
42 | organization: 'abc123',
43 | repository: 'xyz',
44 | sha: 'x650871208002a13ba35cf232c0e30d2c3d64783',
45 | state: 'pending',
46 | taskGroupId: 'qiizERCQQwi9ZS_WkCSjXQ',
47 | created: new Date(),
48 | updated: new Date(),
49 | installationId: 1,
50 | eventType: 'push',
51 | eventId: '26370a80-ed65-11e6-8f4c-80082678482d',
52 | });
53 | await helper.Builds.create({
54 | organization: 'abc123',
55 | repository: 'xyz',
56 | sha: 'y650871208002a13ba35cf232c0e30d2c3d64783',
57 | state: 'pending',
58 | taskGroupId: 'ziizERCQQwi9ZS_WkCSjXQ',
59 | created: new Date(),
60 | updated: new Date(),
61 | installationId: 1,
62 | eventType: 'push',
63 | eventId: 'Unknown',
64 | });
65 |
66 | await helper.OwnersDirectory.create({
67 | installationId: 9090,
68 | owner: 'abc123',
69 | });
70 |
71 | await helper.OwnersDirectory.create({
72 | installationId: 9091,
73 | owner: 'qwerty',
74 | });
75 | });
76 |
77 | setup(async function() {
78 | github = await helper.load('github');
79 | github.inst(9090).setRepositories('coolRepo', 'anotherCoolRepo', 'awesomeRepo', 'nonTCGHRepo');
80 | github.inst(9090).setStatuses({
81 | owner: 'abc123',
82 | repo: 'coolRepo',
83 | ref: 'master',
84 | info: [{creator: {id: 12345}, state: 'success'}, {creator: {id: 55555}, state: 'failure'}],
85 | });
86 | github.inst(9090).setStatuses({
87 | owner: 'abc123',
88 | repo: 'awesomeRepo',
89 | ref: 'master',
90 | info: [
91 | {creator: {id: 12345}, state: 'success'},
92 | {creator: {id: 55555}, state: 'success', target_url: 'Wonderland'},
93 | ],
94 | });
95 | github.inst(9090).setStatuses({
96 | owner: 'abc123',
97 | repo: 'nonTCGHRepo',
98 | ref: 'master',
99 | info: [{creator: {id: 123345}, state: 'success'}],
100 | });
101 | github.inst(9090).setUser({id: 55555, email: 'noreply@github.com', username: 'magicalTCspirit'});
102 | });
103 |
104 | test('all builds', async function() {
105 | let builds = await helper.apiClient.builds();
106 | assert.equal(builds.builds.length, 4);
107 | builds.builds = _.orderBy(builds.builds, ['organization', 'repository']);
108 | assert.equal(builds.builds[0].organization, 'abc123');
109 | assert.equal(builds.builds[1].organization, 'abc123');
110 | assert.equal(builds.builds[2].organization, 'abc123');
111 | assert.equal(builds.builds[3].organization, 'ghi789');
112 | });
113 |
114 | test('org builds', async function() {
115 | let builds = await helper.apiClient.builds({organization: 'abc123'});
116 | assert.equal(builds.builds.length, 3);
117 | builds.builds = _.orderBy(builds.builds, ['organization', 'repository']);
118 | assert.equal(builds.builds[0].organization, 'abc123');
119 | });
120 |
121 | test('repo builds', async function() {
122 | let builds = await helper.apiClient.builds({organization: 'abc123', repository: 'xyz'});
123 | assert.equal(builds.builds.length, 2);
124 | builds.builds = _.orderBy(builds.builds, ['organization', 'repository']);
125 | assert.equal(builds.builds[0].organization, 'abc123');
126 | assert.equal(builds.builds[0].repository, 'xyz');
127 | });
128 |
129 | test('sha builds', async function() {
130 | let builds = await helper.apiClient.builds({
131 | organization: 'abc123',
132 | repository: 'xyz',
133 | sha: 'y650871208002a13ba35cf232c0e30d2c3d64783',
134 | });
135 | assert.equal(builds.builds.length, 1);
136 | builds.builds = _.orderBy(builds.builds, ['organization', 'repository']);
137 | assert.equal(builds.builds[0].organization, 'abc123');
138 | assert.equal(builds.builds[0].repository, 'xyz');
139 | assert.equal(builds.builds[0].sha, 'y650871208002a13ba35cf232c0e30d2c3d64783');
140 | });
141 |
142 | test('integration installation', async function() {
143 | let result = await helper.apiClient.repository('abc123', 'coolRepo');
144 | assert.deepEqual(result, {installed: true});
145 | result = await helper.apiClient.repository('abc123', 'unknownRepo');
146 | assert.deepEqual(result, {installed: false});
147 | result = await helper.apiClient.repository('unknownOwner', 'unknownRepo');
148 | assert.deepEqual(result, {installed: false});
149 | });
150 |
151 | test('build badges', async function() {
152 | var res;
153 |
154 | // status: failure
155 | res = await got(helper.apiClient.buildUrl(helper.apiClient.badge, 'abc123', 'coolRepo', 'master'));
156 | assert.equal(res.headers['content-length'], 8615);
157 |
158 | // status: success
159 | res = await got(helper.apiClient.buildUrl(helper.apiClient.badge, 'abc123', 'awesomeRepo', 'master'));
160 | assert.equal(res.headers['content-length'], 9189);
161 |
162 | // error
163 | res = await got(helper.apiClient.buildUrl(helper.apiClient.badge, 'abc123', 'unknownRepo', 'master'));
164 | assert.equal(res.headers['content-length'], 4268);
165 |
166 | // new repo (no info yet)
167 | res = await got(helper.apiClient.buildUrl(helper.apiClient.badge, 'abc123', 'nonTCGHRepo', 'master'));
168 | assert.equal(res.headers['content-length'], 7873);
169 | });
170 |
171 | test('link for clickable badges', async function() {
172 | var res;
173 |
174 | // check if the function returns a correct link
175 | try {
176 | res = await got(
177 | helper.apiClient.buildUrl(helper.apiClient.latest, 'abc123', 'awesomeRepo', 'master'),
178 | {followRedirect: false});
179 | } catch (e) {
180 | console.log(`Test for redirecting to correct page failed. Error: ${JSON.stringify(e)}`);
181 | }
182 | assert.equal(res.body, 'Found. Redirecting to Wonderland');
183 | });
184 |
185 | test('simple status creation', async function() {
186 | await helper.apiClient.createStatus('abc123', 'awesomeRepo', 'master', {
187 | state: 'error',
188 | });
189 |
190 | let status = github.inst(9090).listStatusesForRef({
191 | owner: 'abc123',
192 | repo: 'awesomeRepo',
193 | ref: 'master',
194 | }).data.pop();
195 | assert.equal(status.state, 'error');
196 | assert.equal(status.target_url, undefined);
197 | assert.equal(status.description, undefined);
198 | assert.equal(status.context, 'default');
199 | });
200 |
201 | test('advanced status creation', async function() {
202 | await helper.apiClient.createStatus('abc123', 'awesomeRepo', 'master', {
203 | state: 'failure',
204 | target_url: 'http://test.com',
205 | description: 'Status title',
206 | context: 'customContext',
207 | });
208 |
209 | let status = github.inst(9090).listStatusesForRef({
210 | owner: 'abc123',
211 | repo: 'awesomeRepo',
212 | ref: 'master',
213 | }).data.pop();
214 | assert.equal(status.state, 'failure');
215 | assert.equal(status.target_url, 'http://test.com');
216 | assert.equal(status.description, 'Status title');
217 | assert.equal(status.context, 'customContext');
218 | });
219 |
220 | test('status creation where integraiton lacks permission', async function() {
221 | try {
222 | await helper.apiClient.createStatus('abc123', 'no-permission', 'master', {
223 | state: 'failure',
224 | target_url: 'http://test.com',
225 | description: 'Status title',
226 | context: 'customContext',
227 | });
228 | } catch (e) {
229 | assert.equal(e.statusCode, 403);
230 | return; // passed
231 | }
232 | throw new Error('endpoint should have failed');
233 | });
234 | test('pull request comment', async function() {
235 | await helper.apiClient.createComment('abc123', 'awesomeRepo', 1, {
236 | body: 'Task failed here',
237 | });
238 |
239 | let comment = github.inst(9090).getComments({
240 | owner: 'abc123',
241 | repo: 'awesomeRepo',
242 | number: 1,
243 | }).data.pop();
244 | assert.equal(comment.body, 'Task failed here');
245 | });
246 |
247 | test('pull request comment where integration lacks permission', async function() {
248 | try {
249 | await helper.apiClient.createComment('abc123', 'no-permission', 1, {body: 'x'});
250 | } catch (e) {
251 | assert.equal(e.statusCode, 403);
252 | return; // passed
253 | }
254 | throw new Error('endpoint should have failed');
255 | });
256 | });
257 |
--------------------------------------------------------------------------------
/test/checkStaging.js:
--------------------------------------------------------------------------------
1 | const Github = require('@octokit/rest');
2 | const child_process = require('child_process');
3 | const fs = require('fs-extra');
4 | const _ = require('lodash');
5 |
6 | const runCommand = (args) => {
7 | return new Promise((resolve, reject) => {
8 | console.log(`Running: ${args.join(' ')}`);
9 | const proc = child_process.spawn(args.shift(), args, {stdio: ['ignore', 'pipe', 2]});
10 | const output = [];
11 |
12 | proc.stdout.on('data', data => {
13 | console.error(data.toString());
14 | output.push(data);
15 | });
16 |
17 | proc.on('close', code => {
18 | if (code === 0) {
19 | resolve(output.join('').toString());
20 | } else {
21 | reject(new Error('git command failed'));
22 | }
23 | });
24 | });
25 | };
26 |
27 | const checkStaging = async () => {
28 | let github = new Github();
29 |
30 | // push a commit to the repo and get its sha
31 | let sha = await pushCommit();
32 |
33 | const update = msg => {
34 | console.log(`** ${msg}`);
35 | };
36 |
37 | update(`polling integration status of https://github.com/taskcluster/taskcluster-github-testing/commit/${sha}`);
38 |
39 | let done = false;
40 | while (!done) {
41 | await new Promise(resolve => setTimeout(resolve, 2000));
42 |
43 | let statuses = await github.repos.listStatusesForRef({
44 | owner: 'taskcluster',
45 | repo: 'taskcluster-github-testing',
46 | ref: sha,
47 | });
48 |
49 | let status = _.find(statuses.data, {context: 'Taskcluster-Staging (push)'});
50 | if (!status) {
51 | update('no Taskcluster-Staging (push) status');
52 | continue;
53 | }
54 |
55 | update(`state: ${status.state}`);
56 | switch (status.state) {
57 | case 'pending':
58 | break;
59 |
60 | case 'failed':
61 | throw new Error('task run failed');
62 | break;
63 |
64 | case 'success':
65 | done = true;
66 | update('PASSED');
67 | break;
68 |
69 | default:
70 | throw new Error(`unexpected status state ${status.state}`);
71 | break;
72 | }
73 | }
74 | };
75 |
76 | // push a dummy comit to taskcluster-github-testing
77 | const pushCommit = async () => {
78 | let tempdir = fs.mkdtempSync('/tmp/tc-gh-checkStaging-');
79 | try {
80 | process.chdir(tempdir);
81 | await runCommand(['git', 'clone', 'git@github.com:taskcluster/taskcluster-github-testing.git', 'testing']);
82 | process.chdir('testing');
83 | fs.writeFileSync('README.md',
84 | 'This repository is used to support `npm run checkStaging` in taskcluster-github\n\n' +
85 | `Last run: ${new Date()}`);
86 | await runCommand(['git', 'add', 'README.md']);
87 | await runCommand(['git', 'commit', '-m', 'checkStaging run']);
88 | await runCommand(['git', 'push']);
89 | return (await runCommand(['git', 'log', '-1', '--pretty=format:%H'])).trim();
90 | } finally {
91 | console.error(`removing ${tempdir}`);
92 | fs.removeSync(tempdir);
93 | }
94 | };
95 |
96 | checkStaging().then(() => process.exit(0)).catch((err) => {
97 | console.error(err);
98 | process.exit(1);
99 | });
100 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.branchlimited.v0.yml:
--------------------------------------------------------------------------------
1 | version: 0
2 | metadata:
3 | name: "name"
4 | description: "description"
5 | owner: "test@test.com"
6 | source: "http://mrrrgn.com"
7 | tasks:
8 | - provisionerId: aprovisioner
9 | workerType: worker
10 | extra:
11 | github:
12 | env: true
13 | events:
14 | - push
15 | branches:
16 | - master
17 | payload:
18 | image: "ubuntu:latest"
19 | command:
20 | - test
21 | metadata:
22 | name: "name"
23 | description: "description"
24 | owner: "test@test.com"
25 | source: "http://mrrrgn.com"
26 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.branchlimited.v1.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | reporting: 'checks-v1'
3 | tasks:
4 | $match:
5 | 'tasks_for == "github-push" && event.ref == "refs/heads/master"':
6 | taskId: {$eval: as_slugid('apple')}
7 | provisionerId: aprovisioner
8 | workerType: worker
9 | payload:
10 | image: "ubuntu:latest"
11 | command:
12 | - test
13 | metadata:
14 | name: "name"
15 | description: "description"
16 | owner: "test@test.com"
17 | source: "http://mrrrgn.com"
18 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.exclude-error.yml:
--------------------------------------------------------------------------------
1 | version: 0
2 | metadata:
3 | name: "name"
4 | description: "description"
5 | owner: "test@test.com"
6 | source: "http://mrrrgn.com"
7 | tasks:
8 | - provisionerId: aprovisioner
9 | workerType: worker
10 | extra:
11 | github:
12 | env: true
13 | events:
14 | - push
15 | branches:
16 | - master
17 | excludeBranches:
18 | - foobar
19 | - master
20 |
21 | payload:
22 | image: "ubuntu:latest"
23 | command:
24 | - test
25 | metadata:
26 | name: "name"
27 | description: "description"
28 | owner: "test@test.com"
29 | source: "http://mrrrgn.com"
30 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.exclude.yml:
--------------------------------------------------------------------------------
1 | version: 0
2 | metadata:
3 | name: "name"
4 | description: "description"
5 | owner: "test@test.com"
6 | source: "http://mrrrgn.com"
7 | tasks:
8 | - provisionerId: aprovisioner
9 | workerType: worker
10 | extra:
11 | github:
12 | env: true
13 | events:
14 | - push
15 | excludeBranches:
16 | - foobar
17 |
18 | payload:
19 | image: "ubuntu:latest"
20 | command:
21 | - test
22 | metadata:
23 | name: "name"
24 | description: "description"
25 | owner: "test@test.com"
26 | source: "http://mrrrgn.com"
27 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.non-github.v0.yml:
--------------------------------------------------------------------------------
1 | version: 0
2 | metadata:
3 | name: "name"
4 | description: "description"
5 | owner: "test@test.com"
6 | source: "http://mrrrgn.com"
7 | tasks:
8 | - provisionerId: aprovisioner
9 | workerType: worker
10 | payload:
11 | image: "ubuntu:latest"
12 | command:
13 | - test
14 | metadata:
15 | name: "name"
16 | description: "description"
17 | owner: "test@test.com"
18 | source: "http://mrrrgn.com"
19 | - provisionerId: aprovisioner
20 | workerType: worker
21 | extra:
22 | mercurial: true
23 | payload:
24 | image: "ubuntu:latest"
25 | command:
26 | - test
27 | metadata:
28 | name: "name"
29 | description: "description"
30 | owner: "test@test.com"
31 | source: "http://mrrrgn.com"
32 |
33 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.non-github.v1.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | tasks:
3 | - taskId: {$eval: as_slugid('😉')}
4 | provisionerId: aprovisioner
5 | workerType: worker
6 | created: {$eval: 'now'}
7 | deadline: {$fromNow: '1 hour'}
8 | payload:
9 | image: "ubuntu:latest"
10 | command:
11 | - test
12 | metadata:
13 | name: "name"
14 | description: "description"
15 | owner: "test@test.com"
16 | source: "http://mrrrgn.com"
17 | - $if: ' tasks_for == "mercurial-push" '
18 | then:
19 | taskId: {$eval: as_slugid('mercurial')}
20 | provisionerId: aprovisioner
21 | workerType: worker
22 | created: {$eval: 'now'}
23 | deadline: {$fromNow: '1 hour'}
24 | payload:
25 | image: "ubuntu:latest"
26 | command:
27 | - test
28 | metadata:
29 | name: "name"
30 | description: "description"
31 | owner: "test@test.com"
32 | source: "http://mrrrgn.com"
33 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.pull_with_exclude.yml:
--------------------------------------------------------------------------------
1 | version: 0
2 | metadata:
3 | name: "name"
4 | description: "description"
5 | owner: "test@test.com"
6 | source: "http://mrrrgn.com"
7 | tasks:
8 | - provisionerId: aprovisioner
9 | workerType: worker
10 | extra:
11 | github:
12 | env: true
13 | events:
14 | - pull_request.*
15 | - push
16 | excludeBranches:
17 | - master
18 | payload:
19 | image: "ubuntu:latest"
20 | command:
21 | - test
22 | metadata:
23 | name: "PR Task"
24 | description: "description"
25 | owner: "test@test.com"
26 | source: "http://mrrrgn.com"
27 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.push_pull_release.v0.yml:
--------------------------------------------------------------------------------
1 | version: 0
2 | metadata:
3 | name: "name"
4 | description: "description"
5 | owner: "test@test.com"
6 | source: "http://mrrrgn.com"
7 | tasks:
8 | - provisionerId: aprovisioner
9 | workerType: worker
10 | extra:
11 | github:
12 | env: true
13 | events:
14 | - push
15 | payload:
16 | image: "ubuntu:latest"
17 | command:
18 | - test
19 | metadata:
20 | name: "Push Task"
21 | description: "description"
22 | owner: "test@test.com"
23 | source: "http://mrrrgn.com"
24 | - provisionerId: aprovisioner2
25 | workerType: worker2
26 | extra:
27 | github:
28 | env: true
29 | events:
30 | - pull_request.opened
31 | - pull_request.synchronize
32 | - pull_request.reopened
33 | payload:
34 | image: "ubuntu:latest"
35 | command:
36 | - test
37 | metadata:
38 | name: "PR task"
39 | description: "description2"
40 | owner: "test@test.com"
41 | source: "http://mrrrgn.com"
42 | - provisionerId: aprovisioner3
43 | workerType: worker3
44 | extra:
45 | github:
46 | env: true
47 | events:
48 | - release
49 | payload:
50 | image: "ubuntu:latest"
51 | command:
52 | - test
53 | metadata:
54 | name: "Release task"
55 | description: "description3"
56 | owner: "test@test.com"
57 | source: "http://mrrrgn.com"
58 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.push_pull_release.v1.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | tasks:
3 | - $if: ' tasks_for == "github-push" '
4 | then:
5 | taskId: {$eval: as_slugid('😄')}
6 | provisionerId: aprovisioner
7 | workerType: worker
8 | created: {$eval: 'now'}
9 | deadline: {$fromNow: '1 hour'}
10 | extra:
11 | github:
12 | env: true
13 | events:
14 | - push
15 | payload:
16 | image: "ubuntu:latest"
17 | command:
18 | - test
19 | metadata:
20 | name: "Push Task"
21 | description: "description"
22 | owner: "test@test.com"
23 | source: "http://mrrrgn.com"
24 |
25 | - $if: ' tasks_for == "github-pull-request" '
26 | then:
27 | taskId: {$eval: as_slugid('pull')}
28 | provisionerId: aprovisioner2
29 | workerType: worker2
30 | created: {$eval: 'now'}
31 | deadline: {$fromNow: '1 hour'}
32 | payload:
33 | image: "ubuntu:latest"
34 | command:
35 | - test
36 | metadata:
37 | name: "PR task"
38 | description: "description2"
39 | owner: "test@test.com"
40 | source: "http://mrrrgn.com"
41 |
42 | - $if: ' tasks_for == "github-release" '
43 | then:
44 | taskId: {$eval: as_slugid('release')}
45 | provisionerId: aprovisioner3
46 | workerType: worker3
47 | created: {$eval: 'now'}
48 | deadline: {$fromNow: '1 hour'}
49 | payload:
50 | image: "ubuntu:latest"
51 | command:
52 | - test
53 | metadata:
54 | name: "Release task"
55 | description: "description3"
56 | owner: "test@test.com"
57 | source: "http://mrrrgn.com"
58 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.release_single.v0.yml:
--------------------------------------------------------------------------------
1 | version: 0
2 | metadata:
3 | name: "name"
4 | description: "description"
5 | owner: "test@test.com"
6 | source: "http://mrrrgn.com"
7 | tasks:
8 | - provisionerId: aprovisioner
9 | workerType: worker
10 | extra:
11 | github:
12 | env: true
13 | events:
14 | - release
15 | payload:
16 | image: "ubuntu:latest"
17 | command:
18 | - test
19 | metadata:
20 | name: "name"
21 | description: "description"
22 | owner: "test@test.com"
23 | source: "http://mrrrgn.com"
24 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.release_single.v1.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | tasks:
3 | - $if: ' tasks_for == "github-release" '
4 | then:
5 | taskId: {$eval: as_slugid('🤓')}
6 | provisionerId: aprovisioner
7 | workerType: worker
8 | created: {$eval: 'now'}
9 | deadline: {$fromNow: '1 hour'}
10 | payload:
11 | image: "ubuntu:latest"
12 | command:
13 | - test
14 | metadata:
15 | name: "name"
16 | description: "description"
17 | owner: "test@test.com"
18 | source: "http://mrrrgn.com"
19 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.single.v0.yml:
--------------------------------------------------------------------------------
1 | version: 0
2 | metadata:
3 | name: "name"
4 | description: "description"
5 | owner: "test@test.com"
6 | source: "http://mrrrgn.com"
7 | tasks:
8 | - provisionerId: aprovisioner
9 | workerType: worker
10 | extra:
11 | github:
12 | env: true
13 | events:
14 | - push
15 |
16 | payload:
17 | image: "ubuntu:latest"
18 | command:
19 | - test
20 | metadata:
21 | name: "name"
22 | description: "description"
23 | owner: "test@test.com"
24 | source: "http://mrrrgn.com"
25 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.single.v1.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | tasks:
3 | - $if: ' tasks_for == "github-push" '
4 | then:
5 | taskId: {$eval: as_slugid('banana')}
6 | provisionerId: aprovisioner
7 | workerType: worker
8 | created: {$eval: 'now'}
9 | deadline: {$fromNow: '1 hour'}
10 | payload:
11 | image: "ubuntu:latest"
12 | command:
13 | - test
14 | metadata:
15 | name: "name"
16 | description: "description"
17 | owner: ${event.pusher.email}
18 | source: ${event.repository.url}
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.star.yml:
--------------------------------------------------------------------------------
1 | version: 0
2 | metadata:
3 | name: "name"
4 | description: "description"
5 | owner: "test@test.com"
6 | source: "http://mrrrgn.com"
7 | tasks:
8 | - provisionerId: aprovisioner
9 | workerType: worker
10 | extra:
11 | github:
12 | env: true
13 | events:
14 | - pull_request.*
15 | payload:
16 | image: "ubuntu:latest"
17 | command:
18 | - test
19 | metadata:
20 | name: "name"
21 | description: "description"
22 | owner: "test@test.com"
23 | source: "http://mrrrgn.com"
24 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.tag.branchlimited.v0.yml:
--------------------------------------------------------------------------------
1 | version: 0
2 | metadata:
3 | name: "name"
4 | description: "description"
5 | owner: "test@test.com"
6 | source: "http://mrrrgn.com"
7 | tasks:
8 | - provisionerId: aprovisioner
9 | workerType: worker
10 | extra:
11 | github:
12 | env: true
13 | events:
14 | - tag
15 | branches:
16 | - master
17 | payload:
18 | image: "ubuntu:latest"
19 | command:
20 | - test
21 | metadata:
22 | name: "name"
23 | description: "description"
24 | owner: "test@test.com"
25 | source: "http://mrrrgn.com"
26 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.tag.branchlimited.v1.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | tasks:
3 | - $if: ' (tasks_for == "github-push") && event.ref != "refs/heads/master" '
4 | then:
5 | taskId: {$eval: as_slugid('😊')}
6 | provisionerId: aprovisioner
7 | workerType: worker
8 | created: {$eval: 'now'}
9 | deadline: {$fromNow: '1 hour'}
10 | payload:
11 | image: "ubuntu:latest"
12 | command:
13 | - test
14 | metadata:
15 | name: "name"
16 | description: "description"
17 | owner: "test@test.com"
18 | source: "http://mrrrgn.com"
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.tag_single.v0.yml:
--------------------------------------------------------------------------------
1 | version: 0
2 | metadata:
3 | name: "name"
4 | description: "description"
5 | owner: "test@test.com"
6 | source: "http://mrrrgn.com"
7 | tasks:
8 | - provisionerId: aprovisioner
9 | workerType: worker
10 | extra:
11 | github:
12 | env: true
13 | events:
14 | - tag
15 | payload:
16 | image: "ubuntu:latest"
17 | command:
18 | - test
19 | metadata:
20 | name: "name"
21 | description: "description"
22 | owner: "test@test.com"
23 | source: "http://mrrrgn.com"
24 |
--------------------------------------------------------------------------------
/test/data/configs/taskcluster.tag_single.v1.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | tasks:
3 | - $if: ' tasks_for == "github-push" '
4 | then:
5 | taskId: {$eval: as_slugid('pineapple')}
6 | provisionerId: aprovisioner
7 | workerType: worker
8 | created: {$eval: 'now'}
9 | deadline: {$fromNow: '1 hour'}
10 | payload:
11 | image: "ubuntu:latest"
12 | command:
13 | - test
14 | metadata:
15 | name: "name"
16 | description: "description"
17 | owner: "test@test.com"
18 | source: "http://mrrrgn.com"
19 |
--------------------------------------------------------------------------------
/test/data/webhooks/webhook.push.bad_secret.json:
--------------------------------------------------------------------------------
1 | {"headers": {"Host": "requestb.in",
2 | "Accept": "*/*",
3 | "User-Agent": "GitHub-Hookshot/f5d5ca1",
4 | "X-GitHub-Event": "push",
5 | "X-GitHub-Delivery": "736b3900-7f8a-11e6-8ec8-b75aea82a0ef",
6 | "content-type": "application/json",
7 | "X-Hub-Signature": "sha1=7afa92aab27dbb6ee39fe95138d05e3b2ce47601",
8 | "Content-Length": 6800}, "body":
9 |
10 |
11 | {"ref":"refs/heads/master","before":"efd1d89119e819a71a645c95c2590c0b3e7d0c83","after":"1edbd696be128a1ac187fac97e6a209fe9df3745","created":false,"deleted":false,"forced":false,"base_ref":null,"compare":"https://github.com/TaskClusterRobot/hooks-testing/compare/efd1d89119e8...1edbd696be12","commits":[{"id":"1edbd696be128a1ac187fac97e6a209fe9df3745","tree_id":"e9923f58e4cd74dc850443bc096784f9aaf69265","distinct":true,"message":"Update README.md","timestamp":"2016-09-20T16:32:10-07:00","url":"https://github.com/TaskClusterRobot/hooks-testing/commit/1edbd696be128a1ac187fac97e6a209fe9df3745","author":{"name":"Lisa Lionheart","email":"taskcluster-accounts@mozilla.com","username":"TaskClusterRobot"},"committer":{"name":"GitHub","email":"noreply@github.com","username":"web-flow"},"added":[],"removed":[],"modified":["README.md"]}],"head_commit":{"id":"1edbd696be128a1ac187fac97e6a209fe9df3745","tree_id":"e9923f58e4cd74dc850443bc096784f9aaf69265","distinct":true,"message":"Update README.md","timestamp":"2016-09-20T16:32:10-07:00","url":"https://github.com/TaskClusterRobot/hooks-testing/commit/1edbd696be128a1ac187fac97e6a209fe9df3745","author":{"name":"Lisa Lionheart","email":"taskcluster-accounts@mozilla.com","username":"TaskClusterRobot"},"committer":{"name":"GitHub","email":"noreply@github.com","username":"web-flow"},"added":[],"removed":[],"modified":["README.md"]},"repository":{"id":68761376,"name":"hooks-testing","full_name":"TaskClusterRobot/hooks-testing","owner":{"name":"TaskClusterRobot","email":"taskcluster-accounts@mozilla.com"},"private":false,"html_url":"https://github.com/TaskClusterRobot/hooks-testing","description":"for testing taskcluster-github","fork":false,"url":"https://github.com/TaskClusterRobot/hooks-testing","forks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/forks","keys_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/keys{/key_id}","collaborators_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/teams","hooks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/hooks","issue_events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/events{/number}","events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/events","assignees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/assignees{/user}","branches_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/branches{/branch}","tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/tags","blobs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/refs{/sha}","trees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/trees{/sha}","statuses_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/statuses/{sha}","languages_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/languages","stargazers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/stargazers","contributors_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contributors","subscribers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscribers","subscription_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscription","commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/commits{/sha}","git_commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/commits{/sha}","comments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/comments{/number}","issue_comment_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/comments{/number}","contents_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contents/{+path}","compare_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/compare/{base}...{head}","merges_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/merges","archive_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/downloads","issues_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues{/number}","pulls_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/pulls{/number}","milestones_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/milestones{/number}","notifications_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/labels{/name}","releases_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/releases{/id}","deployments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/deployments","created_at":1474414120,"updated_at":"2016-09-20T23:28:40Z","pushed_at":1474414330,"git_url":"git://github.com/TaskClusterRobot/hooks-testing.git","ssh_url":"git@github.com:TaskClusterRobot/hooks-testing.git","clone_url":"https://github.com/TaskClusterRobot/hooks-testing.git","svn_url":"https://github.com/TaskClusterRobot/hooks-testing","homepage":null,"size":0,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master","stargazers":0,"master_branch":"master"},"pusher":{"name":"TaskClusterRobot","email":"taskcluster-accounts@mozilla.com"},"sender":{"login":"TaskClusterRobot","id":14795478,"avatar_url":"https://avatars.githubusercontent.com/u/14795478?v=3","gravatar_id":"","url":"https://api.github.com/users/TaskClusterRobot","html_url":"https://github.com/TaskClusterRobot","followers_url":"https://api.github.com/users/TaskClusterRobot/followers","following_url":"https://api.github.com/users/TaskClusterRobot/following{/other_user}","gists_url":"https://api.github.com/users/TaskClusterRobot/gists{/gist_id}","starred_url":"https://api.github.com/users/TaskClusterRobot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TaskClusterRobot/subscriptions","organizations_url":"https://api.github.com/users/TaskClusterRobot/orgs","repos_url":"https://api.github.com/users/TaskClusterRobot/repos","events_url":"https://api.github.com/users/TaskClusterRobot/events{/privacy}","received_events_url":"https://api.github.com/users/TaskClusterRobot/received_events","type":"User","site_admin":false}}}
12 |
--------------------------------------------------------------------------------
/test/data/webhooks/webhook.push.json:
--------------------------------------------------------------------------------
1 | {"headers": {"Host": "requestb.in",
2 | "Accept": "*/*",
3 | "User-Agent": "GitHub-Hookshot/6f33334",
4 | "X-GitHub-Event": "push",
5 | "X-GitHub-Delivery": "9637a980-d8fb-11e6-9830-1244ca57c95f",
6 | "content-type": "application/json",
7 | "X-Hub-Signature": "sha1=ceb38686a1f85978be5cfdcfeccdd3585c16e2bc",
8 | "Content-Length": 6705}, "body":
9 |
10 | {"ref":"refs/heads/master","before":"7a257a6d139708a3188bf2e0cd1f15e466a88d0e","after":"b79ce60be819cdc482c9c6a84dc3c457959aa66f","created":false,"deleted":false,"forced":false,"base_ref":null,"compare":"https://github.com/TaskClusterRobot/hooks-testing/compare/7a257a6d1397...b79ce60be819","commits":[{"id":"b79ce60be819cdc482c9c6a84dc3c457959aa66f","tree_id":"1e853705688e2088b1d25c7d9c3e521c80ba10cf","distinct":true,"message":"Update README.md","timestamp":"2017-01-12T11:16:14-08:00","url":"https://github.com/TaskClusterRobot/hooks-testing/commit/b79ce60be819cdc482c9c6a84dc3c457959aa66f","author":{"name":"Irene","email":null,"username":"owlishDeveloper"},"committer":{"name":"GitHub","email":"noreply@github.com","username":"web-flow"},"added":[],"removed":[],"modified":["README.md"]}],"head_commit":{"id":"b79ce60be819cdc482c9c6a84dc3c457959aa66f","tree_id":"1e853705688e2088b1d25c7d9c3e521c80ba10cf","distinct":true,"message":"Update README.md","timestamp":"2017-01-12T11:16:14-08:00","url":"https://github.com/TaskClusterRobot/hooks-testing/commit/b79ce60be819cdc482c9c6a84dc3c457959aa66f","author":{"name":"Irene","email":null,"username":"owlishDeveloper"},"committer":{"name":"GitHub","email":"noreply@github.com","username":"web-flow"},"added":[],"removed":[],"modified":["README.md"]},"repository":{"id":68761376,"name":"hooks-testing","full_name":"TaskClusterRobot/hooks-testing","owner":{"name":"TaskClusterRobot","email":"taskcluster-accounts@mozilla.com"},"private":false,"html_url":"https://github.com/TaskClusterRobot/hooks-testing","description":"for testing taskcluster-github","fork":false,"url":"https://github.com/TaskClusterRobot/hooks-testing","forks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/forks","keys_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/keys{/key_id}","collaborators_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/teams","hooks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/hooks","issue_events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/events{/number}","events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/events","assignees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/assignees{/user}","branches_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/branches{/branch}","tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/tags","blobs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/refs{/sha}","trees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/trees{/sha}","statuses_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/statuses/{sha}","languages_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/languages","stargazers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/stargazers","contributors_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contributors","subscribers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscribers","subscription_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscription","commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/commits{/sha}","git_commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/commits{/sha}","comments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/comments{/number}","issue_comment_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/comments{/number}","contents_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contents/{+path}","compare_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/compare/{base}...{head}","merges_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/merges","archive_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/downloads","issues_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues{/number}","pulls_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/pulls{/number}","milestones_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/milestones{/number}","notifications_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/labels{/name}","releases_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/releases{/id}","deployments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/deployments","created_at":1474414120,"updated_at":"2017-01-03T18:58:56Z","pushed_at":1484248575,"git_url":"git://github.com/TaskClusterRobot/hooks-testing.git","ssh_url":"git@github.com:TaskClusterRobot/hooks-testing.git","clone_url":"https://github.com/TaskClusterRobot/hooks-testing.git","svn_url":"https://github.com/TaskClusterRobot/hooks-testing","homepage":null,"size":24,"stargazers_count":1,"watchers_count":1,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":1,"default_branch":"master","stargazers":1,"master_branch":"master"},"pusher":{"name":"owlishDeveloper","email":null},"sender":{"login":"owlishDeveloper","id":18102552,"avatar_url":"https://avatars.githubusercontent.com/u/18102552?v=3","gravatar_id":"","url":"https://api.github.com/users/owlishDeveloper","html_url":"https://github.com/owlishDeveloper","followers_url":"https://api.github.com/users/owlishDeveloper/followers","following_url":"https://api.github.com/users/owlishDeveloper/following{/other_user}","gists_url":"https://api.github.com/users/owlishDeveloper/gists{/gist_id}","starred_url":"https://api.github.com/users/owlishDeveloper/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/owlishDeveloper/subscriptions","organizations_url":"https://api.github.com/users/owlishDeveloper/orgs","repos_url":"https://api.github.com/users/owlishDeveloper/repos","events_url":"https://api.github.com/users/owlishDeveloper/events{/privacy}","received_events_url":"https://api.github.com/users/owlishDeveloper/received_events","type":"User","site_admin":false},"installation":{"id":5808}}}
11 |
--------------------------------------------------------------------------------
/test/data/webhooks/webhook.push.no_secret.json:
--------------------------------------------------------------------------------
1 | {"headers": {"Host": "requestb.in",
2 | "Accept": "*/*",
3 | "User-Agent": "GitHub-Hookshot/f5d5ca1",
4 | "X-GitHub-Event": "push",
5 | "X-GitHub-Delivery": "736b3900-7f8a-11e6-8ec8-b75aea82a0ef",
6 | "content-type": "application/json",
7 | "Content-Length": 6800}, "body":
8 |
9 |
10 | {"ref":"refs/heads/master","before":"efd1d89119e819a71a645c95c2590c0b3e7d0c83","after":"1edbd696be128a1ac187fac97e6a209fe9df3745","created":false,"deleted":false,"forced":false,"base_ref":null,"compare":"https://github.com/TaskClusterRobot/hooks-testing/compare/efd1d89119e8...1edbd696be12","commits":[{"id":"1edbd696be128a1ac187fac97e6a209fe9df3745","tree_id":"e9923f58e4cd74dc850443bc096784f9aaf69265","distinct":true,"message":"Update README.md","timestamp":"2016-09-20T16:32:10-07:00","url":"https://github.com/TaskClusterRobot/hooks-testing/commit/1edbd696be128a1ac187fac97e6a209fe9df3745","author":{"name":"Lisa Lionheart","email":"taskcluster-accounts@mozilla.com","username":"TaskClusterRobot"},"committer":{"name":"GitHub","email":"noreply@github.com","username":"web-flow"},"added":[],"removed":[],"modified":["README.md"]}],"head_commit":{"id":"1edbd696be128a1ac187fac97e6a209fe9df3745","tree_id":"e9923f58e4cd74dc850443bc096784f9aaf69265","distinct":true,"message":"Update README.md","timestamp":"2016-09-20T16:32:10-07:00","url":"https://github.com/TaskClusterRobot/hooks-testing/commit/1edbd696be128a1ac187fac97e6a209fe9df3745","author":{"name":"Lisa Lionheart","email":"taskcluster-accounts@mozilla.com","username":"TaskClusterRobot"},"committer":{"name":"GitHub","email":"noreply@github.com","username":"web-flow"},"added":[],"removed":[],"modified":["README.md"]},"repository":{"id":68761376,"name":"hooks-testing","full_name":"TaskClusterRobot/hooks-testing","owner":{"name":"TaskClusterRobot","email":"taskcluster-accounts@mozilla.com"},"private":false,"html_url":"https://github.com/TaskClusterRobot/hooks-testing","description":"for testing taskcluster-github","fork":false,"url":"https://github.com/TaskClusterRobot/hooks-testing","forks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/forks","keys_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/keys{/key_id}","collaborators_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/teams","hooks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/hooks","issue_events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/events{/number}","events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/events","assignees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/assignees{/user}","branches_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/branches{/branch}","tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/tags","blobs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/refs{/sha}","trees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/trees{/sha}","statuses_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/statuses/{sha}","languages_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/languages","stargazers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/stargazers","contributors_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contributors","subscribers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscribers","subscription_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscription","commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/commits{/sha}","git_commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/commits{/sha}","comments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/comments{/number}","issue_comment_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/comments{/number}","contents_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contents/{+path}","compare_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/compare/{base}...{head}","merges_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/merges","archive_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/downloads","issues_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues{/number}","pulls_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/pulls{/number}","milestones_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/milestones{/number}","notifications_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/labels{/name}","releases_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/releases{/id}","deployments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/deployments","created_at":1474414120,"updated_at":"2016-09-20T23:28:40Z","pushed_at":1474414330,"git_url":"git://github.com/TaskClusterRobot/hooks-testing.git","ssh_url":"git@github.com:TaskClusterRobot/hooks-testing.git","clone_url":"https://github.com/TaskClusterRobot/hooks-testing.git","svn_url":"https://github.com/TaskClusterRobot/hooks-testing","homepage":null,"size":0,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master","stargazers":0,"master_branch":"master"},"pusher":{"name":"TaskClusterRobot","email":"taskcluster-accounts@mozilla.com"},"sender":{"login":"TaskClusterRobot","id":14795478,"avatar_url":"https://avatars.githubusercontent.com/u/14795478?v=3","gravatar_id":"","url":"https://api.github.com/users/TaskClusterRobot","html_url":"https://github.com/TaskClusterRobot","followers_url":"https://api.github.com/users/TaskClusterRobot/followers","following_url":"https://api.github.com/users/TaskClusterRobot/following{/other_user}","gists_url":"https://api.github.com/users/TaskClusterRobot/gists{/gist_id}","starred_url":"https://api.github.com/users/TaskClusterRobot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TaskClusterRobot/subscriptions","organizations_url":"https://api.github.com/users/TaskClusterRobot/orgs","repos_url":"https://api.github.com/users/TaskClusterRobot/repos","events_url":"https://api.github.com/users/TaskClusterRobot/events{/privacy}","received_events_url":"https://api.github.com/users/TaskClusterRobot/received_events","type":"User","site_admin":false}}}
11 |
--------------------------------------------------------------------------------
/test/data/webhooks/webhook.push.offbranch.json:
--------------------------------------------------------------------------------
1 | {"headers": {"Host": "requestb.in",
2 | "Accept": "*/*",
3 | "User-Agent": "GitHub-Hookshot/6f33334",
4 | "X-GitHub-Event": "push",
5 | "X-GitHub-Delivery": "9637a980-d8fb-11e6-9830-1244ca57c95f",
6 | "content-type": "application/json",
7 | "X-Hub-Signature": "sha1=ceb38686a1f85978be5cfdcfeccdd3585c16e2bc",
8 | "Content-Length": 6705}, "body":
9 |
10 | {"ref":"refs/heads/foobar","before":"7a257a6d139708a3188bf2e0cd1f15e466a88d0e","after":"b79ce60be819cdc482c9c6a84dc3c457959aa66f","created":false,"deleted":false,"forced":false,"base_ref":null,"compare":"https://github.com/TaskClusterRobot/hooks-testing/compare/7a257a6d1397...b79ce60be819","commits":[{"id":"b79ce60be819cdc482c9c6a84dc3c457959aa66f","tree_id":"1e853705688e2088b1d25c7d9c3e521c80ba10cf","distinct":true,"message":"Update README.md","timestamp":"2017-01-12T11:16:14-08:00","url":"https://github.com/TaskClusterRobot/hooks-testing/commit/b79ce60be819cdc482c9c6a84dc3c457959aa66f","author":{"name":"Irene","email":null,"username":"owlishDeveloper"},"committer":{"name":"GitHub","email":"noreply@github.com","username":"web-flow"},"added":[],"removed":[],"modified":["README.md"]}],"head_commit":{"id":"b79ce60be819cdc482c9c6a84dc3c457959aa66f","tree_id":"1e853705688e2088b1d25c7d9c3e521c80ba10cf","distinct":true,"message":"Update README.md","timestamp":"2017-01-12T11:16:14-08:00","url":"https://github.com/TaskClusterRobot/hooks-testing/commit/b79ce60be819cdc482c9c6a84dc3c457959aa66f","author":{"name":"Irene","email":null,"username":"owlishDeveloper"},"committer":{"name":"GitHub","email":"noreply@github.com","username":"web-flow"},"added":[],"removed":[],"modified":["README.md"]},"repository":{"id":68761376,"name":"hooks-testing","full_name":"TaskClusterRobot/hooks-testing","owner":{"name":"TaskClusterRobot","email":"taskcluster-accounts@mozilla.com"},"private":false,"html_url":"https://github.com/TaskClusterRobot/hooks-testing","description":"for testing taskcluster-github","fork":false,"url":"https://github.com/TaskClusterRobot/hooks-testing","forks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/forks","keys_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/keys{/key_id}","collaborators_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/teams","hooks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/hooks","issue_events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/events{/number}","events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/events","assignees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/assignees{/user}","branches_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/branches{/branch}","tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/tags","blobs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/refs{/sha}","trees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/trees{/sha}","statuses_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/statuses/{sha}","languages_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/languages","stargazers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/stargazers","contributors_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contributors","subscribers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscribers","subscription_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscription","commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/commits{/sha}","git_commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/commits{/sha}","comments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/comments{/number}","issue_comment_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/comments{/number}","contents_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contents/{+path}","compare_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/compare/{base}...{head}","merges_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/merges","archive_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/downloads","issues_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues{/number}","pulls_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/pulls{/number}","milestones_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/milestones{/number}","notifications_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/labels{/name}","releases_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/releases{/id}","deployments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/deployments","created_at":1474414120,"updated_at":"2017-01-03T18:58:56Z","pushed_at":1484248575,"git_url":"git://github.com/TaskClusterRobot/hooks-testing.git","ssh_url":"git@github.com:TaskClusterRobot/hooks-testing.git","clone_url":"https://github.com/TaskClusterRobot/hooks-testing.git","svn_url":"https://github.com/TaskClusterRobot/hooks-testing","homepage":null,"size":24,"stargazers_count":1,"watchers_count":1,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":1,"default_branch":"master","stargazers":1,"master_branch":"master"},"pusher":{"name":"owlishDeveloper","email":null},"sender":{"login":"owlishDeveloper","id":18102552,"avatar_url":"https://avatars.githubusercontent.com/u/18102552?v=3","gravatar_id":"","url":"https://api.github.com/users/owlishDeveloper","html_url":"https://github.com/owlishDeveloper","followers_url":"https://api.github.com/users/owlishDeveloper/followers","following_url":"https://api.github.com/users/owlishDeveloper/following{/other_user}","gists_url":"https://api.github.com/users/owlishDeveloper/gists{/gist_id}","starred_url":"https://api.github.com/users/owlishDeveloper/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/owlishDeveloper/subscriptions","organizations_url":"https://api.github.com/users/owlishDeveloper/orgs","repos_url":"https://api.github.com/users/owlishDeveloper/repos","events_url":"https://api.github.com/users/owlishDeveloper/events{/privacy}","received_events_url":"https://api.github.com/users/owlishDeveloper/received_events","type":"User","site_admin":false},"installation":{"id":5808}}}
11 |
--------------------------------------------------------------------------------
/test/data/webhooks/webhook.push.unicode.json:
--------------------------------------------------------------------------------
1 | {"headers": {"Host": "requestb.in",
2 | "Accept": "*/*",
3 | "User-Agent": "GitHub-Hookshot/6f33334",
4 | "X-GitHub-Event": "push",
5 | "X-GitHub-Delivery": "9637a980-d8fb-11e6-9830-1244ca57c95f",
6 | "content-type": "application/json",
7 | "X-Hub-Signature": "sha1=ceb38686a1f85978be5cfdcfeccdd3585c16e2bc",
8 | "Content-Length": 6705}, "body":
9 |
10 | {"ref":"refs/heads/🌱","before":"7a257a6d139708a3188bf2e0cd1f15e466a88d0e","after":"b79ce60be819cdc482c9c6a84dc3c457959aa66f","created":false,"deleted":false,"forced":false,"base_ref":null,"compare":"https://github.com/TaskClusterRobot/hooks-testing/compare/7a257a6d1397...b79ce60be819","commits":[{"id":"b79ce60be819cdc482c9c6a84dc3c457959aa66f","tree_id":"1e853705688e2088b1d25c7d9c3e521c80ba10cf","distinct":true,"message":"Update README.md","timestamp":"2017-01-12T11:16:14-08:00","url":"https://github.com/TaskClusterRobot/hooks-testing/commit/b79ce60be819cdc482c9c6a84dc3c457959aa66f","author":{"name":"Irene","email":null,"username":"owlishDeveloper"},"committer":{"name":"GitHub","email":"noreply@github.com","username":"web-flow"},"added":[],"removed":[],"modified":["README.md"]}],"head_commit":{"id":"b79ce60be819cdc482c9c6a84dc3c457959aa66f","tree_id":"1e853705688e2088b1d25c7d9c3e521c80ba10cf","distinct":true,"message":"Update README.md","timestamp":"2017-01-12T11:16:14-08:00","url":"https://github.com/TaskClusterRobot/hooks-testing/commit/b79ce60be819cdc482c9c6a84dc3c457959aa66f","author":{"name":"Irene","email":null,"username":"owlishDeveloper"},"committer":{"name":"GitHub","email":"noreply@github.com","username":"web-flow"},"added":[],"removed":[],"modified":["README.md"]},"repository":{"id":68761376,"name":"hooks-testing","full_name":"TaskClusterRobot/hooks-testing","owner":{"name":"TaskClusterRobot","email":"taskcluster-accounts@mozilla.com"},"private":false,"html_url":"https://github.com/TaskClusterRobot/hooks-testing","description":"for testing taskcluster-github","fork":false,"url":"https://github.com/TaskClusterRobot/hooks-testing","forks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/forks","keys_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/keys{/key_id}","collaborators_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/teams","hooks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/hooks","issue_events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/events{/number}","events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/events","assignees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/assignees{/user}","branches_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/branches{/branch}","tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/tags","blobs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/refs{/sha}","trees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/trees{/sha}","statuses_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/statuses/{sha}","languages_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/languages","stargazers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/stargazers","contributors_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contributors","subscribers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscribers","subscription_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscription","commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/commits{/sha}","git_commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/commits{/sha}","comments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/comments{/number}","issue_comment_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/comments{/number}","contents_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contents/{+path}","compare_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/compare/{base}...{head}","merges_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/merges","archive_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/downloads","issues_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues{/number}","pulls_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/pulls{/number}","milestones_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/milestones{/number}","notifications_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/labels{/name}","releases_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/releases{/id}","deployments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/deployments","created_at":1474414120,"updated_at":"2017-01-03T18:58:56Z","pushed_at":1484248575,"git_url":"git://github.com/TaskClusterRobot/hooks-testing.git","ssh_url":"git@github.com:TaskClusterRobot/hooks-testing.git","clone_url":"https://github.com/TaskClusterRobot/hooks-testing.git","svn_url":"https://github.com/TaskClusterRobot/hooks-testing","homepage":null,"size":24,"stargazers_count":1,"watchers_count":1,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":1,"default_branch":"master","stargazers":1,"master_branch":"master"},"pusher":{"name":"owlishDeveloper","email":null},"sender":{"login":"owlishDeveloper","id":18102552,"avatar_url":"https://avatars.githubusercontent.com/u/18102552?v=3","gravatar_id":"","url":"https://api.github.com/users/owlishDeveloper","html_url":"https://github.com/owlishDeveloper","followers_url":"https://api.github.com/users/owlishDeveloper/followers","following_url":"https://api.github.com/users/owlishDeveloper/following{/other_user}","gists_url":"https://api.github.com/users/owlishDeveloper/gists{/gist_id}","starred_url":"https://api.github.com/users/owlishDeveloper/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/owlishDeveloper/subscriptions","organizations_url":"https://api.github.com/users/owlishDeveloper/orgs","repos_url":"https://api.github.com/users/owlishDeveloper/repos","events_url":"https://api.github.com/users/owlishDeveloper/events{/privacy}","received_events_url":"https://api.github.com/users/owlishDeveloper/received_events","type":"User","site_admin":false},"installation":{"id":5808}}}
11 |
--------------------------------------------------------------------------------
/test/data/webhooks/webhook.release.bad_secret.json:
--------------------------------------------------------------------------------
1 | {"headers": {"Host": "requestb.in",
2 | "Accept": "*/*",
3 | "User-Agent": "GitHub-Hookshot/7676889",
4 | "X-GitHub-Event": "release",
5 | "X-GitHub-Delivery": "2c81a200-cd36-11e6-9106-ad0d7be0e22e",
6 | "content-type": "application/json",
7 | "X-Hub-Signature": "sha1=66c244cee32d3a55f7cd0db47d5cbcf952ed5453",
8 | "Content-Length": 8055}, "body":
9 |
10 |
11 | {"action":"published","release":{"url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/releases/5027516","assets_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/releases/5027516/assets","upload_url":"https://uploads.github.com/repos/TaskClusterRobot/hooks-testing/releases/5027516/assets{?name,label}","html_url":"https://github.com/TaskClusterRobot/hooks-testing/releases/tag/testing-789","id":5027516,"tag_name":"testing-789","target_commitish":"master","name":"Testing 123","draft":false,"author":{"login":"TaskClusterRobot","id":14795478,"avatar_url":"https://avatars.githubusercontent.com/u/14795478?v=3","gravatar_id":"","url":"https://api.github.com/users/TaskClusterRobot","html_url":"https://github.com/TaskClusterRobot","followers_url":"https://api.github.com/users/TaskClusterRobot/followers","following_url":"https://api.github.com/users/TaskClusterRobot/following{/other_user}","gists_url":"https://api.github.com/users/TaskClusterRobot/gists{/gist_id}","starred_url":"https://api.github.com/users/TaskClusterRobot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TaskClusterRobot/subscriptions","organizations_url":"https://api.github.com/users/TaskClusterRobot/orgs","repos_url":"https://api.github.com/users/TaskClusterRobot/repos","events_url":"https://api.github.com/users/TaskClusterRobot/events{/privacy}","received_events_url":"https://api.github.com/users/TaskClusterRobot/received_events","type":"User","site_admin":false},"prerelease":false,"created_at":"2016-12-28T19:39:49Z","published_at":"2016-12-28T19:45:24Z","assets":[],"tarball_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/tarball/testing-789","zipball_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/zipball/testing-789","body":"Testing 456"},"repository":{"id":68761376,"name":"hooks-testing","full_name":"TaskClusterRobot/hooks-testing","owner":{"login":"TaskClusterRobot","id":14795478,"avatar_url":"https://avatars.githubusercontent.com/u/14795478?v=3","gravatar_id":"","url":"https://api.github.com/users/TaskClusterRobot","html_url":"https://github.com/TaskClusterRobot","followers_url":"https://api.github.com/users/TaskClusterRobot/followers","following_url":"https://api.github.com/users/TaskClusterRobot/following{/other_user}","gists_url":"https://api.github.com/users/TaskClusterRobot/gists{/gist_id}","starred_url":"https://api.github.com/users/TaskClusterRobot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TaskClusterRobot/subscriptions","organizations_url":"https://api.github.com/users/TaskClusterRobot/orgs","repos_url":"https://api.github.com/users/TaskClusterRobot/repos","events_url":"https://api.github.com/users/TaskClusterRobot/events{/privacy}","received_events_url":"https://api.github.com/users/TaskClusterRobot/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/TaskClusterRobot/hooks-testing","description":"for testing taskcluster-github","fork":false,"url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing","forks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/forks","keys_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/keys{/key_id}","collaborators_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/teams","hooks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/hooks","issue_events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/events{/number}","events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/events","assignees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/assignees{/user}","branches_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/branches{/branch}","tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/tags","blobs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/refs{/sha}","trees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/trees{/sha}","statuses_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/statuses/{sha}","languages_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/languages","stargazers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/stargazers","contributors_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contributors","subscribers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscribers","subscription_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscription","commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/commits{/sha}","git_commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/commits{/sha}","comments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/comments{/number}","issue_comment_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/comments{/number}","contents_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contents/{+path}","compare_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/compare/{base}...{head}","merges_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/merges","archive_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/downloads","issues_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues{/number}","pulls_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/pulls{/number}","milestones_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/milestones{/number}","notifications_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/labels{/name}","releases_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/releases{/id}","deployments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/deployments","created_at":"2016-09-20T23:28:40Z","updated_at":"2016-09-20T23:28:40Z","pushed_at":"2016-12-28T19:39:53Z","git_url":"git://github.com/TaskClusterRobot/hooks-testing.git","ssh_url":"git@github.com:TaskClusterRobot/hooks-testing.git","clone_url":"https://github.com/TaskClusterRobot/hooks-testing.git","svn_url":"https://github.com/TaskClusterRobot/hooks-testing","homepage":null,"size":20,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":0,"default_branch":"master"},"sender":{"login":"TaskClusterRobot","id":14795478,"avatar_url":"https://avatars.githubusercontent.com/u/14795478?v=3","gravatar_id":"","url":"https://api.github.com/users/TaskClusterRobot","html_url":"https://github.com/TaskClusterRobot","followers_url":"https://api.github.com/users/TaskClusterRobot/followers","following_url":"https://api.github.com/users/TaskClusterRobot/following{/other_user}","gists_url":"https://api.github.com/users/TaskClusterRobot/gists{/gist_id}","starred_url":"https://api.github.com/users/TaskClusterRobot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TaskClusterRobot/subscriptions","organizations_url":"https://api.github.com/users/TaskClusterRobot/orgs","repos_url":"https://api.github.com/users/TaskClusterRobot/repos","events_url":"https://api.github.com/users/TaskClusterRobot/events{/privacy}","received_events_url":"https://api.github.com/users/TaskClusterRobot/received_events","type": "User","site_admin":false}}}
12 |
--------------------------------------------------------------------------------
/test/data/webhooks/webhook.release.json:
--------------------------------------------------------------------------------
1 | {"headers": {"Host": "requestb.in",
2 | "Accept": "*/*",
3 | "User-Agent": "GitHub-Hookshot/7676889",
4 | "X-GitHub-Event": "release",
5 | "X-GitHub-Delivery": "2c81a200-cd36-11e6-9106-ad0d7be0e22e",
6 | "content-type": "application/json",
7 | "X-Hub-Signature": "sha1=4081d9cd2caa4323adf3d1308b1cddcfce4c0868",
8 | "Content-Length": 8082}, "body":
9 |
10 |
11 | {"action":"published","release":{"url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/releases/5027516","assets_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/releases/5027516/assets","upload_url":"https://uploads.github.com/repos/TaskClusterRobot/hooks-testing/releases/5027516/assets{?name,label}","html_url":"https://github.com/TaskClusterRobot/hooks-testing/releases/tag/testing-789","id":5027516,"tag_name":"testing-789","target_commitish":"master","name":"Testing 123","draft":false,"author":{"login":"TaskClusterRobot","id":14795478,"avatar_url":"https://avatars.githubusercontent.com/u/14795478?v=3","gravatar_id":"","url":"https://api.github.com/users/TaskClusterRobot","html_url":"https://github.com/TaskClusterRobot","followers_url":"https://api.github.com/users/TaskClusterRobot/followers","following_url":"https://api.github.com/users/TaskClusterRobot/following{/other_user}","gists_url":"https://api.github.com/users/TaskClusterRobot/gists{/gist_id}","starred_url":"https://api.github.com/users/TaskClusterRobot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TaskClusterRobot/subscriptions","organizations_url":"https://api.github.com/users/TaskClusterRobot/orgs","repos_url":"https://api.github.com/users/TaskClusterRobot/repos","events_url":"https://api.github.com/users/TaskClusterRobot/events{/privacy}","received_events_url":"https://api.github.com/users/TaskClusterRobot/received_events","type":"User","site_admin":false},"prerelease":false,"created_at":"2016-12-28T19:39:49Z","published_at":"2016-12-28T19:45:24Z","assets":[],"tarball_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/tarball/testing-789","zipball_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/zipball/testing-789","body":"Testing 456"},"repository":{"id":68761376,"name":"hooks-testing","full_name":"TaskClusterRobot/hooks-testing","owner":{"login":"TaskClusterRobot","id":14795478,"avatar_url":"https://avatars.githubusercontent.com/u/14795478?v=3","gravatar_id":"","url":"https://api.github.com/users/TaskClusterRobot","html_url":"https://github.com/TaskClusterRobot","followers_url":"https://api.github.com/users/TaskClusterRobot/followers","following_url":"https://api.github.com/users/TaskClusterRobot/following{/other_user}","gists_url":"https://api.github.com/users/TaskClusterRobot/gists{/gist_id}","starred_url":"https://api.github.com/users/TaskClusterRobot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TaskClusterRobot/subscriptions","organizations_url":"https://api.github.com/users/TaskClusterRobot/orgs","repos_url":"https://api.github.com/users/TaskClusterRobot/repos","events_url":"https://api.github.com/users/TaskClusterRobot/events{/privacy}","received_events_url":"https://api.github.com/users/TaskClusterRobot/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/TaskClusterRobot/hooks-testing","description":"for testing taskcluster-github","fork":false,"url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing","forks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/forks","keys_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/keys{/key_id}","collaborators_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/teams","hooks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/hooks","issue_events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/events{/number}","events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/events","assignees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/assignees{/user}","branches_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/branches{/branch}","tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/tags","blobs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/refs{/sha}","trees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/trees{/sha}","statuses_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/statuses/{sha}","languages_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/languages","stargazers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/stargazers","contributors_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contributors","subscribers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscribers","subscription_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscription","commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/commits{/sha}","git_commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/commits{/sha}","comments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/comments{/number}","issue_comment_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/comments{/number}","contents_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contents/{+path}","compare_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/compare/{base}...{head}","merges_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/merges","archive_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/downloads","issues_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues{/number}","pulls_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/pulls{/number}","milestones_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/milestones{/number}","notifications_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/labels{/name}","releases_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/releases{/id}","deployments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/deployments","created_at":"2016-09-20T23:28:40Z","updated_at":"2016-09-20T23:28:40Z","pushed_at":"2016-12-28T19:39:53Z","git_url":"git://github.com/TaskClusterRobot/hooks-testing.git","ssh_url":"git@github.com:TaskClusterRobot/hooks-testing.git","clone_url":"https://github.com/TaskClusterRobot/hooks-testing.git","svn_url":"https://github.com/TaskClusterRobot/hooks-testing","homepage":null,"size":20,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":0,"default_branch":"master"},"sender":{"login":"TaskClusterRobot","id":14795478,"avatar_url":"https://avatars.githubusercontent.com/u/14795478?v=3","gravatar_id":"","url":"https://api.github.com/users/TaskClusterRobot","html_url":"https://github.com/TaskClusterRobot","followers_url":"https://api.github.com/users/TaskClusterRobot/followers","following_url":"https://api.github.com/users/TaskClusterRobot/following{/other_user}","gists_url":"https://api.github.com/users/TaskClusterRobot/gists{/gist_id}","starred_url":"https://api.github.com/users/TaskClusterRobot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TaskClusterRobot/subscriptions","organizations_url":"https://api.github.com/users/TaskClusterRobot/orgs","repos_url":"https://api.github.com/users/TaskClusterRobot/repos","events_url":"https://api.github.com/users/TaskClusterRobot/events{/privacy}","received_events_url":"https://api.github.com/users/TaskClusterRobot/received_events","type":"User","site_admin":false},"installation":{"id":5808}}}
12 |
--------------------------------------------------------------------------------
/test/data/webhooks/webhook.tag_push.json:
--------------------------------------------------------------------------------
1 | {"headers": {"Host": "requestb.in",
2 | "Accept": "*/*",
3 | "User-Agent": "GitHub-Hookshot/6f33334",
4 | "X-GitHub-Event": "push",
5 | "X-GitHub-Delivery": "9637a980-d8fb-11e6-9830-1244ca57c95f",
6 | "content-type": "application/json",
7 | "X-Hub-Signature": "sha1=ecbb00f92f620b2d7bc2773a642d2192c83db96b",
8 | "Content-Length": 6161}, "body":
9 |
10 | {"ref":"refs/tags/v1.0.2","before":"0000000000000000000000000000000000000000","after":"b79ce60be819cdc482c9c6a84dc3c457959aa66f","created":true,"deleted":false,"forced":false,"base_ref":null,"compare":"https://github.com/TaskClusterRobot/hooks-testing/compare/v1.0.2","commits":[],"head_commit":{"id":"b79ce60be819cdc482c9c6a84dc3c457959aa66f","tree_id":"1e853705688e2088b1d25c7d9c3e521c80ba10cf","distinct":true,"message":"Update README.md","timestamp":"2017-01-12T11:16:14-08:00","url":"https://github.com/TaskClusterRobot/hooks-testing/commit/b79ce60be819cdc482c9c6a84dc3c457959aa66f","author":{"name":"Irene","email":null,"username":"owlishDeveloper"},"committer":{"name":"GitHub","email":"noreply@github.com","username":"web-flow"},"added":[],"removed":[],"modified":["README.md"]},"repository":{"id":68761376,"name":"hooks-testing","full_name":"TaskClusterRobot/hooks-testing","owner":{"name":"TaskClusterRobot","email":"taskcluster-accounts@mozilla.com"},"private":false,"html_url":"https://github.com/TaskClusterRobot/hooks-testing","description":"","fork":false,"url":"https://github.com/TaskClusterRobot/hooks-testing","forks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/forks","keys_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/keys{/key_id}","collaborators_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/teams","hooks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/hooks","issue_events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/events{/number}","events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/events","assignees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/assignees{/user}","branches_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/branches{/branch}","tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/tags","blobs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/refs{/sha}","trees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/trees{/sha}","statuses_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/statuses/{sha}","languages_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/languages","stargazers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/stargazers","contributors_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contributors","subscribers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscribers","subscription_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscription","commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/commits{/sha}","git_commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/commits{/sha}","comments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/comments{/number}","issue_comment_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/comments{/number}","contents_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contents/{+path}","compare_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/compare/{base}...{head}","merges_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/merges","archive_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/downloads","issues_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues{/number}","pulls_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/pulls{/number}","milestones_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/milestones{/number}","notifications_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/labels{/name}","releases_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/releases{/id}","deployments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/deployments","created_at":1474414120,"updated_at":"2017-01-03T18:58:56Z","pushed_at":1484248575,"git_url":"git://github.com/TaskClusterRobot/hooks-testing.git","ssh_url":"git@github.com:TaskClusterRobot/hooks-testing.git","clone_url":"https://github.com/TaskClusterRobot/hooks-testing.git","svn_url":"https://github.com/TaskClusterRobot/hooks-testing","homepage":null,"size":24,"stargazers_count":1,"watchers_count":1,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":1,"default_branch":"master","stargazers":1,"master_branch":"master"},"pusher":{"name":"owlishDeveloper","email":null},"sender":{"login":"owlishDeveloper","id":18102552,"avatar_url":"https://avatars.githubusercontent.com/u/18102552?v=3","gravatar_id":"","url":"https://api.github.com/users/owlishDeveloper","html_url":"https://github.com/owlishDeveloper","followers_url":"https://api.github.com/users/owlishDeveloper/followers","following_url":"https://api.github.com/users/owlishDeveloper/following{/other_user}","gists_url":"https://api.github.com/users/owlishDeveloper/gists{/gist_id}","starred_url":"https://api.github.com/users/owlishDeveloper/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/owlishDeveloper/subscriptions","organizations_url":"https://api.github.com/users/owlishDeveloper/orgs","repos_url":"https://api.github.com/users/owlishDeveloper/repos","events_url":"https://api.github.com/users/owlishDeveloper/events{/privacy}","received_events_url":"https://api.github.com/users/owlishDeveloper/received_events","type":"User","site_admin":false},"installation":{"id":5808}}}
11 |
--------------------------------------------------------------------------------
/test/data/webhooks/webhook.unknown_event.json:
--------------------------------------------------------------------------------
1 | {"headers": {"Host": "requestb.in",
2 | "Accept": "*/*",
3 | "User-Agent": "GitHub-Hookshot/f5d5ca1",
4 | "X-GitHub-Event": "unknown-events-are-fun",
5 | "X-GitHub-Delivery": "736b3900-7f8a-11e6-8ec8-b75aea82a0ef",
6 | "content-type": "application/json",
7 | "X-Hub-Signature": "sha1=79fa92aab27dbb6ee39fe95138d05e3b2ce47601",
8 | "Content-Length": 6800}, "body":
9 |
10 |
11 | {"ref":"refs/heads/master","before":"efd1d89119e819a71a645c95c2590c0b3e7d0c83","after":"1edbd696be128a1ac187fac97e6a209fe9df3745","created":false,"deleted":false,"forced":false,"base_ref":null,"compare":"https://github.com/TaskClusterRobot/hooks-testing/compare/efd1d89119e8...1edbd696be12","commits":[{"id":"1edbd696be128a1ac187fac97e6a209fe9df3745","tree_id":"e9923f58e4cd74dc850443bc096784f9aaf69265","distinct":true,"message":"Update README.md","timestamp":"2016-09-20T16:32:10-07:00","url":"https://github.com/TaskClusterRobot/hooks-testing/commit/1edbd696be128a1ac187fac97e6a209fe9df3745","author":{"name":"Lisa Lionheart","email":"taskcluster-accounts@mozilla.com","username":"TaskClusterRobot"},"committer":{"name":"GitHub","email":"noreply@github.com","username":"web-flow"},"added":[],"removed":[],"modified":["README.md"]}],"head_commit":{"id":"1edbd696be128a1ac187fac97e6a209fe9df3745","tree_id":"e9923f58e4cd74dc850443bc096784f9aaf69265","distinct":true,"message":"Update README.md","timestamp":"2016-09-20T16:32:10-07:00","url":"https://github.com/TaskClusterRobot/hooks-testing/commit/1edbd696be128a1ac187fac97e6a209fe9df3745","author":{"name":"Lisa Lionheart","email":"taskcluster-accounts@mozilla.com","username":"TaskClusterRobot"},"committer":{"name":"GitHub","email":"noreply@github.com","username":"web-flow"},"added":[],"removed":[],"modified":["README.md"]},"repository":{"id":68761376,"name":"hooks-testing","full_name":"TaskClusterRobot/hooks-testing","owner":{"name":"TaskClusterRobot","email":"taskcluster-accounts@mozilla.com"},"private":false,"html_url":"https://github.com/TaskClusterRobot/hooks-testing","description":"for testing taskcluster-github","fork":false,"url":"https://github.com/TaskClusterRobot/hooks-testing","forks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/forks","keys_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/keys{/key_id}","collaborators_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/teams","hooks_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/hooks","issue_events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/events{/number}","events_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/events","assignees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/assignees{/user}","branches_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/branches{/branch}","tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/tags","blobs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/refs{/sha}","trees_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/trees{/sha}","statuses_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/statuses/{sha}","languages_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/languages","stargazers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/stargazers","contributors_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contributors","subscribers_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscribers","subscription_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/subscription","commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/commits{/sha}","git_commits_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/git/commits{/sha}","comments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/comments{/number}","issue_comment_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues/comments{/number}","contents_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/contents/{+path}","compare_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/compare/{base}...{head}","merges_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/merges","archive_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/downloads","issues_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/issues{/number}","pulls_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/pulls{/number}","milestones_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/milestones{/number}","notifications_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/labels{/name}","releases_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/releases{/id}","deployments_url":"https://api.github.com/repos/TaskClusterRobot/hooks-testing/deployments","created_at":1474414120,"updated_at":"2016-09-20T23:28:40Z","pushed_at":1474414330,"git_url":"git://github.com/TaskClusterRobot/hooks-testing.git","ssh_url":"git@github.com:TaskClusterRobot/hooks-testing.git","clone_url":"https://github.com/TaskClusterRobot/hooks-testing.git","svn_url":"https://github.com/TaskClusterRobot/hooks-testing","homepage":null,"size":0,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master","stargazers":0,"master_branch":"master"},"pusher":{"name":"TaskClusterRobot","email":"taskcluster-accounts@mozilla.com"},"sender":{"login":"TaskClusterRobot","id":14795478,"avatar_url":"https://avatars.githubusercontent.com/u/14795478?v=3","gravatar_id":"","url":"https://api.github.com/users/TaskClusterRobot","html_url":"https://github.com/TaskClusterRobot","followers_url":"https://api.github.com/users/TaskClusterRobot/followers","following_url":"https://api.github.com/users/TaskClusterRobot/following{/other_user}","gists_url":"https://api.github.com/users/TaskClusterRobot/gists{/gist_id}","starred_url":"https://api.github.com/users/TaskClusterRobot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TaskClusterRobot/subscriptions","organizations_url":"https://api.github.com/users/TaskClusterRobot/orgs","repos_url":"https://api.github.com/users/TaskClusterRobot/repos","events_url":"https://api.github.com/users/TaskClusterRobot/events{/privacy}","received_events_url":"https://api.github.com/users/TaskClusterRobot/received_events","type":"User","site_admin":false}}}
12 |
--------------------------------------------------------------------------------
/test/fake_github_test.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert');
2 | const _ = require('lodash');
3 | const github = require('@octokit/rest');
4 | const fakeGithubAuth = require('./github-auth');
5 |
6 | suite('fake github', function() {
7 |
8 | function checkKeys(obj, platonic) {
9 | let ours = _.filter(Object.keys(obj), k => !k.startsWith('_'));
10 | let theirs = Object.keys(platonic);
11 | assert.deepEqual(_.difference(ours, theirs), []);
12 | _.forEach(ours, k => {
13 | if (_.isObject(obj[k]) && obj[k].isSinonProxy) {
14 | checkKeys(obj[k], platonic[k]);
15 | }
16 | });
17 | }
18 |
19 | test('matches real lib', async function() {
20 | let inst = await fakeGithubAuth().getInstallationGithub();
21 | checkKeys(inst, github());
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/github-auth.js:
--------------------------------------------------------------------------------
1 | const Debug = require('debug');
2 | const sinon = require('sinon');
3 | const _ = require('lodash');
4 | const assert = require('assert');
5 |
6 | class FakeGithub {
7 | constructor(installation_id) {
8 | this._installedOn = null;
9 | this._taskcluster_yml_files = {};
10 | this._org_membership = {};
11 | this._repo_collaborators = {};
12 | this._github_users = [];
13 | this._repo_info = {};
14 | this._repositories = {};
15 | this._statuses = {};
16 | this._comments = {};
17 |
18 | const throwError = code => {
19 | let err = new Error();
20 | err.code = code;
21 | throw err;
22 | };
23 |
24 | const stubs = {
25 | 'repos.createStatus': ({owner, repo, sha, state, target_url, description, context}) => {
26 | if (repo === 'no-permission') {
27 | throwError(403);
28 | }
29 | const key = `${owner}/${repo}@${sha}`;
30 | const info = {
31 | state,
32 | target_url,
33 | description,
34 | context,
35 | };
36 | if (!this._statuses[key]) {
37 | this._statuses[key] = [];
38 | }
39 | this._statuses[key].push(info);
40 | },
41 | 'issues.createComment': ({owner, repo, number, body}) => {
42 | if (repo === 'no-permission') {
43 | throwError(403);
44 | }
45 | const key = `${owner}/${repo}@${number}`;
46 | const info = {
47 | body,
48 | };
49 | if (!this._comments[key]) {
50 | this._comments[key]=[];
51 | }
52 | this._comments[key].push(info);
53 | },
54 | 'repos.createCommitComment': () => {},
55 | 'orgs.checkMembership': async ({org, username}) => {
56 | if (this._org_membership[org] && this._org_membership[org].has(username)) {
57 | return {};
58 | } else {
59 | throwError(404);
60 | }
61 | },
62 | 'repos.checkCollaborator': async ({owner, repo, username}) => {
63 | const key = `${owner}/${repo}`;
64 | if (this._repo_collaborators[key] && this._repo_collaborators[key].has(username)) {
65 | return {};
66 | } else {
67 | throwError(404);
68 | }
69 | },
70 | 'repos.get': async ({owner, repo}) => {
71 | const key = `${owner}/${repo}`;
72 | if (this._repo_info[key]) {
73 | return {data: this._repo_info[key]};
74 | } else {
75 | throwError(404);
76 | }
77 | },
78 | 'repos.getContents': async ({owner, repo, path, ref}) => {
79 | assert.equal(path, '.taskcluster.yml');
80 | const key = `${owner}/${repo}@${ref}`;
81 | if (this._taskcluster_yml_files[key]) {
82 | return {data: {content: new Buffer(
83 | JSON.stringify(this._taskcluster_yml_files[key])
84 | ).toString('base64')}};
85 | } else {
86 | let err = new Error();
87 | err.code = 404;
88 | throw err;
89 | }
90 | },
91 | 'users.getByUsername': async ({username}) => {
92 | let user = _.find(this._github_users, {username});
93 | if (user) {
94 | return {data: user};
95 | } else {
96 | throwError(404);
97 | }
98 | },
99 | 'apps.listRepos': async () => {
100 | return {data: this._repositories};
101 | },
102 | 'repos.listStatusesForRef': async ({owner, repo, ref}) => {
103 | const key = `${owner}/${repo}@${ref}`;
104 | if (this._statuses[key]) {
105 | return {data: this._statuses[key]};
106 | } else {
107 | throwError(404);
108 | }
109 | },
110 | 'checks.create': async ({owner, repo, name, head_sha, output, details_url, actions, status, conclusion}) => {
111 | if (repo === 'no-permission') {
112 | throwError(403);
113 | }
114 |
115 | const check_run_id = Math.floor(Math.random()*(9999-1000)) + 1000;
116 |
117 | return {
118 | data: {
119 | id: check_run_id,
120 | check_suite: {id: 5555},
121 | },
122 | };
123 | },
124 | 'checks.update': async ({repo}) => {
125 | if (repo === 'no-permission') {
126 | throwError(403);
127 | }
128 | return {};
129 | },
130 | };
131 |
132 | const debug = Debug('FakeGithub');
133 | _.forEach(stubs, (implementation, name) => {
134 | let atoms = name.split(/\./);
135 | let obj = this; // eslint-disable-line consistent-this
136 | while (atoms.length > 1) {
137 | const atom = atoms.shift();
138 | if (!obj[atom]) {
139 | obj[atom] = {};
140 | }
141 | obj = obj[atom];
142 | }
143 |
144 | const atom = atoms.shift();
145 | obj[atom] = sinon.spy(async (options) => {
146 | debug(`inst(${installation_id}).${name}(${JSON.stringify(options)})`);
147 | return await (implementation || (() => {}))(options);
148 | });
149 | });
150 | }
151 |
152 | setTaskclusterYml({owner, repo, ref, content}) {
153 | const key = `${owner}/${repo}@${ref}`;
154 | this._taskcluster_yml_files[key] = content;
155 | }
156 |
157 | setOrgMember({org, member}) {
158 | if (!this._org_membership[org]) {
159 | this._org_membership[org] = new Set();
160 | }
161 | this._org_membership[org].add(member);
162 | }
163 |
164 | setRepoCollaborator({owner, repo, username}) {
165 | const key = `${owner}/${repo}`;
166 | if (!this._repo_collaborators[key]) {
167 | this._repo_collaborators[key] = new Set();
168 | }
169 | this._repo_collaborators[key].add(username);
170 | }
171 |
172 | setRepoInfo({owner, repo, info}) {
173 | const key = `${owner}/${repo}`;
174 | this._repo_info[key] = info;
175 | }
176 |
177 | setUser({id, email, username}) {
178 | assert(id, 'must provide id to setUser');
179 | assert(email, 'must provide email to setUser');
180 | assert(username, 'must provide username to setUser');
181 | this._github_users.push({id, email, username});
182 | }
183 |
184 | setRepositories(...repoNames) {
185 | // This function accepts 1 to n strings
186 | this._repositories.repositories = [...repoNames].map(repo => {return {name: repo};});
187 | this._repositories.total_count = this._repositories.repositories.length;
188 | }
189 |
190 | setStatuses({owner, repo, ref, info}) {
191 | const key = `${owner}/${repo}@${ref}`;
192 | this._statuses[key] = info;
193 | }
194 |
195 | listStatusesForRef({owner, repo, ref}) {
196 | const key = `${owner}/${repo}@${ref}`;
197 | return {data: this._statuses[key]};
198 | }
199 |
200 | getComments({owner, repo, number}) {
201 | const key = `${owner}/${repo}@${number}`;
202 | return {data: this._comments[key]};
203 | }
204 |
205 | hasNextPage() {
206 | return false;
207 | }
208 | }
209 |
210 | class FakeGithubAuth {
211 | constructor() {
212 | this.installations = {};
213 | }
214 |
215 | resetStubs() {
216 | this.installations = {};
217 | }
218 |
219 | async getInstallationGithub(installation_id) {
220 | return this.inst(installation_id);
221 | }
222 |
223 | // sync shorthand to getInstallationGithub for use in test scripts
224 | inst(installation_id) {
225 | if (!(installation_id in this.installations)) {
226 | this.installations[installation_id] = new FakeGithub(installation_id);
227 | }
228 | return this.installations[installation_id];
229 | }
230 |
231 | // For testing purposes, insert a new install
232 | createInstall(installation_id, owner, repos) {
233 | let installation = new FakeGithub(installation_id);
234 | installation._installedOn = owner;
235 | installation.setRepositories(...repos);
236 | this.installations[installation_id] = installation;
237 | }
238 |
239 | async getIntegrationGithub() {
240 | return {
241 | apps: {
242 | getInstallations: async () => {
243 | return {data: _.map(this.installations, (install, id) => ({
244 | id: parseInt(id, 10),
245 | account: {login: install._installedOn},
246 | }))};
247 | },
248 | },
249 | };
250 | }
251 | }
252 |
253 | module.exports = () => new FakeGithubAuth();
254 |
--------------------------------------------------------------------------------
/test/helper.js:
--------------------------------------------------------------------------------
1 | const http = require('http');
2 | const fs = require('fs');
3 | const _ = require('lodash');
4 | const slugid = require('slugid');
5 | const builder = require('../src/api');
6 | const taskcluster = require('taskcluster-client');
7 | const load = require('../src/main');
8 | const fakeGithubAuth = require('./github-auth');
9 | const data = require('../src/data');
10 | const libUrls = require('taskcluster-lib-urls');
11 | const {fakeauth, stickyLoader, Secrets} = require('taskcluster-lib-testing');
12 | const {FakeClient} = require('taskcluster-lib-pulse');
13 |
14 | exports.load = stickyLoader(load);
15 |
16 | suiteSetup(async function() {
17 | exports.load.inject('profile', 'test');
18 | exports.load.inject('process', 'test');
19 | let fakePulseClient = new FakeClient();
20 | fakePulseClient.namespace = 'taskcluster-fake';
21 | exports.load.inject('pulseClient', fakePulseClient);
22 | });
23 |
24 | // set up the testing secrets
25 | exports.secrets = new Secrets({
26 | secretName: 'project/taskcluster/testing/taskcluster-github',
27 | secrets: {
28 | taskcluster: [
29 | {env: 'TASKCLUSTER_ROOT_URL', cfg: 'taskcluster.rootUrl', name: 'rootUrl'},
30 | {env: 'TASKCLUSTER_CLIENT_ID', cfg: 'taskcluster.credentials.clientId', name: 'clientId'},
31 | {env: 'TASKCLUSTER_ACCESS_TOKEN', cfg: 'taskcluster.credentials.accessToken', name: 'accessToken'},
32 | ],
33 | },
34 | load: exports.load,
35 | });
36 |
37 | // Build an http request from a json file with fields describing
38 | // headers and a body
39 | exports.jsonHttpRequest = function(jsonFile, options) {
40 | let defaultOptions = {
41 | hostname: 'localhost',
42 | port: 60415,
43 | path: '/api/github/v1/github',
44 | method: 'POST',
45 | };
46 | options = _.defaultsDeep(options, defaultOptions);
47 | let jsonData = JSON.parse(fs.readFileSync(jsonFile));
48 | options.headers = jsonData.headers;
49 |
50 | return new Promise (function(accept, reject) {
51 | try {
52 | let req = http.request(options, accept);
53 | req.write(JSON.stringify(jsonData.body));
54 | req.end();
55 | } catch (e) {
56 | reject(e);
57 | }
58 | });
59 | };
60 |
61 | /**
62 | * Set up a fake publisher. Call this before withServer to ensure the server
63 | * uses the same publisher.
64 | */
65 | exports.withFakePublisher = (mock, skipping) => {
66 | suiteSetup(async function() {
67 | if (skipping()) {
68 | return;
69 | }
70 | exports.load.save();
71 |
72 | exports.load.cfg('taskcluster.rootUrl', libUrls.testRootUrl());
73 | await exports.load('publisher');
74 | });
75 |
76 | suiteTeardown(function() {
77 | if (skipping()) {
78 | return;
79 | }
80 | exports.load.restore();
81 | });
82 | };
83 |
84 | /**
85 | * Set helper.Builds and helper.OwnersDirectory to fully-configured entity
86 | * objects, and inject them into the loader. These tables are cleared at
87 | * suiteSetup, but not between test cases.
88 | */
89 | exports.withEntities = (mock, skipping) => {
90 | suiteSetup(async function() {
91 | if (skipping()) {
92 | return;
93 | }
94 |
95 | // Need to generate these each time so that pushes and PRs can run at the same time
96 | // Maybe at some point we should cook up a real solution to this.
97 | const tableVersion = slugid.nice().replace(/[_-]/g, '');
98 | exports.buildsTableName = `TaskclusterGithubBuildsV${tableVersion}`;
99 | exports.ownersTableName = `TaskclusterIntegrationOwnersV${tableVersion}`;
100 | exports.checkRunsTableName = `TaskclusterCheckRunsV${tableVersion}`;
101 | exports.load.cfg('app.buildsTableName', exports.buildsTableName);
102 | exports.load.cfg('app.ownersDirectoryTableName', exports.ownersTableName);
103 | exports.load.cfg('app.checkRunsTableName', exports.checkRunsTableName);
104 |
105 | if (mock) {
106 | const cfg = await exports.load('cfg');
107 | exports.load.inject('Builds', data.Builds.setup({
108 | tableName: 'Builds',
109 | credentials: 'inMemory',
110 | }));
111 | exports.load.inject('OwnersDirectory', data.OwnersDirectory.setup({
112 | tableName: 'OwnersDirectory',
113 | credentials: 'inMemory',
114 | }));
115 | exports.load.inject('CheckRuns', data.CheckRuns.setup({
116 | tableName: 'CheckRuns',
117 | credentials: 'inMemory',
118 | }));
119 | }
120 |
121 | exports.Builds = await exports.load('Builds');
122 | await exports.Builds.ensureTable();
123 |
124 | exports.OwnersDirectory = await exports.load('OwnersDirectory');
125 | await exports.OwnersDirectory.ensureTable();
126 |
127 | exports.CheckRuns = await exports.load('CheckRuns');
128 | await exports.CheckRuns.ensureTable();
129 | });
130 |
131 | const cleanup = async () => {
132 | if (!skipping()) {
133 | await exports.Builds.scan({}, {handler: secret => secret.remove()});
134 | await exports.OwnersDirectory.scan({}, {handler: secret => secret.remove()});
135 | await exports.CheckRuns.scan({}, {handler: secret => secret.remove()});
136 | }
137 | };
138 | suiteSetup(cleanup);
139 | suiteTeardown(cleanup);
140 | };
141 |
142 | /**
143 | * Set the `github` loader component to a fake version.
144 | * This is reset before each test. Call this before withServer.
145 | */
146 | exports.withFakeGithub = (mock, skipping) => {
147 | suiteSetup(function() {
148 | exports.load.inject('github', fakeGithubAuth());
149 | });
150 |
151 | suiteTeardown(function() {
152 | exports.load.remove('github');
153 | });
154 |
155 | setup(async function() {
156 | let fakeGithub = await exports.load('github');
157 | fakeGithub.resetStubs();
158 | });
159 | };
160 |
161 | /**
162 | * Set up an API server. Call this after withEntities, so the server
163 | * uses the same entities classes.
164 | *
165 | * This also sets up helper.apiClient as a client of the service API.
166 | */
167 | exports.withServer = (mock, skipping) => {
168 | let webServer;
169 |
170 | suiteSetup(async function() {
171 | if (skipping()) {
172 | return;
173 | }
174 | const cfg = await exports.load('cfg');
175 |
176 | // even if we are using a "real" rootUrl for access to Azure, we use
177 | // a local rootUrl to test the API, including mocking auth on that
178 | // rootUrl.
179 | const rootUrl = 'http://localhost:60415';
180 | exports.load.cfg('taskcluster.rootUrl', rootUrl);
181 | exports.load.cfg('taskcluster.clientId', null);
182 | exports.load.cfg('taskcluster.accessToken', null);
183 |
184 | fakeauth.start({'test-client': ['*']}, {rootUrl});
185 |
186 | const GithubClient = taskcluster.createClient(builder.reference());
187 |
188 | exports.apiClient = new GithubClient({
189 | credentials: {clientId: 'test-client', accessToken: 'unused'},
190 | rootUrl,
191 | });
192 |
193 | webServer = await exports.load('server');
194 | });
195 |
196 | suiteTeardown(async function() {
197 | if (skipping()) {
198 | return;
199 | }
200 | if (webServer) {
201 | await webServer.terminate();
202 | webServer = null;
203 | }
204 | fakeauth.stop();
205 | });
206 | };
207 |
--------------------------------------------------------------------------------
/test/invalid-yaml.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 0,
3 | "WHAT WHAT WHAT?": {},
4 | "tasks": [
5 | {
6 | "extra": {
7 | "github": {
8 | "env": true
9 | }
10 | }
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --ui tdd
2 | --timeout 240s
3 | --reporter spec
4 |
--------------------------------------------------------------------------------
/test/pr-allowed_test.js:
--------------------------------------------------------------------------------
1 | const debug = require('debug')('test');
2 | const helper = require('./helper');
3 | const assert = require('assert');
4 | const prAllowed = require('../src/pr-allowed');
5 |
6 | suite('allowPullRequests', function() {
7 | helper.withFakeGithub(false, () => false);
8 |
9 | let github = null;
10 |
11 | setup(async function() {
12 | github = await helper.load('github');
13 | });
14 |
15 | suite('getRepoPolicy', function() {
16 | setup(function() {
17 | github.inst(9999).setRepoInfo({
18 | owner: 'taskcluster',
19 | repo: 'testing',
20 | info: {default_branch: 'development'},
21 | });
22 | });
23 |
24 | test('returns "collaborators" when no .taskcluster.yml exists', async function() {
25 | assert.equal(await prAllowed.getRepoPolicy({
26 | organization: 'taskcluster',
27 | repository: 'testing',
28 | instGithub: github.inst(9999),
29 | debug,
30 | }), 'collaborators');
31 | });
32 |
33 | test('returns "collaborators" when .taskcluster.yml omits allowPullRequests', async function() {
34 | github.inst(9999).setTaskclusterYml({
35 | owner: 'taskcluster',
36 | repo: 'testing',
37 | ref: 'development',
38 | content: {},
39 | });
40 |
41 | assert.equal(await prAllowed.getRepoPolicy({
42 | organization: 'taskcluster',
43 | repository: 'testing',
44 | instGithub: github.inst(9999),
45 | debug,
46 | }), 'collaborators');
47 | });
48 |
49 | test('returns value of allowPullRequests from .taskcluster.yml on default branch', async function() {
50 | github.inst(9999).setTaskclusterYml({
51 | owner: 'taskcluster',
52 | repo: 'testing',
53 | ref: 'development',
54 | content: {allowPullRequests: 'maybe'},
55 | });
56 |
57 | assert.equal(await prAllowed.getRepoPolicy({
58 | organization: 'taskcluster',
59 | repository: 'testing',
60 | instGithub: github.inst(9999),
61 | debug,
62 | }), 'maybe');
63 | });
64 |
65 | test('looks at policy.pullRequests for v1', async function() {
66 | github.inst(9999).setTaskclusterYml({
67 | owner: 'taskcluster',
68 | repo: 'testing',
69 | ref: 'development',
70 | content: {version: 1, policy: {pullRequests: 'maybe'}},
71 | });
72 |
73 | assert.equal(await prAllowed.getRepoPolicy({
74 | organization: 'taskcluster',
75 | repository: 'testing',
76 | instGithub: github.inst(9999),
77 | debug,
78 | }), 'maybe');
79 | });
80 |
81 | test('v1: handles policy without pullRequests property', async function() {
82 | github.inst(9999).setTaskclusterYml({
83 | owner: 'taskcluster',
84 | repo: 'testing',
85 | ref: 'development',
86 | content: {version: 1, policy: {otherPolicy: 'sure'}},
87 | });
88 |
89 | assert.equal(await prAllowed.getRepoPolicy({
90 | organization: 'taskcluster',
91 | repository: 'testing',
92 | instGithub: github.inst(9999),
93 | debug,
94 | }), 'collaborators');
95 | });
96 |
97 | test('v1: handles tc.yml without policy property', async function() {
98 | github.inst(9999).setTaskclusterYml({
99 | owner: 'taskcluster',
100 | repo: 'testing',
101 | ref: 'development',
102 | content: {version: 1, tasks: []},
103 | });
104 |
105 | assert.equal(await prAllowed.getRepoPolicy({
106 | organization: 'taskcluster',
107 | repository: 'testing',
108 | instGithub: github.inst(9999),
109 | debug,
110 | }), 'collaborators');
111 | });
112 | });
113 |
114 | suite('isCollaborator', function() {
115 | test('disallows the case where the login is an org member but not collaborator', async function() {
116 | // (this is a behavior change from old behavior; the code doesn't even call the github method)
117 | github.inst(9999).setOrgMember({org: 'buildbot', member: 'djmitche'});
118 | assert.equal(await prAllowed.isCollaborator({
119 | login: 'djmitche',
120 | organization: 'buildbot',
121 | repository: 'buildbot',
122 | sha: 'abcd',
123 | instGithub: github.inst(9999),
124 | debug,
125 | }), false);
126 | });
127 |
128 | test('allows the case where the login is a repo collaborator but not org member', async function() {
129 | github.inst(9999).setRepoCollaborator({owner: 'buildbot', repo: 'bbdocs', username: 'djmitche'});
130 | assert.equal(await prAllowed.isCollaborator({
131 | login: 'djmitche',
132 | organization: 'buildbot',
133 | repository: 'bbdocs',
134 | sha: 'abcd',
135 | instGithub: github.inst(9999),
136 | debug,
137 | }), true);
138 | });
139 |
140 | test('disallows the case where none of this is true', async function() {
141 | assert.equal(await prAllowed.isCollaborator({
142 | login: 'djmitche',
143 | organization: 'buildbot',
144 | repository: 'bbdocs',
145 | sha: 'abcd',
146 | instGithub: github.inst(9999),
147 | debug,
148 | }), false);
149 | });
150 | });
151 | });
152 |
--------------------------------------------------------------------------------
/test/pulse_test.js:
--------------------------------------------------------------------------------
1 | const helper = require('./helper');
2 | const assert = require('assert');
3 | const libUrls = require('taskcluster-lib-urls');
4 |
5 | helper.secrets.mockSuite('pulse', ['taskcluster'], function(mock, skipping) {
6 | helper.withEntities(mock, skipping);
7 | helper.withFakePublisher(mock, skipping);
8 | helper.withFakeGithub(mock, skipping);
9 | helper.withServer(mock, skipping);
10 |
11 | let github = null;
12 | let publisher = null;
13 |
14 | setup(async function() {
15 | await helper.load('cfg');
16 | helper.load.cfg('taskcluster.rootUrl', libUrls.testRootUrl());
17 |
18 | github = await helper.load('github');
19 | github.inst(5808).setUser({id: 14795478, email: 'someuser@github.com', username: 'TaskClusterRobot'});
20 | github.inst(5808).setUser({id: 18102552, email: 'anotheruser@github.com', username: 'owlishDeveloper'});
21 |
22 | publisher = await helper.load('publisher');
23 | });
24 |
25 | /**
26 | * Run a test which verifies that pulse messages are being produced
27 | * for valid webhook requests.
28 | * params: {
29 | * testName: 'some test',
30 | * listenFor: 'some event type',
31 | * exchangeFunc: 'name of exchange function',
32 | * routingKey: {...}, a dict containing a pulse routing key
33 | * details: {...}, a dict of details we expect to seein the msg payload
34 | * jsonFile: 'data file'
35 | * tasks_for: 'the event type; for v1'
36 | * branch: 'the head branch name; for v1'
37 | **/
38 | function pulseTest(params) {
39 | test(params.testName, async function() {
40 | let published = [];
41 | let fakePublish = event => {
42 | published.push(event);
43 | };
44 | publisher.on('message', fakePublish);
45 |
46 | // Trigger a pull-request message
47 | try {
48 | let res = await helper.jsonHttpRequest('./test/data/webhooks/' + params.jsonFile);
49 | res.connection.destroy();
50 | } finally {
51 | publisher.removeListener('message', fakePublish);
52 | }
53 |
54 | let expected = [{
55 | exchange: `exchange/taskcluster-fake/v1/${params.listenFor}`,
56 | routingKey: params.routingKey,
57 | payload: {
58 | organization: 'TaskClusterRobot',
59 | details: params.details,
60 | installationId: 5808,
61 | repository: 'hooks-testing',
62 | eventId: params.eventId,
63 | version: 1,
64 | body: require('./data/webhooks/' + params.jsonFile).body,
65 | tasks_for: params.tasks_for,
66 | branch: params.branch,
67 | },
68 | CCs: [],
69 | }];
70 | if (params.action) {
71 | expected[0].payload.action = params.action;
72 | }
73 | assert.deepEqual(published, expected);
74 | });
75 | }
76 |
77 | pulseTest({
78 | testName: 'Publish Pull Request',
79 | listenFor: 'pull-request',
80 | action: 'opened',
81 | exchangeFunc: 'pullRequest',
82 | routingKey: 'primary.TaskClusterRobot.hooks-testing.opened',
83 | eventId: '81254e00-d9c1-11e6-8964-748a671f0cee',
84 | details: {
85 | 'event.base.ref': 'refs/heads/master',
86 | 'event.base.repo.branch': 'master',
87 | 'event.base.repo.name': 'hooks-testing',
88 | 'event.base.repo.url': 'https://github.com/TaskClusterRobot/hooks-testing.git',
89 | 'event.base.sha': '55e752e3a914db81eee3f90260f7eb69b7169ada',
90 | 'event.base.user.login': 'TaskClusterRobot',
91 | 'event.head.ref': 'refs/heads/owlishDeveloper-patch-2',
92 | 'event.head.repo.branch': 'owlishDeveloper-patch-2',
93 | 'event.head.repo.name': 'hooks-testing',
94 | 'event.head.repo.url': 'https://github.com/TaskClusterRobot/hooks-testing.git',
95 | 'event.head.sha': 'b12ead3c5f3499e34356c970e20d7858f1747542',
96 | 'event.head.user.login': 'owlishDeveloper',
97 | 'event.head.user.id': 18102552,
98 | 'event.pullNumber': 36,
99 | 'event.type': 'pull_request.opened',
100 | 'event.head.user.email': 'anotheruser@github.com',
101 | 'event.title': 'Update README.md',
102 | },
103 | jsonFile: 'webhook.pull_request.open.json',
104 | tasks_for: 'github-pull-request',
105 | branch: 'owlishDeveloper-patch-2',
106 | });
107 |
108 | pulseTest({
109 | testName: 'Publish Push',
110 | listenFor: 'push',
111 | exchangeFunc: 'push',
112 | routingKey: 'primary.TaskClusterRobot.hooks-testing',
113 | eventId: '9637a980-d8fb-11e6-9830-1244ca57c95f',
114 | details: {
115 | 'event.base.ref': 'refs/heads/master',
116 | 'event.base.repo.branch': 'master',
117 | 'event.base.repo.name': 'hooks-testing',
118 | 'event.base.repo.url': 'https://github.com/TaskClusterRobot/hooks-testing.git',
119 | 'event.base.sha': '7a257a6d139708a3188bf2e0cd1f15e466a88d0e',
120 | 'event.base.user.login': 'owlishDeveloper',
121 | 'event.head.ref': 'refs/heads/master',
122 | 'event.head.repo.branch': 'master',
123 | 'event.head.repo.name': 'hooks-testing',
124 | 'event.head.repo.url': 'https://github.com/TaskClusterRobot/hooks-testing.git',
125 | 'event.head.sha': 'b79ce60be819cdc482c9c6a84dc3c457959aa66f',
126 | 'event.head.user.login': 'owlishDeveloper',
127 | 'event.head.user.id': 18102552,
128 | 'event.type': 'push',
129 | 'event.head.user.email': 'anotheruser@github.com',
130 | },
131 | jsonFile: 'webhook.push.json',
132 | tasks_for: 'github-push',
133 | branch: 'master',
134 | });
135 |
136 | pulseTest({
137 | testName: 'Publish Release',
138 | listenFor: 'release',
139 | exchangeFunc: 'release',
140 | routingKey: 'primary.TaskClusterRobot.hooks-testing',
141 | eventId: '2c81a200-cd36-11e6-9106-ad0d7be0e22e',
142 | details: {
143 | 'event.type': 'release',
144 | 'event.base.repo.branch': 'master',
145 | 'event.head.user.login': 'TaskClusterRobot',
146 | 'event.head.user.id': 14795478,
147 | 'event.version': 'testing-789',
148 | 'event.name': 'Testing 123',
149 | 'event.head.repo.name': 'hooks-testing',
150 | 'event.head.repo.url': 'https://github.com/TaskClusterRobot/hooks-testing.git',
151 | 'event.release.url': 'https://api.github.com/repos/TaskClusterRobot/hooks-testing/releases/5027516',
152 | 'event.prerelease': false,
153 | 'event.draft': false,
154 | 'event.tar': 'https://api.github.com/repos/TaskClusterRobot/hooks-testing/tarball/testing-789',
155 | 'event.zip': 'https://api.github.com/repos/TaskClusterRobot/hooks-testing/zipball/testing-789',
156 | 'event.head.user.email': 'someuser@github.com',
157 | },
158 | jsonFile: 'webhook.release.json',
159 | tasks_for: 'github-release',
160 | branch: 'master',
161 | });
162 |
163 | pulseTest({
164 | testName: 'Publish Tag Push',
165 | listenFor: 'push',
166 | exchangeFunc: 'push',
167 | routingKey: 'primary.TaskClusterRobot.hooks-testing',
168 | eventId: '9637a980-d8fb-11e6-9830-1244ca57c95f',
169 | details: {
170 | 'event.base.ref': 'refs/tags/v1.0.2',
171 | 'event.base.repo.name': 'hooks-testing',
172 | 'event.base.repo.url': 'https://github.com/TaskClusterRobot/hooks-testing.git',
173 | 'event.base.sha': '0000000000000000000000000000000000000000',
174 | 'event.base.user.login': 'owlishDeveloper',
175 | 'event.head.ref': 'refs/tags/v1.0.2',
176 | 'event.head.tag': 'v1.0.2',
177 | 'event.head.repo.name': 'hooks-testing',
178 | 'event.head.repo.url': 'https://github.com/TaskClusterRobot/hooks-testing.git',
179 | 'event.head.sha': 'b79ce60be819cdc482c9c6a84dc3c457959aa66f',
180 | 'event.head.user.login': 'owlishDeveloper',
181 | 'event.head.user.id': 18102552,
182 | 'event.type': 'tag',
183 | 'event.head.user.email': 'anotheruser@github.com',
184 | },
185 | jsonFile: 'webhook.tag_push.json',
186 | tasks_for: 'github-push',
187 | branch: 'v1.0.2',
188 | });
189 | });
190 |
--------------------------------------------------------------------------------
/test/references_test.js:
--------------------------------------------------------------------------------
1 | const builder = require('../src/api');
2 | const exchanges = require('../src/exchanges');
3 | const helper = require('./helper');
4 | const References = require('taskcluster-lib-references');
5 |
6 | suite('references_test.js', function() {
7 | test('references validate', async function() {
8 | const schemaset = await helper.load('schemaset');
9 | const references = References.fromService({schemaset, builder, exchanges});
10 | references.validate();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/test/sync_test.js:
--------------------------------------------------------------------------------
1 | const helper = require('./helper');
2 | const assert = require('assert');
3 |
4 | /**
5 | * Tests of installation syncing
6 | */
7 | helper.secrets.mockSuite('syncInstallations', ['taskcluster'], function(mock, skipping) {
8 | helper.withEntities(mock, skipping);
9 | helper.withFakeGithub(mock, skipping);
10 | helper.withServer(mock, skipping);
11 |
12 | let github;
13 |
14 | suiteSetup(async function() {
15 | await helper.OwnersDirectory.scan({}, {
16 | handler: owner => owner.remove(),
17 | });
18 |
19 | github = await helper.load('github');
20 | });
21 |
22 | test('integration installation', async function() {
23 | let result = await helper.apiClient.repository('abc123', 'coolRepo');
24 | assert.deepEqual(result, {installed: false});
25 | github.createInstall(12345, 'abc123', ['coolRepo']);
26 | github.createInstall(12346, 'abc124', ['coolerRepo']);
27 | github.createInstall(12347, 'abc125', ['coolestRepo']);
28 |
29 | await helper.load('syncInstallations');
30 |
31 | result = await helper.apiClient.repository('abc123', 'coolRepo');
32 | result = await helper.apiClient.repository('abc124', 'coolerRepo');
33 | result = await helper.apiClient.repository('abc125', 'coolestRepo');
34 | assert.deepEqual(result, {installed: true});
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/test/tc-yaml_test.js:
--------------------------------------------------------------------------------
1 | const TcYaml = require('../src/tc-yaml');
2 | const assume = require('assume');
3 |
4 | suite('tc-yaml_test.js', function() {
5 | suite('VersionOne', function() {
6 | const tcyaml = TcYaml.instantiate(1);
7 | const cfg = {
8 | taskcluster: {
9 | schedulerId: 'test-sched',
10 | },
11 | app: {
12 | checkTaskRoute: 'checks-queue',
13 | statusTaskRoute: 'statuses-queue',
14 | },
15 | };
16 | const now = new Date().toJSON();
17 |
18 | test('compileTasks with no tasks', function() {
19 | const config = {
20 | tasks: [],
21 | };
22 | tcyaml.compileTasks(config, cfg, {}, now);
23 | assume(config.tasks).to.deeply.equal([]);
24 | });
25 |
26 | test('compileTasks with one task sets default properties', function() {
27 | const config = {
28 | tasks: [{}],
29 | };
30 |
31 | tcyaml.compileTasks(config, cfg, {}, now);
32 | assume(config.tasks).to.deeply.equal([{
33 | taskId: config.tasks[0].taskId,
34 | task: {
35 | created: now,
36 | taskGroupId: config.tasks[0].taskId, // matches taskId
37 | schedulerId: 'test-sched',
38 | routes: ['statuses-queue'],
39 | },
40 | }]);
41 | });
42 |
43 | test('compileTasks with one taskId sets taskGroupId', function() {
44 | const config = {
45 | tasks: [{
46 | taskId: 'task-1',
47 | }],
48 | };
49 | tcyaml.compileTasks(config, cfg, {}, now);
50 | assume(config.tasks).to.deeply.equal([{
51 | taskId: 'task-1',
52 | task: {
53 | created: now,
54 | taskGroupId: 'task-1',
55 | schedulerId: 'test-sched',
56 | routes: ['statuses-queue'],
57 | },
58 | }]);
59 | });
60 |
61 | test('compileTasks with taskGroupId and one task sets taskId', function() {
62 | const config = {
63 | tasks: [{
64 | taskGroupId: 'tgid-1',
65 | }],
66 | };
67 | tcyaml.compileTasks(config, cfg, {}, now);
68 | assume(config.tasks).to.deeply.equal([{
69 | taskId: 'tgid-1',
70 | task: {
71 | created: now,
72 | taskGroupId: 'tgid-1',
73 | schedulerId: 'test-sched',
74 | routes: ['statuses-queue'],
75 | },
76 | }]);
77 | });
78 |
79 | test('compileTasks with two tasks sets default properties', function() {
80 | const config = {
81 | tasks: [{}, {}],
82 | };
83 | tcyaml.compileTasks(config, cfg, {}, now);
84 | // taskGroupIds match
85 | assume(config.tasks[0].task.taskGroupId).to.equal(config.tasks[1].task.taskGroupId);
86 | // taskIds don't
87 | assume(config.tasks[0].taskId).to.not.equal(config.tasks[1].taskId);
88 | // taskGroupId does not match any taskId
89 | assume(config.tasks[0].taskId).to.not.equal(config.tasks[0].task.taskGroupId);
90 | assume(config.tasks[1].taskId).to.not.equal(config.tasks[1].task.taskGroupId);
91 | });
92 |
93 | test('compileTasks forces schedulerId, but uses user-supplied taskId/taskGroupId', function() {
94 | const config = {
95 | tasks: [{
96 | taskId: 'task-1',
97 | taskGroupId: 'tgid-1',
98 | schedulerId: 'my-scheduler-id',
99 | }, {
100 | taskId: 'task-2',
101 | taskGroupId: 'tgid-2',
102 | schedulerId: 'my-scheduler-id',
103 | }],
104 | };
105 | tcyaml.compileTasks(config, cfg, {}, now);
106 | assume(config.tasks).to.deeply.equal([{
107 | taskId: 'task-1',
108 | task: {
109 | created: now,
110 | taskGroupId: 'tgid-1',
111 | schedulerId: 'test-sched',
112 | routes: ['statuses-queue'],
113 | },
114 | }, {
115 | taskId: 'task-2',
116 | task: {
117 | created: now,
118 | taskGroupId: 'tgid-2',
119 | schedulerId: 'test-sched',
120 | routes: ['statuses-queue'],
121 | },
122 | }]);
123 | });
124 |
125 | test('compileTasks sets checks route if we have reporting in the YML', function() {
126 | const config = {
127 | tasks: [{
128 | taskId: 'task-1',
129 | }],
130 | reporting: 'checks-v1',
131 | };
132 | tcyaml.compileTasks(config, cfg, {}, now);
133 | assume(config.tasks).to.deeply.equal([{
134 | taskId: 'task-1',
135 | task: {
136 | created: now,
137 | taskGroupId: 'task-1',
138 | schedulerId: 'test-sched',
139 | routes: ['checks-queue'],
140 | },
141 | }]);
142 | });
143 | });
144 | });
145 |
--------------------------------------------------------------------------------
/test/valid-yaml.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 0,
3 | "metadata": {
4 | "name": "TaskCluster GitHub Tests",
5 | "description": "Tests for taskcluster github in production",
6 | "owner": "{{ event.head.user.email }}",
7 | "source": "{{ event.head.repo.url }}"
8 | },
9 | "tasks": [
10 | {
11 | "provisionerId": "dummy-provisioner",
12 | "workerType": "dummy-worker",
13 | "extra": {
14 | "github": {
15 | "env": true,
16 | "events": [
17 | "pull_request.opened",
18 | "pull_request.synchronize",
19 | "pull_request.reopened",
20 | "push",
21 | "tag"
22 | ]
23 | }
24 | },
25 | "payload": {
26 | "maxRunTime": 3600,
27 | "image": "node:5",
28 | "command": [
29 | "/bin/bash",
30 | "-lc",
31 | "echo 'This works!'"
32 | ]
33 | },
34 | "metadata": {
35 | "name": "TaskCluster GitHub Tests",
36 | "description": "Tests for taskcluster github in production",
37 | "owner": "{{ event.head.user.email }}",
38 | "source": "{{ event.head.repo.url }}"
39 | }
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/test/webhook_test.js:
--------------------------------------------------------------------------------
1 | const helper = require('./helper');
2 | const assert = require('assert');
3 |
4 | helper.secrets.mockSuite('webhook', ['taskcluster'], function(mock, skipping) {
5 | helper.withEntities(mock, skipping);
6 | helper.withFakeGithub(mock, skipping);
7 | helper.withServer(mock, skipping);
8 |
9 | let github = null;
10 |
11 | setup(async function() {
12 | github = await helper.load('github');
13 | github.inst(5808).setUser({id: 14795478, email: 'someuser@github.com', username: 'TaskClusterRobot'});
14 | github.inst(5808).setUser({id: 18102552, email: 'anotheruser@github.com', username: 'owlishDeveloper'});
15 | });
16 |
17 | // Check the status code returned from a request containing some test data
18 | function statusTest(testName, jsonFile, statusCode) {
19 | test(testName, async function() {
20 | let response = await helper.jsonHttpRequest('./test/data/webhooks/' + jsonFile);
21 | assert.equal(response.statusCode, statusCode);
22 | response.connection.destroy();
23 | });
24 | }
25 |
26 | // Good data: should all return 200 responses
27 | statusTest('Pull Request Opened', 'webhook.pull_request.open.json', 204);
28 | statusTest('Pull Request Closed', 'webhook.pull_request.close.json', 204);
29 | statusTest('Push', 'webhook.push.json', 204);
30 | statusTest('Release', 'webhook.release.json', 204);
31 | statusTest('Tag', 'webhook.tag_push.json', 204);
32 |
33 | // Bad data: should all return 400 responses
34 | statusTest('Push without secret', 'webhook.push.no_secret.json', 400);
35 | statusTest('Unknown Event', 'webhook.unknown_event.json', 400);
36 | statusTest('Push with bad secret', 'webhook.push.bad_secret.json', 403);
37 | statusTest('Release with bad secret', 'webhook.release.bad_secret.json', 403);
38 | });
39 |
--------------------------------------------------------------------------------
/user-config-example.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Running the tests does not require any credentials (you need not
3 | # even create user-config.yml).
4 |
--------------------------------------------------------------------------------