├── .github
└── assignment.yml
├── .gitignore
├── .vscode
├── launch.json
├── settings.json
└── tasks.json
├── .vscodeignore
├── LICENSE
├── README.md
├── images
├── icon.png
└── in_action.gif
├── package-lock.json
├── package.json
├── resources
├── dark
│ └── refresh.svg
└── light
│ └── refresh.svg
├── src
├── extension.ts
├── git-credential-node.d.ts
├── github-issues-prs.ts
└── utils.ts
├── thirdparty
└── octicons
│ ├── LICENSE
│ ├── README
│ ├── dark
│ ├── bug.svg
│ ├── git-pull-request.svg
│ └── plus-small.svg
│ └── light
│ ├── bug.svg
│ ├── git-pull-request.svg
│ └── plus-small.svg
├── thirdpartynotices.txt
├── tsconfig.json
└── tslint.json
/.github/assignment.yml:
--------------------------------------------------------------------------------
1 | {
2 | perform: true,
3 | assignees: [ chrmarti ]
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | out
2 | node_modules
3 | *.vsix
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | // A launch configuration that compiles the extension and then opens it inside a new window
2 | {
3 | "version": "0.1.0",
4 | "configurations": [
5 | {
6 | "name": "Launch Extension",
7 | "type": "extensionHost",
8 | "request": "launch",
9 | "runtimeExecutable": "${execPath}",
10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}"],
11 | "stopOnEntry": false,
12 | "sourceMaps": true,
13 | "outFiles": ["${workspaceRoot}/out/src/**/*.js"],
14 | "preLaunchTask": "npm"
15 | },
16 | {
17 | "name": "Launch Tests",
18 | "type": "extensionHost",
19 | "request": "launch",
20 | "runtimeExecutable": "${execPath}",
21 | "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ],
22 | "stopOnEntry": false,
23 | "sourceMaps": true,
24 | "outFiles": ["${workspaceRoot}/out/test/**/*.js"],
25 | "preLaunchTask": "npm"
26 | },
27 | {
28 | "type": "node",
29 | "request": "attach",
30 | "name": "Attach to Extension Host",
31 | "protocol": "legacy",
32 | "port": 5870,
33 | "sourceMaps": true,
34 | "restart": true,
35 | "outDir": "${workspaceRoot}/out/src"
36 | }
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "files.exclude": {
4 | "out": false // set this to true to hide the "out" folder with the compiled JS files
5 | },
6 | "search.exclude": {
7 | "out": true // set this to false to include "out" folder in search results
8 | },
9 | "typescript.tsdk": "./node_modules/typescript/lib" // we want to use the TS server from our node_modules folder to control its version
10 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | // Available variables which can be used inside of strings.
2 | // ${workspaceRoot}: the root folder of the team
3 | // ${file}: the current opened file
4 | // ${fileBasename}: the current opened file's basename
5 | // ${fileDirname}: the current opened file's dirname
6 | // ${fileExtname}: the current opened file's extension
7 | // ${cwd}: the current working directory of the spawned process
8 |
9 | // A task runner that calls a custom npm script that compiles the extension.
10 | {
11 | "version": "0.1.0",
12 |
13 | // we want to run npm
14 | "command": "npm",
15 |
16 | // the command is a shell script
17 | "isShellCommand": true,
18 |
19 | // show the output window only if unrecognized errors occur.
20 | "showOutput": "silent",
21 |
22 | // we run the custom script "compile" as defined in package.json
23 | "args": ["run", "compile", "--loglevel", "silent"],
24 |
25 | // The tsc compiler is started in watching mode
26 | "isWatching": true,
27 |
28 | // use the standard tsc in watch mode problem matcher to find compile problems in the output.
29 | "problemMatcher": "$tsc-watch"
30 | }
--------------------------------------------------------------------------------
/.vscodeignore:
--------------------------------------------------------------------------------
1 | .vscode/**
2 | .vscode-test/**
3 | out/test/**
4 | test/**
5 | src/**
6 | **/*.map
7 | .gitignore
8 | tsconfig.json
9 | vsc-extension-quickstart.md
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) Microsoft Corporation
2 |
3 | All rights reserved.
4 |
5 | MIT License
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
8 | files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
9 | modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
10 | is furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
15 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
16 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
17 | OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GitHub Issues
2 |
3 | View the issues assigned to you in the Explorer viewlet. Currently the username and repository are taken from the Git configuration and only issues assigned to that user and to the next two milestones are shown.
4 |
5 | 
6 |
7 | ## Release Notes
8 |
9 | ### 0.9.3
10 |
11 | - Ignore folder casing for owner and repo (#44).
12 | - Update username and host settings to application scope.
13 | - Update to use @types/vscode.
14 |
15 | ### 0.9.2
16 |
17 | - Update https-proxy-agent (https://www.npmjs.com/advisories/1184).
18 |
19 | ### 0.9.1
20 |
21 | - Avoid naming overlap with PR extension.
22 |
23 | ### 0.9.0
24 |
25 | - Support for GitHub Enterprise with the `"github.host"` setting (@Ikuyadeu)
26 |
27 | ### 0.8.0
28 |
29 | - Show all milestones and improve sorting
30 | - Open single page for Open Milestone
31 |
32 | ### 0.7.0
33 |
34 | - Checkout Pull Request: Improve finding existing remote and branch
35 | - 'Multi-root ready' keyword
36 |
37 | ### 0.6.0
38 |
39 | - Add Copy Url command (@Ikuyadeu)
40 | - Fix tslint semicolon setting (@Ikuyadeu)
41 | - Add Open Milestone command
42 | - Add Checkout Pull Request command
43 |
44 | ### 0.5.0
45 |
46 | - Add multiple workspace folder support
47 | - Add setting for additional GitHub repositories
48 |
49 | ### 0.4.0
50 |
51 | - Add action for creating issues (@jens1o)
52 | - Fix parsing of repository names with dots (@wraith13)
53 |
54 | ### 0.3.x
55 |
56 | - Bugfixes
57 |
58 | ### 0.2.0
59 |
60 | - Support for private repositories (using Git credentials manager).
61 | - Add `github.username` setting.
62 |
63 | ### 0.1.0
64 |
65 | Initial release.
66 |
67 | ## Contributing
68 |
69 | File bugs and feature requests in [GitHub Issues](https://github.com/Microsoft/vscode-github-issues-prs/issues).
70 |
71 | Checkout the source code in the [GitHub Repository](https://github.com/Microsoft/vscode-github-issues-prs).
72 |
73 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
74 |
75 | ## License
76 | [MIT](LICENSE)
77 |
--------------------------------------------------------------------------------
/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/vscode-github-issues-prs/7b5bd4e050c210c89b7197709db75313cfa10a0c/images/icon.png
--------------------------------------------------------------------------------
/images/in_action.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/vscode-github-issues-prs/7b5bd4e050c210c89b7197709db75313cfa10a0c/images/in_action.gif
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "github-issues-prs",
3 | "version": "0.9.3",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@octokit/rest": {
8 | "version": "15.18.1",
9 | "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-15.18.1.tgz",
10 | "integrity": "sha512-g2tecjp2TEtYV8bKAFvfQtu+W29HM7ektmWmw8zrMy9/XCKDEYRErR2YvvhN9+IxkLC4O3lDqYP4b6WgsL6Utw==",
11 | "requires": {
12 | "before-after-hook": "^1.1.0",
13 | "btoa-lite": "^1.0.0",
14 | "debug": "^3.1.0",
15 | "http-proxy-agent": "^2.1.0",
16 | "https-proxy-agent": "^2.2.0",
17 | "lodash": "^4.17.4",
18 | "node-fetch": "^2.1.1",
19 | "universal-user-agent": "^2.0.0",
20 | "url-template": "^2.0.8"
21 | }
22 | },
23 | "@types/node": {
24 | "version": "10.3.0",
25 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.0.tgz",
26 | "integrity": "sha512-hWzNviaVFIr1TqcRA8ou49JaSHp+Rfabmnqg2kNvusKqLhPU0rIsGPUj5WJJ7ld4Bb7qdgLmIhLfCD1qS08IVA==",
27 | "dev": true
28 | },
29 | "@types/vscode": {
30 | "version": "1.53.0",
31 | "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.53.0.tgz",
32 | "integrity": "sha512-XjFWbSPOM0EKIT2XhhYm3D3cx3nn3lshMUcWNy1eqefk+oqRuBq8unVb6BYIZqXy9lQZyeUl7eaBCOZWv+LcXQ==",
33 | "dev": true
34 | },
35 | "agent-base": {
36 | "version": "4.2.1",
37 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
38 | "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
39 | "requires": {
40 | "es6-promisify": "^5.0.0"
41 | }
42 | },
43 | "before-after-hook": {
44 | "version": "1.4.0",
45 | "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-1.4.0.tgz",
46 | "integrity": "sha512-l5r9ir56nda3qu14nAXIlyq1MmUSs0meCIaFAh8HwkFwP1F8eToOuS3ah2VAHHcY04jaYD7FpJC5JTXHYRbkzg=="
47 | },
48 | "btoa-lite": {
49 | "version": "1.0.0",
50 | "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz",
51 | "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc="
52 | },
53 | "cross-spawn": {
54 | "version": "5.1.0",
55 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
56 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
57 | "requires": {
58 | "lru-cache": "^4.0.1",
59 | "shebang-command": "^1.2.0",
60 | "which": "^1.2.9"
61 | }
62 | },
63 | "debug": {
64 | "version": "3.1.0",
65 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
66 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
67 | "requires": {
68 | "ms": "2.0.0"
69 | }
70 | },
71 | "es6-promise": {
72 | "version": "4.2.6",
73 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz",
74 | "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q=="
75 | },
76 | "es6-promisify": {
77 | "version": "5.0.0",
78 | "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
79 | "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
80 | "requires": {
81 | "es6-promise": "^4.0.3"
82 | }
83 | },
84 | "execa": {
85 | "version": "0.6.3",
86 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.6.3.tgz",
87 | "integrity": "sha1-V7aaWU8IF1nGnlNw8NF7nLEWWP4=",
88 | "requires": {
89 | "cross-spawn": "^5.0.1",
90 | "get-stream": "^3.0.0",
91 | "is-stream": "^1.1.0",
92 | "npm-run-path": "^2.0.0",
93 | "p-finally": "^1.0.0",
94 | "signal-exit": "^3.0.0",
95 | "strip-eof": "^1.0.0"
96 | }
97 | },
98 | "get-stream": {
99 | "version": "3.0.0",
100 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
101 | "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
102 | },
103 | "git-credential-node": {
104 | "version": "1.1.0",
105 | "resolved": "https://registry.npmjs.org/git-credential-node/-/git-credential-node-1.1.0.tgz",
106 | "integrity": "sha1-JQdUSsAx0Ags2eD4M6rHWPFf2A0=",
107 | "requires": {
108 | "execa": "^0.6.0"
109 | }
110 | },
111 | "http-proxy-agent": {
112 | "version": "2.1.0",
113 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
114 | "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
115 | "requires": {
116 | "agent-base": "4",
117 | "debug": "3.1.0"
118 | }
119 | },
120 | "https-proxy-agent": {
121 | "version": "2.2.4",
122 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
123 | "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
124 | "requires": {
125 | "agent-base": "^4.3.0",
126 | "debug": "^3.1.0"
127 | },
128 | "dependencies": {
129 | "agent-base": {
130 | "version": "4.3.0",
131 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
132 | "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
133 | "requires": {
134 | "es6-promisify": "^5.0.0"
135 | }
136 | }
137 | }
138 | },
139 | "is-stream": {
140 | "version": "1.1.0",
141 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
142 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
143 | },
144 | "isexe": {
145 | "version": "2.0.0",
146 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
147 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
148 | },
149 | "lodash": {
150 | "version": "4.17.21",
151 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
152 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
153 | },
154 | "lru-cache": {
155 | "version": "4.1.1",
156 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
157 | "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
158 | "requires": {
159 | "pseudomap": "^1.0.2",
160 | "yallist": "^2.1.2"
161 | }
162 | },
163 | "macos-release": {
164 | "version": "2.1.0",
165 | "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.1.0.tgz",
166 | "integrity": "sha512-8TCbwvN1mfNxbBv0yBtfyIFMo3m1QsNbKHv7PYIp/abRBKVQBXN7ecu3aeGGgT18VC/Tf397LBDGZF9KBGJFFw=="
167 | },
168 | "moment": {
169 | "version": "2.24.0",
170 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
171 | "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
172 | },
173 | "ms": {
174 | "version": "2.0.0",
175 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
176 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
177 | },
178 | "nice-try": {
179 | "version": "1.0.5",
180 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
181 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
182 | },
183 | "node-fetch": {
184 | "version": "2.6.1",
185 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
186 | "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
187 | },
188 | "npm-run-path": {
189 | "version": "2.0.2",
190 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
191 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
192 | "requires": {
193 | "path-key": "^2.0.0"
194 | }
195 | },
196 | "os-name": {
197 | "version": "3.0.0",
198 | "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.0.0.tgz",
199 | "integrity": "sha512-7c74tib2FsdFbQ3W+qj8Tyd1R3Z6tuVRNNxXjJcZ4NgjIEQU9N/prVMqcW29XZPXGACqaXN3jq58/6hoaoXH6g==",
200 | "requires": {
201 | "macos-release": "^2.0.0",
202 | "windows-release": "^3.1.0"
203 | }
204 | },
205 | "p-finally": {
206 | "version": "1.0.0",
207 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
208 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
209 | },
210 | "path-key": {
211 | "version": "2.0.1",
212 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
213 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
214 | },
215 | "pseudomap": {
216 | "version": "1.0.2",
217 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
218 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
219 | },
220 | "semver": {
221 | "version": "5.5.1",
222 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
223 | "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw=="
224 | },
225 | "shebang-command": {
226 | "version": "1.2.0",
227 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
228 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
229 | "requires": {
230 | "shebang-regex": "^1.0.0"
231 | }
232 | },
233 | "shebang-regex": {
234 | "version": "1.0.0",
235 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
236 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
237 | },
238 | "signal-exit": {
239 | "version": "3.0.2",
240 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
241 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
242 | },
243 | "strip-eof": {
244 | "version": "1.0.0",
245 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
246 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
247 | },
248 | "typescript": {
249 | "version": "3.3.4000",
250 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.4000.tgz",
251 | "integrity": "sha512-jjOcCZvpkl2+z7JFn0yBOoLQyLoIkNZAs/fYJkUG6VKy6zLPHJGfQJYFHzibB6GJaF/8QrcECtlQ5cpvRHSMEA==",
252 | "dev": true
253 | },
254 | "universal-user-agent": {
255 | "version": "2.0.3",
256 | "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-2.0.3.tgz",
257 | "integrity": "sha512-eRHEHhChCBHrZsA4WEhdgiOKgdvgrMIHwnwnqD0r5C6AO8kwKcG7qSku3iXdhvHL3YvsS9ZkSGN8h/hIpoFC8g==",
258 | "requires": {
259 | "os-name": "^3.0.0"
260 | }
261 | },
262 | "url-template": {
263 | "version": "2.0.8",
264 | "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz",
265 | "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE="
266 | },
267 | "which": {
268 | "version": "1.2.14",
269 | "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
270 | "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=",
271 | "requires": {
272 | "isexe": "^2.0.0"
273 | }
274 | },
275 | "windows-release": {
276 | "version": "3.1.0",
277 | "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.1.0.tgz",
278 | "integrity": "sha512-hBb7m7acFgQPQc222uEQTmdcGLeBmQLNLFIh0rDk3CwFOBrfjefLzEfEfmpMq8Af/n/GnFf3eYf203FY1PmudA==",
279 | "requires": {
280 | "execa": "^0.10.0"
281 | },
282 | "dependencies": {
283 | "cross-spawn": {
284 | "version": "6.0.5",
285 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
286 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
287 | "requires": {
288 | "nice-try": "^1.0.4",
289 | "path-key": "^2.0.1",
290 | "semver": "^5.5.0",
291 | "shebang-command": "^1.2.0",
292 | "which": "^1.2.9"
293 | }
294 | },
295 | "execa": {
296 | "version": "0.10.0",
297 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz",
298 | "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==",
299 | "requires": {
300 | "cross-spawn": "^6.0.0",
301 | "get-stream": "^3.0.0",
302 | "is-stream": "^1.1.0",
303 | "npm-run-path": "^2.0.0",
304 | "p-finally": "^1.0.0",
305 | "signal-exit": "^3.0.0",
306 | "strip-eof": "^1.0.0"
307 | }
308 | }
309 | }
310 | },
311 | "yallist": {
312 | "version": "2.1.2",
313 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
314 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
315 | }
316 | }
317 | }
318 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "github-issues-prs",
3 | "displayName": "GitHub Issues",
4 | "description": "View the issues assigned to you in the Explorer viewlet.",
5 | "version": "0.9.3",
6 | "publisher": "ms-vscode",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/Microsoft/vscode-github-issues-prs.git"
10 | },
11 | "license": "MIT",
12 | "bugs": {
13 | "url": "https://github.com/Microsoft/vscode-github-issues-prs/issues"
14 | },
15 | "engines": {
16 | "vscode": "^1.31.0"
17 | },
18 | "categories": [
19 | "Other"
20 | ],
21 | "keywords": [
22 | "multi-root ready"
23 | ],
24 | "activationEvents": [
25 | "onView:githubIssuesPrs"
26 | ],
27 | "main": "./out/src/extension",
28 | "icon": "images/icon.png",
29 | "contributes": {
30 | "views": {
31 | "explorer": [
32 | {
33 | "id": "githubIssuesPrs",
34 | "name": "GitHub Issues"
35 | }
36 | ]
37 | },
38 | "commands": [
39 | {
40 | "command": "githubIssuesPrs.refresh",
41 | "title": "Refresh",
42 | "icon": {
43 | "light": "resources/light/refresh.svg",
44 | "dark": "resources/dark/refresh.svg"
45 | }
46 | },
47 | {
48 | "command": "githubIssuesPrs.createIssue",
49 | "title": "Create Issue",
50 | "icon": {
51 | "light": "thirdparty/octicons/light/plus-small.svg",
52 | "dark": "thirdparty/octicons/dark/plus-small.svg"
53 | }
54 | },
55 | {
56 | "command": "githubIssuesPrs.openMilestone",
57 | "title": "Open Milestone"
58 | },
59 | {
60 | "command": "githubIssuesPrs.openIssue",
61 | "title": "Open Issue"
62 | },
63 | {
64 | "command": "githubIssuesPrs.openPullRequest",
65 | "title": "Open Pull Request"
66 | },
67 | {
68 | "command": "githubIssuesPrs.checkoutPullRequest",
69 | "title": "Checkout Pull Request"
70 | },
71 | {
72 | "command": "githubIssuesPrs.copyNumber",
73 | "title": "Copy Number"
74 | },
75 | {
76 | "command": "githubIssuesPrs.copyText",
77 | "title": "Copy Text"
78 | },
79 | {
80 | "command": "githubIssuesPrs.copyMarkdown",
81 | "title": "Copy Markdown"
82 | },
83 | {
84 | "command": "githubIssuesPrs.copyUrl",
85 | "title": "Copy Url"
86 | }
87 | ],
88 | "menus": {
89 | "view/title": [
90 | {
91 | "command": "githubIssuesPrs.refresh",
92 | "when": "view == githubIssuesPrs",
93 | "group": "navigation"
94 | },
95 | {
96 | "command": "githubIssuesPrs.createIssue",
97 | "when": "view == githubIssuesPrs",
98 | "group": "navigation"
99 | }
100 | ],
101 | "view/item/context": [
102 | {
103 | "command": "githubIssuesPrs.openMilestone",
104 | "when": "view == githubIssuesPrs && viewItem == milestone",
105 | "group": "1_navigation"
106 | },
107 | {
108 | "command": "githubIssuesPrs.openIssue",
109 | "when": "view == githubIssuesPrs && viewItem == issue",
110 | "group": "1_navigation"
111 | },
112 | {
113 | "command": "githubIssuesPrs.copyNumber",
114 | "when": "view == githubIssuesPrs && viewItem == issue",
115 | "group": "9_cutcopypaste"
116 | },
117 | {
118 | "command": "githubIssuesPrs.copyText",
119 | "when": "view == githubIssuesPrs && viewItem == issue",
120 | "group": "9_cutcopypaste"
121 | },
122 | {
123 | "command": "githubIssuesPrs.copyMarkdown",
124 | "when": "view == githubIssuesPrs && viewItem == issue",
125 | "group": "9_cutcopypaste"
126 | },
127 | {
128 | "command": "githubIssuesPrs.copyUrl",
129 | "when": "view == githubIssuesPrs && viewItem == issue",
130 | "group": "9_cutcopypaste"
131 | },
132 | {
133 | "command": "githubIssuesPrs.openPullRequest",
134 | "when": "view == githubIssuesPrs && viewItem == pull_request",
135 | "group": "1_navigation"
136 | },
137 | {
138 | "command": "githubIssuesPrs.checkoutPullRequest",
139 | "when": "view == githubIssuesPrs && viewItem == pull_request",
140 | "group": "1_navigation"
141 | },
142 | {
143 | "command": "githubIssuesPrs.copyNumber",
144 | "when": "view == githubIssuesPrs && viewItem == pull_request",
145 | "group": "9_cutcopypaste"
146 | },
147 | {
148 | "command": "githubIssuesPrs.copyText",
149 | "when": "view == githubIssuesPrs && viewItem == pull_request",
150 | "group": "9_cutcopypaste"
151 | },
152 | {
153 | "command": "githubIssuesPrs.copyMarkdown",
154 | "when": "view == githubIssuesPrs && viewItem == pull_request",
155 | "group": "9_cutcopypaste"
156 | },
157 | {
158 | "command": "githubIssuesPrs.copyUrl",
159 | "when": "view == githubIssuesPrs && viewItem == pull_request",
160 | "group": "9_cutcopypaste"
161 | }
162 | ]
163 | },
164 | "configuration": {
165 | "type": "object",
166 | "title": "GitHub configuration",
167 | "properties": {
168 | "github.username": {
169 | "scope": "application",
170 | "type": [
171 | "string",
172 | "null"
173 | ],
174 | "default": null,
175 | "description": "The username to use when accessing GitHub. The default is to consult the Git credential manager."
176 | },
177 | "github.repositories": {
178 | "type": "array",
179 | "default": [],
180 | "description": "A list of repositories to query for issues."
181 | },
182 | "github.host": {
183 | "scope": "application",
184 | "type": "string",
185 | "default": "github.com",
186 | "description": "The host name to access GitHub. Change this to your GitHub Enterprise host."
187 | }
188 | }
189 | }
190 | },
191 | "scripts": {
192 | "vscode:prepublish": "tsc -p ./",
193 | "compile": "tsc -watch -p ./"
194 | },
195 | "devDependencies": {
196 | "@types/node": "10.3.0",
197 | "@types/vscode": "^1.31.0",
198 | "typescript": "3.3.4000"
199 | },
200 | "dependencies": {
201 | "@octokit/rest": "15.18.1",
202 | "git-credential-node": "1.1.0",
203 | "moment": "2.24.0"
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/resources/dark/refresh.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/resources/light/refresh.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/extension.ts:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { ExtensionContext, window } from 'vscode';
4 |
5 | import { GitHubIssuesPrsProvider } from './github-issues-prs';
6 |
7 | export function activate(context: ExtensionContext) {
8 | window.registerTreeDataProvider('githubIssuesPrs', new GitHubIssuesPrsProvider(context));
9 | }
10 |
--------------------------------------------------------------------------------
/src/git-credential-node.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'git-credential-node' {
2 |
3 | interface Credentials {
4 | username: string;
5 | password: string;
6 | }
7 |
8 | function fill(url: string): Promise;
9 | }
--------------------------------------------------------------------------------
/src/github-issues-prs.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 |
3 | import * as GitHub from '@octokit/rest';
4 | import { fill } from 'git-credential-node';
5 | import * as moment from 'moment';
6 |
7 | import { EventEmitter, TreeDataProvider, TreeItem, ExtensionContext, QuickPickItem, Uri, TreeItemCollapsibleState, WorkspaceFolder, window, workspace, commands, env } from 'vscode';
8 |
9 | import { exec, allMatches, fetchAll } from './utils';
10 |
11 | interface GitRemote {
12 | url: string;
13 | owner: string;
14 | repo: string;
15 | username: string | null;
16 | password: string | null;
17 | folders: WorkspaceFolder[];
18 | }
19 |
20 | class Milestone extends TreeItem {
21 |
22 | public issues: Issue[] = [];
23 |
24 | constructor(label: string, public item: any | undefined) {
25 | super(label, TreeItemCollapsibleState.Collapsed);
26 | this.contextValue = 'milestone';
27 | }
28 | }
29 |
30 | class Issue extends TreeItem {
31 | constructor(label: string, public query: { remote: GitRemote; assignee: string | undefined; }, public item: any) {
32 | super(label);
33 | }
34 | }
35 |
36 | interface RemoteQuickPickItem extends QuickPickItem {
37 | remote: GitRemote;
38 | }
39 |
40 | export class GitHubIssuesPrsProvider implements TreeDataProvider {
41 |
42 | private _onDidChangeTreeData = new EventEmitter();
43 | readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
44 |
45 | private fetching = false;
46 | private lastFetch: number | undefined;
47 | private children: Promise | undefined;
48 |
49 | private username: string | undefined;
50 | private host: string;
51 | private repositories: string[];
52 |
53 | constructor(private context: ExtensionContext) {
54 | const subscriptions = context.subscriptions;
55 | subscriptions.push(commands.registerCommand('githubIssuesPrs.refresh', this.refresh, this));
56 | subscriptions.push(commands.registerCommand('githubIssuesPrs.createIssue', this.createIssue, this));
57 | subscriptions.push(commands.registerCommand('githubIssuesPrs.openMilestone', this.openMilestone, this));
58 | subscriptions.push(commands.registerCommand('githubIssuesPrs.openIssue', this.openIssue, this));
59 | subscriptions.push(commands.registerCommand('githubIssuesPrs.openPullRequest', this.openIssue, this));
60 | subscriptions.push(commands.registerCommand('githubIssuesPrs.checkoutPullRequest', this.checkoutPullRequest, this));
61 | subscriptions.push(commands.registerCommand('githubIssuesPrs.copyNumber', this.copyNumber, this));
62 | subscriptions.push(commands.registerCommand('githubIssuesPrs.copyText', this.copyText, this));
63 | subscriptions.push(commands.registerCommand('githubIssuesPrs.copyMarkdown', this.copyMarkdown, this));
64 | subscriptions.push(commands.registerCommand('githubIssuesPrs.copyUrl', this.copyUrl, this));
65 |
66 | subscriptions.push(window.onDidChangeActiveTextEditor(this.poll, this));
67 |
68 | const config = workspace.getConfiguration('github');
69 | this.username = config.get('username');
70 | this.repositories = config.get('repositories') || [];
71 | this.host = config.get('host') || 'github.com';
72 | subscriptions.push(workspace.onDidChangeConfiguration(() => {
73 | const config = workspace.getConfiguration('github');
74 | const newUsername = config.get('username');
75 | const newRepositories = config.get('repositories') || [];
76 | const newHost = config.get('host');
77 | if (newUsername !== this.username || JSON.stringify(newRepositories) !== JSON.stringify(this.repositories) || newHost !== this.host) {
78 | this.username = newUsername;
79 | this.repositories = newRepositories;
80 | this.host = newHost || 'github.com';
81 | this.refresh();
82 | }
83 | }));
84 |
85 | subscriptions.push(workspace.onDidChangeWorkspaceFolders(this.refresh, this));
86 | }
87 |
88 | getTreeItem(element: TreeItem): TreeItem {
89 | return element;
90 | }
91 |
92 | async getChildren(element?: TreeItem): Promise {
93 | if (element instanceof Milestone) {
94 | return element.issues;
95 | }
96 |
97 | if (!this.children) {
98 | try {
99 | this.fetching = true;
100 | this.lastFetch = Date.now();
101 | await (this.children = this.fetchChildren());
102 | } finally {
103 | this.fetching = false;
104 | }
105 | }
106 |
107 | return this.children;
108 | }
109 |
110 | private async refresh() {
111 | if (!this.fetching) {
112 | this.children = undefined;
113 | await this.getChildren();
114 | this._onDidChangeTreeData.fire(undefined);
115 | }
116 | }
117 |
118 | private async createIssue() {
119 |
120 | const remotes = await this.getGitHubRemotes();
121 | if (!remotes.length) {
122 | return false;
123 | }
124 |
125 | let urls: RemoteQuickPickItem[] = remotes.map(remote => {
126 | let remoteItem: RemoteQuickPickItem = {
127 | label: remote.owner + '/' + remote.repo,
128 | description: '',
129 | remote: remote
130 | };
131 |
132 | return remoteItem;
133 | });
134 |
135 | if (!urls.length) {
136 | window.showInformationMessage('There is no remote to get data from!');
137 | return;
138 | }
139 |
140 | const callback = async (selectedRemote: RemoteQuickPickItem | undefined) => {
141 | if (!selectedRemote) {
142 | return;
143 | }
144 |
145 | const github = new GitHub(this.getAPIOption());
146 |
147 | if (selectedRemote.remote.username && selectedRemote.remote.password) {
148 | github.authenticate({
149 | type: 'basic',
150 | username: selectedRemote.remote.username,
151 | password: selectedRemote.remote.password
152 | });
153 | }
154 |
155 | const data = await github.repos.get({
156 | owner: selectedRemote.remote.owner,
157 | repo: selectedRemote.remote.repo
158 | });
159 | // TODO: Store in cache
160 | await env.openExternal(Uri.parse(data.data.html_url + '/issues/new'));
161 |
162 | };
163 |
164 | // shortcut when there is just one remote
165 | if (urls.length === 1) {
166 | callback(urls[0]);
167 | return;
168 | }
169 |
170 | const pick = await window.showQuickPick(
171 | urls,
172 | {
173 | placeHolder: 'Select the remote you want to create an issue on'
174 | }
175 | );
176 | callback(pick);
177 | }
178 |
179 | private async poll() {
180 | if (!this.lastFetch || this.lastFetch + 30 * 60 * 1000 < Date.now()) {
181 | return this.refresh();
182 | }
183 | }
184 |
185 | private async fetchChildren(element?: TreeItem): Promise {
186 | const remotes = await this.getGitHubRemotes();
187 | if (!remotes.length) {
188 | return [new TreeItem('No GitHub repositories found')];
189 | }
190 |
191 | const { username, password } = remotes[0];
192 | const assignee = this.username || username || undefined;
193 | if (!assignee) {
194 | const configure = new TreeItem('Configure "github.username" in settings');
195 | configure.command = {
196 | title: 'Open Settings',
197 | command: 'workbench.action.openGlobalSettings'
198 | };
199 | return [configure];
200 | }
201 |
202 | let issues: Issue[];
203 | try {
204 | issues = await this.fetchAllIssues(remotes, assignee, username || undefined, password || undefined);
205 | } catch (err) {
206 | if (err.code === 401 && password) {
207 | issues = await this.fetchAllIssues(remotes, assignee, username || undefined, undefined);
208 | } else {
209 | throw err;
210 | }
211 | }
212 |
213 | if (!issues.length) {
214 | return [new TreeItem(`No issues found for @${assignee}`)];
215 | }
216 |
217 | const milestoneIndex: { [title: string]: Milestone; } = {};
218 | const milestones: Milestone[] = [];
219 | for (const issue of issues) {
220 | const m = issue.item.milestone;
221 | const milestoneLabel = m && m.title || 'No Milestone';
222 | let milestone = milestoneIndex[milestoneLabel];
223 | if (!milestone) {
224 | milestone = new Milestone(milestoneLabel, m);
225 | milestoneIndex[milestoneLabel] = milestone;
226 | milestones.push(milestone);
227 | } else if (m && m.due_on && !(milestone.item && milestone.item.due_on)) {
228 | milestone.item = m;
229 | }
230 | milestone.issues.push(issue);
231 | }
232 |
233 | for (const milestone of milestones) {
234 | milestone.label = `${milestone.label} (${milestone.issues.length})`;
235 | }
236 |
237 | milestones.sort((a, b) => {
238 | // No Milestone
239 | if (!a.item) {
240 | return 1;
241 | } else if (!b.item) {
242 | return -1;
243 | }
244 |
245 | const t1 = this.parseDueOn(a);
246 | const t2 = this.parseDueOn(b);
247 | if (t1 && t2) {
248 | if (!t1.isSame(t2)) {
249 | return t1.isBefore(t2) ? -1 : 1;
250 | }
251 | } else if (t1) {
252 | return -1;
253 | } else if (t2) {
254 | return 1;
255 | }
256 |
257 | return a.item.title.localeCompare(b.item.title);
258 | });
259 |
260 | if (milestones.length) {
261 | milestones[0].collapsibleState = TreeItemCollapsibleState.Expanded;
262 | }
263 |
264 | return milestones;
265 | }
266 |
267 | private parseDueOn(m: Milestone) {
268 | if (!m.item) {
269 | return;
270 | }
271 |
272 | if (m.item.due_on) {
273 | const t = moment.utc(m.item.due_on, 'YYYY-MM-DDTHH:mm:ssZ');
274 | if (t.isValid()) {
275 | return t;
276 | }
277 | }
278 |
279 | if (m.item.title) {
280 | const t = moment.utc(m.item.title, 'MMMM YYYY');
281 | if (t.isValid()) {
282 | return t.add(14, 'days'); // "best guess"
283 | }
284 | }
285 | }
286 |
287 | private async fetchAllIssues(remotes: GitRemote[], assignee: string, username?: string, password?: string) {
288 | const github = new GitHub(this.getAPIOption());
289 | if (username && password) {
290 | github.authenticate({
291 | type: 'basic',
292 | username,
293 | password
294 | });
295 | }
296 |
297 | const params = {
298 | q: `is:open assignee:${assignee}`,
299 | sort: 'created',
300 | order: 'asc',
301 | per_page: 100
302 | };
303 | const items = await fetchAll(github, github.search.issues(params));
304 |
305 | return items
306 | .map((item: any) => ({
307 | item,
308 | remote: remotes.find(remote => item.repository_url.toLowerCase().endsWith(`/${remote.owner.toLowerCase()}/${remote.repo.toLowerCase()}`))
309 | }))
310 | .filter(({ remote }) => !!remote)
311 | .map(({ item, remote }) => {
312 | const issue = new Issue(`${item.title} (#${item.number})`, { remote: remote!, assignee }, item);
313 | const icon = item.pull_request ? 'git-pull-request.svg' : 'bug.svg';
314 | issue.iconPath = {
315 | light: this.context.asAbsolutePath(path.join('thirdparty', 'octicons', 'light', icon)),
316 | dark: this.context.asAbsolutePath(path.join('thirdparty', 'octicons', 'dark', icon))
317 | };
318 | issue.command = {
319 | title: 'Open',
320 | command: item.pull_request ? 'githubIssuesPrs.openPullRequest' : 'githubIssuesPrs.openIssue',
321 | arguments: [issue]
322 | };
323 | issue.contextValue = item.pull_request ? 'pull_request' : 'issue';
324 | return issue;
325 | });
326 | }
327 |
328 | private async openMilestone(milestone: Milestone) {
329 | const issue = milestone.issues[0];
330 | const item = issue.item;
331 | const assignee = issue.query.assignee;
332 | const url = `https://github.com/issues?utf8=%E2%9C%93&q=is%3Aopen+${item.milestone ? `milestone%3A%22${item.milestone.title}%22` : 'no%3Amilestone'}${assignee ? '+assignee%3A' + assignee : ''}`;
333 | return env.openExternal(Uri.parse(url));
334 | }
335 |
336 | private async openIssue(issue: Issue) {
337 | return env.openExternal(Uri.parse(issue.item.html_url));
338 | }
339 |
340 | private async checkoutPullRequest(issue: Issue) {
341 |
342 | const remote = issue.query.remote;
343 | const folder = remote.folders[0];
344 | if (!folder) {
345 | return window.showInformationMessage(`The repository '${remote.owner}/${remote.repo}' is not checked out in any open workspace folder.`);
346 | }
347 |
348 | const status = await exec(`git status --short --porcelain`, { cwd: folder.uri.fsPath });
349 | if (status.stdout) {
350 | return window.showInformationMessage(`There are local changes in the workspace folder. Commit or stash them before checking out the pull request.`);
351 | }
352 |
353 | const github = new GitHub(this.getAPIOption());
354 | const p = Uri.parse(issue.item.repository_url).path;
355 | const repo = path.basename(p);
356 | const owner = path.basename(path.dirname(p));
357 | const pr = await github.pullRequests.get({ owner, repo, number: issue.item.number });
358 | const repo_login = pr.data.head!.repo.owner.login;
359 | const user_login = pr.data.user!.login;
360 | const clone_url = pr.data.head!.repo.clone_url;
361 | const remoteBranch = pr.data.head!.ref;
362 | try {
363 | let remote: string | undefined = undefined;
364 | const remotes = await exec(`git remote -v`, { cwd: folder.uri.fsPath });
365 | let m: RegExpExecArray | null;
366 | const r = /^([^\s]+)\s+([^\s]+)\s+\(fetch\)/gm;
367 | while (m = r.exec(remotes.stdout)) {
368 | let fetch_url = m[2];
369 | if (!fetch_url.endsWith('.git')) {
370 | fetch_url += '.git';
371 | }
372 | if (fetch_url === clone_url) {
373 | remote = m[1];
374 | break;
375 | }
376 | }
377 | if (!remote) {
378 | remote = await window.showInputBox({
379 | prompt: 'Name for the remote to add',
380 | value: repo_login
381 | });
382 | if (!remote) {
383 | return;
384 | }
385 | await exec(`git remote add ${remote} ${clone_url}`, { cwd: folder.uri.fsPath });
386 | }
387 | try {
388 | await exec(`git fetch ${remote} ${remoteBranch}`, { cwd: folder.uri.fsPath });
389 | } catch (err) {
390 | console.error(err);
391 | // git fetch prints to stderr, continue
392 | }
393 | const localBranch = await window.showInputBox({
394 | prompt: 'Name for the local branch to checkout',
395 | value: remoteBranch.startsWith(`${user_login}/`) ? remoteBranch : `${user_login}/${remoteBranch}`
396 | });
397 | if (!localBranch) {
398 | return;
399 | }
400 | await exec(`git checkout -b ${localBranch} ${remote}/${remoteBranch}`, { cwd: folder.uri.fsPath });
401 | } catch (err) {
402 | console.error(err);
403 | // git checkout prints to stderr, continue
404 | }
405 | }
406 |
407 | private async copyNumber(issue: Issue) {
408 | return env.clipboard.writeText(`#${issue.item.number}`);
409 | }
410 |
411 | private async copyText(issue: Issue) {
412 | return env.clipboard.writeText(issue.label as string);
413 | }
414 |
415 | private async copyMarkdown(issue: Issue) {
416 | return env.clipboard.writeText(`[#${issue.item.number}](${issue.item.html_url})`);
417 | }
418 |
419 | private async copyUrl(issue: Issue) {
420 | return env.clipboard.writeText(issue.item.html_url);
421 | }
422 |
423 | private async getGitHubRemotes() {
424 | const remotes: Record = {};
425 | for (const folder of workspace.workspaceFolders || []) {
426 | try {
427 | const { stdout } = await exec('git remote -v', { cwd: folder.uri.fsPath });
428 | for (const url of new Set(allMatches(/^[^\s]+\s+([^\s]+)/gm, stdout, 1))) {
429 | const m = new RegExp(`[^\\s]*${this.host.replace(/\./g, '\\.')}[/:]([^/]+)\/([^ ]+)[^\\s]*`).exec(url);
430 |
431 | if (m) {
432 | const [url, owner, rawRepo] = m;
433 | const repo = rawRepo.replace(/\.git$/, '');
434 | let remote = remotes[`${owner}/${repo}`];
435 | if (!remote) {
436 | const data = await fill(url);
437 | remote = { url, owner, repo, username: data && data.username, password: data && data.password, folders: [] };
438 | remotes[`${owner}/${repo}`] = remote;
439 | }
440 | remote.folders.push(folder);
441 | }
442 | }
443 | } catch (e) {
444 | // ignore
445 | }
446 | }
447 | for (const rawRepo of this.repositories) {
448 | const m = /^\s*([^/\s]+)\/([^/\s]+)\s*$/.exec(rawRepo);
449 | if (m) {
450 | const [, owner, repo] = m;
451 | let remote = remotes[`${owner}/${repo}`];
452 | if (!remote) {
453 | const url = `https://${this.host}/${owner}/${repo}.git`;
454 | const data = await fill(url);
455 | remote = { url, owner, repo, username: data && data.username, password: data && data.password, folders: [] };
456 | remotes[`${owner}/${repo}`] = remote;
457 | }
458 | }
459 | }
460 | return Object.keys(remotes)
461 | .map(key => remotes[key]);
462 | }
463 |
464 | private getAPIOption() {
465 | if (this.host === 'github.com') {
466 | return { host: 'api.github.com' };
467 | } else {
468 | return { host: this.host, pathPrefix: '/api/v3' };
469 | }
470 | }
471 | }
472 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import * as cp from 'child_process';
2 | import * as GitHub from '@octokit/rest';
3 |
4 | export interface ExecResult {
5 | error: Error | null;
6 | stdout: string;
7 | stderr: string;
8 | }
9 |
10 | export function exec(command: string, options: cp.ExecOptions) {
11 | return new Promise((resolve, reject) => {
12 | cp.exec(command, options, (error, stdout, stderr) => {
13 | (error || stderr ? reject : resolve)({ error, stdout, stderr });
14 | });
15 | });
16 | }
17 |
18 | export function sleep(millis: number) {
19 | return new Promise(resolve => {
20 | setTimeout(resolve, millis);
21 | });
22 | }
23 |
24 | export function allMatches(regex: RegExp, string: string, group: number) {
25 | return {
26 | [Symbol.iterator]: function* () {
27 | let m: RegExpExecArray | null;
28 | while (m = regex.exec(string)) {
29 | yield m[group];
30 | if (regex.lastIndex === m.index) {
31 | regex.lastIndex++;
32 | }
33 | }
34 | }
35 | };
36 | }
37 |
38 | export async function fetchAll(github: GitHub, first: Promise) {
39 | const all = [];
40 |
41 | let res = await first;
42 | all.push(...res.data.items);
43 |
44 | while (github.hasNextPage(res)) {
45 | res = await github.getNextPage(res);
46 | all.push(...res.data.items);
47 | }
48 |
49 | return all;
50 | }
--------------------------------------------------------------------------------
/thirdparty/octicons/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2012-2016 GitHub, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/thirdparty/octicons/README:
--------------------------------------------------------------------------------
1 | From https://github.com/primer/octicons/.
--------------------------------------------------------------------------------
/thirdparty/octicons/dark/bug.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/thirdparty/octicons/dark/git-pull-request.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/thirdparty/octicons/dark/plus-small.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/thirdparty/octicons/light/bug.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/thirdparty/octicons/light/git-pull-request.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/thirdparty/octicons/light/plus-small.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/thirdpartynotices.txt:
--------------------------------------------------------------------------------
1 | THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
2 | For Microsoft vscode-github-issues-prs
3 |
4 | This project incorporates material from the projects listed below. The original copyright notice
5 | and the license under which Microsoft received such material are set out below. Microsoft reserves
6 | all other rights not expressly granted, whether by implication, estoppel or otherwise.
7 |
8 |
9 | 1. copy-paste, version 1.3.0, https://github.com/xavi-/node-copy-paste
10 |
11 | Developed by: Xavi Ramirez
12 | License: This project is released under The MIT License.
13 | Links to: https://opensource.org/licenses/mit-license.php
14 |
15 |
16 | 2. git-credential-node, version 1.1.0, https://github.com/parro-it/git-credential-node
17 |
18 | Copyright (C) 2017 Andrea Parodi
19 |
20 | Permission is hereby granted, free of charge, to any person obtaining a copy
21 | of this software and associated documentation files (the "Software"), to deal
22 | in the Software without restriction, including without limitation the rights
23 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24 | copies of the Software, and to permit persons to whom the Software is
25 | furnished to do so, subject to the following conditions:
26 |
27 | The above copyright notice and this permission notice shall be included in
28 | all copies or substantial portions of the Software.
29 |
30 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
36 | THE SOFTWARE.
37 |
38 |
39 | 3. github, version 9.2.0, https://github.com/mikedeboer/node-github
40 |
41 | The MIT License
42 |
43 | Copyright (c) 2012 Cloud9 IDE, Inc. (Mike de Boer)
44 |
45 | Permission is hereby granted, free of charge, to any person obtaining a copy
46 | of this software and associated documentation files (the "Software"), to deal
47 | in the Software without restriction, including without limitation the rights
48 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
49 | copies of the Software, and to permit persons to whom the Software is
50 | furnished to do so, subject to the following conditions:
51 |
52 | The above copyright notice and this permission notice shall be included in
53 | all copies or substantial portions of the Software.
54 |
55 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
56 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
57 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
58 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
59 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
60 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
61 | THE SOFTWARE.
62 |
63 |
64 | 4. octicons, version 5.0.1, https://github.com/primer/octicons
65 |
66 | MIT License
67 |
68 | Copyright (c) 2012-2016 GitHub, Inc.
69 |
70 | Permission is hereby granted, free of charge, to any person obtaining a copy
71 | of this software and associated documentation files (the "Software"), to deal
72 | in the Software without restriction, including without limitation the rights
73 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
74 | copies of the Software, and to permit persons to whom the Software is
75 | furnished to do so, subject to the following conditions:
76 |
77 | The above copyright notice and this permission notice shall be included in all
78 | copies or substantial portions of the Software.
79 |
80 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
81 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
82 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
83 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
84 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
85 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
86 | SOFTWARE.
87 |
88 |
89 | 5. node-open, version 0.0.5, https://github.com/pwnall/node-open
90 |
91 | Copyright (c) 2012 Jay Jordan
92 |
93 | Permission is hereby granted, free of charge, to any person
94 | obtaining a copy of this software and associated documentation
95 | files (the "Software"), to deal in the Software without
96 | restriction, including without limitation the rights to use,
97 | copy, modify, merge, publish, distribute, sublicense, and/or sell
98 | copies of the Software, and to permit persons to whom the
99 | Software is furnished to do so, subject to the following
100 | conditions:
101 |
102 | The above copyright notice and this permission notice shall be
103 | included in all copies or substantial portions of the Software.
104 |
105 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
106 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
107 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
108 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
109 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
110 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
111 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
112 | OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es6",
5 | "strict": true,
6 | "noUnusedLocals": true,
7 | "outDir": "out",
8 | "lib": [
9 | "es6"
10 | ],
11 | "sourceMap": true,
12 | "rootDir": "."
13 | },
14 | "exclude": [
15 | "node_modules",
16 | ".vscode-test"
17 | ]
18 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "no-unused-expression": true,
4 | "no-duplicate-variable": true,
5 | "curly": true,
6 | "class-name": true,
7 | "semicolon": true,
8 | "triple-equals": true
9 | }
10 | }
--------------------------------------------------------------------------------