diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index cfa047947af..2f53bcbc04e 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -6,9 +6,10 @@ "packages": { "": { "name": "vscode-codeql", - "version": "1.5.0", + "version": "1.5.1", "license": "MIT", "dependencies": { + "@octokit/rest": "^18.5.6", "child-process-promise": "^2.2.1", "classnames": "~2.2.6", "fs-extra": "^9.0.1", @@ -274,6 +275,142 @@ "node": ">= 8" } }, + "node_modules/@octokit/auth-token": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz", + "integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==", + "dependencies": { + "@octokit/types": "^6.0.3" + } + }, + "node_modules/@octokit/core": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz", + "integrity": "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==", + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.0", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.4.tgz", + "integrity": "sha512-SWTdXsVheRmlotWNjKzPOb6Js6tjSqA2a8z9+glDJng0Aqjzti8MEWOtuT8ZSu6wHnci7LZNuarE87+WJBG4vg==", + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.3.2.tgz", + "integrity": "sha512-oJhK/yhl9Gt430OrZOzAl2wJqR0No9445vmZ9Ey8GjUZUpwuu/vmEFP0TDhDXdpGDoxD6/EIFHJEcY8nHXpDTA==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.13.5", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.5.tgz", + "integrity": "sha512-3WSAKBLa1RaR/7GG+LQR/tAZ9fp9H9waE9aPXallidyci9oZsfgsLn5M836d3LuDC6Fcym+2idRTBpssHZePVg==", + "dependencies": { + "@octokit/types": "^6.13.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.3.1.tgz", + "integrity": "sha512-3B2iguGmkh6bQQaVOtCsS0gixrz8Lg0v4JuXPqBcFqLKuJtxAUf3K88RxMEf/naDOI73spD+goJ/o7Ie7Cvdjg==", + "dependencies": { + "@octokit/types": "^6.16.2", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.0.tgz", + "integrity": "sha512-4cPp/N+NqmaGQwbh3vUsYqokQIzt7VjsgTYVXiwpUP2pxd5YiZB2XuTedbb0SPtv9XS7nzAKjAuQxmY8/aZkiA==", + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/request/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@octokit/rest": { + "version": "18.6.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.6.0.tgz", + "integrity": "sha512-MdHuXHDJM7e5sUBe3K9tt7th0cs4csKU5Bb52LRi2oHAeIMrMZ4XqaTrEv660HoUPoM1iDlnj27Ab/Nh3MtwlA==", + "dependencies": { + "@octokit/core": "^3.5.0", + "@octokit/plugin-paginate-rest": "^2.6.2", + "@octokit/plugin-request-log": "^1.0.2", + "@octokit/plugin-rest-endpoint-methods": "5.3.1" + } + }, + "node_modules/@octokit/types": { + "version": "6.16.4", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.16.4.tgz", + "integrity": "sha512-UxhWCdSzloULfUyamfOg4dJxV9B+XjgrIZscI0VCbp4eNrjmorGEw+4qdwcpTsu6DIrm9tQsFQS2pK5QkqQ04A==", + "dependencies": { + "@octokit/openapi-types": "^7.3.2" + } + }, "node_modules/@sinonjs/commons": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.0.tgz", @@ -897,9 +1034,6 @@ "@typescript-eslint/types": "4.26.0", "@typescript-eslint/visitor-keys": "4.26.0" }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" @@ -1856,6 +1990,11 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, + "node_modules/before-after-hook": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz", + "integrity": "sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw==" + }, "node_modules/big-integer": { "version": "1.6.48", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", @@ -3066,6 +3205,11 @@ "integrity": "sha1-OjYof1A05pnnV3kBBSwubJQlFjE=", "dev": true }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, "node_modules/detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", @@ -10632,6 +10776,11 @@ "through2-filter": "^3.0.0" } }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, "node_modules/universalify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", @@ -11946,6 +12095,132 @@ "fastq": "^1.6.0" } }, + "@octokit/auth-token": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz", + "integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==", + "requires": { + "@octokit/types": "^6.0.3" + } + }, + "@octokit/core": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz", + "integrity": "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==", + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.0", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + } + } + }, + "@octokit/graphql": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.4.tgz", + "integrity": "sha512-SWTdXsVheRmlotWNjKzPOb6Js6tjSqA2a8z9+glDJng0Aqjzti8MEWOtuT8ZSu6wHnci7LZNuarE87+WJBG4vg==", + "requires": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.3.2.tgz", + "integrity": "sha512-oJhK/yhl9Gt430OrZOzAl2wJqR0No9445vmZ9Ey8GjUZUpwuu/vmEFP0TDhDXdpGDoxD6/EIFHJEcY8nHXpDTA==" + }, + "@octokit/plugin-paginate-rest": { + "version": "2.13.5", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.5.tgz", + "integrity": "sha512-3WSAKBLa1RaR/7GG+LQR/tAZ9fp9H9waE9aPXallidyci9oZsfgsLn5M836d3LuDC6Fcym+2idRTBpssHZePVg==", + "requires": { + "@octokit/types": "^6.13.0" + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "requires": {} + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.3.1.tgz", + "integrity": "sha512-3B2iguGmkh6bQQaVOtCsS0gixrz8Lg0v4JuXPqBcFqLKuJtxAUf3K88RxMEf/naDOI73spD+goJ/o7Ie7Cvdjg==", + "requires": { + "@octokit/types": "^6.16.2", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.0.tgz", + "integrity": "sha512-4cPp/N+NqmaGQwbh3vUsYqokQIzt7VjsgTYVXiwpUP2pxd5YiZB2XuTedbb0SPtv9XS7nzAKjAuQxmY8/aZkiA==", + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", + "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + } + } + }, + "@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "18.6.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.6.0.tgz", + "integrity": "sha512-MdHuXHDJM7e5sUBe3K9tt7th0cs4csKU5Bb52LRi2oHAeIMrMZ4XqaTrEv660HoUPoM1iDlnj27Ab/Nh3MtwlA==", + "requires": { + "@octokit/core": "^3.5.0", + "@octokit/plugin-paginate-rest": "^2.6.2", + "@octokit/plugin-request-log": "^1.0.2", + "@octokit/plugin-rest-endpoint-methods": "5.3.1" + } + }, + "@octokit/types": { + "version": "6.16.4", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.16.4.tgz", + "integrity": "sha512-UxhWCdSzloULfUyamfOg4dJxV9B+XjgrIZscI0VCbp4eNrjmorGEw+4qdwcpTsu6DIrm9tQsFQS2pK5QkqQ04A==", + "requires": { + "@octokit/openapi-types": "^7.3.2" + } + }, "@sinonjs/commons": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.0.tgz", @@ -13276,6 +13551,11 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, + "before-after-hook": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz", + "integrity": "sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw==" + }, "big-integer": { "version": "1.6.48", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", @@ -14287,6 +14567,11 @@ "integrity": "sha1-OjYof1A05pnnV3kBBSwubJQlFjE=", "dev": true }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, "detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", @@ -20392,6 +20677,11 @@ "through2-filter": "^3.0.0" } }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, "universalify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 5eb13ea415e..b46b5456f61 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -38,6 +38,7 @@ "onView:codeQLAstViewer", "onView:test-explorer", "onCommand:codeQL.checkForUpdatesToCLI", + "onCommand:codeQL.authenticateToGitHub", "onCommand:codeQLDatabases.chooseDatabaseFolder", "onCommand:codeQLDatabases.chooseDatabaseArchive", "onCommand:codeQLDatabases.chooseDatabaseInternet", @@ -236,10 +237,18 @@ } }, "commands": [ + { + "command": "codeQL.authenticateToGitHub", + "title": "CodeQL: Authenticate to GitHub" + }, { "command": "codeQL.runQuery", "title": "CodeQL: Run Query" }, + { + "command": "codeQL.runRemoteQuery", + "title": "CodeQL: Run Remote Query" + }, { "command": "codeQL.runQueries", "title": "CodeQL: Run Queries in Selected Files" @@ -654,10 +663,18 @@ } ], "commandPalette": [ + { + "command": "codeQL.authenticateToGitHub", + "when": "config.codeQL.canary" + }, { "command": "codeQL.runQuery", "when": "resourceLangId == ql && resourceExtname == .ql" }, + { + "command": "codeQL.runRemoteQuery", + "when": "config.codeQL.canary && editorLangId == ql && resourceExtname == .ql" + }, { "command": "codeQL.runQueries", "when": "false" @@ -800,6 +817,10 @@ "command": "codeQL.runQuery", "when": "editorLangId == ql && resourceExtname == .ql" }, + { + "command": "codeQL.runRemoteQuery", + "when": "config.codeQL.canary && editorLangId == ql && resourceExtname == .ql" + }, { "command": "codeQL.viewAst", "when": "resourceScheme == codeql-zip-archive" @@ -868,6 +889,7 @@ "format-staged": "lint-staged" }, "dependencies": { + "@octokit/rest": "^18.5.6", "child-process-promise": "^2.2.1", "classnames": "~2.2.6", "fs-extra": "^9.0.1", diff --git a/extensions/ql-vscode/src/authentication.ts b/extensions/ql-vscode/src/authentication.ts new file mode 100644 index 00000000000..ac21b7f9e02 --- /dev/null +++ b/extensions/ql-vscode/src/authentication.ts @@ -0,0 +1,81 @@ +import * as vscode from 'vscode'; +import * as Octokit from '@octokit/rest'; + +const GITHUB_AUTH_PROVIDER_ID = 'github'; + +// 'repo' scope should be enough for triggering workflows. For a comprehensive list, see: +// https://docs.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps +const SCOPES = ['repo']; + +interface OctokitAndToken { + octokit: Octokit.Octokit; + token: string; +} + +/** + * Handles authentication to GitHub, using the VS Code [authentication API](https://code.visualstudio.com/api/references/vscode-api#authentication). + */ +export class Credentials { + private octokitAndToken: OctokitAndToken | undefined; + + // Explicitly make the constructor private, so that we can't accidentally call the constructor from outside the class + // without also initializing the class. + // eslint-disable-next-line @typescript-eslint/no-empty-function + private constructor() { } + + static async initialize(context: vscode.ExtensionContext): Promise { + const c = new Credentials(); + c.registerListeners(context); + c.octokitAndToken = await c.createOctokit(false); + return c; + } + + private async createOctokit(createIfNone: boolean): Promise { + const session = await vscode.authentication.getSession(GITHUB_AUTH_PROVIDER_ID, SCOPES, { createIfNone }); + + if (session) { + return { + octokit: new Octokit.Octokit({ + auth: session.accessToken + }), + token: session.accessToken + }; + } else { + return undefined; + } + } + + registerListeners(context: vscode.ExtensionContext): void { + // Sessions are changed when a user logs in or logs out. + context.subscriptions.push(vscode.authentication.onDidChangeSessions(async e => { + if (e.provider.id === GITHUB_AUTH_PROVIDER_ID) { + this.octokitAndToken = await this.createOctokit(false); + } + })); + } + + async getOctokit(): Promise { + if (this.octokitAndToken) { + return this.octokitAndToken.octokit; + } + + this.octokitAndToken = await this.createOctokit(true); + // octokit shouldn't be undefined, since we've set "createIfNone: true". + // The following block is mainly here to prevent a compiler error. + if (!this.octokitAndToken) { + throw new Error('Did not initialize Octokit.'); + } + return this.octokitAndToken.octokit; + } + + async getToken(): Promise { + if (this.octokitAndToken) { + return this.octokitAndToken.token; + } + this.octokitAndToken = await this.createOctokit(true); + if (!this.octokitAndToken) { + throw new Error('Did not initialize Octokit.'); + } + return this.octokitAndToken.token; + } +} diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 58c878d1436..89b7ad802e6 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -23,6 +23,7 @@ import { CodeQLCliServer, CliVersionConstraint } from './cli'; import { CliConfigListener, DistributionConfigListener, + isCanary, MAX_QUERIES, QueryHistoryConfigListener, QueryServerConfigListener @@ -70,6 +71,9 @@ import { } from './commandRunner'; import { CodeQlStatusBarHandler } from './status-bar'; +import { Credentials } from './authentication'; +import runRemoteQuery from './run-remote-query'; + /** * extension.ts * ------------ @@ -640,6 +644,17 @@ async function activateWithInstalledDistribution( } ) ); + // The "runRemoteQuery" command is internal-only. + ctx.subscriptions.push( + commandRunner('codeQL.runRemoteQuery', async ( + uri: Uri | undefined + ) => { + if (isCanary()) { + const credentials = await Credentials.initialize(ctx); + await runRemoteQuery(credentials, uri || window.activeTextEditor?.document.uri); + } + }) + ); ctx.subscriptions.push( commandRunner( 'codeQL.openReferencedFile', @@ -712,6 +727,20 @@ async function activateWithInstalledDistribution( void helpers.showAndLogInformationMessage(text); })); + // The "authenticateToGitHub" command is internal-only. + ctx.subscriptions.push( + commandRunner('codeQL.authenticateToGitHub', async () => { + if (isCanary()) { + /** + * Credentials for authenticating to GitHub. + * These are used when making API calls. + */ + const credentials = await Credentials.initialize(ctx); + const octokit = await credentials.getOctokit(); + const userInfo = await octokit.users.getAuthenticated(); + void helpers.showAndLogInformationMessage(`Authenticated to GitHub as user: ${userInfo.data.login}`); + } + })); void logger.log('Starting language server.'); ctx.subscriptions.push(client.start()); diff --git a/extensions/ql-vscode/src/run-remote-query.ts b/extensions/ql-vscode/src/run-remote-query.ts new file mode 100644 index 00000000000..29afdbfce58 --- /dev/null +++ b/extensions/ql-vscode/src/run-remote-query.ts @@ -0,0 +1,59 @@ +import { Uri } from 'vscode'; +import * as yaml from 'js-yaml'; +import * as fs from 'fs-extra'; +import { showAndLogErrorMessage, showAndLogInformationMessage } from './helpers'; +import { Credentials } from './authentication'; + +interface Config { + repositories: string[]; + ref?: string; + language: string; +} + +// Test "controller" repository and workflow. +const OWNER = 'dsp-testing'; +const REPO = 'qc-controller'; +const WORKFLOW_ID = 'codeql-query.yml'; + +export default async function runRemoteQuery(credentials: Credentials, uri?: Uri) { + if (!uri?.fsPath.endsWith('.ql')) { + return; + } + + const octokit = await credentials.getOctokit(); + const token = await credentials.getToken(); + + const queryFile = uri.fsPath; + const query = await fs.readFile(queryFile, 'utf8'); + + const repositoriesFile = queryFile.substring(0, queryFile.length - '.ql'.length) + '.repositories'; + if (!(await fs.pathExists(repositoriesFile))) { + void showAndLogErrorMessage(`Missing file: '${repositoriesFile}' to specify the repositories to run against. This file must be a sibling of ${queryFile}.`); + return; + } + + const config = yaml.safeLoad(await fs.readFile(repositoriesFile, 'utf8')) as Config; + + const ref = config.ref || 'main'; + const language = config.language; + const repositories = JSON.stringify(config.repositories); + + try { + await octokit.rest.actions.createWorkflowDispatch({ + owner: OWNER, + repo: REPO, + workflow_id: WORKFLOW_ID, + ref: ref, + inputs: { + language, + repositories, + query, + token + } + }); + void showAndLogInformationMessage(`Successfully scheduled runs. [Click here to see the progress](https://github.com/${OWNER}/${REPO}/actions).`); + + } catch (error) { + void showAndLogErrorMessage(error); + } +}