As of state for June 2022
There are two projects' packages structures in source tree right now:
io.snyk.plugin
- Old one (legacy). Grouping classes/components by it's IntelliJ framework role, i.e..events
,.services
, etc. mixed with our own components groups:.net
,.cli
, etc.snyk
- New one. Grouping by Snyk Product (.advisor
,.iac
, etc) or service (.common
,.net
, etc)
todo: Unify project packages structure to either io.snyk.plugin
or snyk
Ideally all communication between components/services should happen through events' mechanism (see SnykScanListener
and it's usages/implementations as example).
todo: But that's not always true presently. :(
All communication through network should go through HttpClient
/ApiClient
from .net
package to reuse Client whenever possible (mostly to reuse Interceptors, SSL Certificate handlers, timeouts, etc.)
todo: Unify .net
packages from io.snyk.plugin
and snyk
roots
Communication with CLI goes through CliAdapter
(with ConsoleCommandRunner
call under the hood): i.e. you need to inherit from it for every specific CLI usage/call (see OssService
as example)
None-CLI product (SnykCode) communicate directly to Code backend using java-code-sdk
library. Implementation of required classes are inside io.snyk.plugin.snykcode
package
todo: Unify .snykcode
network related classes with .net
package classes.
ProductType
holds textual representation for each supported product and should be the source of truth and the only place for such texts.
All cached scan results (except SnykCode?) should be holding in SnykCachedResults
service and requested/updated there when needed.
All project-files-on-disk update (changed, deleted, created, moved, copied) events happened through correspondent implementations of SnykBulkFileListener
class (see existing implementations for examples of using)
All (most) of the setup needed to be done on plugin start or new Project opening happened at SnykPostStartupActivity
All application-wide persisted settings should be held in SnykApplicationSettingsStateService
and Project-wide settings at correspondent SnykProjectSettingsStateService
Any new scan/re-scan should be executed through SnykTaskQueueService
Scan related options/settings are held inside SnykApplicationSettingsStateService
while for the representation of scan results in the Tree we have also filters (by Severity or Product) which should operate on top of the settings.
For UI (especially on top level) we're trying to use Idea built-in panels and factory classes/methods like SimpleToolWindowPanel
or JPanel
/JBPanel
with BorderLayout
as it seems to be more "native" for Idea. For more complicated custom panels we use JPanel
with GridConstraints
(see io.snyk.plugin.ui.UIUtils
for the clue)
Beware of needed wrapping of any UI related code inside of ApplicationManager.getApplication().invokeLater{}
if you call it from any background thread.
All our instances of Idea's @Service
classes should be called through correspondent wrapper get<xxx>Service()
in io.snyk.plugin.Utils
as some checks need to be done (especially for project's services). Also, Jetbrains changed already in the past recommended way to invoke such services, so would be nice to have such invocations centralised for possible future changes.
Before creating PR (if any user related changes been made) please don't forget to update CHANGELOG.md (follow existing structure) to be reflected in released plugin's Change Notes.
Build process happens through Gradle (as well as all dependency's connection). Managed in build.gradle.kts
root file, parametrised by gradle.properties
file.
Release happens automatically every Tuesday at 9am UTC. Also, could be run manually through GitHub's Actions/Release
workflow run. Specified in release.yml
file.
Should be mostly done trough IntelliJ Platform Testing Framework and placed into integTest
source root except simple independent Unit tests (test
source root).
Mocks are not recommended by Jetbrains, but we heavily (and successfully) use Mockk framework. Just be careful not to mock whole world and make sure you're testing real functionality and not mocked one.
- From the toolbar click
Run
->Run
- Click
Edit Configuration
->Add new configuration
- Select
Gradle
from the configuration list - Type
runIde
in theRun
textbox to select therunIde
run command - Click
Apply
andRun
to run the extension`
If you want to run the plugin in other IDE distribution (e.g. Rider), you should pass set IDE Contents
directory as a localIdeDirectory
property in gradle.properties
.
Here's an example for local Rider installation:
localIdeDirectory=/Users/michel/Library/Application Support/JetBrains/Toolbox/apps/Rider/ch-0/221.5787.36/Rider.app/Contents
You can copy the full path from IDE Settings in the JetBrains Toolbox.
- IntelliJ Platform Plugin SDK - that's your "Holy book" :)
- Gradle IntelliJ Plugin - needed for plugin development. See it's usage documentations
- IntelliJ Platform Explorer - here you can find examples of any(?) Extension Point usage. Imho better look into IntelliJ Idea sources for implementation.
- Forum/FAQ for IntelliJ IDEA Open API and Plugin Development
- Slack channel for Plugin development for IntelliJ Platform
- IntelliJ Platform UI Guidelines
- Icons search in IntelliJ platform built-in list