@salesforce/afv-skills 1.34.0 → 1.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +239 -0
- package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +21 -0
- package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +90 -0
- package/skills/automation-sandbox-post-copy-config-generate/examples/sample_sop_excerpt.md +31 -0
- package/skills/automation-sandbox-post-copy-config-generate/examples/sample_sop_to_config.json +50 -0
- package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +76 -0
- package/skills/automation-sandbox-post-copy-config-generate/references/sop_parsing_patterns.md +157 -0
- package/skills/automation-sandbox-post-copy-config-generate/references/source_format_handling.md +230 -0
- package/skills/dx-apexguru-scan/SKILL.md +403 -0
- package/skills/dx-apexguru-scan/examples/README.md +54 -0
- package/skills/dx-apexguru-scan/examples/sample-decoded-summary.json +176 -0
- package/skills/dx-apexguru-scan/examples/sample-full-no-runtime-response.json +26 -0
- package/skills/dx-apexguru-scan/examples/sample-succeeded-response.json +15 -0
- package/skills/dx-apexguru-scan/references/api-reference.md +81 -0
- package/skills/dx-apexguru-scan/references/authentication.md +134 -0
- package/skills/dx-apexguru-scan/references/error-handling.md +56 -0
- package/skills/dx-apexguru-scan/references/violation-catalog.md +28 -0
- package/skills/dx-apexguru-scan/scripts/build-zip.sh +87 -0
- package/skills/dx-apexguru-scan/scripts/decode-report.js +389 -0
- package/skills/dx-apexguru-scan/scripts/resolve-token.sh +151 -0
- package/skills/dx-apexguru-scan/scripts/run-scan.sh +153 -0
- package/skills/dx-apexguru-scan/scripts/scan.sh +96 -0
- package/skills/dx-apexguru-scan/scripts/validate-token.js +121 -0
- package/skills/dx-devops-pipeline-manage/SKILL.md +263 -0
- package/skills/dx-devops-pipeline-manage/examples/common-workflows.md +177 -0
- package/skills/dx-devops-pipeline-manage/references/cli-commands.md +298 -0
- package/skills/dx-devops-pipeline-manage/references/parsing-patterns.md +134 -0
- package/skills/dx-devops-pipeline-manage/scripts/check-activation-ready.sh +34 -0
- package/skills/dx-devops-pipeline-manage/scripts/validate-org-type.sh +17 -0
- package/skills/dx-devops-pipeline-manage/scripts/verify-operation.sh +82 -0
- package/skills/dx-devops-promote/SKILL.md +214 -0
- package/skills/dx-devops-promote/examples/promotion-workflows.md +212 -0
- package/skills/dx-devops-promote/references/cli-commands.md +303 -0
- package/skills/experience-lwc-base-components-integrate/SKILL.md +176 -0
- package/skills/experience-lwc-base-components-integrate/references/lbc-expert-guidance.md +127 -0
- package/skills/experience-lwc-base-components-integrate/references/lightning-component-index.md +179 -0
- package/skills/experience-lwc-base-components-integrate/references/lightning-components.md +5429 -0
- package/skills/experience-lwc-base-components-integrate/scripts/extract-component-docs.sh +61 -0
- package/skills/experience-lwc-rtl-validate/SKILL.md +149 -0
- package/skills/experience-lwc-rtl-validate/references/rtl-expert.md +892 -0
- package/skills/experience-lwc-rtl-validate/scripts/scan-rtl-css.sh +206 -0
- package/skills/experience-lwc-typescript-migrate/SKILL.md +207 -0
- package/skills/experience-lwc-typescript-migrate/assets/dts-template.ts +15 -0
- package/skills/experience-lwc-typescript-migrate/assets/type-patterns.ts +44 -0
- package/skills/experience-lwc-typescript-migrate/scripts/find-consumers.sh +128 -0
- package/skills/experience-ui-bundle-localize/SKILL.md +323 -0
- package/skills/experience-ui-bundle-localize/references/gotchas.md +249 -0
- package/skills/experience-ui-bundle-localize/references/i18n-setup.md +169 -0
- package/skills/experience-ui-bundle-localize/references/interpolation.md +311 -0
- package/skills/experience-ui-bundle-localize/references/label-xml.md +282 -0
- package/skills/experience-ui-bundle-localize/references/verifying.md +219 -0
- package/skills/experience-ui-bundle-localize/scripts/check-i18n-wired.sh +195 -0
- package/skills/experience-ui-bundle-localize/scripts/check-manifest-registered.sh +100 -0
- package/skills/experience-ui-bundle-localize/scripts/check-org-api-version.sh +40 -0
- package/skills/experience-ui-bundle-localize/scripts/detect-bundle-type.sh +57 -0
- package/skills/platform-custom-lightning-type-generate/SKILL.md +3 -0
- package/skills/platform-custom-lightning-type-generate/assets/primitive-types-and-constraints.md +1 -1
- package/skills/platform-mcp-tool-widget-coordinate/SKILL.md +250 -0
- package/skills/platform-mcp-tool-widget-coordinate/examples/action-name-source-prompt.md +74 -0
- package/skills/platform-mcp-tool-widget-coordinate/examples/apex-invocable-source-prompt.md +90 -0
- package/skills/platform-mcp-tool-widget-coordinate/examples/nested-object-source-prompt.md +191 -0
- package/skills/platform-mcp-tool-widget-coordinate/examples/pasted-tool-output-prompt.md +85 -0
- package/skills/platform-mcp-tool-widget-coordinate/references/build-plan-format.md +74 -0
- package/skills/platform-mcp-tool-widget-coordinate/references/mcp-tool-output-discovery.md +184 -0
- package/skills/platform-mcp-tool-widget-coordinate/references/two-clt-modeling.md +128 -0
- package/skills/platform-mcp-tool-widget-coordinate/references/validation-gates.md +181 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"scanId": "255f5ecb-d967-47ad-9ca3-864d21bb425c",
|
|
3
|
+
"status": "SUCCEEDED",
|
|
4
|
+
"analysisMode": "full",
|
|
5
|
+
"report": "W3sicnVsZSI6ICJTY2hlbWFHZXRHbG9iYWxEZXNjcmliZU5vdEVmZmljaWVudCIsICJtZXNzYWdlIjogIkF2b2lkIHVzaW5nIFNjaGVtYS5nZXRHbG9iYWxEZXNjcmliZSgpIGluIEFwZXguIFRoaXMgbWV0aG9kIGNhdXNlcyB1bm5lY2Vzc2FyeSBvdmVyaGVhZCwgZGVjcmVhc2VzIHBlcmZvcm1hbmNlLCBhbmQgaW5jcmVhc2VzIHRoZSBsYXRlbmN5IG9mIHRoZSBhc3NvY2lhdGVkIGVudHJ5IHBvaW50LiIsICJsb2NhdGlvbnMiOiBbeyJzdGFydExpbmUiOiAyOCwgImNvbW1lbnQiOiAiU21hbGxBbnRpUGF0dGVybnMuYWNjb3VudFR5cGUiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn1dLCAicHJpbWFyeUxvY2F0aW9uSW5kZXgiOiAwLCAicmVzb3VyY2VzIjogWyJodHRwczovL2hlbHAuc2FsZXNmb3JjZS5jb20vcy9hcnRpY2xlVmlldz9pZD14Y2xvdWQuYXBleGd1cnVfYW50aXBhdHRlcm5fc2NoZW1hX2dldGdsb2JhbGRlc2NyaWJlX25vdF9lZmZpY2llbnQuaHRtJnR5cGU9NSJdLCAic2V2ZXJpdHkiOiA0LCAic3VnZ2VzdGlvbnMiOiBbeyJtZXNzYWdlIjogIiAgICAgICAgcmV0dXJuIEFjY291bnQuc09iamVjdFR5cGU7IiwgImxvY2F0aW9uIjogeyJzdGFydExpbmUiOiAyOCwgImNvbW1lbnQiOiAiU21hbGxBbnRpUGF0dGVybnMuYWNjb3VudFR5cGUiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn19XSwgIm1ldGFkYXRhIjogeyJvcmlnaW5hbF9jb2RlIjogIiAgICAgICAgcmV0dXJuIFNjaGVtYS5nZXRHbG9iYWxEZXNjcmliZSgpLmdldCgnQWNjb3VudCcpOyIsICJjbGFzc19uYW1lIjogIlNtYWxsQW50aVBhdHRlcm5zIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyJ9fSwgeyJydWxlIjogIlNvcWxJbkFMb29wIiwgIm1lc3NhZ2UiOiAiWW91J3JlIGNhbGxpbmcgYW4gZXhwZW5zaXZlIFNPUUwgaW4gYSBsb29wLCB3aGljaCBjYW4gY2F1c2UgcGVyZm9ybWFuY2UgaXNzdWVzLiBPcHRpbWl6ZSB5b3VyIFNPUUwgdG8gY2FsbCBvbmNlIGFuZCByZXVzZSB0aGUgcmV0dXJuIHZhbHVlLiIsICJsb2NhdGlvbnMiOiBbeyJzdGFydExpbmUiOiA0LCAiY29tbWVudCI6ICJTbWFsbEFudGlQYXR0ZXJucy5zb3FsSW5Mb29wIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyJ9XSwgInByaW1hcnlMb2NhdGlvbkluZGV4IjogMCwgInJlc291cmNlcyI6IFsiaHR0cHM6Ly9oZWxwLnNhbGVzZm9yY2UuY29tL3MvYXJ0aWNsZVZpZXc/aWQ9eGNsb3VkLmFwZXhndXJ1X2FudGlwYXR0ZXJuX3NvcWxfaW5fbG9vcC5odG0mdHlwZT01Il0sICJzZXZlcml0eSI6IDQsICJzdWdnZXN0aW9ucyI6IFt7Im1lc3NhZ2UiOiAicHVibGljIHN0YXRpYyB2b2lkIHNvcWxJbkxvb3AoTGlzdDxBY2NvdW50PiBhY2NvdW50cykge1xuICAgIFNldDxJZD4gYWNjb3VudElkcyA9IG5ldyBTZXQ8SWQ+KCk7XG4gICAgZm9yIChBY2NvdW50IGFjYyA6IGFjY291bnRzKSB7XG4gICAgICAgIGFjY291bnRJZHMuYWRkKGFjYy5JZCk7XG4gICAgfVxuICAgIE1hcDxJZCwgTGlzdDxDb250YWN0Pj4gY29udGFjdHNCeUFjY291bnRJZCA9IG5ldyBNYXA8SWQsIExpc3Q8Q29udGFjdD4+KCk7XG4gICAgZm9yIChDb250YWN0IGMgOiBbU0VMRUNUIElkLCBOYW1lIEZST00gQ29udGFjdCBXSEVSRSBBY2NvdW50SWQgSU4gOmFjY291bnRJZHNdKSB7XG4gICAgICAgIGlmICghY29udGFjdHNCeUFjY291bnRJZC5jb250YWluc0tleShjLkFjY291bnRJZCkpIHtcbiAgICAgICAgICAgIGNvbnRhY3RzQnlBY2NvdW50SWQucHV0KGMuQWNjb3VudElkLCBuZXcgTGlzdDxDb250YWN0PigpKTtcbiAgICAgICAgfVxuICAgICAgICBjb250YWN0c0J5QWNjb3VudElkLmdldChjLkFjY291bnRJZCkuYWRkKGMpO1xuICAgIH1cbiAgICBmb3IgKEFjY291bnQgYWNjIDogYWNjb3VudHMpIHtcbiAgICAgICAgTGlzdDxDb250YWN0PiBjb250YWN0cyA9IGNvbnRhY3RzQnlBY2NvdW50SWQuY29udGFpbnNLZXkoYWNjLklkKSA/IGNvbnRhY3RzQnlBY2NvdW50SWQuZ2V0KGFjYy5JZCkgOiBuZXcgTGlzdDxDb250YWN0PigpO1xuICAgICAgICBTeXN0ZW0uZGVidWcoY29udGFjdHMuc2l6ZSgpKTtcbiAgICB9XG59IiwgImxvY2F0aW9uIjogeyJzdGFydExpbmUiOiA0LCAiY29tbWVudCI6ICJTbWFsbEFudGlQYXR0ZXJucy5zb3FsSW5Mb29wIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyJ9fV0sICJtZXRhZGF0YSI6IHsib3JpZ2luYWxfY29kZSI6ICJMaW5lIDI6XG5MaW5lIDM6ICAgIHB1YmxpYyBzdGF0aWMgdm9pZCBzb3FsSW5Mb29wKExpc3Q8QWNjb3VudD4gYWNjb3VudHMpIHtcbkxpbmUgNDogICAgICAgIGZvciAoQWNjb3VudCBhY2MgOiBhY2NvdW50cykge1xuTGluZSA1OiAgICAgICAgICAgIExpc3Q8Q29udGFjdD4gY29udGFjdHMgPSBbU0VMRUNUIElkLCBOYW1lIEZST00gQ29udGFjdCBXSEVSRSBBY2NvdW50SWQgPSA6YWNjLklkXTtcbkxpbmUgNjogICAgICAgICAgICBTeXN0ZW0uZGVidWcoY29udGFjdHMuc2l6ZSgpKTtcbkxpbmUgNzogICAgICAgIH1cbiIsICJjbGFzc19uYW1lIjogIlNtYWxsQW50aVBhdHRlcm5zIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyIsICJjYXRlZ29yeSI6ICJzb3FsX2luX2xvb3AifX0sIHsicnVsZSI6ICJTb3FsSW5BTG9vcE9uZUhvcCIsICJtZXNzYWdlIjogIllvdSdyZSBjYWxsaW5nIGEgbWV0aG9kIHRoYXQgY29udGFpbnMgYW4gU09RTCBxdWVyeSB3aXRoaW4gYSBsb29wLiBUbyBhdm9pZCBib3R0bGVuZWNrcyBhbmQgc3RheSB3aXRoaW4gZ292ZXJub3IgbGltaXRzLCBtb3ZlIFNPUUwgcXVlcmllcyBvdXRzaWRlIG9mIGxvb3BzLiIsICJsb2NhdGlvbnMiOiBbeyJzdGFydExpbmUiOiA1MSwgImNvbW1lbnQiOiAiU21hbGxBbnRpUGF0dGVybnMubm90aWZ5T3duZXJzIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyJ9LCB7InN0YXJ0TGluZSI6IDU4LCAiY29tbWVudCI6ICJTT1FMIGNhbGwgaW46IFNtYWxsQW50aVBhdHRlcm5zLmdldENhc2VCeUlkIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyJ9XSwgInByaW1hcnlMb2NhdGlvbkluZGV4IjogMCwgInJlc291cmNlcyI6IFsiaHR0cHM6Ly9oZWxwLnNhbGVzZm9yY2UuY29tL3MvYXJ0aWNsZVZpZXc/aWQ9eGNsb3VkLmFwZXhndXJ1X2FudGlwYXR0ZXJuX3NvcWxfaW5fbG9vcF9vbmVfaG9wLmh0bSZ0eXBlPTUiXSwgInNldmVyaXR5IjogNCwgIm1ldGFkYXRhIjogeyJvcmlnaW5hbF9jb2RlIjogIkxpbmUgNDk6XG5MaW5lIDUwOiAgICBwdWJsaWMgc3RhdGljIHZvaWQgbm90aWZ5T3duZXJzKExpc3Q8SWQ+IGNhc2VJZHMpIHtcbkxpbmUgNTE6ICAgICAgICBmb3IgKElkIGNhc2VJZCA6IGNhc2VJZHMpIHtcbkxpbmUgNTI6ICAgICAgICAgICAgQ2FzZSBjID0gZ2V0Q2FzZUJ5SWQoY2FzZUlkKTtcbkxpbmUgNTM6ICAgICAgICAgICAgU3lzdGVtLmRlYnVnKCdPd25lcjogJyArIGMuT3duZXJJZCk7XG5MaW5lIDU0OiAgICAgICAgfVxuIiwgImNsYXNzX25hbWUiOiAiU21hbGxBbnRpUGF0dGVybnMiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIiwgImNhdGVnb3J5IjogInNvcWxfaW5fbG9vcF9ob3AifX0sIHsicnVsZSI6ICJEbWxJbkFMb29wIiwgIm1lc3NhZ2UiOiAiWW91J3JlIGNhbGxpbmcgYW4gZXhwZW5zaXZlIERNTCBpbiBhIGxvb3AsIHdoaWNoIGNhbiBjYXVzZSBwZXJmb3JtYW5jZSBpc3N1ZXMuIE9wdGltaXplIHlvdXIgRE1MIHN0YXRlbWVudCB0byBjYWxsIG9uY2UuIiwgImxvY2F0aW9ucyI6IFt7InN0YXJ0TGluZSI6IDEzLCAiY29tbWVudCI6ICJTbWFsbEFudGlQYXR0ZXJucy5kbWxJbkxvb3AiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn1dLCAicHJpbWFyeUxvY2F0aW9uSW5kZXgiOiAwLCAicmVzb3VyY2VzIjogWyJodHRwczovL2hlbHAuc2FsZXNmb3JjZS5jb20vcy9hcnRpY2xlVmlldz9pZD14Y2xvdWQuYXBleGd1cnVfYW50aXBhdHRlcm5fZG1sX2luX2xvb3AuaHRtJnR5cGU9NSJdLCAic2V2ZXJpdHkiOiA0LCAic3VnZ2VzdGlvbnMiOiBbeyJtZXNzYWdlIjogInB1YmxpYyBzdGF0aWMgdm9pZCBkbWxJbkxvb3AoTGlzdDxDb250YWN0PiBjb250YWN0cykge1xuICAgIGZvciAoQ29udGFjdCBjIDogY29udGFjdHMpIHtcbiAgICAgICAgYy5EZXNjcmlwdGlvbiA9ICd0b3VjaGVkJztcbiAgICB9XG4gICAgaWYgKCFjb250YWN0cy5pc0VtcHR5KCkpIHtcbiAgICAgICAgdXBkYXRlIGNvbnRhY3RzO1xuICAgIH1cbn0iLCAibG9jYXRpb24iOiB7InN0YXJ0TGluZSI6IDEzLCAiY29tbWVudCI6ICJTbWFsbEFudGlQYXR0ZXJucy5kbWxJbkxvb3AiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn19XSwgIm1ldGFkYXRhIjogeyJvcmlnaW5hbF9jb2RlIjogIkxpbmUgMTI6ICAgICAgICAgICAgYy5EZXNjcmlwdGlvbiA9ICd0b3VjaGVkJztcbkxpbmUgMTM6ICAgICAgICAgICAgdXBkYXRlIGM7XG5MaW5lIDE0OiAgICAgICAgfVxuIiwgImNsYXNzX25hbWUiOiAiU21hbGxBbnRpUGF0dGVybnMiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIiwgImNhdGVnb3J5IjogImRtbF9pbl9sb29wIn19LCB7InJ1bGUiOiAiU29xbFdpdGhOZWdhdGl2ZUV4cHJlc3Npb25zIiwgIm1lc3NhZ2UiOiAiQ2FsbGluZyBTT1FMIHdpdGggbmVnYXRpdmUgZXhwcmVzc2lvbnMgY2F1c2VzIHBlcmZvcm1hbmNlIGlzc3Vlcy4iLCAibG9jYXRpb25zIjogW3sic3RhcnRMaW5lIjogMzIsICJjb21tZW50IjogIlNtYWxsQW50aVBhdHRlcm5zLm5vblByb3NwZWN0aW5nT3BwcyIsICJmaWxlIjogImZvcmNlLWFwcC9tYWluL2RlZmF1bHQvY2xhc3Nlcy9TbWFsbEFudGlQYXR0ZXJucy5jbHMifV0sICJwcmltYXJ5TG9jYXRpb25JbmRleCI6IDAsICJyZXNvdXJjZXMiOiBbImh0dHBzOi8vaGVscC5zYWxlc2ZvcmNlLmNvbS9zL2FydGljbGVWaWV3P2lkPXhjbG91ZC5hcGV4Z3VydV9hbnRpcGF0dGVybl9zb3FsX3dpdGhfbmVnYXRpdmVfZXhwcmVzc2lvbnMuaHRtJnR5cGU9NSJdLCAic2V2ZXJpdHkiOiA0LCAibWV0YWRhdGEiOiB7Im9yaWdpbmFsX2NvZGUiOiAiW1NFTEVDVCBJZCwgTmFtZSwgU3RhZ2VOYW1lIEZST00gT3Bwb3J0dW5pdHkgV0hFUkUgU3RhZ2VOYW1lICE9ICdQcm9zcGVjdGluZyddIiwgImNsYXNzX25hbWUiOiAiU21hbGxBbnRpUGF0dGVybnMiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIiwgImNhdGVnb3J5IjogInNvcWxfd2l0aF9uZWdhdGl2ZV9leHByZXNzaW9ucyJ9fSwgeyJydWxlIjogIlNvcWxXaXRob3V0QVdoZXJlQ2xhdXNlT3JMaW1pdFN0YXRlbWVudCIsICJtZXNzYWdlIjogIkNhbGxpbmcgU09RTCB3aXRob3V0IGEgV0hFUkUgY2xhdXNlIG9yIExJTUlUIHN0YXRlbWVudCBjYXVzZXMgcGVyZm9ybWFuY2UgaXNzdWVzLiIsICJsb2NhdGlvbnMiOiBbeyJzdGFydExpbmUiOiAxOCwgImNvbW1lbnQiOiAiU21hbGxBbnRpUGF0dGVybnMuYWxsT3BwcyIsICJmaWxlIjogImZvcmNlLWFwcC9tYWluL2RlZmF1bHQvY2xhc3Nlcy9TbWFsbEFudGlQYXR0ZXJucy5jbHMifV0sICJwcmltYXJ5TG9jYXRpb25JbmRleCI6IDAsICJyZXNvdXJjZXMiOiBbImh0dHBzOi8vaGVscC5zYWxlc2ZvcmNlLmNvbS9zL2FydGljbGVWaWV3P2lkPXhjbG91ZC5hcGV4Z3VydV9hbnRpcGF0dGVybl9zb3FsX3dpdGhvdXRfd2hlcmVfY2xhdXNlX29yX2xpbWl0X3N0YXRlbWVudC5odG0mdHlwZT01Il0sICJzZXZlcml0eSI6IDQsICJtZXRhZGF0YSI6IHsib3JpZ2luYWxfY29kZSI6ICJbU0VMRUNUIElkLCBOYW1lLCBBbW91bnQsIFN0YWdlTmFtZSBGUk9NIE9wcG9ydHVuaXR5XSIsICJjbGFzc19uYW1lIjogIlNtYWxsQW50aVBhdHRlcm5zIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyIsICJjYXRlZ29yeSI6ICJzb3FsX3dpdGhvdXRfd2hlcmVfY2xhdXNlX29yX2xpbWl0X3N0YXRlbWVudCJ9fSwgeyJydWxlIjogIlNvcWxXaXRob3V0QVdoZXJlQ2xhdXNlT3JMaW1pdFN0YXRlbWVudCIsICJtZXNzYWdlIjogIkNhbGxpbmcgU09RTCB3aXRob3V0IGEgV0hFUkUgY2xhdXNlIG9yIExJTUlUIHN0YXRlbWVudCBjYXVzZXMgcGVyZm9ybWFuY2UgaXNzdWVzLiIsICJsb2NhdGlvbnMiOiBbeyJzdGFydExpbmUiOiA3NCwgImNvbW1lbnQiOiAiU21hbGxBbnRpUGF0dGVybnMuc29ydGVkQ29udGFjdHNCeU5hbWUiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn1dLCAicHJpbWFyeUxvY2F0aW9uSW5kZXgiOiAwLCAicmVzb3VyY2VzIjogWyJodHRwczovL2hlbHAuc2FsZXNmb3JjZS5jb20vcy9hcnRpY2xlVmlldz9pZD14Y2xvdWQuYXBleGd1cnVfYW50aXBhdHRlcm5fc29xbF93aXRob3V0X3doZXJlX2NsYXVzZV9vcl9saW1pdF9zdGF0ZW1lbnQuaHRtJnR5cGU9NSJdLCAic2V2ZXJpdHkiOiA0LCAibWV0YWRhdGEiOiB7Im9yaWdpbmFsX2NvZGUiOiAiW1NFTEVDVCBJZCwgTmFtZSBGUk9NIENvbnRhY3RdIiwgImNsYXNzX25hbWUiOiAiU21hbGxBbnRpUGF0dGVybnMiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIiwgImNhdGVnb3J5IjogInNvcWxfd2l0aG91dF93aGVyZV9jbGF1c2Vfb3JfbGltaXRfc3RhdGVtZW50In19LCB7InJ1bGUiOiAiU29xbFdpdGhvdXRBV2hlcmVDbGF1c2VPckxpbWl0U3RhdGVtZW50IiwgIm1lc3NhZ2UiOiAiQ2FsbGluZyBTT1FMIHdpdGhvdXQgYSBXSEVSRSBjbGF1c2Ugb3IgTElNSVQgc3RhdGVtZW50IGNhdXNlcyBwZXJmb3JtYW5jZSBpc3N1ZXMuIiwgImxvY2F0aW9ucyI6IFt7InN0YXJ0TGluZSI6IDgxLCAiY29tbWVudCI6ICJTbWFsbEFudGlQYXR0ZXJucy5hY2NvdW50c0J5SWQiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn1dLCAicHJpbWFyeUxvY2F0aW9uSW5kZXgiOiAwLCAicmVzb3VyY2VzIjogWyJodHRwczovL2hlbHAuc2FsZXNmb3JjZS5jb20vcy9hcnRpY2xlVmlldz9pZD14Y2xvdWQuYXBleGd1cnVfYW50aXBhdHRlcm5fc29xbF93aXRob3V0X3doZXJlX2NsYXVzZV9vcl9saW1pdF9zdGF0ZW1lbnQuaHRtJnR5cGU9NSJdLCAic2V2ZXJpdHkiOiA0LCAibWV0YWRhdGEiOiB7Im9yaWdpbmFsX2NvZGUiOiAiW1NFTEVDVCBJZCwgTmFtZSBGUk9NIEFjY291bnRdIiwgImNsYXNzX25hbWUiOiAiU21hbGxBbnRpUGF0dGVybnMiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIiwgImNhdGVnb3J5IjogInNvcWxfd2l0aG91dF93aGVyZV9jbGF1c2Vfb3JfbGltaXRfc3RhdGVtZW50In19LCB7InJ1bGUiOiAiU29xbCBBZ2dyZWdhdGlvbiIsICJtZXNzYWdlIjogIllvdSdyZSB1c2luZyBsb29wcyB0byBtYW51YWxseSBjb21wdXRlIHZhbHVlcywgd2hpY2ggY2F1c2VzIHBlcmZvcm1hbmNlIGlzc3VlcywgYW5kIGNhbiBleGNlZWQgZ292ZXJub3IgbGltaXRzLiBJbnN0ZWFkLCB1c2UgQXBleCBhZ2dyZWdhdGUgZnVuY3Rpb25zIGxpa2UgU1VNIGFuZCBDT1VOVC4iLCAibG9jYXRpb25zIjogW3sic3RhcnRMaW5lIjogMzYsICJjb21tZW50IjogIlNtYWxsQW50aVBhdHRlcm5zLnRvdGFsT3BlbkFtb3VudCIsICJmaWxlIjogImZvcmNlLWFwcC9tYWluL2RlZmF1bHQvY2xhc3Nlcy9TbWFsbEFudGlQYXR0ZXJucy5jbHMifV0sICJwcmltYXJ5TG9jYXRpb25JbmRleCI6IDAsICJyZXNvdXJjZXMiOiBbImh0dHBzOi8vaGVscC5zYWxlc2ZvcmNlLmNvbS9zL2FydGljbGVWaWV3P2lkPXhjbG91ZC5hcGV4Z3VydV9hbnRpcGF0dGVybl9hZ2dyZWdhdGluZ19pbl9hcGV4Lmh0bSZ0eXBlPTUiXSwgInNldmVyaXR5IjogNCwgInN1Z2dlc3Rpb25zIjogW3sibWVzc2FnZSI6ICJBZ2dyZWdhdGVSZXN1bHQgYXIgPSBbU0VMRUNUIFNVTShBbW91bnQpIHRvdGFsIEZST00gT3Bwb3J0dW5pdHkgV0hFUkUgQWNjb3VudElkID0gOmFjY291bnRJZCBBTkQgSXNDbG9zZWQgPSBmYWxzZV07XG5EZWNpbWFsIHRvdGFsID0gKERlY2ltYWwpYXIuZ2V0KCd0b3RhbCcpOyIsICJsb2NhdGlvbiI6IHsic3RhcnRMaW5lIjogMzYsICJjb21tZW50IjogIlNtYWxsQW50aVBhdHRlcm5zLnRvdGFsT3BlbkFtb3VudCIsICJmaWxlIjogImZvcmNlLWFwcC9tYWluL2RlZmF1bHQvY2xhc3Nlcy9TbWFsbEFudGlQYXR0ZXJucy5jbHMifX1dLCAibWV0YWRhdGEiOiB7Im9yaWdpbmFsX2NvZGUiOiAiICAgICAgICAgICAgIFNFTEVDVCBBbW91bnQgRlJPTSBPcHBvcnR1bml0eSBXSEVSRSBBY2NvdW50SWQgPSA6YWNjb3VudElkIEFORCBJc0Nsb3NlZCA9IGZhbHNlICAgICAgICAgIiwgImNsYXNzX25hbWUiOiAiU21hbGxBbnRpUGF0dGVybnMiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIiwgImNhdGVnb3J5IjogInJlY29yZF9hZ2dyZWdhdGlvbl9pbl9hcGV4In19LCB7InJ1bGUiOiAiU29xbCBBZ2dyZWdhdGlvbiIsICJtZXNzYWdlIjogIllvdSdyZSB1c2luZyBsb29wcyB0byBtYW51YWxseSBjb21wdXRlIHZhbHVlcywgd2hpY2ggY2F1c2VzIHBlcmZvcm1hbmNlIGlzc3VlcywgYW5kIGNhbiBleGNlZWQgZ292ZXJub3IgbGltaXRzLiBJbnN0ZWFkLCB1c2UgQXBleCBhZ2dyZWdhdGUgZnVuY3Rpb25zIGxpa2UgU1VNIGFuZCBDT1VOVC4iLCAibG9jYXRpb25zIjogW3sic3RhcnRMaW5lIjogOTcsICJjb21tZW50IjogIlNtYWxsQW50aVBhdHRlcm5zLnN1bU9wZW5PcHBBbW91bnRzIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyJ9XSwgInByaW1hcnlMb2NhdGlvbkluZGV4IjogMCwgInJlc291cmNlcyI6IFsiaHR0cHM6Ly9oZWxwLnNhbGVzZm9yY2UuY29tL3MvYXJ0aWNsZVZpZXc/aWQ9eGNsb3VkLmFwZXhndXJ1X2FudGlwYXR0ZXJuX2FnZ3JlZ2F0aW5nX2luX2FwZXguaHRtJnR5cGU9NSJdLCAic2V2ZXJpdHkiOiA0LCAic3VnZ2VzdGlvbnMiOiBbeyJtZXNzYWdlIjogIkFnZ3JlZ2F0ZVJlc3VsdCBhciA9IFtTRUxFQ1QgU1VNKEFtb3VudCkgdG90YWwgRlJPTSBPcHBvcnR1bml0eSBXSEVSRSBBY2NvdW50SWQgPSA6YWNjb3VudElkIEFORCBJc0Nsb3NlZCA9IGZhbHNlXTtcbkRlY2ltYWwgdG90YWwgPSAoRGVjaW1hbClhci5nZXQoJ3RvdGFsJyk7IiwgImxvY2F0aW9uIjogeyJzdGFydExpbmUiOiA5NywgImNvbW1lbnQiOiAiU21hbGxBbnRpUGF0dGVybnMuc3VtT3Blbk9wcEFtb3VudHMiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn19XSwgIm1ldGFkYXRhIjogeyJvcmlnaW5hbF9jb2RlIjogIlNFTEVDVCBBbW91bnQgRlJPTSBPcHBvcnR1bml0eSBXSEVSRSBBY2NvdW50SWQgPSA6YWNjb3VudElkIEFORCBJc0Nsb3NlZCA9IGZhbHNlIiwgImNsYXNzX25hbWUiOiAiU21hbGxBbnRpUGF0dGVybnMiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIiwgImNhdGVnb3J5IjogInJlY29yZF9hZ2dyZWdhdGlvbl9pbl9hcGV4In19LCB7InJ1bGUiOiAiU29xbFdpdGhBcGV4RmlsdGVyIiwgIm1lc3NhZ2UiOiAiQ2FsbGluZyBTT1FMIHdpdGhvdXQgYSBXSEVSRSBjbGF1c2Ugb3IgTElNSVQgc3RhdGVtZW50IHJldHJpZXZlcyBhbGwgcmVjb3JkcywgY2F1c2VzIHBlcmZvcm1hbmNlIGlzc3VlcywgYW5kIGNhbiBleGNlZWQgZ292ZXJub3IgbGltaXRzLiIsICJsb2NhdGlvbnMiOiBbeyJzdGFydExpbmUiOiA2NiwgImNvbW1lbnQiOiAiU21hbGxBbnRpUGF0dGVybnMucHJvY2Vzc0hpZ2hWYWx1ZU9wcHMiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn1dLCAicHJpbWFyeUxvY2F0aW9uSW5kZXgiOiAwLCAicmVzb3VyY2VzIjogWyJodHRwczovL2hlbHAuc2FsZXNmb3JjZS5jb20vcy9hcnRpY2xlVmlldz9pZD14Y2xvdWQuYXBleGd1cnVfYW50aXBhdHRlcm5fc29xbF93aXRoX2FwZXhfZmlsdGVyLmh0bSZ0eXBlPTUiXSwgInNldmVyaXR5IjogNCwgInN1Z2dlc3Rpb25zIjogW3sibWVzc2FnZSI6ICJTRUxFQ1QgSWQsIE5hbWUsIEFtb3VudCwgU3RhZ2VOYW1lIEZST00gT3Bwb3J0dW5pdHkgV0hFUkUgQW1vdW50ICE9IE5VTEwgQU5EIEFtb3VudCA+PSAxMDAwMCIsICJsb2NhdGlvbiI6IHsic3RhcnRMaW5lIjogNjYsICJjb21tZW50IjogIlNtYWxsQW50aVBhdHRlcm5zLnByb2Nlc3NIaWdoVmFsdWVPcHBzIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyJ9fV0sICJtZXRhZGF0YSI6IHsib3JpZ2luYWxfY29kZSI6ICJTRUxFQ1QgSWQsIE5hbWUsIEFtb3VudCwgU3RhZ2VOYW1lIEZST00gT3Bwb3J0dW5pdHkiLCAiY2xhc3NfbmFtZSI6ICJTbWFsbEFudGlQYXR0ZXJucyIsICJmaWxlIjogImZvcmNlLWFwcC9tYWluL2RlZmF1bHQvY2xhc3Nlcy9TbWFsbEFudGlQYXR0ZXJucy5jbHMiLCAiY2F0ZWdvcnkiOiAicmVjb3JkX2ZpbHRlcmluZ19pbl9hcGV4In19LCB7InJ1bGUiOiAiU29ydGluZ0luQXBleCIsICJtZXNzYWdlIjogIllvdXIgU09RTCBxdWVyeSByZXRyaWV2ZXMgcmVjb3JkcyBpbiBhbiB1bnNvcnRlZCBvcmRlci4gVGhpcyB3YXN0ZXMgQ1BVIHRpbWUgYW5kIGNhbiBleGNlZWQgZ292ZXJub3IgbGltaXRzLiBVc2UgdGhlIE9SREVSIEJZIGNsYXVzZSB0byBlZmZpY2llbnRseSBzb3J0IGFuZCByZXRyaWV2ZSByZWNvcmRzLiIsICJsb2NhdGlvbnMiOiBbeyJzdGFydExpbmUiOiA3NCwgImNvbW1lbnQiOiAiU21hbGxBbnRpUGF0dGVybnMuc29ydGVkQ29udGFjdHNCeU5hbWUiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn1dLCAicHJpbWFyeUxvY2F0aW9uSW5kZXgiOiAwLCAicmVzb3VyY2VzIjogWyJodHRwczovL2hlbHAuc2FsZXNmb3JjZS5jb20vcy9hcnRpY2xlVmlldz9pZD14Y2xvdWQuYXBleGd1cnVfYW50aXBhdHRlcm5fc29ydGluZ19pbl9hcGV4Lmh0bSZ0eXBlPTUiXSwgInNldmVyaXR5IjogNCwgInN1Z2dlc3Rpb25zIjogW3sibWVzc2FnZSI6ICIgICAgICAgIExpc3Q8Q29udGFjdD4gY29udGFjdExpc3QgPSBbU0VMRUNUIElkLCBOYW1lIEZST00gQ29udGFjdCBPUkRFUiBCWSBOYW1lXTtcbi8vICAgICAgICAgY29udGFjdExpc3Quc29ydCgpOyIsICJsb2NhdGlvbiI6IHsic3RhcnRMaW5lIjogNzQsICJjb21tZW50IjogIlNtYWxsQW50aVBhdHRlcm5zLnNvcnRlZENvbnRhY3RzQnlOYW1lIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyJ9fV0sICJtZXRhZGF0YSI6IHsib3JpZ2luYWxfY29kZSI6ICIgICAgICAgIExpc3Q8Q29udGFjdD4gY29udGFjdExpc3QgPSBbU0VMRUNUIElkLCBOYW1lIEZST00gQ29udGFjdF07XG4gICAgICAgIGNvbnRhY3RMaXN0LnNvcnQoKTtcbiIsICJjbGFzc19uYW1lIjogIlNtYWxsQW50aVBhdHRlcm5zIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyIsICJjYXRlZ29yeSI6ICJyZWNvcmRfc29ydGluZ19pbl9hcGV4In19LCB7InJ1bGUiOiAiU29xbFdpdGhVbnVzZWRGaWVsZHMiLCAibWVzc2FnZSI6ICJZb3UncmUgcmV0cmlldmluZyB0aGUgdW51c2VkIGZpZWxkIE5hbWUgaW4gYW4gU09RTCBxdWVyeS4gVG8gYXZvaWQgaW5jcmVhc2luZyByZXNvdXJjZSBjb25zdW1wdGlvbiBhbmQgcXVlcnkgdGltZSwgc2VsZWN0IG9ubHkgdGhlIG5lY2Vzc2FyeSBmaWVsZHMuIiwgImxvY2F0aW9ucyI6IFt7InN0YXJ0TGluZSI6IDUsICJjb21tZW50IjogIlNtYWxsQW50aVBhdHRlcm5zLnNvcWxJbkxvb3AiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn1dLCAicHJpbWFyeUxvY2F0aW9uSW5kZXgiOiAwLCAicmVzb3VyY2VzIjogWyJodHRwczovL2hlbHAuc2FsZXNmb3JjZS5jb20vcy9hcnRpY2xlVmlldz9pZD14Y2xvdWQuYXBleGd1cnVfYW50aXBhdHRlcm5fc29xbF93aXRoX3VudXNlZF9maWVsZHMuaHRtJnR5cGU9NSJdLCAic2V2ZXJpdHkiOiA0LCAic3VnZ2VzdGlvbnMiOiBbeyJtZXNzYWdlIjogIlNFTEVDVCBJZCBGUk9NIENvbnRhY3QgV0hFUkUgQWNjb3VudElkID0gOmFjYy5JZCIsICJsb2NhdGlvbiI6IHsic3RhcnRMaW5lIjogNSwgImNvbW1lbnQiOiAiU21hbGxBbnRpUGF0dGVybnMuc29xbEluTG9vcCIsICJmaWxlIjogImZvcmNlLWFwcC9tYWluL2RlZmF1bHQvY2xhc3Nlcy9TbWFsbEFudGlQYXR0ZXJucy5jbHMifX1dLCAibWV0YWRhdGEiOiB7Im9yaWdpbmFsX2NvZGUiOiAiU0VMRUNUIElkLCBOYW1lIEZST00gQ29udGFjdCBXSEVSRSBBY2NvdW50SWQgPSA6YWNjLklkIiwgImNsYXNzX25hbWUiOiAiU21hbGxBbnRpUGF0dGVybnMiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIiwgImNhdGVnb3J5IjogInNvcWxfdW51c2VkX2ZpZWxkcyJ9fSwgeyJydWxlIjogIlNvcWxXaXRoVW51c2VkRmllbGRzIiwgIm1lc3NhZ2UiOiAiWW91J3JlIHJldHJpZXZpbmcgdGhlIHVudXNlZCBmaWVsZHMgVHlwZSwgSW5kdXN0cnksIFJhdGluZywgUGhvbmUsIFdlYnNpdGUgaW4gYW4gU09RTCBxdWVyeS4gVG8gYXZvaWQgaW5jcmVhc2luZyByZXNvdXJjZSBjb25zdW1wdGlvbiBhbmQgcXVlcnkgdGltZSwgc2VsZWN0IG9ubHkgdGhlIG5lY2Vzc2FyeSBmaWVsZHMuIiwgImxvY2F0aW9ucyI6IFt7InN0YXJ0TGluZSI6IDIyLCAiY29tbWVudCI6ICJTbWFsbEFudGlQYXR0ZXJucy5nZXRBY2NvdW50TmFtZSIsICJmaWxlIjogImZvcmNlLWFwcC9tYWluL2RlZmF1bHQvY2xhc3Nlcy9TbWFsbEFudGlQYXR0ZXJucy5jbHMifV0sICJwcmltYXJ5TG9jYXRpb25JbmRleCI6IDAsICJyZXNvdXJjZXMiOiBbImh0dHBzOi8vaGVscC5zYWxlc2ZvcmNlLmNvbS9zL2FydGljbGVWaWV3P2lkPXhjbG91ZC5hcGV4Z3VydV9hbnRpcGF0dGVybl9zb3FsX3dpdGhfdW51c2VkX2ZpZWxkcy5odG0mdHlwZT01Il0sICJzZXZlcml0eSI6IDQsICJzdWdnZXN0aW9ucyI6IFt7Im1lc3NhZ2UiOiAiU0VMRUNUIElkLCBOYW1lIEZST00gQWNjb3VudCBXSEVSRSBJZCA9IDphY2NvdW50SWQgTElNSVQgMSIsICJsb2NhdGlvbiI6IHsic3RhcnRMaW5lIjogMjIsICJjb21tZW50IjogIlNtYWxsQW50aVBhdHRlcm5zLmdldEFjY291bnROYW1lIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyJ9fV0sICJtZXRhZGF0YSI6IHsib3JpZ2luYWxfY29kZSI6ICJTRUxFQ1QgSWQsIE5hbWUsIFR5cGUsIEluZHVzdHJ5LCBSYXRpbmcsIFBob25lLCBXZWJzaXRlICAgICAgICAgICAgICAgICAgICAgIEZST00gQWNjb3VudCBXSEVSRSBJZCA9IDphY2NvdW50SWQgTElNSVQgMSIsICJjbGFzc19uYW1lIjogIlNtYWxsQW50aVBhdHRlcm5zIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyIsICJjYXRlZ29yeSI6ICJzb3FsX3VudXNlZF9maWVsZHMifX0sIHsicnVsZSI6ICJTb3FsV2l0aFVudXNlZEZpZWxkcyIsICJtZXNzYWdlIjogIllvdSdyZSByZXRyaWV2aW5nIHRoZSB1bnVzZWQgZmllbGQgU3RhZ2VOYW1lIGluIGFuIFNPUUwgcXVlcnkuIFRvIGF2b2lkIGluY3JlYXNpbmcgcmVzb3VyY2UgY29uc3VtcHRpb24gYW5kIHF1ZXJ5IHRpbWUsIHNlbGVjdCBvbmx5IHRoZSBuZWNlc3NhcnkgZmllbGRzLiIsICJsb2NhdGlvbnMiOiBbeyJzdGFydExpbmUiOiA2NiwgImNvbW1lbnQiOiAiU21hbGxBbnRpUGF0dGVybnMucHJvY2Vzc0hpZ2hWYWx1ZU9wcHMiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn1dLCAicHJpbWFyeUxvY2F0aW9uSW5kZXgiOiAwLCAicmVzb3VyY2VzIjogWyJodHRwczovL2hlbHAuc2FsZXNmb3JjZS5jb20vcy9hcnRpY2xlVmlldz9pZD14Y2xvdWQuYXBleGd1cnVfYW50aXBhdHRlcm5fc29xbF93aXRoX3VudXNlZF9maWVsZHMuaHRtJnR5cGU9NSJdLCAic2V2ZXJpdHkiOiA0LCAic3VnZ2VzdGlvbnMiOiBbeyJtZXNzYWdlIjogIlNFTEVDVCBJZCwgTmFtZSwgQW1vdW50IEZST00gT3Bwb3J0dW5pdHkiLCAibG9jYXRpb24iOiB7InN0YXJ0TGluZSI6IDY2LCAiY29tbWVudCI6ICJTbWFsbEFudGlQYXR0ZXJucy5wcm9jZXNzSGlnaFZhbHVlT3BwcyIsICJmaWxlIjogImZvcmNlLWFwcC9tYWluL2RlZmF1bHQvY2xhc3Nlcy9TbWFsbEFudGlQYXR0ZXJucy5jbHMifX1dLCAibWV0YWRhdGEiOiB7Im9yaWdpbmFsX2NvZGUiOiAiU0VMRUNUIElkLCBOYW1lLCBBbW91bnQsIFN0YWdlTmFtZSBGUk9NIE9wcG9ydHVuaXR5IiwgImNsYXNzX25hbWUiOiAiU21hbGxBbnRpUGF0dGVybnMiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIiwgImNhdGVnb3J5IjogInNvcWxfdW51c2VkX2ZpZWxkcyJ9fSwgeyJydWxlIjogIlNPYmplY3RNYXBJbkFGb3JMb29wIiwgIm1lc3NhZ2UiOiAiVG8gY29uc3VtZSBsZXNzIHJlc291cmNlcywgYnVpbGQgdGhlIG1hcCBmcm9tIGEgbGlzdCBvZiBzT2JqZWN0cyB1c2luZyB0aGUgbWFwIGNvbnN0cnVjdG9yLiBEb24ndCBtYW51YWxseSBidWlsZCBhIG1hcCB1c2luZyBhIEZPUiBsb29wIGFuZCBwdXQoKS4iLCAibG9jYXRpb25zIjogW3sic3RhcnRMaW5lIjogODEsICJjb21tZW50IjogIlNtYWxsQW50aVBhdHRlcm5zLmFjY291bnRzQnlJZCIsICJmaWxlIjogImZvcmNlLWFwcC9tYWluL2RlZmF1bHQvY2xhc3Nlcy9TbWFsbEFudGlQYXR0ZXJucy5jbHMifV0sICJwcmltYXJ5TG9jYXRpb25JbmRleCI6IDAsICJyZXNvdXJjZXMiOiBbImh0dHBzOi8vaGVscC5zYWxlc2ZvcmNlLmNvbS9zL2FydGljbGVWaWV3P2lkPXhjbG91ZC5hcGV4Z3VydV9hbnRpcGF0dGVybl9zb2JqZWN0X21hcF9pbl9mb3JfbG9vcC5odG0mdHlwZT01Il0sICJzZXZlcml0eSI6IDQsICJzdWdnZXN0aW9ucyI6IFt7Im1lc3NhZ2UiOiAiTWFwPElkLEFjY291bnQ+IGFjY01hcCA9IG5ldyBNYXA8SWQsQWNjb3VudD4oW1NFTEVDVCBJZCwgTmFtZSBGUk9NIEFjY291bnRdKTtcblxuLy8gSWYgdGhlIG51bWJlciBvZiB0aGUgcmVjb3JkcyBmcm9tIHRoZSBxdWVyeSBpcyB0b28gbGFyZ2UsIHlvdSBjYW4gYXBwbHkgdGhlIFwicHV0QWxsXCIgbWV0aG9kOlxuXG5NYXA8SWQsQWNjb3VudD4gYWNjTWFwID0gbmV3IE1hcDxJZCxBY2NvdW50PigpO1xuZm9yIChMaXN0PEFjY291bnQ+IHJlY29yZHM6IFtTRUxFQ1QgSWQsIE5hbWUgRlJPTSBBY2NvdW50XSkge1xuICAgIGFjY01hcC5wdXRBbGwocmVjb3Jkcyk7XG59IiwgImxvY2F0aW9uIjogeyJzdGFydExpbmUiOiA4MSwgImNvbW1lbnQiOiAiU21hbGxBbnRpUGF0dGVybnMuYWNjb3VudHNCeUlkIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyJ9fV0sICJtZXRhZGF0YSI6IHsib3JpZ2luYWxfY29kZSI6ICJMaW5lIDgwOiAgICAgICAgTWFwPElkLCBBY2NvdW50PiBhY2NNYXAgPSBuZXcgTWFwPElkLCBBY2NvdW50PigpO1xuTGluZSA4MTogICAgICAgIGZvciAoQWNjb3VudCBhY2MgOiBbU0VMRUNUIElkLCBOYW1lIEZST00gQWNjb3VudF0pIHtcbkxpbmUgODI6ICAgICAgICAgICAgYWNjTWFwLnB1dChhY2MuSWQsIGFjYyk7XG5MaW5lIDgzOiAgICAgICAgfVxuIiwgImNsYXNzX25hbWUiOiAiU21hbGxBbnRpUGF0dGVybnMiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn19LCB7InJ1bGUiOiAiQ29weWluZ0xpc3RPclNldEVsZW1lbnRzVXNpbmdBRm9yTG9vcCIsICJtZXNzYWdlIjogIllvdSdyZSB1c2luZyBhIGxvb3AgdG8gaW5kaXZpZHVhbGx5IGNvcHkgZWxlbWVudHMgZnJvbSBhIGxpc3Qgb3Igc2V0LiBUaGlzIHdhc3RlcyBDUFUgdGltZSBhbmQgY2FuIGV4Y2VlZCBnb3Zlcm5vciBsaW1pdHMuIEFzc2lnbiBmdW5jdGlvbiBvciBxdWVyeSBvdXRwdXRzIGRpcmVjdGx5LCBvciB1c2UgdGhlIGFkZEFsbCgpIG1ldGhvZCB0byBjb3B5IGxpc3Qgb3Igc2V0IGVsZW1lbnRzLiIsICJsb2NhdGlvbnMiOiBbeyJzdGFydExpbmUiOiA4OSwgImNvbW1lbnQiOiAiU21hbGxBbnRpUGF0dGVybnMuY29weUFjY291bnROYW1lcyIsICJmaWxlIjogImZvcmNlLWFwcC9tYWluL2RlZmF1bHQvY2xhc3Nlcy9TbWFsbEFudGlQYXR0ZXJucy5jbHMifV0sICJwcmltYXJ5TG9jYXRpb25JbmRleCI6IDAsICJyZXNvdXJjZXMiOiBbImh0dHBzOi8vaGVscC5zYWxlc2ZvcmNlLmNvbS9zL2FydGljbGVWaWV3P2lkPXhjbG91ZC5hcGV4Z3VydV9hbnRpcGF0dGVybl9jb3B5aW5nX2VsZW1lbnRzX3dpdGhfZm9yX2xvb3AuaHRtJnR5cGU9NSJdLCAic2V2ZXJpdHkiOiA0LCAic3VnZ2VzdGlvbnMiOiBbeyJtZXNzYWdlIjogImNvcHkuYWRkQWxsKG5hbWVzKTtcbi8vICAgICAgICAgZm9yIChTdHJpbmcgbmFtZSA6IG5hbWVzKSB7XG4vLyAgICAgICAgICAgICBjb3B5LmFkZChuYW1lKTtcbi8vICAgICAgICAgfSIsICJsb2NhdGlvbiI6IHsic3RhcnRMaW5lIjogODksICJjb21tZW50IjogIlNtYWxsQW50aVBhdHRlcm5zLmNvcHlBY2NvdW50TmFtZXMiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn19XSwgIm1ldGFkYXRhIjogeyJvcmlnaW5hbF9jb2RlIjogIkxpbmUgODk6ICAgICAgICBmb3IgKFN0cmluZyBuYW1lIDogbmFtZXMpIHtcbkxpbmUgOTA6ICAgICAgICAgICAgY29weS5hZGQobmFtZSk7XG5MaW5lIDkxOiAgICAgICAgfVxuIiwgImNsYXNzX25hbWUiOiAiU21hbGxBbnRpUGF0dGVybnMiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn19LCB7InJ1bGUiOiAiQnVzeUxvb3BEZWxheSIsICJtZXNzYWdlIjogIllvdSdyZSB1c2luZyBhbiBlbXB0eSBsb29wIHRvIGRlbGF5IHByb2dyYW0gZXhlY3V0aW9uLiBUaGlzIHdhc3RlcyBDUFUgdGltZSBhbmQgdXNlcyB0aGUgQXBleCBDUFUgZ292ZXJub3IgbGltaXQuIElmIHRoZSBkZWxheSB3YXMgYWRkZWQgYmVjYXVzZSBvZiBhbiBhc3luYyBqb2IsIHVzZSBTeXN0ZW0uZW5xdWV1ZUpvYiB3aXRoIGEgZGVsYXkgcGFyYW1ldGVyLiIsICJsb2NhdGlvbnMiOiBbeyJzdGFydExpbmUiOiAxMDYsICJjb21tZW50IjogIlNtYWxsQW50aVBhdHRlcm5zLmJ1c3lXYWl0IiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyJ9XSwgInByaW1hcnlMb2NhdGlvbkluZGV4IjogMCwgInJlc291cmNlcyI6IFsiaHR0cHM6Ly9oZWxwLnNhbGVzZm9yY2UuY29tL3MvYXJ0aWNsZVZpZXc/aWQ9eGNsb3VkLmFwZXhndXJ1X2FudGlwYXR0ZXJuX2J1c3lfbG9vcF9kZWxheS5odG0mdHlwZT01Il0sICJzZXZlcml0eSI6IDQsICJtZXRhZGF0YSI6IHsib3JpZ2luYWxfY29kZSI6ICJMaW5lIDEwNTogICAgICAgIExvbmcgZW5kVGltZSA9IHN0YXJ0VGltZSArIDUwMDA7XG5MaW5lIDEwNjogICAgICAgIHdoaWxlIChEYXRlVGltZS5ub3coKS5nZXRUaW1lKCkgPCBlbmRUaW1lKSB7XG5MaW5lIDEwNzogICAgICAgICAgICAvLyBzcGlucyBkb2luZyBub3RoaW5nIHVudGlsIHRoZSBkZWxheSBlbGFwc2VzXG5MaW5lIDEwODogICAgICAgIH1cbkxpbmUgMTA5OiAgICAgICAgU3lzdGVtLmRlYnVnKCdCdXN5IHdhaXQgZmluaXNoZWQuJyk7XG4iLCAiY2xhc3NfbmFtZSI6ICJTbWFsbEFudGlQYXR0ZXJucyIsICJmaWxlIjogImZvcmNlLWFwcC9tYWluL2RlZmF1bHQvY2xhc3Nlcy9TbWFsbEFudGlQYXR0ZXJucy5jbHMifX0sIHsicnVsZSI6ICJVc2luZ1RoZVRlc3RNZXRob2RLZXl3b3JkIiwgIm1lc3NhZ2UiOiAiVGhlIHRlc3RNZXRob2Qga2V5d29yZCBpcyBkZXByZWNhdGVkLiBUbyBzdGF5IHdpdGhpbiBvcmcgbGltaXRzLCBtb3ZlIHRlc3QgbWV0aG9kcyB0byBhIHNlcGFyYXRlIEBpc1Rlc3QgY2xhc3MuIiwgImxvY2F0aW9ucyI6IFt7InN0YXJ0TGluZSI6IDEzNiwgImNvbW1lbnQiOiAiU21hbGxBbnRpUGF0dGVybnMubGVnYWN5VGVzdE1ldGhvZCIsICJmaWxlIjogImZvcmNlLWFwcC9tYWluL2RlZmF1bHQvY2xhc3Nlcy9TbWFsbEFudGlQYXR0ZXJucy5jbHMifV0sICJwcmltYXJ5TG9jYXRpb25JbmRleCI6IDAsICJyZXNvdXJjZXMiOiBbImh0dHBzOi8vaGVscC5zYWxlc2ZvcmNlLmNvbS9zL2FydGljbGVWaWV3P2lkPXhjbG91ZC5hcGV4Z3VydV90ZXN0X2Nhc2VfYW50aXBhdHRlcm5fdXNpbmdfdGVzdG1ldGhvZC5odG0mdHlwZT01Il0sICJzZXZlcml0eSI6IDMsICJtZXRhZGF0YSI6IHsib3JpZ2luYWxfY29kZSI6ICIgICAgcHVibGljIHN0YXRpYyB0ZXN0TWV0aG9kIHZvaWQgbGVnYWN5VGVzdE1ldGhvZCgpIHtcbiIsICJjbGFzc19uYW1lIjogIlNtYWxsQW50aVBhdHRlcm5zIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyJ9fSwgeyJydWxlIjogIlJlZHVuZGFudCBTb3FsIiwgIm1lc3NhZ2UiOiAiQ29uc2lkZXIgbWVyZ2luZyBtdWx0aXBsZSBTT1FMIHF1ZXJpZXMgb24gdGhlIHNhbWUgc09iamVjdCBpbnRvIG9uZSBxdWVyeSB0byByZWR1Y2UgZGF0YWJhc2Ugcm91bmQgdHJpcHMgYW5kIGltcHJvdmUgcGVyZm9ybWFuY2UuIiwgImxvY2F0aW9ucyI6IFt7InN0YXJ0TGluZSI6IDM2LCAiY29tbWVudCI6ICJTbWFsbEFudGlQYXR0ZXJucy50b3RhbE9wZW5BbW91bnQiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn0sIHsic3RhcnRMaW5lIjogMzksICJjb21tZW50IjogIlJlZHVuZGFudCBTT1FMIiwgImZpbGUiOiAiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1NtYWxsQW50aVBhdHRlcm5zLmNscyJ9XSwgInByaW1hcnlMb2NhdGlvbkluZGV4IjogMCwgInJlc291cmNlcyI6IFsiaHR0cHM6Ly9oZWxwLnNhbGVzZm9yY2UuY29tL3MvYXJ0aWNsZVZpZXc/aWQ9eGNsb3VkLmFwZXhndXJ1X2FudGlwYXR0ZXJuX3JlZHVuZGFudF9zb3FsLmh0bSZ0eXBlPTUiXSwgInNldmVyaXR5IjogNCwgInN1Z2dlc3Rpb25zIjogW3sibWVzc2FnZSI6ICJMaXN0PE9wcG9ydHVuaXR5PiBvcGVuT3BwcyA9IFtTRUxFQ1QgQW1vdW50IEZST00gT3Bwb3J0dW5pdHkgV0hFUkUgQWNjb3VudElkID0gOmFjY291bnRJZCBBTkQgSXNDbG9zZWQgPSBmYWxzZV07XG5EZWNpbWFsIHRvdGFsID0gMDtcbmZvciAoT3Bwb3J0dW5pdHkgbyA6IG9wZW5PcHBzKSB7XG4gICAgdG90YWwgKz0gby5BbW91bnQ7XG59XG5TeXN0ZW0uZGVidWcoJ09wZW4gb3BwIGNvdW50OiAnICsgb3Blbk9wcHMuc2l6ZSgpKTsiLCAibG9jYXRpb24iOiB7InN0YXJ0TGluZSI6IDM2LCAiY29tbWVudCI6ICJTbWFsbEFudGlQYXR0ZXJucy50b3RhbE9wZW5BbW91bnQiLCAiZmlsZSI6ICJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvU21hbGxBbnRpUGF0dGVybnMuY2xzIn19XSwgIm1ldGFkYXRhIjogeyJvcmlnaW5hbF9jb2RlIjogIkxpbmUgMzY6ICAgICAgICBMaXN0PE9wcG9ydHVuaXR5PiBmb3JUb3RhbCA9IFtcbkxpbmUgMzc6ICAgICAgICAgICAgU0VMRUNUIEFtb3VudCBGUk9NIE9wcG9ydHVuaXR5IFdIRVJFIEFjY291bnRJZCA9IDphY2NvdW50SWQgQU5EIElzQ2xvc2VkID0gZmFsc2VcbkxpbmUgMzg6ICAgICAgICBdO1xuTGluZSAzOTogICAgICAgIExpc3Q8T3Bwb3J0dW5pdHk+IGZvckNvdW50ID0gW1xuTGluZSA0MDogICAgICAgICAgICBTRUxFQ1QgQW1vdW50IEZST00gT3Bwb3J0dW5pdHkgV0hFUkUgQWNjb3VudElkID0gOmFjY291bnRJZCBBTkQgSXNDbG9zZWQgPSBmYWxzZVxuTGluZSA0MTogICAgICAgIF07XG4iLCAiY2xhc3NfbmFtZSI6ICJTbWFsbEFudGlQYXR0ZXJucyIsICJmaWxlIjogImZvcmNlLWFwcC9tYWluL2RlZmF1bHQvY2xhc3Nlcy9TbWFsbEFudGlQYXR0ZXJucy5jbHMiLCAiY2F0ZWdvcnkiOiAicmVkdW5kYW50X3NvcWxfcXVlcnkifX1d",
|
|
6
|
+
"message": null,
|
|
7
|
+
"violationCount": 20,
|
|
8
|
+
"filesScanned": 1,
|
|
9
|
+
"violationBreakdown": {
|
|
10
|
+
"SOQL_IN_LOOP": 1,
|
|
11
|
+
"SOQL_UNUSED_FIELDS": 3,
|
|
12
|
+
"GGD": 1,
|
|
13
|
+
"SOQL_NEGATIVE_EXPR": 1,
|
|
14
|
+
"SOQL_AGGREGATION": 2,
|
|
15
|
+
"REDUNDANT_SOQL": 1,
|
|
16
|
+
"SOQL_NO_WHERE_LIMIT": 3,
|
|
17
|
+
"SOQL_SORT_IN_APEX": 1,
|
|
18
|
+
"SOQL_FILTER_IN_APEX": 1,
|
|
19
|
+
"BUSY_DELAY_LOOP": 1,
|
|
20
|
+
"COPY_LIST_ELEMENTS_LOOP": 1,
|
|
21
|
+
"DML_IN_LOOP": 1,
|
|
22
|
+
"DEPRECATED_TEST_METHOD_KEYWORD": 1,
|
|
23
|
+
"SOQL_IN_LOOP_1HOP": 1,
|
|
24
|
+
"CREATE_MAP_WITH_LOOP": 1
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"scanId": "scan-7f3a91",
|
|
3
|
+
"status": "SUCCEEDED",
|
|
4
|
+
"analysisMode": "static",
|
|
5
|
+
"violationCount": 4,
|
|
6
|
+
"filesScanned": 3,
|
|
7
|
+
"violationBreakdown": {
|
|
8
|
+
"SOQL_IN_LOOP": 1,
|
|
9
|
+
"DML_IN_LOOP": 1,
|
|
10
|
+
"SOQL_NO_WHERE_OR_LIMIT": 1,
|
|
11
|
+
"GGD": 1
|
|
12
|
+
},
|
|
13
|
+
"report": "W3sicnVsZSI6IlNPUUxfSU5fTE9PUCIsIm1lc3NhZ2UiOiJTT1FMIHF1ZXJ5IGV4ZWN1dGVkIGluc2lkZSBhIGZvci1sb29wIG92ZXIgQWNjb3VudHMiLCJzZXZlcml0eSI6MiwibG9jYXRpb24iOnsiZmlsZSI6ImZvcmNlLWFwcC9tYWluL2RlZmF1bHQvY2xhc3Nlcy9BY2NvdW50U2VydmljZS5jbHMiLCJsaW5lIjo0Mn0sImZpeGVzIjpbIk1vdmUgdGhlIHF1ZXJ5IG91dHNpZGUgdGhlIGxvb3A7IGJ1aWxkIGEgTWFwPElkLENvbnRhY3Q+IGZvciBpbi1sb29wIGxvb2t1cHMiXSwicmVzb3VyY2VzIjpbImh0dHBzOi8vZGV2ZWxvcGVyLnNhbGVzZm9yY2UuY29tL2RvY3MvYXBleGd1cnUvc29xbC1pbi1sb29wIl19LHsicnVsZSI6IkRNTF9JTl9MT09QIiwibWVzc2FnZSI6InVwZGF0ZSBzdGF0ZW1lbnQgaW5zaWRlIGEgbG9vcCIsInNldmVyaXR5IjoxLCJsb2NhdGlvbiI6eyJmaWxlIjoiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL0FjY291bnRTZXJ2aWNlLmNscyIsImxpbmUiOjYwfSwiZml4ZXMiOlsiQ29sbGVjdCByZWNvcmRzIGludG8gYSBMaXN0IGFuZCB1cGRhdGUgb25jZSBhZnRlciB0aGUgbG9vcCJdLCJyZXNvdXJjZXMiOltdfSx7InJ1bGUiOiJTT1FMX05PX1dIRVJFX09SX0xJTUlUIiwibWVzc2FnZSI6IlNFTEVDVCB3aXRoIG5laXRoZXIgV0hFUkUgbm9yIExJTUlUIG9uIE9wcG9ydHVuaXR5Iiwic2V2ZXJpdHkiOjIsImxvY2F0aW9uIjp7ImZpbGUiOiJmb3JjZS1hcHAvbWFpbi9kZWZhdWx0L2NsYXNzZXMvUmVwb3J0QnVpbGRlci5jbHMiLCJsaW5lIjoxNX0sImZpeGVzIjpbIkFkZCBhIHNlbGVjdGl2ZSBXSEVSRSBvbiBhbiBpbmRleGVkIGZpZWxkIGFuZCBhIExJTUlUIl0sInJlc291cmNlcyI6W119LHsicnVsZSI6IkdHRCIsIm1lc3NhZ2UiOiJTY2hlbWEuZ2V0R2xvYmFsRGVzY3JpYmUoKSBjYWxsZWQgb24gZXZlcnkgcmVxdWVzdCIsInNldmVyaXR5IjozLCJsb2NhdGlvbiI6eyJmaWxlIjoiZm9yY2UtYXBwL21haW4vZGVmYXVsdC9jbGFzc2VzL1V0aWxzLmNscyIsImxpbmUiOjEyfSwiZml4ZXMiOlsiVXNlIFNPYmplY3RUeXBlLmdldERlc2NyaWJlKCkgZm9yIHRoZSBzcGVjaWZpYyBvYmplY3QiXSwicmVzb3VyY2VzIjpbXX1d",
|
|
14
|
+
"message": null
|
|
15
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# ApexGuru SFAP Scan API Reference
|
|
2
|
+
|
|
3
|
+
**Base URL (production):** `https://api.salesforce.com/platform/scale/v1-beta.1/apex-guru`
|
|
4
|
+
|
|
5
|
+
The host is chosen from the token's environment (prod/stage/dev); `stage.` and
|
|
6
|
+
`dev.api.salesforce.com` are internal-only. Customers on a prod org always hit
|
|
7
|
+
production. See `authentication.md` for the routing table.
|
|
8
|
+
|
|
9
|
+
**Auth:** `Authorization: Bearer <sfap_api JWT>`. Org comes from the token's
|
|
10
|
+
`tnk` claim (`core/<instance>/<orgId>`) — no org id in the request.
|
|
11
|
+
|
|
12
|
+
## Call 1 — Submit a scan
|
|
13
|
+
|
|
14
|
+
```http
|
|
15
|
+
POST /scan
|
|
16
|
+
-H "Authorization: Bearer $JWT"
|
|
17
|
+
-F "file=@project.zip;type=application/zip"
|
|
18
|
+
[ -F "fastMode=true" ] # optional: skip LLM-heavy fix generation → faster/cheaper
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- Zip may use **any** layout — the API walks the whole archive and extracts
|
|
22
|
+
every Apex file it finds. No `force-app/` tree at the root is required
|
|
23
|
+
(`build-zip.sh` collects every `.cls`/`.trigger` under the project root,
|
|
24
|
+
preserving relative paths).
|
|
25
|
+
- Limits: **200MB compressed**, **1GB decompressed**.
|
|
26
|
+
- Success → `202`:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{ "scanId": "…", "status": "QUEUED" }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Call 2 — Poll for result
|
|
33
|
+
|
|
34
|
+
```http
|
|
35
|
+
GET /scan/{scanId}
|
|
36
|
+
-H "Authorization: Bearer $JWT"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- Status progression: `QUEUED → RUNNING → SUCCEEDED` (or `FAILED`).
|
|
40
|
+
- Poll ~every **15s**.
|
|
41
|
+
- On `SUCCEEDED`:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"scanId": "…",
|
|
46
|
+
"status": "SUCCEEDED",
|
|
47
|
+
"analysisMode": "static", // "static" = source-only | "full" = org onboarded, runtime metrics
|
|
48
|
+
"violationCount": 39,
|
|
49
|
+
"filesScanned": 16,
|
|
50
|
+
"violationBreakdown": { "SOQL_IN_LOOP": 15, "DML_IN_LOOP": 8, "GGD": 2 },
|
|
51
|
+
"report": "<base64-encoded JSON array of violations>",
|
|
52
|
+
"message": null
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## The `report` field
|
|
57
|
+
|
|
58
|
+
Base64 → decode → **JSON array of violations**. Each violation carries:
|
|
59
|
+
|
|
60
|
+
- `rule` — the antipattern id (e.g. `SOQL_IN_LOOP`).
|
|
61
|
+
- `message` — human-readable description.
|
|
62
|
+
- `severity` — 1 (Critical) … 5 (Info).
|
|
63
|
+
- source location — `file` + `line`.
|
|
64
|
+
- suggested fix(es).
|
|
65
|
+
- help-doc `resources`.
|
|
66
|
+
|
|
67
|
+
`decode-report.js` tolerates field-name drift (`location`/`locations[0]`,
|
|
68
|
+
`fixes`/`suggestedFixes`, `severity`/`sev`) so minor API-shape changes don't break parsing.
|
|
69
|
+
|
|
70
|
+
## Status codes
|
|
71
|
+
|
|
72
|
+
| Code | Meaning |
|
|
73
|
+
|------|---------|
|
|
74
|
+
| 202 | Scan accepted (submit) |
|
|
75
|
+
| 200 | Poll OK |
|
|
76
|
+
| 400 | Malformed zip, no Apex inside, or over size limit |
|
|
77
|
+
| 401 | Bad / expired / wrong-env token |
|
|
78
|
+
| 403 | Scan owned by a different org (token `tnk` mismatch) |
|
|
79
|
+
| 404 | Unknown scanId, or archived (~30-day GC) |
|
|
80
|
+
|
|
81
|
+
See `error-handling.md` for resolutions.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Authentication — SFAP Access Token
|
|
2
|
+
|
|
3
|
+
The ApexGuru SFAP Scan API authenticates with an **SFAP access token (JWT)
|
|
4
|
+
carrying the `sfap_api` scope**. There is **no org id parameter** — the target
|
|
5
|
+
org, and the environment (prod/stage/dev) the request is routed to, are both
|
|
6
|
+
derived from the token's `tnk` claim, formatted `core/<instance>/<orgId>`.
|
|
7
|
+
Customers authenticate a production org (`tnk core/prod/<orgId>`), so their
|
|
8
|
+
requests always go to production.
|
|
9
|
+
|
|
10
|
+
## Where the JWT comes from (Tharun's interim guide)
|
|
11
|
+
|
|
12
|
+
The token is derived from an **authenticated `sf` CLI org**. `resolve-token.sh`
|
|
13
|
+
obtains the org's live session id and exchanges it for the SFAP JWT by calling
|
|
14
|
+
`<instanceUrl>/ide/auth`, which returns `{ "jwt": "...", "message": "..." }`.
|
|
15
|
+
|
|
16
|
+
This is Tharun's **interim** approach — the Code Analyzer team may provide a cleaner
|
|
17
|
+
path later. `resolve-token.sh` is the **single place** that changes when it does;
|
|
18
|
+
everything downstream consumes the token opaquely.
|
|
19
|
+
|
|
20
|
+
### How the session id is obtained (important)
|
|
21
|
+
|
|
22
|
+
`sf org display --json` **redacts** `accessToken` on recent CLI versions
|
|
23
|
+
(≈2.14x+) — it returns a `[REDACTED]…` placeholder, not a usable token. Sending
|
|
24
|
+
that to `/ide/auth` yields a bare `401 "No session ID sent"`. So the resolver does
|
|
25
|
+
**not** use `sf org display`'s access token. Instead it:
|
|
26
|
+
|
|
27
|
+
1. reads the (non-redacted) `instanceUrl` from `sf org display --json`,
|
|
28
|
+
2. gets a frontdoor URL from `sf org open --url-only --json`,
|
|
29
|
+
3. follows that URL with `curl` to mint the real `sid` session cookie, and
|
|
30
|
+
4. sends the `sid` as `Authorization: Bearer` to `<instanceUrl>/ide/auth`.
|
|
31
|
+
|
|
32
|
+
The `sid` is a secret: it lives only in a `0600` temp cookie jar that the script
|
|
33
|
+
deletes on exit, and is never echoed.
|
|
34
|
+
|
|
35
|
+
## How `resolve-token.sh` resolves the token
|
|
36
|
+
|
|
37
|
+
In priority order (first hit wins):
|
|
38
|
+
|
|
39
|
+
1. **`APEXGURU_SFAP_TOKEN`** environment variable — the raw JWT. Most portable;
|
|
40
|
+
works headless and in CI. Use this in environments where `sf org open` can't
|
|
41
|
+
run (no browser session), supplying a production `sfap_api` JWT directly.
|
|
42
|
+
2. **`APEXGURU_SFAP_TOKEN_FILE`** environment variable — a path to a file containing
|
|
43
|
+
the JWT (whitespace trimmed). Keeps the token out of shell history.
|
|
44
|
+
3. **`sf` CLI → frontdoor `sid` → `<instanceUrl>/ide/auth`** — derives the JWT from
|
|
45
|
+
an authenticated **production** org. Requires `sf`, `jq`, and `curl`. Org
|
|
46
|
+
selection: `--org <alias>` flag, else the `APEXGURU_SF_ORG` env var, else the
|
|
47
|
+
CLI's default/target org. The user just needs to be logged in
|
|
48
|
+
(`sf org login web`).
|
|
49
|
+
|
|
50
|
+
If none succeed, the script returns:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{"error":"no SFAP token found","hint":"Sign in to an authorized Salesforce org to run an ApexGuru scan and compare your code against production performance data."}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Setting up the `sf` CLI path
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Log in once to a PRODUCTION org (My Domain or internal login host):
|
|
60
|
+
sf org login web --instance-url "https://<your-prod-host>" --alias <your-alias>
|
|
61
|
+
|
|
62
|
+
# Then the skill derives the JWT automatically. To target a specific org:
|
|
63
|
+
bash run-scan.sh <zip> <raw-out.json> --org <your-alias>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The `/ide/auth` response also carries a `message` field; only `jwt` is consumed.
|
|
67
|
+
Treat the derived JWT as a secret — the scripts never echo it (see the mandatory
|
|
68
|
+
script rules in SKILL.md). `resolve-token.sh` never prints the token to stdout:
|
|
69
|
+
it writes the JWT to a `0600` temp file and returns only the path in a
|
|
70
|
+
`tokenFile` field; `run-scan.sh` reads that file into memory and deletes it
|
|
71
|
+
immediately.
|
|
72
|
+
|
|
73
|
+
> **Note:** some orgs gate `/ide/auth` behind MFA enrollment — the exchange then
|
|
74
|
+
> returns an HTML redirect to a two-factor setup page instead of a JWT. If that
|
|
75
|
+
> happens, either complete MFA enrollment for that org or supply a production
|
|
76
|
+
> `sfap_api` JWT directly via `APEXGURU_SFAP_TOKEN`.
|
|
77
|
+
|
|
78
|
+
## Local pre-flight token check (no network)
|
|
79
|
+
|
|
80
|
+
Once a token is found, `resolve-token.sh` pipes it to `validate-token.js`, which
|
|
81
|
+
decodes the JWT's freely-readable header/body claims (it does **not** verify the
|
|
82
|
+
signature — only the server can) to catch the two things behind almost every
|
|
83
|
+
`401` **before** we zip and upload, and to detect the token's environment:
|
|
84
|
+
|
|
85
|
+
1. **Missing scope** — the `scp`/`scope` claim doesn't include `sfap_api`.
|
|
86
|
+
2. **Expired** — `exp` is in the past.
|
|
87
|
+
|
|
88
|
+
It also reads the environment from the `tnk` instance segment (and `iss` host) —
|
|
89
|
+
`core/prod/...` → prod, `core/stagecomstg2/...` → stage, `core/falcondeva/...` →
|
|
90
|
+
dev — and reports it as `detectedEnv` so `resolve-token.sh` routes to the
|
|
91
|
+
matching host. This is **not** a block: a stage/dev token routes to the stage/dev
|
|
92
|
+
endpoint instead of being rejected.
|
|
93
|
+
|
|
94
|
+
Only **provable** failures block (exit 1 with a clear `error`/`hint`). Anything the
|
|
95
|
+
script can't read — an opaque non-JWT token, or a missing claim — is a non-fatal
|
|
96
|
+
warning, so this never blocks a valid token it simply can't introspect. The token
|
|
97
|
+
is passed on **stdin**, never as a command-line argument, so it stays out of the
|
|
98
|
+
process list.
|
|
99
|
+
|
|
100
|
+
Example fail-fast message (instead of a bare HTTP 401 deep in the poll loop):
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{"ok":false,"error":"token scope does not include sfap_api","hint":"Re-mint the token with the sfap_api scope. See references/authentication.md."}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Setup for the user
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Normal path: just log in to a production org with the sf CLI
|
|
110
|
+
# (the JWT is derived automatically).
|
|
111
|
+
sf org login web --instance-url "https://<your-prod-host>" --alias <your-alias>
|
|
112
|
+
|
|
113
|
+
# Override A: env var for the session (CI/headless, or to bypass sf CLI)
|
|
114
|
+
export APEXGURU_SFAP_TOKEN="eyJ...<production sfap_api JWT>..."
|
|
115
|
+
|
|
116
|
+
# Override B: token file (not echoed into history)
|
|
117
|
+
export APEXGURU_SFAP_TOKEN_FILE="$HOME/.apexguru/sfap-token"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Endpoint routing
|
|
121
|
+
|
|
122
|
+
The base URL is chosen from the token's detected environment, so the request
|
|
123
|
+
always lands where the token was issued:
|
|
124
|
+
|
|
125
|
+
| Env | Host | Base URL |
|
|
126
|
+
|-------|-----------------------------|-----------------------------------------------------------------|
|
|
127
|
+
| prod | `api.salesforce.com` | `https://api.salesforce.com/platform/scale/v1-beta.1/apex-guru` |
|
|
128
|
+
| stage | `stage.api.salesforce.com` | `https://stage.api.salesforce.com/platform/scale/v1-beta.1/apex-guru` |
|
|
129
|
+
| dev | `dev.api.salesforce.com` | `https://dev.api.salesforce.com/platform/scale/v1-beta.1/apex-guru` |
|
|
130
|
+
|
|
131
|
+
There is no `--env` flag: routing is automatic from the `tnk` claim, and an
|
|
132
|
+
undetectable environment defaults to prod. Customers authenticate a production
|
|
133
|
+
org, so they always hit `api.salesforce.com`; the stage/dev hosts exist for
|
|
134
|
+
internal testing against non-prod orgs.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Error Handling Guide
|
|
2
|
+
|
|
3
|
+
`run-scan.sh` emits `{error, httpStatus, status, hint}` on failure and maps each HTTP
|
|
4
|
+
status to an actionable hint. Relay the hint to the user.
|
|
5
|
+
|
|
6
|
+
## HTTP status codes
|
|
7
|
+
|
|
8
|
+
| Code | Likely cause | Resolution |
|
|
9
|
+
|------|--------------|------------|
|
|
10
|
+
| **400** | Malformed zip, no Apex inside, or over size limit | Re-package with `build-zip.sh` (collects every `.cls`/`.trigger` and checks the 200MB compressed limit). If genuinely large, scope to a subset of the project. Decompressed limit is 1GB. |
|
|
11
|
+
| **401** | Token bad or expired | Re-authenticate the Salesforce org (or supply a fresh `sfap_api` JWT via `APEXGURU_SFAP_TOKEN`). The endpoint is routed to the token's environment automatically. See `authentication.md`. |
|
|
12
|
+
| **403** | Scan owned by a different org | The token's `tnk`-claim org must match the scan owner. Use the token for the org that submitted the scan. |
|
|
13
|
+
| **404** | Unknown `scanId`, or scan archived | Scans are GC'd after ~30 days. Re-submit to get a fresh `scanId`. |
|
|
14
|
+
|
|
15
|
+
## Scan `status: FAILED`
|
|
16
|
+
|
|
17
|
+
The poll returns `FAILED` with a `message`. Surface the message verbatim, address the
|
|
18
|
+
stated cause, and re-submit. This is distinct from an HTTP error — the request
|
|
19
|
+
succeeded but the scan itself failed server-side.
|
|
20
|
+
|
|
21
|
+
## Timeout (scan didn't finish)
|
|
22
|
+
|
|
23
|
+
`run-scan.sh` polls up to `--max-polls` × `--interval` seconds (default 40 × 15s =
|
|
24
|
+
10 min). If it times out:
|
|
25
|
+
|
|
26
|
+
- Large projects legitimately take longer → re-run with `--max-polls 80`.
|
|
27
|
+
- Or add `--fast` to skip LLM-heavy fix generation.
|
|
28
|
+
|
|
29
|
+
## `analysisMode` and the three ApexGuru states
|
|
30
|
+
|
|
31
|
+
**Not an error.** The API's `analysisMode` plus the presence of runtime metrics
|
|
32
|
+
(`cpu_time_percentage` on any violation) distinguish three states. `decode-report.js`
|
|
33
|
+
handles all three automatically — this is background for interpreting the output:
|
|
34
|
+
|
|
35
|
+
| State | `analysisMode` | Runtime metrics | Header | What it means |
|
|
36
|
+
|-------|----------------|-----------------|--------|---------------|
|
|
37
|
+
| Not onboarded | `static` | none | **Static only** | The org is **not onboarded** to ApexGuru — only source-only analysis ran. |
|
|
38
|
+
| Onboarded, no runtime data yet | `full` | none | **Static only** | The org **is onboarded**, but there's no runtime data for this code yet. Result is static-equivalent. |
|
|
39
|
+
| Onboarded, enriched | `full` | present | **Production insights** | Onboarded **and** runtime metrics were applied (CPU hotspots, severity adjusted from production performance). |
|
|
40
|
+
|
|
41
|
+
Runtime data is populated by ApexGuru's weekend runs, or on demand from Scale
|
|
42
|
+
Center. So `full` + no runtime data is expected for a freshly-onboarded org or
|
|
43
|
+
newly-added code — it is **not** an error, and re-scanning won't change it until
|
|
44
|
+
runtime data exists. `decode-report.js --present` prints the correct guidance line
|
|
45
|
+
for each state ("generate a runtime report in Scale Center"); print it verbatim.
|
|
46
|
+
|
|
47
|
+
## Missing local prerequisites
|
|
48
|
+
|
|
49
|
+
| Symptom | Fix |
|
|
50
|
+
|---------|-----|
|
|
51
|
+
| `no SFAP token found` | No authenticated org and no token env var. Print the script's `hint` verbatim as the entire message — do not add framing or alternative instructions: "Sign in to an authorized Salesforce org to run an ApexGuru scan and compare your code against production performance data." |
|
|
52
|
+
| `token scope does not include sfap_api` | Re-mint the token with the `sfap_api` scope. Pre-empts a 401. |
|
|
53
|
+
| `token expired Ns ago` | Re-mint a fresh SFAP token and retry. Pre-empts a 401. |
|
|
54
|
+
| `no Apex (.cls/.trigger) files found` | Point `build-zip.sh` at a folder that contains Apex classes or triggers (any layout — an sfdx project, a `force-app/` subtree, or a loose folder of `.cls` files) |
|
|
55
|
+
| `zip: command not found` / `jq: command not found` | Install the missing tool (`brew install jq`, etc.) |
|
|
56
|
+
| `network error submitting scan` | Check connectivity/VPN to the API host |
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# ApexGuru Violation Catalog
|
|
2
|
+
|
|
3
|
+
Performance antipatterns ApexGuru detects, with what they mean and the typical fix.
|
|
4
|
+
Use this to explain a violation when the user asks "what does this mean / how do I
|
|
5
|
+
fix it?". The API's `report` carries per-violation `message`, `fixes`, and help-doc
|
|
6
|
+
`resources` — prefer those when present; this catalog is the fallback explanation.
|
|
7
|
+
|
|
8
|
+
| Rule (`rule`) | What it flags | Why it hurts | Typical fix |
|
|
9
|
+
|---------------|---------------|--------------|-------------|
|
|
10
|
+
| `SOQL_IN_LOOP` | A SOQL query inside a `for`/`while` loop | Each iteration issues a query → hits the 100-SOQL governor limit fast; slow | Move the query outside the loop; query once and build a `Map<Id, SObject>` keyed for lookup inside the loop |
|
|
11
|
+
| `DML_IN_LOOP` | `insert`/`update`/`delete`/`upsert` inside a loop | Each iteration issues DML → hits the 150-DML limit; slow | Collect records into a `List` and perform one DML statement after the loop |
|
|
12
|
+
| `GGD` | `Schema.getGlobalDescribe()` usage | Describes **every** object in the org — expensive, grows with org size | Use targeted describes (`SObjectType.getDescribe()`, `Schema.describeSObjects([...])`) for only the objects needed |
|
|
13
|
+
| `SOQL_NO_WHERE_OR_LIMIT` | SOQL with neither `WHERE` nor `LIMIT` | Full-table scan; can return huge result sets and blow the heap / row limits | Add a selective `WHERE` clause and/or a `LIMIT`; ensure filters are on indexed fields |
|
|
14
|
+
| `SOQL_UNUSED_FIELDS` | SELECTed fields never read | Wastes query CPU/heap fetching unused columns | Select only fields the code actually uses |
|
|
15
|
+
|
|
16
|
+
## Severity mapping
|
|
17
|
+
|
|
18
|
+
ApexGuru severities follow the standard 1–5 scale: **1 = Critical, 2 = High,
|
|
19
|
+
3 = Moderate, 4 = Low, 5 = Info.** `DML_IN_LOOP` and `SOQL_IN_LOOP` are usually the
|
|
20
|
+
highest-impact (governor-limit) findings.
|
|
21
|
+
|
|
22
|
+
## Static vs. full attribution
|
|
23
|
+
|
|
24
|
+
- **Static** findings come from source analysis alone — the antipattern exists in the
|
|
25
|
+
code regardless of runtime.
|
|
26
|
+
- **Full** findings are enriched with production metrics — e.g. a `SOQL_IN_LOOP`
|
|
27
|
+
that actually executed against large data volumes in production is prioritized
|
|
28
|
+
higher. Always label which mode produced the results.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Package a customer's Apex into a .zip for the ApexGuru SFAP Scan API.
|
|
3
|
+
#
|
|
4
|
+
# The API walks the ENTIRE uploaded archive and extracts every Apex file it
|
|
5
|
+
# finds — it does NOT require a force-app/ tree at the root (confirmed against
|
|
6
|
+
# production). So we scan whatever the customer points us at: collect every
|
|
7
|
+
# Apex source file (`.cls` / `.trigger`) under <project-root>, preserving its
|
|
8
|
+
# relative path, and zip that. A plain sfdx project, a loose folder of classes,
|
|
9
|
+
# or a deeply-nested monorepo subtree all work.
|
|
10
|
+
#
|
|
11
|
+
# Usage: bash build-zip.sh <project-root> <output-zip-path>
|
|
12
|
+
#
|
|
13
|
+
# <project-root> is any folder that contains Apex somewhere beneath it (an sfdx
|
|
14
|
+
# project root, a force-app/ subtree, or an ad-hoc folder of .cls files).
|
|
15
|
+
#
|
|
16
|
+
# Output (stdout, single line JSON):
|
|
17
|
+
# {"zip":"<abs path>","bytes":123,"humanSize":"1.2M","apexFileCount":N,"scanRoot":"<path>"}
|
|
18
|
+
# On failure: {"error":"...","hint":"..."} and exit 1.
|
|
19
|
+
#
|
|
20
|
+
# API limits enforced here (fail fast, before upload): 200MB compressed.
|
|
21
|
+
# (1GB decompressed is enforced server-side; we cannot cheaply predict it.)
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
|
|
24
|
+
MAX_COMPRESSED_BYTES=$((200 * 1024 * 1024))
|
|
25
|
+
|
|
26
|
+
PROJECT_ROOT="${1:-}"
|
|
27
|
+
OUT_ZIP="${2:-}"
|
|
28
|
+
|
|
29
|
+
if [ -z "$PROJECT_ROOT" ] || [ -z "$OUT_ZIP" ]; then
|
|
30
|
+
printf '{"error":"missing args","hint":"usage: build-zip.sh <project-root> <output-zip-path>"}\n'
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
if [ ! -d "$PROJECT_ROOT" ]; then
|
|
35
|
+
printf '{"error":"project root not found: %s","hint":"pass a folder that contains Apex (.cls/.trigger) files"}\n' "$PROJECT_ROOT"
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# Resolve absolute paths.
|
|
40
|
+
ROOT_ABS="$(cd "$PROJECT_ROOT" && pwd)"
|
|
41
|
+
case "$OUT_ZIP" in
|
|
42
|
+
/*) OUT_ABS="$OUT_ZIP" ;;
|
|
43
|
+
*) OUT_ABS="$(pwd)/$OUT_ZIP" ;;
|
|
44
|
+
esac
|
|
45
|
+
|
|
46
|
+
rm -f "$OUT_ABS"
|
|
47
|
+
|
|
48
|
+
# The set of Apex source files under the root (relative paths, noise excluded).
|
|
49
|
+
# Defined once as a function so the count pass and the zip pass use the exact
|
|
50
|
+
# same list. `zip -@` reads a newline-delimited list, so we emit newline-
|
|
51
|
+
# terminated relative paths with the leading "./" stripped.
|
|
52
|
+
apex_files() {
|
|
53
|
+
find . -type f \( -name '*.cls' -o -name '*.trigger' \) \
|
|
54
|
+
-not -path '*/node_modules/*' -not -path '*/.git/*' -print \
|
|
55
|
+
| sed 's#^\./##'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
APEX_COUNT=0
|
|
59
|
+
while IFS= read -r _; do
|
|
60
|
+
APEX_COUNT=$((APEX_COUNT + 1))
|
|
61
|
+
done < <(cd "$ROOT_ABS" && apex_files)
|
|
62
|
+
|
|
63
|
+
if [ "$APEX_COUNT" -eq 0 ]; then
|
|
64
|
+
printf '{"error":"no Apex (.cls/.trigger) files found under %s","hint":"point build-zip.sh at a folder that contains Apex classes or triggers"}\n' "$ROOT_ABS"
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Feed the file list to zip via stdin (-@) so we never hit ARG_MAX on large
|
|
69
|
+
# projects. Paths are relative to ROOT_ABS, so the archive mirrors the project
|
|
70
|
+
# layout (the API re-walks it regardless of structure).
|
|
71
|
+
( cd "$ROOT_ABS" && apex_files | zip -q -X "$OUT_ABS" -@ >/dev/null )
|
|
72
|
+
|
|
73
|
+
if [ ! -f "$OUT_ABS" ]; then
|
|
74
|
+
printf '{"error":"zip creation failed","hint":"check write permissions for %s"}\n' "$OUT_ABS"
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
BYTES=$(wc -c < "$OUT_ABS" | tr -d '[:space:]')
|
|
79
|
+
if [ "$BYTES" -gt "$MAX_COMPRESSED_BYTES" ]; then
|
|
80
|
+
printf '{"error":"zip is %s bytes, over the 200MB compressed limit","hint":"scope the scan to a subset of the project or split it"}\n' "$BYTES"
|
|
81
|
+
exit 1
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
HUMAN=$(echo "$BYTES" | awk '{ split("B K M G", u); s=$1; i=1; while (s>=1024 && i<4){s/=1024; i++} printf (i==1 ? "%d%s" : "%.1f%s"), s, u[i] }')
|
|
85
|
+
|
|
86
|
+
jq -cn --arg zip "$OUT_ABS" --argjson bytes "$BYTES" --arg human "$HUMAN" --argjson count "$APEX_COUNT" --arg root "$ROOT_ABS" \
|
|
87
|
+
'{zip:$zip, bytes:$bytes, humanSize:$human, apexFileCount:$count, scanRoot:$root}'
|