From 9816a069bea1a446913c83f66e4559f2027d6277 Mon Sep 17 00:00:00 2001 From: Ray Chen Date: Thu, 12 Dec 2024 10:02:52 -0800 Subject: [PATCH] Support new interfaces (#9474) * Support new interfaces * Supported output contract and added file logger * Use local cloned repo for generation * Set the right path to use in generation --- tools/spec-gen-sdk/package-lock.json | 826 ++---------------- tools/spec-gen-sdk/package.json | 2 - .../spec-gen-sdk/src/automation/entrypoint.ts | 325 +++---- tools/spec-gen-sdk/src/automation/logging.ts | 127 +-- .../src/automation/reportStatus.ts | 79 +- .../automation/updateBreakingChangesLabels.ts | 5 + tools/spec-gen-sdk/src/automation/workflow.ts | 467 ++++------ .../src/automation/workflowPackage.ts | 243 +----- tools/spec-gen-sdk/src/cli/cli.ts | 20 +- tools/spec-gen-sdk/src/cli/config/index.ts | 1 - tools/spec-gen-sdk/src/cli/config/schema.ts | 145 +-- .../spec-gen-sdk/src/types/ExecutionReport.ts | 33 + .../src/types/ExecutionReportSchema.json | 104 +++ tools/spec-gen-sdk/src/types/Message.ts | 7 +- tools/spec-gen-sdk/src/types/PackageData.ts | 13 +- tools/spec-gen-sdk/src/types/SpecConfig.ts | 50 +- tools/spec-gen-sdk/src/utils/gitUtils.ts | 2 +- tools/spec-gen-sdk/src/utils/githubUtils.ts | 95 +- 18 files changed, 725 insertions(+), 1819 deletions(-) create mode 100644 tools/spec-gen-sdk/src/types/ExecutionReport.ts create mode 100644 tools/spec-gen-sdk/src/types/ExecutionReportSchema.json diff --git a/tools/spec-gen-sdk/package-lock.json b/tools/spec-gen-sdk/package-lock.json index cd1bc0fe200..f4570d30b8c 100644 --- a/tools/spec-gen-sdk/package-lock.json +++ b/tools/spec-gen-sdk/package-lock.json @@ -9,8 +9,6 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@azure/identity": "^4.2.0", - "@azure/storage-blob": "~12.17.0", "@octokit/auth-app": "^2.4.5", "@octokit/rest": "18.0.3", "ajv": "^6.12.6", @@ -79,360 +77,6 @@ "node": ">=6.0.0" } }, - "node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/abort-controller/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@azure/core-auth": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.9.0.tgz", - "integrity": "sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw==", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-util": "^1.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-auth/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@azure/core-client": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", - "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.4.0", - "@azure/core-rest-pipeline": "^1.9.1", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.6.1", - "@azure/logger": "^1.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-client/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@azure/core-http": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.4.tgz", - "integrity": "sha512-Fok9VVhMdxAFOtqiiAtg74fL0UJkt0z3D+ouUUxcRLzZNBioPRAMJFVxiWoJljYpXsRi4GDQHzQHDc9AiYaIUQ==", - "deprecated": "This package is no longer supported. Please migrate to use @azure/core-rest-pipeline", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/core-util": "^1.1.1", - "@azure/logger": "^1.0.0", - "@types/node-fetch": "^2.5.0", - "@types/tunnel": "^0.0.3", - "form-data": "^4.0.0", - "node-fetch": "^2.6.7", - "process": "^0.11.10", - "tslib": "^2.2.0", - "tunnel": "^0.0.6", - "uuid": "^8.3.0", - "xml2js": "^0.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@azure/core-http/node_modules/@azure/abort-controller": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", - "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", - "license": "MIT", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@azure/core-http/node_modules/@azure/core-tracing": { - "version": "1.0.0-preview.13", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", - "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", - "license": "MIT", - "dependencies": { - "@opentelemetry/api": "^1.0.1", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@azure/core-http/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@azure/core-lro": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", - "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-util": "^1.2.0", - "@azure/logger": "^1.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-lro/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@azure/core-paging": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", - "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-paging/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@azure/core-rest-pipeline": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.18.0.tgz", - "integrity": "sha512-QSoGUp4Eq/gohEFNJaUOwTN7BCc2nHTjjbm75JT0aD7W65PWM1H/tItz0GsABn22uaKyGxiMhWQLt2r+FGU89Q==", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.8.0", - "@azure/core-tracing": "^1.0.1", - "@azure/core-util": "^1.11.0", - "@azure/logger": "^1.0.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-rest-pipeline/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@azure/core-tracing": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.2.0.tgz", - "integrity": "sha512-UKTiEJPkWcESPYJz3X5uKRYyOcJD+4nYph+KpfdPRnQJVrZfk0KJgdnaAWKfhsBBtAf/D58Az4AvCJEmWgIBAg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-tracing/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@azure/core-util": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.11.0.tgz", - "integrity": "sha512-DxOSLua+NdpWoSqULhjDyAZTXFdP/LKkqtYuxxz1SCN289zk3OG8UOpnCQAz/tygyACBtWp/BoO72ptK7msY8g==", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^2.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-util/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@azure/identity": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.5.0.tgz", - "integrity": "sha512-EknvVmtBuSIic47xkOqyNabAme0RYTw52BTMz8eBgU1ysTyMrD1uOoM+JdS0J/4Yfp98IBT3osqq3BfwSaNaGQ==", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.9.0", - "@azure/core-client": "^1.9.2", - "@azure/core-rest-pipeline": "^1.17.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.11.0", - "@azure/logger": "^1.0.0", - "@azure/msal-browser": "^3.26.1", - "@azure/msal-node": "^2.15.0", - "events": "^3.0.0", - "jws": "^4.0.0", - "open": "^8.0.0", - "stoppable": "^1.1.0", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/identity/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@azure/logger": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.4.tgz", - "integrity": "sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ==", - "license": "MIT", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/logger/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@azure/msal-browser": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.27.0.tgz", - "integrity": "sha512-+b4ZKSD8+vslCtVRVetkegEhOFMLP3rxDWJY212ct+2r6jVg6OSQKc1Qz3kCoXo0FgwaXkb+76TMZfpHp8QtgA==", - "license": "MIT", - "dependencies": { - "@azure/msal-common": "14.16.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@azure/msal-common": { - "version": "14.16.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz", - "integrity": "sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@azure/msal-node": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.1.tgz", - "integrity": "sha512-1NEFpTmMMT2A7RnZuvRl/hUmJU+GLPjh+ShyIqPktG2PvSd2yvPnzGd/BxIBAAvJG5nr9lH4oYcQXepDbaE7fg==", - "license": "MIT", - "dependencies": { - "@azure/msal-common": "14.16.0", - "jsonwebtoken": "^9.0.0", - "uuid": "^8.3.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@azure/storage-blob": { - "version": "12.17.0", - "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.17.0.tgz", - "integrity": "sha512-sM4vpsCpcCApagRW5UIjQNlNylo02my2opgp0Emi8x888hZUvJ3dN69Oq20cEGXkMUWnoCrBaB0zyS3yeB87sQ==", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-http": "^3.0.0", - "@azure/core-lro": "^2.2.0", - "@azure/core-paging": "^1.1.1", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/logger": "^1.0.0", - "events": "^3.0.0", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@azure/storage-blob/node_modules/@azure/abort-controller": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", - "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", - "license": "MIT", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@azure/storage-blob/node_modules/@azure/core-tracing": { - "version": "1.0.0-preview.13", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", - "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", - "license": "MIT", - "dependencies": { - "@opentelemetry/api": "^1.0.1", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@azure/storage-blob/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -449,9 +93,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", + "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", "dev": true, "license": "MIT", "engines": { @@ -500,14 +144,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", + "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.3", + "@babel/types": "^7.26.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -647,13 +291,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", + "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -917,17 +561,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.3.tgz", + "integrity": "sha512-yTmc8J+Sj8yLzwr4PD5Xb/WF3bOYu2C2OoSZPzbuqRm4n98XirsbzaX+GloeO376UnSYIYJ4NCanwV5/ugZkwA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.3", + "@babel/parser": "^7.26.3", "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/types": "^7.26.3", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -946,9 +590,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", + "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", "dev": true, "license": "MIT", "dependencies": { @@ -1146,9 +790,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.15.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz", - "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==", + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.16.0.tgz", + "integrity": "sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg==", "dev": true, "license": "MIT", "engines": { @@ -1985,15 +1629,6 @@ "@octokit/openapi-types": "^12.11.0" } }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2213,24 +1848,14 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "18.19.64", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.64.tgz", - "integrity": "sha512-955mDqvO2vFf/oL7V3WiUtiz+BugyX8uVbaT2H8oj3+8dRyH2FLiNdowe7eNqRM7IOIZvzDH76EoAT+gwm6aIQ==", + "version": "18.19.67", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.67.tgz", + "integrity": "sha512-wI8uHusga+0ZugNp0Ol/3BqQfEcCCNfojtO6Oou9iVNGPTL6QNSdnUdqq85fRgIorLhLMuPIKpsN98QE9Nh+KQ==", "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } }, - "node_modules/@types/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, "node_modules/@types/prettier": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", @@ -2262,15 +1887,6 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, - "node_modules/@types/tunnel": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", - "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -2289,17 +1905,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.15.0.tgz", - "integrity": "sha512-+zkm9AR1Ds9uLWN3fkoeXgFppaQ+uEVtfOV62dDmsy9QCNqlRHWNEck4yarvRNrvRcHQLGfqBNui3cimoz8XAg==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.17.0.tgz", + "integrity": "sha512-HU1KAdW3Tt8zQkdvNoIijfWDMvdSweFYm4hWh+KwhPstv+sCmWb89hCIP8msFm9N1R/ooh9honpSuvqKWlYy3w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.15.0", - "@typescript-eslint/type-utils": "8.15.0", - "@typescript-eslint/utils": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0", + "@typescript-eslint/scope-manager": "8.17.0", + "@typescript-eslint/type-utils": "8.17.0", + "@typescript-eslint/utils": "8.17.0", + "@typescript-eslint/visitor-keys": "8.17.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2323,16 +1939,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.15.0.tgz", - "integrity": "sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.17.0.tgz", + "integrity": "sha512-Drp39TXuUlD49F7ilHHCG7TTg8IkA+hxCuULdmzWYICxGXvDXmDmWEjJYZQYgf6l/TFfYNE167m7isnc3xlIEg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.15.0", - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/typescript-estree": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0", + "@typescript-eslint/scope-manager": "8.17.0", + "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/typescript-estree": "8.17.0", + "@typescript-eslint/visitor-keys": "8.17.0", "debug": "^4.3.4" }, "engines": { @@ -2352,14 +1968,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz", - "integrity": "sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.17.0.tgz", + "integrity": "sha512-/ewp4XjvnxaREtqsZjF4Mfn078RD/9GmiEAtTeLQ7yFdKnqwTOgRMSvFz4et9U5RiJQ15WTGXPLj89zGusvxBg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0" + "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/visitor-keys": "8.17.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2370,14 +1986,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.15.0.tgz", - "integrity": "sha512-UU6uwXDoI3JGSXmcdnP5d8Fffa2KayOhUUqr/AiBnG1Gl7+7ut/oyagVeSkh7bxQ0zSXV9ptRh/4N15nkCqnpw==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.17.0.tgz", + "integrity": "sha512-q38llWJYPd63rRnJ6wY/ZQqIzPrBCkPdpIsaCfkR3Q4t3p6sb422zougfad4TFW9+ElIFLVDzWGiGAfbb/v2qw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.15.0", - "@typescript-eslint/utils": "8.15.0", + "@typescript-eslint/typescript-estree": "8.17.0", + "@typescript-eslint/utils": "8.17.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -2398,9 +2014,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.15.0.tgz", - "integrity": "sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.17.0.tgz", + "integrity": "sha512-gY2TVzeve3z6crqh2Ic7Cr+CAv6pfb0Egee7J5UAVWCpVvDI/F71wNfolIim4FE6hT15EbpZFVUj9j5i38jYXA==", "dev": true, "license": "MIT", "engines": { @@ -2412,14 +2028,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.15.0.tgz", - "integrity": "sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.17.0.tgz", + "integrity": "sha512-JqkOopc1nRKZpX+opvKqnM3XUlM7LpFMD0lYxTqOTKQfCWAmxw45e3qlOCsEqEB2yuacujivudOFpCnqkBDNMw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/visitor-keys": "8.15.0", + "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/visitor-keys": "8.17.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2441,16 +2057,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.15.0.tgz", - "integrity": "sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.17.0.tgz", + "integrity": "sha512-bQC8BnEkxqG8HBGKwG9wXlZqg37RKSMY7v/X8VEWD8JG2JuTHuNK0VFvMPMUKQcbk6B+tf05k+4AShAEtCtJ/w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.15.0", - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/typescript-estree": "8.15.0" + "@typescript-eslint/scope-manager": "8.17.0", + "@typescript-eslint/types": "8.17.0", + "@typescript-eslint/typescript-estree": "8.17.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2469,13 +2085,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.15.0.tgz", - "integrity": "sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.17.0.tgz", + "integrity": "sha512-1Hm7THLpO6ww5QU6H/Qp+AusUUl+z/CAm3cNZZ0jQvon9yicgO7Rwd+/WWRpMKLYV6p2UvdbR27c86rzCPpreg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/types": "8.17.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -2522,18 +2138,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2626,12 +2230,6 @@ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "license": "MIT" }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -2908,9 +2506,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001680", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", - "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", + "version": "1.0.30001686", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001686.tgz", + "integrity": "sha512-Y7deg0Aergpa24M3qLC5xjNklnKnhsmSyR/V89dLZ1n0ucJIFNs7PgR2Yfa/Zf6W79SbBicgtGxZr2juHkEUIA==", "dev": true, "funding": [ { @@ -3070,18 +2668,6 @@ "text-hex": "1.0.x" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -3217,24 +2803,6 @@ "node": ">=0.10.0" } }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", @@ -3307,9 +2875,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.63", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.63.tgz", - "integrity": "sha512-ddeXKuY9BHo/mw145axlyWjlJ1UBt4WK3AlvkT7W2AbqfRQoacVoRUCF6wL3uIx/8wT9oLKXzI+rFqHHscByaA==", + "version": "1.5.68", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.68.tgz", + "integrity": "sha512-FgMdJlma0OzUYlbrtZ4AeXjKxKPk6KT8WOP8BjcqxWtlg8qyJQjRzPJzUtUn5GBg1oQ26hFs7HOOHJMYiJRnvQ==", "dev": true, "license": "ISC" }, @@ -3389,9 +2957,9 @@ } }, "node_modules/eslint": { - "version": "9.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz", - "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==", + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.16.0.tgz", + "integrity": "sha512-whp8mSQI4C8VXd+fLgSM0lh3UlmcFtVwUQjyKCFfsp+2ItAIYhlq/hqGahGqHE6cv9unM41VlqKk2VtKYR2TaA==", "dev": true, "license": "MIT", "dependencies": { @@ -3400,7 +2968,7 @@ "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.9.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.15.0", + "@eslint/js": "9.16.0", "@eslint/plugin-kit": "^0.2.3", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -3605,15 +3173,6 @@ "node": ">=0.10.0" } }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -3902,20 +3461,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -4066,9 +3611,9 @@ } }, "node_modules/globals": { - "version": "15.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.12.0.tgz", - "integrity": "sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==", + "version": "15.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.13.0.tgz", + "integrity": "sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g==", "dev": true, "license": "MIT", "engines": { @@ -4157,32 +3702,6 @@ "dev": true, "license": "MIT" }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -4291,21 +3810,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4379,18 +3883,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -5255,7 +4747,7 @@ "npm": ">=6" } }, - "node_modules/jsonwebtoken/node_modules/jwa": { + "node_modules/jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", @@ -5266,7 +4758,7 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/jsonwebtoken/node_modules/jws": { + "node_modules/jws": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", @@ -5276,27 +4768,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/jwa": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", - "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", - "license": "MIT", - "dependencies": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -5537,27 +5008,6 @@ "node": ">=8.6" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -5733,23 +5183,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "license": "MIT", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -6064,15 +5497,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -6228,9 +5652,9 @@ } }, "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, "license": "MIT", "engines": { @@ -6345,12 +5769,6 @@ "node": ">=10" } }, - "node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", - "license": "ISC" - }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -6506,16 +5924,6 @@ "node": ">=8" } }, - "node_modules/stoppable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", - "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", - "license": "MIT", - "engines": { - "node": ">=4", - "npm": ">=6" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -6733,9 +6141,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", - "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, "license": "MIT", "engines": { @@ -6991,15 +6399,6 @@ "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" } }, - "node_modules/tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "license": "MIT", - "engines": { - "node": ">=0.6.11 <=0.7.0 || >=0.7.3" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -7051,15 +6450,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.15.0.tgz", - "integrity": "sha512-wY4FRGl0ZI+ZU4Jo/yjdBu0lVTSML58pu6PgGtJmCufvzfV565pUF6iACQt092uFOd49iLOTX/sEVmHtbSrS+w==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.17.0.tgz", + "integrity": "sha512-409VXvFd/f1br1DCbuKNFqQpXICoTB+V51afcwG1pn1a3Cp92MqAUges3YjwEdQ0cMUoCIodjVDAYzyD8h3SYA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.15.0", - "@typescript-eslint/parser": "8.15.0", - "@typescript-eslint/utils": "8.15.0" + "@typescript-eslint/eslint-plugin": "8.17.0", + "@typescript-eslint/parser": "8.17.0", + "@typescript-eslint/utils": "8.17.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7177,15 +6576,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -7349,28 +6739,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "license": "MIT", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/tools/spec-gen-sdk/package.json b/tools/spec-gen-sdk/package.json index 070da26a372..0f52409800f 100644 --- a/tools/spec-gen-sdk/package.json +++ b/tools/spec-gen-sdk/package.json @@ -31,8 +31,6 @@ "node": ">=20.10.0" }, "dependencies": { - "@azure/identity": "^4.2.0", - "@azure/storage-blob": "~12.17.0", "@octokit/auth-app": "^2.4.5", "@octokit/rest": "18.0.3", "ajv": "^6.12.6", diff --git a/tools/spec-gen-sdk/src/automation/entrypoint.ts b/tools/spec-gen-sdk/src/automation/entrypoint.ts index 93dd6c69df1..9e1fd755423 100644 --- a/tools/spec-gen-sdk/src/automation/entrypoint.ts +++ b/tools/spec-gen-sdk/src/automation/entrypoint.ts @@ -1,120 +1,68 @@ import { RestEndpointMethodTypes } from '@octokit/rest'; import { Octokit } from '@octokit/rest'; -import { - BlobServiceClient, - ContainerClient, -} from '@azure/storage-blob'; +import * as fs from 'fs'; import * as winston from 'winston'; -import { getAuthenticatedOctokit, RepoKey } from '../utils/githubUtils'; +import { getAuthenticatedOctokit, getRepoKey, RepoKey } from '../utils/githubUtils'; import { FailureType, setFailureType, WorkflowContext, - workflowFilterSdkMain, workflowInit, workflowMain } from './workflow'; -import { TriggerType } from '../types/TriggerType'; import { - getBlobName, loggerConsoleTransport, loggerDevOpsTransport, - loggerStorageAccountTransport, + loggerFileTransport, loggerTestTransport, - loggerWaitToFinish, sdkAutoLogLevels } from './logging'; -import { sdkAutoReportStatus } from './reportStatus'; -import { SDKAutomationState } from './sdkAutomationState'; -import { DefaultAzureCredential } from '@azure/identity'; -import * as pkginfo from 'pkginfo'; +import path from 'path'; +import { generateReport } from './reportStatus'; +import { SpecConfig, SdkRepoConfig, getSpecConfig, specConfigPath } from '../types/SpecConfig'; +import { getSwaggerToSdkConfig, SwaggerToSdkConfig } from '../types/SwaggerToSdkConfig'; interface SdkAutoOptions { specRepo: RepoKey; - pullNumber: number; sdkName: string; branchPrefix: string; - filterSwaggerToSdk?: boolean; + localSpecRepoPath: string; + localSdkRepoPath: string; + tspConfigPath?: string; + readmePath?: string; + pullNumber?: number; + specCommitSha?: string; + specRepoHttpsUrl?: string; + workingFolder: string; github: { token?: string; - id?: number; - privateKey?: string; commentAuthorName?: string; }; - storage: { - name: string; - prefix: string; - downloadCommand: string; - isPublic: boolean; - }; - - buildID?: string; runEnv: 'local' | 'azureDevOps' | 'test'; } +type SpecPrInfo = { + head: {owner: string; repo: string}; + base: {owner: string; repo: string}; +} + export type SdkAutoContext = { config: SdkAutoOptions; octokit: Octokit; getGithubAccessToken: (owner: string) => Promise; logger: winston.Logger; - useMergedRoutine: boolean; - trigger: TriggerType; - specCommitSha: string; - specHeadRef: string; - specHtmlUrl: string; - specIsPrivate: boolean; - specPrInfo: {head: {owner: string; repo: string}, base: {owner: string; repo: string}}; - specPrBaseBranch: string; - specPrHeadBranch: string; - specPrTitle: string; - specPrHtmlUrl: string; - workingFolder: string; - blobContainerClient: ContainerClient; - logsBlobUrl?: string; - version: string; - autorestConfig?: string; + specPrInfo: SpecPrInfo | undefined; + specPrBaseBranch: string | undefined; + specPrHeadBranch: string | undefined; + fullLogFileName: string; + filterLogFileName: string; + specRepoConfig: SpecConfig; + sdkRepoConfig: SdkRepoConfig; + swaggerToSdkConfig: SwaggerToSdkConfig }; -const getAutorestConfigFromPRComment = async ( - octokit: Octokit, - owner: string, - repo: string, - pullNumber: number, - sdkName: string, - logger: winston.Logger -) => { - let comments: (string | undefined)[] | undefined = undefined; - try { - const res = await octokit.issues.listComments({ - owner, - repo, - issue_number: pullNumber - }); - if (res.status !== 200) { - throw new Error(`Error: Get autorest configuration from PR https://github.com/${owner}/${repo}/pull/${pullNumber} failed with status code: ${res.status}. If autorest config is requried, please re-run the failed job in pipeline run or emit '/azp run' in the PR comment to trigger the re-run if the status code is retryable.`); - } - comments = res.data.map(e => e.body).map(e => { - if (!e.includes('\r\n') && e.includes('\n')) { - return e.replace(/\n/g, '\r\n'); - } - return e; - }); - } catch (e) { - logger.warn(e.message); - } - - const regexToFilterComment = new RegExp(`#+ *${sdkName}`); - let autorestConfigComment: string|undefined = undefined; - for (const comment of comments!) { - if (!comment) { continue; } - if (comment.match(regexToFilterComment)) { - autorestConfigComment = comment; - } - } - return autorestConfigComment; -}; export const getSdkAutoContext = async (options: SdkAutoOptions): Promise => { const logger = winston.createLogger({ @@ -129,63 +77,32 @@ export const getSdkAutoContext = async (options: SdkAutoOptions): Promise { - const credential = new DefaultAzureCredential({ - loggingOptions: { allowLoggingAccountIdentifiers: true }, - }); - const serviceClient = new BlobServiceClient( - `https://${options.storage.name}.blob.core.windows.net`, - credential - ); - const blobContainerClient = serviceClient.getContainerClient(options.storage.prefix); - - if (options.filterSwaggerToSdk || options.runEnv === 'test') { - return { blobContainerClient }; + }; } - - logger.info(`Ensure blob container exists: ${blobContainerClient.url}`); - await blobContainerClient.createIfNotExists(); - - const blobLogger = await loggerStorageAccountTransport( - { blobContainerClient, config: options }, - getBlobName({ config: options }, 'logs.txt') - ); - logger.add(blobLogger.blobTransport); - const logsBlobName = `${blobLogger.blobName}`; - logger.info(`Log to ${logsBlobName}`); - return { blobContainerClient, logsBlobName }; + return { + config: options, + octokit, + getGithubAccessToken, + logger, + specPrInfo, + specPrBaseBranch: specPR?.base.ref, + specPrHeadBranch: specPR?.head.ref, + fullLogFileName, + filterLogFileName, + specRepoConfig, + sdkRepoConfig, + swaggerToSdkConfig + }; }; const getGithubContext = async (options: SdkAutoOptions, logger: winston.Logger) => { - const {octokit, getToken: getGithubAccessToken} = getAuthenticatedOctokit(options.github, logger); + const {octokit, getToken: getGithubAccessToken} = getAuthenticatedOctokit(options.github, logger); + if (!options.pullNumber) { + return { octokit, getGithubAccessToken, specPR: undefined }; + } let specPR: RestEndpointMethodTypes['pulls']['get']['response']['data']; do { const rsp = await octokit.pulls.get({ @@ -246,24 +144,16 @@ const getGithubContext = async (options: SdkAutoOptions, logger: winston.Logger) }); specPR = rsp.data; } while (specPR.mergeable === null && !specPR.merged); - return { octokit, getGithubAccessToken, specPR }; }; export const sdkAutoMain = async (options: SdkAutoOptions) => { const sdkContext = await getSdkAutoContext(options); let workflowContext: WorkflowContext | undefined = undefined; - let workflowFilterSdkMainStatus: SDKAutomationState | undefined = undefined; - // identify the sdkAutoMain whether to run the sdk filter or SdkGen - const runSdkFilter = options.filterSwaggerToSdk; try { - if (runSdkFilter) { - await workflowFilterSdkMain(sdkContext); - } else { - workflowContext = await workflowInit(sdkContext); - await workflowMain(workflowContext); - } + workflowContext = await workflowInit(sdkContext); + await workflowMain(workflowContext); } catch (e) { if (workflowContext) { sdkContext.logger.error(`FatalError: ${e.message}. Please refer to the inner logs for details or report this issue through https://aka.ms/azsdk/support/specreview-channel.`); @@ -274,23 +164,11 @@ export const sdkAutoMain = async (options: SdkAutoOptions) => { if (e.stack) { sdkContext.logger.error(`ErrorStack: ${e.stack}.`); } - if (runSdkFilter) { - workflowFilterSdkMainStatus = 'failed'; - console.log(`##vso[task.setVariable variable=SkipAll;isOutput=true]true`); - // hardcode the skipped job name when the runSdkFilter failed. That can help customer can easily jump to devops pipeline info - console.log(`##vso[task.setVariable variable=SkippedJobs]azure-sdk-for-go`); - console.log(`##vso[task.complete result=Failed;]`); - } } if (workflowContext) { - await sdkAutoReportStatus(workflowContext); - } - await loggerWaitToFinish(sdkContext.logger); - if (runSdkFilter) { - return workflowFilterSdkMainStatus; - } else { - return workflowContext?.status; + await generateReport(workflowContext); } + return workflowContext?.status; }; export const getLanguageByRepoName = (repoName: string) => { @@ -310,3 +188,58 @@ export const getLanguageByRepoName = (repoName: string) => { return repoName; } }; + +export const loadConfigContent = (fileName: string, logger: winston.Logger) => { + logger.info(`Load config file: ${specConfigPath}`); + try { + const fileContent = fs.readFileSync(fileName).toString(); + const result = JSON.parse(fileContent); + return result; + } + catch (error) { + logger.error(`IOError: Fails to read config [${fileName}]'. Please ensure the spec config exists with the correct path and the content is valid. Error: ${error.message}`); + throw error; + } +}; + +export const getSdkRepoConfig = async (options: SdkAutoOptions, specRepoConfig: SpecConfig) => { + const specRepo = options.specRepo; + const sdkName = options.sdkName; + const getConfigRepoKey = (repo: RepoKey | string | undefined, fallback: RepoKey): RepoKey => { + if (repo === undefined) { + return fallback; + } + const repoKey = getRepoKey(repo); + if (!repoKey.owner) { + repoKey.owner = fallback.owner; + } + return repoKey; + }; + let sdkRepoConfig = specRepoConfig.sdkRepositoryMappings[sdkName]; + if (sdkRepoConfig === undefined) { + throw new Error(`ConfigError: SDK ${sdkName} is not defined in SpecConfig. Please add the related config at the 'specificationRepositoryConfiguration.json' file under the root folder of the azure-rest-api-specs(-pr) repository.`); + } + + if (typeof sdkRepoConfig === 'string') { + sdkRepoConfig = { + mainRepository: getRepoKey(sdkRepoConfig) + } as SdkRepoConfig; + } + + sdkRepoConfig.mainRepository = getConfigRepoKey(sdkRepoConfig.mainRepository, { + owner: specRepo.owner, + name: sdkName + }); + sdkRepoConfig.mainBranch = + sdkRepoConfig.mainBranch ?? "main"; + sdkRepoConfig.integrationRepository = getConfigRepoKey( + sdkRepoConfig.integrationRepository, + sdkRepoConfig.mainRepository + ); + sdkRepoConfig.integrationBranchPrefix = sdkRepoConfig.integrationBranchPrefix ?? 'sdkAutomation'; + sdkRepoConfig.secondaryRepository = getConfigRepoKey(sdkRepoConfig.secondaryRepository, sdkRepoConfig.mainRepository); + sdkRepoConfig.secondaryBranch = sdkRepoConfig.secondaryBranch ?? sdkRepoConfig.mainBranch; + sdkRepoConfig.configFilePath = sdkRepoConfig.configFilePath ?? 'swagger_to_sdk_config.json'; + + return sdkRepoConfig; +}; diff --git a/tools/spec-gen-sdk/src/automation/logging.ts b/tools/spec-gen-sdk/src/automation/logging.ts index 07a59b47587..d516b651009 100644 --- a/tools/spec-gen-sdk/src/automation/logging.ts +++ b/tools/spec-gen-sdk/src/automation/logging.ts @@ -1,8 +1,5 @@ import * as winston from 'winston'; import { default as Transport } from 'winston-transport'; -import { AppendBlobClient } from '@azure/storage-blob'; -import { SdkAutoContext } from './entrypoint'; -import { PackageData } from '../types/PackageData'; import { SDKAutomationState } from './sdkAutomationState'; export const sdkAutoLogLevels = { @@ -123,121 +120,13 @@ export class CommentCaptureTransport extends Transport { } } -export const getBlobName = (context: Pick, fileName: string, pkg?: PackageData) => { - let blobName = `${context.config.specRepo.owner}/${context.config.pullNumber}/${context.config.sdkName}`; - if (pkg) { - blobName = `${blobName}/${pkg.name.replace('/', '_')}`; - } - return `${blobName}/${fileName}`; -}; - -export const loggerStorageAccountTransport = async ( - context: Pick, - blobName: string -) => { - const blobClient = context.blobContainerClient.getAppendBlobClient(blobName); - if (context.config.runEnv === 'test') { - return { - blobTransport: new CommentCaptureTransport({ extraLevelFilter: [], output: [] }), - blobUrl: blobClient.url, - blobName - }; - } - - await blobClient.deleteIfExists(); - await blobClient.create(); - const blobTransport = new StorageBlobTransport({ - format: winston.format.combine(winston.format.timestamp({ format: 'hh:mm:ss.SS' })), - blobClient +export const loggerFileTransport = (fileName: string) => { + return new winston.transports.File({ + filename: fileName, + level: 'info', + format: winston.format.combine( + winston.format.timestamp({ format: 'hh:mm:ss.SSS' }), + winston.format.printf(formatLog) + ), }); - - return { - blobTransport, - blobUrl: blobClient.url, - blobName - }; -}; - -interface StorageBlobTransportOptions extends winston.transport.TransportStreamOptions { - blobClient: AppendBlobClient; -} - -class StorageBlobTransport extends Transport { - private bufferedMessages: string[] = []; - private bufferedCallbacks: (() => void)[] = []; - private waitToFlushCallbacks: (() => void)[] = []; - private isWriting: boolean = false; - - constructor(private opts: StorageBlobTransportOptions) { - super(opts); - } - - public log(info: WinstonInfo, callback: () => void): void { - this.bufferedMessages.push(formatLog(info) + '\n'); - // this.bufferedCallbacks.push(callback); - // tslint:disable-next-line: no-floating-promises - this.writeBufferedMessages(); - callback(); - } - - public async waitToFlush(): Promise { - if (!this.isWriting) { - return; - } - return new Promise(resolve => { - this.waitToFlushCallbacks.push(resolve); - }); - } - - // public close() { - // if (!this.isWriting) { - // this.emit('finish'); - // return; - // } - - // this.bufferedCallbacks.push(() => this.emit('finish')); - // } - - private async writeBufferedMessages(): Promise { - if (this.isWriting) { - return; - } - - this.isWriting = true; - await new Promise((resolve) => setTimeout(resolve, 100)); - while (this.bufferedMessages.length > 0 || this.bufferedCallbacks.length > 0) { - const toWrite = this.bufferedMessages.join(''); - const toCall = this.bufferedCallbacks; - this.bufferedMessages = []; - this.bufferedCallbacks = []; - - try { - if (toWrite.length > 0) { - await this.opts.blobClient.appendBlock(toWrite, toWrite.length); - } - } catch (err) { - this.emit('error', err); - } - - for (const cb of toCall) { - cb(); - } - } - this.isWriting = false; - - const waitToFlushCallbacks = this.waitToFlushCallbacks; - this.waitToFlushCallbacks = []; - for (const cb of waitToFlushCallbacks) { - cb(); - } - } -} - -export const loggerWaitToFinish = async (logger: winston.Logger) => { - logger.info('Wait for logger transports to complete'); - for (const transport of logger.transports) { - if (transport instanceof StorageBlobTransport) { - await transport.waitToFlush(); - } - } }; diff --git a/tools/spec-gen-sdk/src/automation/reportStatus.ts b/tools/spec-gen-sdk/src/automation/reportStatus.ts index 910d21309d7..ed039f2445a 100644 --- a/tools/spec-gen-sdk/src/automation/reportStatus.ts +++ b/tools/spec-gen-sdk/src/automation/reportStatus.ts @@ -11,9 +11,86 @@ import { updateBreakingChangesLabel } from './updateBreakingChangesLabels'; import { formatSuppressionLine } from '../utils/reportFormat'; import { removeAnsiEscapeCodes } from '../utils/utils'; import { CommentCaptureTransport } from './logging'; +import { ExecutionReport, PackageReport } from '../types/ExecutionReport'; +import { writeTmpJsonFile } from '../utils/fsUtils'; const commentLimit = 60; +export const generateReport = (context: WorkflowContext) => { + context.logger.log('section', 'Generate report'); + /* + const captureTransport = new CommentCaptureTransport({ + extraLevelFilter: ['error', 'warn'], + level: 'debug', + output: context.messages + });*/ + //context.logger.add(captureTransport); + + let executionReport: ExecutionReport; + const packageReports: PackageReport[] = []; + + let hasSuppressions = false + let hasAbsentSuppressions = false; + let areBreakingChangeSuppressed = false; + if (context.pendingPackages.length > 0) { + setSdkAutoStatus(context, 'failed'); + setFailureType(context, FailureType.PipelineFrameworkFailed); + context.logger.error(`GenerationError: The following packages are still pending.`); + for (const pkg of context.pendingPackages) { + context.logger.error(`\t${pkg.name}`); + context.handledPackages.push(pkg); + } + } + + for (const pkg of context.handledPackages) { + setSdkAutoStatus(context, pkg.status); + hasSuppressions = Boolean(pkg.presentSuppressionLines.length > 0); + hasAbsentSuppressions = Boolean(pkg.absentSuppressionLines.length > 0); + if(pkg.hasBreakingChange && hasSuppressions && !hasAbsentSuppressions) { + areBreakingChangeSuppressed = true; + } + const packageReport: PackageReport = { + packageName: pkg.name, + result: pkg.status, + artifactPaths: pkg.artifactPaths, + readmeMd: pkg.readmeMd, + typespecProject: pkg.typespecProject, + version: pkg.version, + apiViewArtifact: pkg.apiViewArtifactPath, + language: pkg.language, + hasBreakingChange: pkg.hasBreakingChange, + breakingChangeLabel: context.swaggerToSdkConfig.packageOptions.breakingChangesLabel, + areBreakingChangeSuppressed, + presentBreakingChangeSuppressions: pkg.presentSuppressionLines, + absentBreakingChangeSuppressions: pkg.absentSuppressionLines, + installInstructions: pkg.installationInstructions + } + packageReports.push(packageReport); + context.logger.info(`package [${pkg.name}] hasBreakingChange [${pkg.hasBreakingChange}] isBetaMgmtSdk [${pkg.isBetaMgmtSdk}] hasSuppressions [${hasSuppressions}] hasAbsentSuppressions [${hasAbsentSuppressions}]`); + } + + executionReport = { + packages: packageReports, + executionResult: context.status, + fullLogPath: context.fullLogFileName, + filteredLogPath: context.filterLogFileName, + sdkArtifactFolder: context.sdkArtifactFolder, + sdkApiViewArtifactFolder: context.sdkApiViewArtifactFolder + }; + + writeTmpJsonFile(context, 'executionReport.json', executionReport); + context.logger.info(`Main status [${context.status}]`); + if (context.status === 'failed') { + console.log(`##vso[task.complete result=Failed;]`); + sendFailure(); + } else { + sendSuccess(); + } + + context.logger.log('endsection', 'Generate report'); + //context.logger.remove(captureTransport); +} + export const sdkAutoReportStatus = async (context: WorkflowContext) => { context.logger.log('section', 'Report status'); @@ -95,7 +172,7 @@ export const sdkAutoReportStatus = async (context: WorkflowContext) => { const encode = (str: string): string => Buffer.from(str, 'binary').toString('base64'); console.log(`##vso[task.setVariable variable=SubTitle]${encode(subTitle)}`); - const outputPath = path.join(context.workingFolder, 'pipe.log'); + const outputPath = path.join(context.config.workingFolder, 'pipe.log'); context.logger.info(`Writing unified pipeline message to ${outputPath}`); const content = JSON.stringify(pipelineResultData); diff --git a/tools/spec-gen-sdk/src/automation/updateBreakingChangesLabels.ts b/tools/spec-gen-sdk/src/automation/updateBreakingChangesLabels.ts index ba5b1366e55..cc24be9647f 100644 --- a/tools/spec-gen-sdk/src/automation/updateBreakingChangesLabels.ts +++ b/tools/spec-gen-sdk/src/automation/updateBreakingChangesLabels.ts @@ -27,6 +27,11 @@ export async function updateBreakingChangesLabel( hasSuppressions: boolean, hasAbsentSuppressions: boolean, ): Promise { + + if (!context.config.pullNumber) { + context.logger.error('Error: Pull request number is not provided.'); + return; + } const sdkName = context.config.sdkName; const sdkBreakingChangesLabelsConfig: { breakingChange: string | undefined; diff --git a/tools/spec-gen-sdk/src/automation/workflow.ts b/tools/spec-gen-sdk/src/automation/workflow.ts index 1a26b7cd456..54893f630b0 100644 --- a/tools/spec-gen-sdk/src/automation/workflow.ts +++ b/tools/spec-gen-sdk/src/automation/workflow.ts @@ -4,19 +4,12 @@ import * as fs from 'fs'; import { mkdirpSync } from 'fs-extra'; import { default as Transport } from 'winston-transport'; import { findSDKToGenerateFromTypeSpecProject } from '../utils/typespecUtils'; -import { SdkAutoContext } from './entrypoint'; import simpleGit, { SimpleGit, SimpleGitOptions } from 'simple-git'; import { - gitAddAll, - gitCheckoutBranch, - gitGetCommitter, - gitGetDiffFileList, - gitRemoveAllBranches, - gitSetRemoteWithAuth + gitGetDiffFileList } from '../utils/gitUtils'; -import { getSdkRepoConfig, getSpecConfig, SdkRepoConfig, specConfigPath } from '../types/SpecConfig'; -import { getGithubFileContent, RepoKey, repoKeyToString } from '../utils/githubUtils'; -import { getSwaggerToSdkConfig, SwaggerToSdkConfig } from '../types/SwaggerToSdkConfig'; +import { specConfigPath } from '../types/SpecConfig'; +import { repoKeyToString } from '../utils/githubUtils'; import { runSdkAutoCustomScript, setSdkAutoStatus } from '../utils/runScript'; import { deleteTmpJsonFile, @@ -31,13 +24,14 @@ import { GenerateOutput, getGenerateOutput } from '../types/GenerateOutput'; import { getPackageData, PackageData } from '../types/PackageData'; import { workflowPkgMain } from './workflowPackage'; import { SDKAutomationState } from './sdkAutomationState'; -import { CommentCaptureTransport, getBlobName } from './logging'; +import { CommentCaptureTransport } from './logging'; import { findSwaggerToSDKConfiguration } from '../utils/readme'; import { getInitOutput } from '../types/InitOutput'; import { MessageRecord } from '../types/Message'; import { sdkSuppressionsFileName, SdkSuppressionsYml, SdkPackageSuppressionsEntry, validateSdkSuppressionsFile } from '../types/sdkSuppressions'; import { parseYamlContent, removeDuplicatesFromRelatedFiles } from '../utils/utils'; import { SDKSuppressionContentList } from '../utils/handleSuppressionLines'; +import { SdkAutoContext } from './entrypoint'; export const remoteIntegration = 'integration'; export const remoteMain = 'main'; @@ -67,22 +61,20 @@ export enum FailureType { } export type WorkflowContext = SdkAutoContext & { - sdkRepoConfig: SdkRepoConfig; - swaggerToSdkConfig: SwaggerToSdkConfig; specRepo: SimpleGit; specFolder: string; sdkRepo: SimpleGit; sdkFolder: string; + sdkArtifactFolder?: string; + sdkApiViewArtifactFolder?: string; pendingPackages: PackageData[]; handledPackages: PackageData[]; status: SDKAutomationState; failureType?: FailureType; messages: string[]; messageCaptureTransport: Transport; - legacyAfterScripts: string[]; scriptEnvs: { [key: string]: string | undefined }; tmpFolder: string; - skipLegacy: boolean; extraResultRecords: MessageRecord[]; }; @@ -101,21 +93,14 @@ export const workflowInit = async (context: SdkAutoContext): Promise { - await workflowCallInitScript(context); - const changedSpecs = await workflowDetectChangedSpec(context); - context.logger.remove(context.messageCaptureTransport); - - const callMode = - context.swaggerToSdkConfig.advancedOptions.generationCallMode ?? - 'one-for-all-configs'; - if (callMode === 'one-for-all-configs') { - await workflowHandleReadmeMdOrTypeSpecProject(context, changedSpecs); + if (context.specPrInfo) { + await workflowValidateSdkConfigForSpecPr(context); + await workflowCallInitScript(context); + await workflowGenerateSdkForSpecPr(context); } else { - for (const changedSpec of changedSpecs) { - await workflowHandleReadmeMdOrTypeSpecProject(context, [changedSpec]); - } + await workflowValidateSdkConfig(context); + await workflowCallInitScript(context); + await workflowGenerateSdk(context); } setSdkAutoStatus(context, 'succeeded'); }; -export const workflowFilterSdkMain = async (context: SdkAutoContext) => { - const specConfigContentPromise = workflowInitGetSpecConfig(context); - const specContextPromise = (await workflowInitSpecRepo(context, { checkoutMainBranch: false }))(); - const specContext = await specContextPromise; - const specConfigContent = await specConfigContentPromise; - const specConfig = getSpecConfig(specConfigContent, context.config.specRepo); +export const workflowValidateSdkConfigForSpecPr = async (context: SdkAutoContext) => { + const specContext = workflowInitSpecRepo(context); const changedSpecs = await workflowDetectChangedSpec({ ...context, ...specContext }); - context.logger.log('section', 'Filter SDK to generate'); + context.logger.log('section', 'Validate SDK configuration'); const sdkToGenerate = new Set(); const commit = await specContext.specRepo.revparse(branchMain); @@ -173,51 +147,92 @@ export const workflowFilterSdkMain = async (context: SdkAutoContext) => { const entry = await specContext.specRepo.revparse(`${commit}:${ch.typespecProject}`); const blob = await specContext.specRepo.catFile(['-p', entry]); const content = blob.toString(); - const config = findSDKToGenerateFromTypeSpecProject(content, specConfig); - if (!config || config.length === 0) { - context.logger.warn(`Warning: cannot find supported emitter in tspconfig.yaml for typespec project ${ch.typespecProject}. This typespec project will be skipped from SDK generation. Please add the right emitter config in the 'tspconfig.yaml' file. The example project can be found at https://github.com/Azure/azure-rest-api-specs/blob/main/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml.`); + const config = findSDKToGenerateFromTypeSpecProject(content, context.specRepoConfig); + // todo map the sdkName by the sdk language + if (!config || config.length === 0 || !config.includes(context.config.sdkName)) { + context.logger.warn(`Warning: cannot find supported emitter in tspconfig.yaml for typespec project ${ch.typespecProject}. This typespec project will be skipped from SDK generation. Please add the right emitter config in the 'tspconfig.yaml' file. The example project can be found at https://aka.ms/azsdk/sample-arm-tsproject-tspconfig`); continue; } - for (const repo of config) { - sdkToGenerate.add(repo); - } + sdkToGenerate.add(context.config.sdkName); } else if (ch.readmeMd) { const entry = await specContext.specRepo.revparse(`${commit}:${ch.readmeMd}`); const blob = await specContext.specRepo.catFile(['-p', entry]); const content = blob.toString(); const config = findSwaggerToSDKConfiguration(content); if (!config || config.repositories.length === 0) { - context.logger.warn(`Warning: 'swagger-to-sdk' section cannot be found in ${ch.readmeMd}. This readme file will be skipped from SDK generation. Please add the section to the readme file according to this guidance https://github.com/Azure/azure-rest-api-specs/blob/main/documentation/code-gen/configure-go-sdk.md#swagger-to-sdk.`); + context.logger.warn(`Warning: 'swagger-to-sdk' section cannot be found in ${ch.readmeMd}. This readme file will be skipped from SDK generation. Please add the section to the readme file according to this guidance https://aka.ms/azsdk/sample-readme-sdk-config`); continue; } - for (const repoConfig of config.repositories) { - sdkToGenerate.add(repoConfig.repo); + else if (!config.repositories.some(r => r.repo === context.config.sdkName)) { + context.logger.warn(`Warning: ${context.config.sdkName} cannot be found in the 'swagger-to-sdk' section in the ${ch.readmeMd}. This SDK will be skipped from SDK generation. Please add the right config to the readme file according to this guidance https://aka.ms/azsdk/sample-readme-sdk-config`); + continue; } + sdkToGenerate.add(context.config.sdkName); } } + if (sdkToGenerate.size === 0) { + throw new Error(`No SDKs are enabled for generation. Please check the configuration in the realted tspconfig.yaml or readme.md`); + } context.logger.info(`SDK to generate:`); const enabledJobs: { [sdkName: string]: { sdkName: string } } = {}; for (const sdkName of [...sdkToGenerate]) { - if (specConfig.sdkRepositoryMappings[sdkName] === undefined) { - context.logger.warn(`\tWarning: ${sdkName} not found in ${specConfigPath}. This SDK will be skipped from SDK generation. Please add the right config to the readme file according to this guidance https://github.com/Azure/azure-rest-api-specs/blob/main/documentation/code-gen/configure-go-sdk.md#swagger-to-sdk.`); + if (context.specRepoConfig.sdkRepositoryMappings[sdkName] === undefined) { + context.logger.warn(`\tWarning: ${sdkName} not found in ${specConfigPath}. This SDK will be skipped from SDK generation. Please add the right config to the ${specConfigPath} according to this guidance https://aka.ms/azsdk/spec-repo-config`); continue; } context.logger.info(`\t${sdkName}`); enabledJobs[sdkName] = { sdkName }; } - console.log(`##vso[task.setVariable variable=EnabledJobs;isOutput=true]${JSON.stringify(enabledJobs)}`); - if (sdkToGenerate.size === 0 || Object.keys(enabledJobs).length === 0) { - console.log(`##vso[task.setVariable variable=SkipAll;isOutput=true]true`); - } + context.logger.log('endsection', 'Validate SDK config for spec PR scenario'); +}; - const skipJobs: string[] = Object.keys(specConfig.sdkRepositoryMappings).filter( - (sdkName) => !sdkToGenerate.has(sdkName) - ); - console.log(`##vso[task.setVariable variable=SkippedJobs]${skipJobs.join(' ')}`); - console.log(`##vso[task.setVariable variable=QueueJobs]${Object.keys(enabledJobs).join(' ')}`); +export const workflowValidateSdkConfig = async (context: SdkAutoContext) => { + context.logger.log('section', 'Validate SDK configuration'); + let sdkToGenerate = ""; - context.logger.log('endsection', 'Filter SDK to generate'); + let tspConfigPath, readmeMdPath; + if(context.config.tspConfigPath) { + tspConfigPath = path.join(context.config.localSpecRepoPath, context.config.tspConfigPath); + } + if(context.config.readmePath) { + readmeMdPath = path.join(context.config.localSpecRepoPath, context.config.readmePath); + } + + if (!tspConfigPath && !readmeMdPath) { + throw new Error(`ConfigError: 'tspConfigPath' and 'readmePath' are not provided. Please provide at least one of them.`); + } + + if (tspConfigPath) { + const tspConfigContent = fs.readFileSync(tspConfigPath).toString(); + const config = findSDKToGenerateFromTypeSpecProject(tspConfigContent, context.specRepoConfig); + if (!config || config.length === 0 || !config.includes(context.config.sdkName)) { + context.logger.warn(`Warning: cannot find supported emitter in tspconfig.yaml for typespec project ${tspConfigPath}. This typespec project will be skipped from SDK generation. Please add the right emitter config in the 'tspconfig.yaml' file. The example project can be found at https://aka.ms/azsdk/sample-arm-tsproject-tspconfig`); + } + else { + sdkToGenerate = context.config.sdkName; + } + } + else if (readmeMdPath) { + const readmeContent = fs.readFileSync(readmeMdPath).toString(); + const config = findSwaggerToSDKConfiguration(readmeContent); + if (!config || config.repositories.length === 0) { + context.logger.warn(`Warning: 'swagger-to-sdk' section cannot be found in ${readmeMdPath}. Please add the section to the readme file according to this guidance https://aka.ms/azsdk/sample-readme-sdk-config`); + } + else if (!config.repositories.some(r => r.repo === context.config.sdkName)) { + context.logger.warn(`Warning: ${context.config.sdkName} cannot be found in the 'swagger-to-sdk' section in the ${readmeMdPath}. Please add the right config to the readme file according to this guidance https://aka.ms/azsdk/sample-readme-sdk-config`); + } + else { + sdkToGenerate = context.config.sdkName; + } + } + if (sdkToGenerate) { + context.logger.info(`SDK to generate:${context.config.sdkName}`); + } + else { + throw new Error(`No SDKs are enabled for generation. Please check the configuration in the realted tspconfig.yaml or readme.md`); + } + context.logger.log('endsection', 'Validate SDK configuration'); }; const workflowHandleReadmeMdOrTypeSpecProject = async (context: WorkflowContext, changedSpecs: ChangedSpecs[]) => { @@ -228,8 +243,6 @@ const workflowHandleReadmeMdOrTypeSpecProject = async (context: WorkflowContext, const typespecProjectList: string[] = []; const suppressionFileMap: Map = new Map(); - const specConfigContent = await workflowInitGetSpecConfig(context); - const specConfig = getSpecConfig(specConfigContent, context.config.specRepo); for (const changedSpec of changedSpecs) { if (changedSpec.typespecProject) { let content: string | undefined = undefined; @@ -239,7 +252,7 @@ const workflowHandleReadmeMdOrTypeSpecProject = async (context: WorkflowContext, const typespecPath = `${path.join(context.specFolder, changedSpec.typespecProject!)}`; context.logger.error(`IOError: Fails to read typespec file with path of '${typespecPath}'. Skipping the typespec case and continue the run. Please ensure the typespec exists with the correct path. Error: ${error.message}`); } - const config = findSDKToGenerateFromTypeSpecProject(content, specConfig)?.filter( + const config = findSDKToGenerateFromTypeSpecProject(content, context.specRepoConfig)?.filter( (r) => r === context.config.sdkName )[0]; if (config === undefined || config.length === 0) { @@ -264,7 +277,6 @@ const workflowHandleReadmeMdOrTypeSpecProject = async (context: WorkflowContext, context.logger.warn(`\tWarning: ${context.config.sdkName} cannot be found in ${changedSpec.readmeMd}. This SDK will be skipped from SDK generation. Please add the right config to the readme file according to this guidance https://github.com/Azure/azure-rest-api-specs/blob/main/documentation/code-gen/configure-go-sdk.md#swagger-to-sdk.`); continue; } - context.legacyAfterScripts.push(...(config.after_scripts ?? [])); context.logger.info(`\t${changedSpec.readmeMd}`); readmeMdList.push(changedSpec.readmeMd!); suppressionFileMap.set(changedSpec.readmeMd!, changedSpec.suppressionFile); @@ -283,14 +295,6 @@ const workflowHandleReadmeMdOrTypeSpecProject = async (context: WorkflowContext, } const changedFiles = [...changedFilesSet]; - - const headCommit = await context.sdkRepo.revparse(branchMain); - - context.logger.log('git', `Checkout branch ${branchSdkGen}`); - await gitCheckoutBranch(context, context.sdkRepo, branchMain); - await context.sdkRepo.raw(['branch', branchSdkGen, headCommit, '--force']); - await gitCheckoutBranch(context, context.sdkRepo, branchSdkGen); - const { status, generateInput, generateOutput } = await workflowCallGenerateScript( context, changedFiles, @@ -315,12 +319,10 @@ const workflowHandleReadmeMdOrTypeSpecProject = async (context: WorkflowContext, context.pendingPackages = (generateOutput?.packages ?? []).map((result) => getPackageData(context, result, sdkSuppressionsYml)); - const fileList = await workflowSaveGenerateResult(context); - await workflowDetectChangedPackages(context, fileList, readmeMdList); + workflowDetectChangedPackages(context); context.logger.remove(context.messageCaptureTransport); for (const pkg of context.pendingPackages) { - await workflowSetSdkRemoteAuth(context); await workflowPkgMain(context, pkg); } @@ -328,10 +330,70 @@ const workflowHandleReadmeMdOrTypeSpecProject = async (context: WorkflowContext, context.pendingPackages = []; }; -export const workflowInitGetSpecConfig = async (context: SdkAutoContext) => { - context.logger.log('github', `Get ${specConfigPath} from ${repoKeyToString(context.config.specRepo)}`); - const fileContent = await getGithubFileContent(context, context.config.specRepo, specConfigPath); - return fileContent; +const workflowGenerateSdkForSpecPr = async (context: WorkflowContext) => { + const changedSpecs = await workflowDetectChangedSpec(context); + context.logger.remove(context.messageCaptureTransport); + const callMode = + context.swaggerToSdkConfig.advancedOptions.generationCallMode ?? + 'one-for-all-configs'; + if (callMode === 'one-for-all-configs') { + await workflowHandleReadmeMdOrTypeSpecProject(context, changedSpecs); + } else { + for (const changedSpec of changedSpecs) { + await workflowHandleReadmeMdOrTypeSpecProject(context, [changedSpec]); + } + } +} + +const workflowGenerateSdk = async (context: WorkflowContext) => { + let readmeMdList: string[] = []; + let typespecProjectList: string[] = []; + let suppressionFile; + const filterSuppressionFileMap: Map = new Map(); + if (context.config.tspConfigPath) { + context.logger.log('info', `Handle the following typespec project: ${context.config.tspConfigPath}`); + typespecProjectList.push(context.config.tspConfigPath.replace('/tspconfig.yaml', '')); + suppressionFile = path.join(context.config.localSpecRepoPath, context.config.tspConfigPath.replace('tspconfig.yaml', sdkSuppressionsFileName)); + if (fs.existsSync(suppressionFile)) { + filterSuppressionFileMap.set(context.config.tspConfigPath, suppressionFile); + } + } else if (context.config.readmePath) { + context.logger.log('info', `Handle the following readme.md: ${context.config.readmePath}`); + readmeMdList.push(context.config.readmePath); + suppressionFile = path.join(context.config.localSpecRepoPath, context.config.readmePath.replace('readme.md', sdkSuppressionsFileName)); + if (fs.existsSync(suppressionFile)) { + filterSuppressionFileMap.set(context.config.readmePath, suppressionFile); + } + } + else { + context.logger.error(`ConfigError: 'tspConfigPath' and 'readmePath' are not provided. Please provide at least one of them.`); + return; + } + + const { status, generateOutput } = await workflowCallGenerateScript( + context, + [], + readmeMdList, + typespecProjectList + ); + + if (!generateOutput && status === 'failed') { + context.logger.warn('Warning: Package processing is skipped as the SDK generation fails. Please look into the above generation errors or report this issue through https://aka.ms/azsdk/support/specreview-channel.'); + return; + } + const sdkSuppressionsYml = await workflowInitGetSdkSuppressionsYml(context, filterSuppressionFileMap); + context.pendingPackages = + (generateOutput?.packages ?? []).map((result) => getPackageData(context, result, sdkSuppressionsYml)); + + workflowDetectChangedPackages(context); + + context.logger.remove(context.messageCaptureTransport); + for (const pkg of context.pendingPackages) { + await workflowPkgMain(context, pkg); + } + + context.handledPackages.push(...context.pendingPackages); + context.pendingPackages = []; }; export const workflowInitGetSdkSuppressionsYml = async ( @@ -389,170 +451,20 @@ export const workflowInitGetSdkSuppressionsYml = async ( return suppressionFileMap; }; -export const workflowInitGetDefaultBranch = async (context: SdkAutoContext, repo: RepoKey) => { - const rsp = await context.octokit.repos.get({ - owner: repo.owner, - repo: repo.name - }); - - return rsp.data.default_branch; -}; - -const workflowInitConfig = async (context: SdkAutoContext) => { - const sdkRepoConfig = await getSdkRepoConfig(context); - - context.logger.info(`mainRepository: ${repoKeyToString(sdkRepoConfig.mainRepository)}`); - context.logger.info(`mainBranch: ${sdkRepoConfig.mainBranch}`); - context.logger.info(`integrationRepository: ${repoKeyToString(sdkRepoConfig.integrationRepository)}`); - context.logger.info(`integrationBranchPrefix: ${sdkRepoConfig.integrationBranchPrefix}`); - context.logger.info(`secondaryRepository: ${repoKeyToString(sdkRepoConfig.secondaryRepository)}`); - context.logger.info(`secondaryBranch: ${sdkRepoConfig.secondaryBranch}`); - - context.logger.log( - 'github', - `Get ${sdkRepoConfig.configFilePath} from ${repoKeyToString(sdkRepoConfig.mainRepository)}` - ); - return async () => { - const fileContent = await getGithubFileContent( - context, - sdkRepoConfig.mainRepository, - sdkRepoConfig.configFilePath, - sdkRepoConfig.mainBranch - ); - - if (!fileContent) { - throw new Error(`ConfigError: ${repoKeyToString(sdkRepoConfig.mainRepository)} ${sdkRepoConfig.configFilePath} doesn't exist. Please refer to the https://github.com/Azure/azure-rest-api-specs/blob/main/documentation/samplefiles/README.md#swagger-to-sdk sample file to add the right configuration.`); - } - - const swaggerToSdkConfig = getSwaggerToSdkConfig(fileContent); - - return { sdkRepoConfig, swaggerToSdkConfig }; - }; -}; - -const workflowInitSpecRepo = async ( +const workflowInitSpecRepo = ( context: SdkAutoContext, - opts: { checkoutMainBranch: boolean } = { checkoutMainBranch: true } -) => { - const specFolder = path.join(context.workingFolder, context.config.specRepo.name); - mkdirpSync(specFolder); - - const specRepo = simpleGit({ ...simpleGitOptions, baseDir: path.resolve(process.cwd(), specFolder) }); - await specRepo.init(false); - - await gitSetRemoteWithAuth(context, specRepo, remoteMain, context.config.specRepo); - - await gitRemoveAllBranches(context, specRepo); - - let refSpec = `pull/${context.config.pullNumber}/merge`; - let fetchResult; - let toFetch: string[]; - if (context.trigger === 'pullRequest') { - toFetch = [ - `+refs/${refSpec}:refs/heads/${branchMain}`, - `+refs/heads/${context.specPrBaseBranch}:refs/heads/${branchBase}` - ]; - } else { - refSpec = `heads/${context.specPrBaseBranch}`; - toFetch = [`+refs/${refSpec}:refs/${refSpec}`]; - } - - context.logger.log('git', `Fetch ${refSpec} on ${repoKeyToString(context.config.specRepo)}`); - - return async () => { - fetchResult = await specRepo.fetch([remoteMain, '--update-head-ok', '--force', ...toFetch]); - if (!fetchResult) { - throw new Error(`Error: Failed to fetch spec repo. Code: ${fetchResult}. Please re-run the failed job in pipeline run or emit '/azp run' in the PR comment to trigger the re-run if the error is retryable`); - } - - if (context.trigger === 'pullRequest') { - const headSha = await specRepo.revparse(branchMain); - if (headSha !== context.specCommitSha) { - if (context.config.runEnv !== 'test') { - context.logger.warn(`Warning: HeadSha mismatches. Update to ${headSha}.`); - } - context.specCommitSha = headSha; - } - } else { - const headCommit = await specRepo.show([context.specCommitSha]); - await specRepo.raw(['branch', '--copy', headCommit, branchMain, '--force']); - const baseCommit = await specRepo.revparse('HEAD'); - await specRepo.raw(['branch', '--copy', baseCommit, branchBase, '--force']); - } - - if (opts.checkoutMainBranch) { - await gitCheckoutBranch(context, specRepo, branchMain); - } - - return { specFolder, specRepo }; - }; -}; - -const workflowSetSdkRemoteAuth = async ( - context: Pick ) => { - const { sdkRepo, sdkRepoConfig } = context; - await gitSetRemoteWithAuth(context, sdkRepo, remoteMain, sdkRepoConfig.mainRepository); - await gitSetRemoteWithAuth(context, sdkRepo, remoteSecondary, sdkRepoConfig.secondaryRepository); - await gitSetRemoteWithAuth(context, sdkRepo, remoteIntegration, sdkRepoConfig.integrationRepository); -}; + const specFolder = context.config.localSpecRepoPath; + const specRepo = simpleGit({ ...simpleGitOptions, baseDir: specFolder }); + return { specFolder, specRepo }; +} -const workflowInitSdkRepo = async ( +const workflowInitSdkRepo = ( context: SdkAutoContext, - sdkRepoConfig: SdkRepoConfig, - swaggerToSdkConfig: SwaggerToSdkConfig ) => { - const cloneDir = - swaggerToSdkConfig.advancedOptions.cloneDir; - const sdkFolderName = cloneDir - ? path.join(sdkRepoConfig.mainRepository.name, cloneDir) - : sdkRepoConfig.mainRepository.name; - const sdkFolder = path.join(context.workingFolder, sdkFolderName); - mkdirpSync(sdkFolder); - - const sdkRepo = simpleGit({ ...simpleGitOptions, baseDir: path.resolve(process.cwd(), sdkFolder) }); - await sdkRepo.init(false); - - await workflowSetSdkRemoteAuth({ ...context, sdkRepo, sdkRepoConfig }); - await gitRemoveAllBranches(context, sdkRepo); - - context.logger.log('git', `Fetch ${repoKeyToString(sdkRepoConfig.mainRepository)} to ${branchMain}`); - - let fetchResult = await sdkRepo.fetch([ - branchMain, - '--update-head-ok', - '--force', - `+refs/heads/${sdkRepoConfig.mainBranch}:refs/heads/${branchMain}` - ]); - if (!fetchResult) { - throw new Error(`Error: Failed to fetch spec repo. Code: ${fetchResult}. Please re-run the failed job in pipeline run or emit '/azp run' in the PR comment to trigger the re-run if the error is retryable.`); - } - - context.logger.log('git', `Checkout ${branchMain}`); - await gitCheckoutBranch(context, sdkRepo, branchMain); - - if ( - sdkRepoConfig.secondaryRepository.name === sdkRepoConfig.mainRepository.name && - sdkRepoConfig.secondaryRepository.owner === sdkRepoConfig.mainRepository.owner && - sdkRepoConfig.secondaryBranch === sdkRepoConfig.mainBranch - ) { - context.logger.log('git', `Checkout ${branchSecondary} from ${branchMain}`); - await sdkRepo.raw(['branch', '--copy', '--force', branchMain, branchSecondary]); - } else { - context.logger.log('git', `Fetch ${repoKeyToString(sdkRepoConfig.secondaryRepository)} to ${branchSecondary}`); - - fetchResult = await sdkRepo.fetch([ - branchSecondary, - '--update-head-ok', - '--force', - `+refs/heads/${sdkRepoConfig.secondaryBranch}:refs/heads/${branchSecondary}` - ]); - if (!fetchResult) { - throw new Error(`Error: Failed to fetch spec repo. Code: ${fetchResult}. Please re-run the failed job in pipeline run or emit '/azp run' in the PR comment to trigger the re-run if the error is retryable.`); - } - } - - return { sdkRepo, sdkFolder }; + const sdkFolder = context.config.localSdkRepoPath; + const sdkRepo = simpleGit({ ...simpleGitOptions, baseDir: sdkFolder }); + return { sdkFolder, sdkRepo }; }; const fileInitInput = 'initInput.json'; @@ -593,8 +505,8 @@ const workflowDetectChangedSpec = async ( context: Pick ) => { const repo = context.specRepo; - const headCommit = await repo.revparse(branchMain); - const baseCommit = await repo.revparse(branchBase); + const headCommit = await repo.revparse("HEAD"); + const baseCommit = await repo.revparse("HEAD^"); const diff = await repo.diff(['--name-status', headCommit, baseCommit]); const diffFileList = await gitGetDiffFileList(diff, context, 'in spec PR'); @@ -704,19 +616,18 @@ const workflowCallGenerateScript = async ( const generateInput: GenerateInput = { dryRun: false, - specFolder: path.relative(context.sdkFolder, context.specFolder), - headSha: context.specCommitSha, - headRef: context.specHeadRef, - repoHttpsUrl: context.specHtmlUrl, - trigger: context.trigger, + specFolder: path.relative(context.config.workingFolder, context.specFolder), + headSha: context.config.specCommitSha ?? "", + headRef: "", + repoHttpsUrl: context.config.specRepoHttpsUrl ?? "", + trigger: "pullRequest", changedFiles, installInstructionInput: { - isPublic: context.config.storage.isPublic, - downloadUrlPrefix: `${getBlobName(context, '')}`, - downloadCommandTemplate: context.config.storage.downloadCommand, - trigger: context.trigger - }, - autorestConfig: context.autorestConfig + isPublic: false, + downloadUrlPrefix: "https://artprodcus3.artifacts.visualstudio.com", + downloadCommandTemplate: "downloadCommand", + trigger: "pullRequest" + } }; if (context.swaggerToSdkConfig.generateOptions.generateScript === undefined) { @@ -772,47 +683,17 @@ const workflowCallGenerateScript = async ( return { ...statusContext, generateInput, generateOutput }; }; -const workflowSaveGenerateResult = async (context: WorkflowContext) => { - context.logger.log('section', 'Commit generate result'); - context.logger.log('git', 'Add * in SDK repo'); - await gitAddAll(context.sdkRepo); - - const diff = await context.sdkRepo.diff(['--name-status', 'HEAD']); - const fileList = await gitGetDiffFileList(diff, context, 'after SDK generate'); - if (fileList.length === 0) { - context.logger.warn('Warning: No file changes detected after the generation. Please refer to the generation errors to understand the reasons.'); - setSdkAutoStatus(context, 'warning'); - } - - context.logger.log('git', 'Commit all the changes'); - await gitGetCommitter(context.sdkRepo); - await context.sdkRepo.raw(['commit', '-m', 'CodeGen Result']); - - context.logger.log('endsection', 'Commit generate result'); - return fileList; -}; - -const workflowDetectChangedPackages = async (context: WorkflowContext, fileList: string[], readmeMdList: string[]) => { +const workflowDetectChangedPackages = (context: WorkflowContext) => { context.logger.log('section', 'Detect changed packages'); - if (context.pendingPackages.length === 0) { - const searchConfig = context.swaggerToSdkConfig.packageOptions.packageFolderFromFileSearch; - if (!searchConfig) { - context.logger.warn(`Warning: Skip detecting changed packages based on the config in swagger_to_sdk.config. Please refer to the schema https://github.com/Azure/azure-rest-api-specs/blob/main/documentation/sdkautomation/SwaggerToSdkConfigSchema.json for 'packageOptions' configuration.`); - return; - } - //TODO can we delete this? - context.logger.info(`TODO ${fileList.length * readmeMdList.length}`); - } - context.logger.info(`${context.pendingPackages.length} packages found after generation:`); for (const pkg of context.pendingPackages) { - context.logger.info(`\t${pkg.relativeFolderPath}`); - for (const extraPath of pkg.extraRelativeFolderPaths) { + context.logger.info(`\t${pkg.relativeFolderPath}`); + for (const extraPath of pkg.extraRelativeFolderPaths) { context.logger.info(`\t- ${extraPath}`); - } + } } if (context.pendingPackages.length === 0) { - context.logger.warn(`Warning: No package detected after generation. Please refer to the above logs to understand why the package hasn't been generated. `); + context.logger.warn(`Warning: No package detected after generation. Please refer to the above logs to understand why the package hasn't been generated. `); } }; diff --git a/tools/spec-gen-sdk/src/automation/workflowPackage.ts b/tools/spec-gen-sdk/src/automation/workflowPackage.ts index 2b8a22a902e..ccd5ff7afbf 100644 --- a/tools/spec-gen-sdk/src/automation/workflowPackage.ts +++ b/tools/spec-gen-sdk/src/automation/workflowPackage.ts @@ -3,46 +3,25 @@ import path from 'path'; import fs, { copyFileSync, existsSync } from 'fs'; import { InstallInstructionScriptInput } from '../types/InstallInstructionScriptInput'; import { getInstallInstructionScriptOutput } from '../types/InstallInstructionScriptOutput'; -import { getGenerationBranchName, getIntegrationBranchName, PackageData } from '../types/PackageData'; +import { PackageData } from '../types/PackageData'; import { deleteTmpJsonFile, readTmpJsonFile, writeTmpJsonFile } from '../utils/fsUtils'; -import { RepoKey, repoKeyToString } from '../utils/githubUtils'; -import { gitCheckoutBranch, gitGetCommitter } from '../utils/gitUtils'; import { isLineMatch, runSdkAutoCustomScript, setSdkAutoStatus } from '../utils/runScript'; -import { CommentCaptureTransport, getBlobName, loggerStorageAccountTransport } from './logging'; +import { CommentCaptureTransport } from './logging'; import { - branchMain, - branchSdkGen, - branchSecondary, - FailureType, - remoteIntegration, - remoteMain, - setFailureType, WorkflowContext } from './workflow'; import { mkdirpSync } from 'fs-extra'; import { getLanguageByRepoName } from './entrypoint'; -import { FileStatusResult } from 'simple-git'; export const workflowPkgMain = async (context: WorkflowContext, pkg: PackageData) => { - context.logger.log('section', `Handle package ${pkg.name}`); - const captureTransport = new CommentCaptureTransport({ extraLevelFilter: ['error', 'warn'], level: 'debug', output: pkg.messages }); context.logger.add(captureTransport); - - const { blobTransport, blobName } = await loggerStorageAccountTransport( - context, - getBlobName(context, 'logs.txt', pkg) - ); - pkg.logsBlobUrl = `${blobName}`; - context.logger.add(blobTransport); - context.logger.info(`Package log to ${pkg.logsBlobUrl}`); - - const syncConfig = workflowPkgGetSyncConfig(context, pkg); - const pushBranchPromise = (await workflowPkgUpdateBranch(context, pkg, syncConfig))(); + context.logger.log('section', `Handle package ${pkg.name}`); + context.logger.info(`Package log to a new logFile`); await workflowPkgCallBuildScript(context, pkg); await workflowPkgCallChangelogScript(context, pkg); @@ -50,12 +29,9 @@ export const workflowPkgMain = async (context: WorkflowContext, pkg: PackageData await workflowPkgSaveSDKArtifact(context, pkg); await workflowPkgSaveApiViewArtifact(context, pkg); await workflowPkgCallInstallInstructionScript(context, pkg); - await pushBranchPromise; - await workflowPkgUpdatePR(context, pkg, syncConfig); setSdkAutoStatus(pkg, 'succeeded'); - context.logger.remove(blobTransport); context.logger.remove(captureTransport); context.logger.log('endsection', `Handle package ${pkg.name}`); }; @@ -167,10 +143,11 @@ const workflowPkgSaveSDKArtifact = async (context: WorkflowContext, pkg: Package return; } - const destination = path.join(context.workingFolder, 'generatedSdkArtifacts'); + const destination = path.join(context.config.workingFolder, 'generatedSdkArtifacts'); if (!existsSync(destination)) { mkdirpSync(destination); } + context.sdkArtifactFolder = destination; console.log(`##vso[task.setVariable variable=HasSDKArtifact]true`); const artifactName = `SDK_Artifact_${language}`; // it's the artifact in pipeline artifacts console.log(`##vso[task.setVariable variable=sdkArtifactName]${artifactName}`); @@ -192,10 +169,11 @@ const workflowPkgSaveApiViewArtifact = async (context: WorkflowContext, pkg: Pac } const language = pkg.language ?? getLanguageByRepoName(context.sdkRepoConfig.mainRepository.name); - const destination = path.join(context.workingFolder, 'sdkApiViewArtifacts'); + const destination = path.join(context.config.workingFolder, 'sdkApiViewArtifacts'); if (!existsSync(destination)) { mkdirpSync(destination); } + context.sdkApiViewArtifactFolder = destination; console.log(`##vso[task.setVariable variable=HasApiViewArtifact]true`); const artifactName = `sdkApiViewArtifact_${language}`; // it's the artifact in pipeline artifacts console.log(`##vso[task.setVariable variable=ArtifactName]${artifactName}`); @@ -205,7 +183,7 @@ const workflowPkgSaveApiViewArtifact = async (context: WorkflowContext, pkg: Pac const apiViewArtifactMeta = { packageName: pkg.name, apiViewArtifact: fileName, - specCommitSha: context.specCommitSha, + specCommitSha: context.config.specCommitSha, language, artifactName }; @@ -227,12 +205,12 @@ const workflowPkgCallInstallInstructionScript = async ( context.logger.log('section', 'Call InstallInstructionScript'); const input: InstallInstructionScriptInput = { - isPublic: context.config.storage.isPublic, - downloadUrlPrefix: `${getBlobName(context, '', pkg)}`, - downloadCommandTemplate: context.config.storage.downloadCommand, + isPublic: false, + downloadUrlPrefix: "", + downloadCommandTemplate: "", packageName: pkg.name, artifacts: pkg.artifactPaths.map((p) => path.basename(p)), - trigger: context.trigger + trigger: "pullRequest" }; writeTmpJsonFile(context, fileInstallInstructionInput, input); deleteTmpJsonFile(context, fileInstallInstructionOutput); @@ -252,198 +230,3 @@ const workflowPkgCallInstallInstructionScript = async ( context.logger.log('section', 'Call InstallInstructionScript'); }; -export type SyncConfig = { - baseBranch: string; - baseRepo: RepoKey; - baseRemote: string; - targetBranch: string; - targetRepo: RepoKey; - targetRemote: string; - hasChanges?: boolean; -}; - -const workflowPkgGetSyncConfig = (context: WorkflowContext, pkg: PackageData) => { - const config: SyncConfig = { - baseBranch: context.sdkRepoConfig.mainBranch, - baseRepo: context.sdkRepoConfig.mainRepository, - baseRemote: remoteMain, - targetBranch: getIntegrationBranchName(context, pkg.name), - targetRepo: context.sdkRepoConfig.integrationRepository, - targetRemote: remoteIntegration - }; - - if (!context.useMergedRoutine) { - config.baseRepo = context.sdkRepoConfig.integrationRepository; - config.baseRemote = remoteIntegration; - config.targetBranch = getGenerationBranchName(context, pkg.name); - } - - context.logger.info( - `baseBranch [${config.baseBranch}] baseRepo [${repoKeyToString(config.baseRepo)}] baseRemote [${config.baseRemote}]` - ); - context.logger.info( - `targetBranch [${config.targetBranch}] targetRepo [${repoKeyToString(config.targetRepo)}] baseRemote [${config.targetRemote - }]` - ); - return config; -}; - -const workflowPkgUpdateBranch = async (context: WorkflowContext, pkg: PackageData, syncConfig: SyncConfig) => { - if ( - repoKeyToString(context.sdkRepoConfig.mainRepository) === repoKeyToString(syncConfig.baseRepo) && - context.sdkRepoConfig.mainBranch === syncConfig.baseBranch - ) { - context.logger.info('Skip sync baseBranch due to same remote same branch'); - } else { - context.logger.log('git', `Push ${branchMain} to ${repoKeyToString(syncConfig.baseRepo)}:${syncConfig.baseBranch}`); - const pushResult = await context.sdkRepo.push([syncConfig.baseRemote, `+refs/heads/${branchMain}:refs/heads/${syncConfig.baseBranch}`]); - if (!pushResult) { - context.logger.warn( - `Warning: Failed to push with code [${pushResult}]: ${repoKeyToString(syncConfig.baseRepo)}:${syncConfig.baseBranch}. This doesn't impact SDK generation.` - ); - setSdkAutoStatus(pkg, 'warning'); - } - } - - context.logger.log('git', `Create targetBranch ${syncConfig.targetBranch}`); - const baseCommit = await context.sdkRepo.revparse(branchSecondary); - await context.sdkRepo.raw(['branch', syncConfig.targetBranch, baseCommit, '--force']); - await gitCheckoutBranch(context, context.sdkRepo, syncConfig.targetBranch, false); - - const foldersToAdd = [pkg.relativeFolderPath, ...pkg.extraRelativeFolderPaths] - .filter((p) => p !== undefined) - .map((p) => path.relative('.', p)); - context.logger.log('git', `Checkout sdk folders from ${branchSdkGen} and commit: ${foldersToAdd.join(' ')}`); - const sdkGenCommit = await context.sdkRepo.revparse(branchSdkGen); - const sdkGenTree = await context.sdkRepo.revparse(`${sdkGenCommit}^{tree}`); - if (foldersToAdd.length > 0) { - await context.sdkRepo.raw(['read-tree', sdkGenTree]); - await context.sdkRepo.raw(['checkout', '--', '.']); - } - - await context.sdkRepo.raw(['update-index', '--refresh']); - - const statusFiles = await context.sdkRepo.status(); - const fileList = statusFiles.files.map((item: FileStatusResult) => item.path); - - let commitMsg = `CodeGen from PR ${context.config.pullNumber} in ${repoKeyToString(context.config.specRepo)}\n`; - const commitMsgsuffix = await context.specRepo.log(); - commitMsg += commitMsgsuffix.latest?.message; - - await gitGetCommitter(context.sdkRepo); - await context.sdkRepo.raw(['commit', '-m', commitMsg]); - syncConfig.hasChanges = fileList.length > 0; - - context.logger.log('git', `Push ${syncConfig.targetBranch} to ${repoKeyToString(syncConfig.targetRepo)}`); - - // Push in parallel to speed up - return async () => { - const result = await context.sdkRepo.push([syncConfig.targetRemote, `+refs/heads/${syncConfig.targetBranch}:refs/heads/${syncConfig.targetBranch}`]); - if (!result) { - context.logger.error( - `GitError: Failed to push with code [${result}]: ${repoKeyToString(syncConfig.targetRepo)}:${syncConfig.targetBranch}. Please re-run the pipeline if the error is retryable or report this issue through https://aka.ms/azsdk/support/specreview-channel.` - ); - setSdkAutoStatus(pkg, 'failed'); - setFailureType(context, FailureType.PipelineFrameworkFailed); - } - }; -}; - -const workflowPkgUpdatePR = async (context: WorkflowContext, pkg: PackageData, syncConfig: SyncConfig) => { - if (context.useMergedRoutine) { - const intRepo = context.sdkRepoConfig.integrationRepository; - const genPrHead = `${intRepo.owner}:${getGenerationBranchName(context, pkg.name)}`; - context.logger.log('github', `Get GenerationPR and close it if exist`); - const { data: genPr } = await context.octokit.pulls.list({ - owner: intRepo.owner, - repo: intRepo.name, - state: 'open', - head: genPrHead - }); - for (const pr of genPr) { - context.logger.log('github', `Close GenerationPR ${repoKeyToString(intRepo)}/${pr.number}`); - await context.octokit.pulls.update({ - owner: intRepo.owner, - repo: intRepo.name, - pull_number: pr.number, - state: 'closed' - }); - } - } - - const head = `${syncConfig.targetRepo.owner}:${syncConfig.targetBranch}`; - const headLabel = syncConfig.targetRepo.owner === syncConfig.baseRepo.owner ? syncConfig.targetBranch : head; - - const title = `[${context.useMergedRoutine ? 'ReleasePR' : 'AutoPR'} ${pkg.name}] ${context.specPrTitle}`; - let body = `Create to sync ${context.specPrHtmlUrl}`; - if (context.useMergedRoutine) { - body = `${body}\n[ReCreate this PR](https://github.com/${syncConfig.baseRepo.name}/compare/${syncConfig.baseBranch}...${headLabel}?expand=1)`; - } - - body = `${body}\n\n${pkg.installationInstructions ?? ''}`; - body = `${body}\n This pull request has been automatically generated for preview purposes.`; - context.logger.log( - 'github', - `Get PR in ${repoKeyToString(syncConfig.baseRepo)} from ${head} to ${syncConfig.baseBranch}` - ); - const { data: existingPrs } = await context.octokit.pulls.list({ - owner: syncConfig.baseRepo.owner, - repo: syncConfig.baseRepo.name, - state: context.useMergedRoutine ? 'all' : 'open', - head, - base: syncConfig.baseBranch, - sort: 'created', - direction: 'desc' - }); - - let targetPr: typeof existingPrs[0] | undefined = existingPrs[0]; - const draft = - context.trigger === 'pullRequest' - ? context.swaggerToSdkConfig.advancedOptions.draftGenerationPR - : context.swaggerToSdkConfig.advancedOptions.draftIntegrationPR; - if (targetPr !== undefined && (targetPr.merged_at === null || targetPr.merged_at === undefined)) { - context.logger.log('github', `Update existing PR ${targetPr.html_url}`); - await context.octokit.pulls.update({ - owner: syncConfig.baseRepo.owner, - repo: syncConfig.baseRepo.name, - pull_number: targetPr.number, - title, - body, - maintainer_can_modify: false, - draft - }); - if (!syncConfig.hasChanges) { - context.logger.log('github', 'Not showing PR in comment because there is no diff'); - targetPr = undefined; - } - } else if (!syncConfig.hasChanges) { - context.logger.log('github', 'Skip creating PR because there is no diff'); - targetPr = undefined; - } else { - context.logger.log('github', `Create new PR`); - const rsp = await context.octokit.pulls.create({ - owner: syncConfig.baseRepo.owner, - repo: syncConfig.baseRepo.name, - head: headLabel, - base: syncConfig.baseBranch, - title, - body, - maintainer_can_modify: false, - draft - }); - targetPr = rsp.data; - context.logger.log('github', `PR created at ${targetPr.html_url}`); - - if (context.useMergedRoutine && context.swaggerToSdkConfig.advancedOptions.closeIntegrationPR) { - context.logger.log('github', `Close IntegrationPR`); - await context.octokit.pulls.update({ - owner: syncConfig.baseRepo.owner, - repo: syncConfig.baseRepo.name, - pull_number: targetPr.number, - state: 'closed' - }); - } - } - - pkg.generationPullRequestUrl = targetPr?.html_url; -}; diff --git a/tools/spec-gen-sdk/src/cli/cli.ts b/tools/spec-gen-sdk/src/cli/cli.ts index c50bfa25569..d7028c40757 100644 --- a/tools/spec-gen-sdk/src/cli/cli.ts +++ b/tools/spec-gen-sdk/src/cli/cli.ts @@ -1,7 +1,7 @@ import { getRepository } from '../utils/githubUtils'; -import { sdkAutoMain } from '../automation/entrypoint'; import { SDKAutomationState } from '../automation/sdkAutomationState'; import { sdkAutomationCliConfig } from './config'; +import { sdkAutoMain } from '../automation/entrypoint'; // tslint:disable-next-line: no-floating-promises (async () => { @@ -10,30 +10,30 @@ import { sdkAutomationCliConfig } from './config'; let status: SDKAutomationState | undefined = undefined; try { - const repo = getRepository(config.specRepo); + const repo = getRepository(config.specRepoHttpsUrl); process.chdir(config.workingFolder); status = await sdkAutoMain({ specRepo: repo, + localSpecRepoPath: config.localSpecRepoPath, + localSdkRepoPath: config.localSdkRepoPath, + tspConfigPath: config.tspConfigPath, + readmePath: config.readmePath, + specCommitSha: config.specCommitSha, + specRepoHttpsUrl: config.specRepoHttpsUrl, pullNumber: config.prNumber, sdkName: config.sdkRepoName, - filterSwaggerToSdk: config.executionMode === 'SDK_FILTER', + workingFolder: config.workingFolder, github: { token: config.githubToken, - id: config.githubApp.id, - privateKey: config.githubApp.privateKey }, - storage: config.blobStorage, - runEnv: config.isTriggeredByUP ? 'azureDevOps' : 'local', + runEnv: config.isTriggeredByPipeline ? 'azureDevOps' : 'local', branchPrefix: 'sdkAuto' }); } catch (e) { console.error(e.message); console.error(e.stack); status = 'failed'; - if (config.executionMode === 'SDK_FILTER') { - console.log(`##vso[task.setVariable variable=SkipAll;isOutput=true]true`); - } } const elapsed = process.hrtime(start); diff --git a/tools/spec-gen-sdk/src/cli/config/index.ts b/tools/spec-gen-sdk/src/cli/config/index.ts index 9736b86976d..358756df665 100644 --- a/tools/spec-gen-sdk/src/cli/config/index.ts +++ b/tools/spec-gen-sdk/src/cli/config/index.ts @@ -1,4 +1,3 @@ import { configurationSchema } from './schema'; export const sdkAutomationCliConfig = configurationSchema.validate().getProperties(); -sdkAutomationCliConfig.githubApp.privateKey = sdkAutomationCliConfig.githubApp.privateKey.replace(/\\n/g, '\n'); diff --git a/tools/spec-gen-sdk/src/cli/config/schema.ts b/tools/spec-gen-sdk/src/cli/config/schema.ts index 6938de9d302..69afbae8fad 100644 --- a/tools/spec-gen-sdk/src/cli/config/schema.ts +++ b/tools/spec-gen-sdk/src/cli/config/schema.ts @@ -8,28 +8,17 @@ dotenv.config(); export type SDKAutomationCliConfig = { env: string; workingFolder: string; - executionMode: string; - isTriggeredByUP: boolean; + isTriggeredByPipeline: boolean; githubToken: string; - azureCliArgs: { - azureDevopsPat: string; - buildId: string; - }; - specRepo: string; + localSpecRepoPath: string; + localSdkRepoPath: string; + tspConfigPath?: string; + readmePath?: string; sdkRepoName: string; prNumber: number; - githubApp: { - id: number; - privateKey: string; - }; + specCommitSha: string; + specRepoHttpsUrl: string; githubCommentAuthorName: string; - blobStorage: { - name: string; - prefix: string; - downloadCommand: string; - isPublic: boolean; - }; - testRunId?: string; }; const emptyValidator = (value?: string): void => { @@ -51,16 +40,10 @@ export const configurationSchema: Config = convict = convict = convict = convict -u {URL} -o {FILENAME}', - doc: '', - env: 'BLOB_DOWNLOAD_COMMAND', - arg: 'blob-download-command', - format: String - } - }, - testRunId: { - default: '', - doc: 'Used in integration test only. See ./integrationtest/utils.ts#getRunIdPrefix()', - env: 'TEST_RUN_ID', - arg: 'test-run-id', - format: String } }); diff --git a/tools/spec-gen-sdk/src/types/ExecutionReport.ts b/tools/spec-gen-sdk/src/types/ExecutionReport.ts new file mode 100644 index 00000000000..d4f4c21d51a --- /dev/null +++ b/tools/spec-gen-sdk/src/types/ExecutionReport.ts @@ -0,0 +1,33 @@ +import { SDKAutomationState } from '../automation/sdkAutomationState'; +import { requireJsonc } from '../utils/requireJsonc'; +import { getTypeTransformer } from './validator'; + +export const executionReportSchema = requireJsonc(__dirname + '/ExecutionReportSchema.json'); + +export type ExecutionReport = { + packages: PackageReport[]; + executionResult: SDKAutomationState; + fullLogPath: string; + filteredLogPath?: string; + sdkArtifactFolder?: string; + sdkApiViewArtifactFolder?: string; +}; + +export type PackageReport = { + packageName?: string; + result: SDKAutomationState; + artifactPaths?: string[]; + readmeMd?: string[]; + typespecProject?: string[] + version?: string; + apiViewArtifact?: string; + language?: string; + hasBreakingChange?: boolean; + breakingChangeLabel?: string; + areBreakingChangeSuppressed?: boolean; + presentBreakingChangeSuppressions?: string[]; + absentBreakingChangeSuppressions?: string[]; + installInstructions?: string; +}; + +export const getExecutionReport = getTypeTransformer(executionReportSchema, 'ExecutionReport'); diff --git a/tools/spec-gen-sdk/src/types/ExecutionReportSchema.json b/tools/spec-gen-sdk/src/types/ExecutionReportSchema.json new file mode 100644 index 00000000000..050152f549b --- /dev/null +++ b/tools/spec-gen-sdk/src/types/ExecutionReportSchema.json @@ -0,0 +1,104 @@ +{ + "type": "object", + "properties": { + "packages": { + "type": "array", + "items": { + "$ref": "#/definitions/PackageResult" + } + }, + "executionResult": { + "type": "string", + "enum": ["failed", "succeeded", "warning"], + "default": "succeeded" + }, + "fullLogPath": { + "type": "string" + }, + "filteredLogPath": { + "type": "string" + }, + "sdkArtifactFolder": { + "type": "string" + }, + "sdkApiViewArtifactFolder": { + "type": "string" + } + }, + "required": ["packages", "executionResult", "fullLogPath", "filteredLogPath"], + "definitions": { + "PackageResult": { + "properties": { + "packageName": { + // Name of package. Will be used in branch name and PR title. + // By default it's folder name of first entry in path. + "type": "string" + }, + "result": { + // Status of package. By default it's succeeded. + "type": "string", + "enum": ["failed", "succeeded", "warning"], + "default": "succeeded" + }, + "readmeMd": { + // List of related readmeMd of this package. + // Must provide this field if dryRun is true. + "type": "array", + "items": { + "type": "string" + } + }, + "typespecProject": { + // List of related typespec project of this package. + // Must provide this field if dryRun is true. + "type": "array", + "items": { + "type": "string" + } + }, + "version": { + "type": "string" + }, + "artifactPaths": { + "type": "array", + "items": { + "type": "string" + } + }, + "apiViewArtifact": { + "type": "string" + }, + "language": { + "type": "string" + }, + "hasBreakingChange": { + "type": "boolean" + }, + "breakingChangeLabel": { + "type": "string" + }, + "areBreakingChangeSuppressed": { + "type": "boolean" + }, + "presentBreakingChangeSuppressions": { + "type": "array", + "items": { + "type": "string" + } + }, + "absentBreakingChangeSuppressions": { + "type": "array", + "items": { + "type": "string" + } + }, + "installInstructions": { + // See #InstallInstructionScriptOutput + "$ref": "InstallInstructionScriptOutput" + } + }, + "required": ["executionResult", "artifactPaths", "language"] + } + } + } + \ No newline at end of file diff --git a/tools/spec-gen-sdk/src/types/Message.ts b/tools/spec-gen-sdk/src/types/Message.ts index d1f0bf9804e..ccca1e7d1f8 100644 --- a/tools/spec-gen-sdk/src/types/Message.ts +++ b/tools/spec-gen-sdk/src/types/Message.ts @@ -62,13 +62,8 @@ export type MarkdownMessageRecord = BaseMessageRecord & { * * MessageRecords end up being printed in the contents of tables in relevant validation tool check in GitHub PR. * These records are transferred from the Azure DevOps Azure.azure-rest-api-specs-pipeline build runs - * to the GitHub via pipe.log file (pipeline.ts / unifiedPipelineResultFileName) and Azure blob. + * to the GitHub via pipe.log file (pipeline.ts / unifiedPipelineResultFileName). * - * The pipe.log gets uploaded to the blob via - * publishResult.ts / resultPublisher.uploadLog - * - * The blob contents are read by the pipeline-bot via - * resultComposer.ts / parseCompleteMessageData * * Examples: * Save message record from OAD to pipe.log: diff --git a/tools/spec-gen-sdk/src/types/PackageData.ts b/tools/spec-gen-sdk/src/types/PackageData.ts index b21eaf0b540..600ea0b1b4a 100644 --- a/tools/spec-gen-sdk/src/types/PackageData.ts +++ b/tools/spec-gen-sdk/src/types/PackageData.ts @@ -130,6 +130,8 @@ export type PackageData = SDKRepositoryPackageData & { */ isBetaMgmtSdk?: boolean; isDataPlane: boolean; + readmeMd?: string[]; + typespecProject?: string[]; }; export const getGenerationBranchName = (context: WorkflowContext, packageName: string) => { @@ -191,6 +193,9 @@ export const getPackageData = (context: WorkflowContext, result: PackageResult, sdkSuppressionFilePath = suppressionContent.sdkSuppressionFilePath; } + if(context.specPrInfo) { + sdkSuppressionFilePath = `https://github.com/${context.specPrInfo.head.owner}/${context.specPrInfo.head.repo}/blob/${context.specPrHeadBranch}/${sdkSuppressionFilePath}`; + } return { name, isDataPlane, @@ -201,10 +206,10 @@ export const getPackageData = (context: WorkflowContext, result: PackageResult, absentSuppressionLines, presentSuppressionLines, parseSuppressionLinesErrors, - sdkSuppressionFilePath: sdkSuppressionFilePath && `https://github.com/${context.specPrInfo.head.owner}/${context.specPrInfo.head.repo}/blob/${context.specPrHeadBranch}/${sdkSuppressionFilePath}`, + sdkSuppressionFilePath: sdkSuppressionFilePath, isBetaMgmtSdk, logsBlobUrl: '', - isPrivatePackage: !context.config.storage.isPublic, + isPrivatePackage: false, changedFilePaths: [], generationBranch: getGenerationBranchName(context, name), generationRepository: integrationRepository, @@ -221,6 +226,8 @@ export const getPackageData = (context: WorkflowContext, result: PackageResult, artifactBlobUrls: [], changelogs: result.changelog?.content.split('\n') ?? [], breakingChangeItems, - version: result.version + version: result.version, + readmeMd: result.readmeMd, + typespecProject: result.typespecProject }; }; diff --git a/tools/spec-gen-sdk/src/types/SpecConfig.ts b/tools/spec-gen-sdk/src/types/SpecConfig.ts index 69d8932ef55..04191ed318b 100644 --- a/tools/spec-gen-sdk/src/types/SpecConfig.ts +++ b/tools/spec-gen-sdk/src/types/SpecConfig.ts @@ -1,6 +1,4 @@ -import { SdkAutoContext } from '../automation/entrypoint'; -import { workflowInitGetDefaultBranch, workflowInitGetSpecConfig } from '../automation/workflow'; -import { getRepoKey, RepoKey } from '../utils/githubUtils'; +import { RepoKey } from '../utils/githubUtils'; import { requireJsonc } from '../utils/requireJsonc'; import { getTypeTransformer } from './validator'; export const specConfigPath = 'specificationRepositoryConfiguration.json'; @@ -39,49 +37,3 @@ export const getSpecConfig = (data: unknown, specRepo: RepoKey) => { return specConfig; }; - -export const getSdkRepoConfig = async (context: SdkAutoContext) => { - const data = await workflowInitGetSpecConfig(context); - const specRepo = context.config.specRepo; - const sdkName = context.config.sdkName; - - const getConfigRepoKey = (repo: RepoKey | string | undefined, fallback: RepoKey): RepoKey => { - if (repo === undefined) { - return fallback; - } - const repoKey = getRepoKey(repo); - if (!repoKey.owner) { - repoKey.owner = fallback.owner; - } - return repoKey; - }; - - const specConfig = getSpecConfig(data, specRepo); - let sdkRepoConfig = specConfig.sdkRepositoryMappings[sdkName]; - if (sdkRepoConfig === undefined) { - throw new Error(`ConfigError: SDK ${sdkName} is not defined in SpecConfig. Please add the related config at the 'specificationRepositoryConfiguration.json' file under the root folder of the azure-rest-api-specs(-pr) repository.`); - } - - if (typeof sdkRepoConfig === 'string') { - sdkRepoConfig = { - mainRepository: getRepoKey(sdkRepoConfig) - } as SdkRepoConfig; - } - - sdkRepoConfig.mainRepository = getConfigRepoKey(sdkRepoConfig.mainRepository, { - owner: specRepo.owner, - name: sdkName - }); - sdkRepoConfig.mainBranch = - sdkRepoConfig.mainBranch ?? (await workflowInitGetDefaultBranch(context, sdkRepoConfig.mainRepository)); - sdkRepoConfig.integrationRepository = getConfigRepoKey( - sdkRepoConfig.integrationRepository, - sdkRepoConfig.mainRepository - ); - sdkRepoConfig.integrationBranchPrefix = sdkRepoConfig.integrationBranchPrefix ?? 'sdkAutomation'; - sdkRepoConfig.secondaryRepository = getConfigRepoKey(sdkRepoConfig.secondaryRepository, sdkRepoConfig.mainRepository); - sdkRepoConfig.secondaryBranch = sdkRepoConfig.secondaryBranch ?? sdkRepoConfig.mainBranch; - sdkRepoConfig.configFilePath = sdkRepoConfig.configFilePath ?? 'swagger_to_sdk_config.json'; - - return sdkRepoConfig; -}; diff --git a/tools/spec-gen-sdk/src/utils/gitUtils.ts b/tools/spec-gen-sdk/src/utils/gitUtils.ts index 403069e940a..4ce894805c2 100644 --- a/tools/spec-gen-sdk/src/utils/gitUtils.ts +++ b/tools/spec-gen-sdk/src/utils/gitUtils.ts @@ -1,5 +1,5 @@ -import { SdkAutoContext } from '../automation/entrypoint'; import { CleanOptions, ResetMode, SimpleGit } from 'simple-git'; +import { SdkAutoContext } from '../automation/entrypoint'; export const gitSetRemoteWithAuth = async ( context: Pick, diff --git a/tools/spec-gen-sdk/src/utils/githubUtils.ts b/tools/spec-gen-sdk/src/utils/githubUtils.ts index 61263265b48..8f1f3651b9c 100644 --- a/tools/spec-gen-sdk/src/utils/githubUtils.ts +++ b/tools/spec-gen-sdk/src/utils/githubUtils.ts @@ -1,5 +1,4 @@ import { Octokit } from '@octokit/rest'; -import { createAppAuth } from '@octokit/auth-app'; import * as winston from 'winston'; import { SdkAutoContext } from '../automation/entrypoint'; @@ -22,24 +21,21 @@ export interface Repository { * Get a GitHubRepository object from the provided string or GitHubRepository object. * @param repository The repository name or object. */ -export function getRepository(repository: string | Repository): Repository { - let result: Repository; - if (!repository) { - result = { - name: repository, - owner: "" - }; - } else if (typeof repository === "string") { - let slashIndex: number = repository.indexOf("/"); - if (slashIndex === -1) { - slashIndex = repository.indexOf("\\"); +export function getRepository(repoUrl: string): Repository { + let result: Repository = { + owner: '', + name: '' + }; + const urlPattern = /https:\/\/github\.com\/([^/]+)\/([^/]+)/; + if (repoUrl) { + const match = repoUrl.match(urlPattern); + if (!match) { + throw new Error(`Error: Invalid spec repository URL [${repoUrl}] provided. This is a sample of correct format: https://github.com/azure/azure-rest-api-specs`); } result = { - name: repository.substr(slashIndex + 1), - owner: slashIndex === -1 ? "" : repository.substr(0, slashIndex) + owner: match[1], + name: match[2] }; - } else { - result = repository; } return result; } @@ -127,7 +123,7 @@ interface AuthenticatedOctokit { } export const getAuthenticatedOctokit = ( - opts: { id?: number; privateKey?: string; token?: string }, + opts: { token?: string }, logger: winston.Logger ): AuthenticatedOctokit => { if (opts.token) { @@ -139,71 +135,6 @@ export const getAuthenticatedOctokit = ( getToken: () => Promise.resolve(opts.token!) }; } - - if (opts.id && opts.privateKey) { - const appAuthOctokit = new Octokit({ - authStrategy: createAppAuth, - auth: { - appId: opts.id, - privateKey: opts.privateKey - }, - log: logger - }); - const installationIdCache: { [owner: string]: number } = {}; - const getInstallationId = async (owner: string) => { - let installationId = installationIdCache[owner]; - if (installationId === undefined) { - try { - const { - data: { id } - } = await appAuthOctokit.apps.getOrgInstallation({ org: owner }); - installationId = id; - installationIdCache[owner] = id; - } catch (e) { - try { - logger.warn(`Retrying to get installation app from user. Error details: ${JSON.stringify(e)}.`); - const { - data: { id } - } = await appAuthOctokit.apps.getUserInstallation({ username: owner }); - installationId = id; - installationIdCache[owner] = id; - } catch (e) { - logger.error(`ConfigError: GitHubApp ${opts.id} doesn't have installation for: ${owner}. Please report this issue through https://aka.ms/azsdk/support/specreview-channel or reach out the SDK Automation owner to set up the GitHub application correctly.`); - logger.error(`Error details: ${JSON.stringify(e)}.`); - return undefined; - } - } - } - return installationId; - }; - - const getAccessToken = async (owner: string) => { - const installationId = await getInstallationId(owner); - const auth = await appAuthOctokit.auth({ - type: 'installation', - installationId - }) as { token: string }; - return auth.token; - }; - - const octokit = new Octokit({ - log: logger - }); - octokit.hook.wrap('request', async (request, options) => { - const owner = options.owner ?? options.org; - if (typeof owner !== 'string') { - return request(options); - } - const token = await getAccessToken(owner); - options.headers.Authorization = `token ${token}`; - return request(options); - }); - return { - octokit, - getToken: getAccessToken - }; - } - throw new Error('ConfigError: Invalid GitHub auth config. Please report this issue through https://aka.ms/azsdk/support/specreview-channel.'); };