diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 100644 index 000000000..43c994c2d --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..1082a1c96 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,79 @@ +# AGENTS.md + +Instructions for AI coding agents (Claude Code, Cursor, Codex, etc.) working in this repository. + +## Project overview + +`gephi-plugins` is the scaffold and Maven build harness contributors fork to develop and submit +plugins for [Gephi](https://gephi.org), the graph visualization platform. A plugin is a NetBeans +module that implements one of Gephi's extension points (SPIs) — this repo does not contain Gephi +itself or, on `master`, any plugin source by default. See `README.md` for the day-to-day +getting-started flow (create/build/run/submit a plugin) and `ARCHITECTURE.md` for how the repository +(including its unusual three-branch model), the `gephi-maven-plugin` build lifecycle, and Gephi's +SPI/Lookup extension mechanism fit together. + +Read `ARCHITECTURE.md` before assuming something is broken: an empty `` list in `pom.xml`, +or the absence of `modules/pom.xml`, is expected on `master` — see its "Repository / branch model" +section. + +## Build and test + +Requires JDK 17 and Maven. + +- Scaffold a new plugin (interactive prompts): `mvn org.gephi:gephi-maven-plugin:generate` +- Build and validate every plugin currently listed in `pom.xml`: `mvn clean package` +- Build/test a single plugin module: `mvn -pl modules/ clean package` +- Run a single module's unit tests only: `mvn -pl modules/ test` +- Run Gephi with the module(s) installed (build first): `mvn org.gephi:gephi-maven-plugin:run` +- IDE run/debug configs are defined in `nbactions.xml`; the IntelliJ debug setup (remote debugger + + `-Drun.params.debug` VM option) is documented in `README.md`. + +`mvn package` always runs `gephi-maven-plugin:validate` at the `validate` phase — if a build fails +there, the error names the specific manifest/pom problem; fix that rather than working around it. + +## Adding or changing a plugin + +- One plugin (or one suite of related modules) per folder under `modules/`, added to the root + `pom.xml`'s `` list — `generate` does both steps for you; do it manually the same way if + extending an existing plugin's suite. +- A plugin's `pom.xml` inherits from `org.gephi:gephi-plugin-parent` (published from this repo's + `parent-pom` branch — not present in a normal `master` checkout). Add dependencies without a + ``; the parent's `dependencyManagement` supplies the version matching the target Gephi + release. See `ARCHITECTURE.md`. +- Register SPI implementations with `@ServiceProvider(service = ...)`; see `ARCHITECTURE.md`'s + "How Gephi can be extended" section for which SPI fits a given feature, and the + [core Gephi ARCHITECTURE.md](https://github.com/gephi/gephi/blob/master/ARCHITECTURE.md) for the + full API/SPI/Lookup design. +- Bump the plugin's own `` in its `pom.xml` on every update — the autoupdate site keys off + it, and reviewers check for it. +- Only list packages meant for other modules to use under `` in the plugin's + `pom.xml`. + +## Code style + +See `CONTRIBUTING.md`'s "Code quality" section rather than duplicating it here. + +## PR / commit guidelines + +- Plugin submissions (new plugin or update) target the `master-forge` branch, not `master` — see + README's "Submit a plugin" / "Update a plugin" sections. Changes to the scaffold itself (root + `pom.xml`, `.github/workflows`, this file, `ARCHITECTURE.md`) target `master`. +- Use `.github/issue_template.md`'s structure when filing or triaging bug reports. +- Keep commits scoped to one plugin or one logical change — a PR touching unrelated plugins in the + same suite stands out during review. + +### Reviewing a third-party plugin PR + +Most activity in this repo is reviewing plugin submissions rather than writing plugin code. When +asked to review one, work through `CONTRIBUTING.md`'s "Reviewing a plugin submission PR" checklist +(build, `pom.xml` config, manifest/branding, SPI registration, licensing, file hygiene, tests, PR +template completeness) and report findings against specific, named items from it rather than general +impressions — these PRs reliably have the same handful of holes, and the checklist exists to catch +them without re-deriving them each time. + +## Security + +- Never commit secrets, API keys, or tokens. `release-pom.yml` publishes to Maven Central using + repository secrets (GPG key, OSSRH credentials) — never hardcode credentials locally to bypass it. +- This is a public repository — don't add personal or employer-internal tooling references (private + registries, internal URLs, machine-specific paths) to any committed file. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 000000000..9ed93fac2 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,152 @@ +# gephi-plugins Architecture + +## What this repository is + +`gephi-plugins` is not a plugin, and (on the branch you're most likely looking at) it is not a +collection of plugins either. It is the scaffold and Maven build harness that contributors fork to +develop and submit plugins for [Gephi](https://gephi.org), the graph visualization platform built on +the Apache NetBeans Platform. + +```text +Your plugin module(s) (modules/, added by you or by `generate`) + │ implements a Gephi SPI, declares Maven deps on Gephi/NetBeans API modules + ▼ +Gephi (downloaded as a build dependency; see root pom.xml) + │ +NetBeans Platform (module system, Lookup, `.nbm` packaging) + │ + Java +``` + +A plugin is nothing more than another NetBeans module, built the same way Gephi's own modules are +built, that Gephi discovers at startup because it's on the classpath. This repository exists to make +that build set up correctly (manifest, packaging, dependency versions) without contributors needing +to hand-roll it. + +## Repository / branch model + +This is the part that isn't obvious from a single checkout: the same GitHub repository serves three +different purposes on three different branches. Confusion here (e.g. "why is `` empty?", +"where is `modules/pom.xml`?") almost always traces back to not knowing which branch does what. + +| Branch | Purpose | +|---|---| +| `master` | The template. `pom.xml`'s `` list starts empty. Contributors fork this branch and add their own plugin(s) to it locally — this is what `mvn org.gephi:gephi-maven-plugin:generate` does. Most day-to-day plugin development happens against a `master`-based fork. | +| `master-forge` | Where plugin submissions land. It accumulates every community plugin in one big multi-module build, and is what generates the plugin listing at gephi.org. PRs submitting or updating a plugin target this branch, not `master` (see README's "Submit a plugin"). | +| `parent-pom` | Hosts `modules/pom.xml`, the actual `gephi-plugin-parent` Maven artifact (see below). Pushing to this branch triggers `release-pom.yml`, which deploys `org.gephi:gephi-plugin-parent` to Maven Central. | + +`build.yml` (the main CI workflow) runs on every branch push *except* `master-forge`, `master`, +`parent-pom`, and `gh-pages` — i.e. on the feature/topic branches contributors actually push to. +`test-generation.yml` runs on `master` and is an integration test for the scaffold itself: it runs +`generate`, `package`, and the `release` profile's `build-metadata`/`create-autoupdate` goals against +a throwaway fixture plugin, so a `gephi-maven-plugin` version bump in `pom.xml` is caught before real +plugin repos pick it up. + +## How Gephi can be extended + +Gephi's extensibility model is what determines what a plugin *is*. This section summarizes the +mechanism; see the [core Gephi ARCHITECTURE.md](https://github.com/gephi/gephi/blob/master/ARCHITECTURE.md) +for the full treatment (API/SPI design philosophy, controllers/models, Lookup internals). + +Gephi separates **APIs** (functionality a module offers to others, e.g. `ProjectController`) from +**SPIs** (Service Provider Interfaces — extension points meant to be implemented by core modules +*and* plugins alike, e.g. `Importer`, `Layout`, `Statistics`). A plugin always extends an SPI; it +never needs to modify Gephi core to add functionality. + +| SPI | Extension point | +|---|---| +| Import SPI | File, database, and wizard importers | +| Layout SPI | Layout algorithms | +| Statistics SPI | Metrics and other graph algorithms | +| Tools SPI | Tools in the visualization toolbar | +| Export SPI | File exporters for graphs and graphics | +| Filters SPI | Filters | +| Preview SPI | Preview builders and renderers | +| Generator SPI | Graph generators | +| Data Laboratory SPI | Data Laboratory manipulators | +| Appearance SPI | Transformers for ranking and partitioning nodes/edges | +| Project SPI | Persistence providers for `.gephi` project files | +| Visualization SPI | Renderers for the newer `VisualizationEngine` module (work in progress) | + +Implementations are discovered at runtime through **Lookup**, NetBeans's service-registry mechanism, +not through any Gephi-specific plugin registry: + +```java +@ServiceProvider(service = Layout.class) +public class MyLayout implements Layout { +} +``` + +Annotating a class this way is what makes it show up in Gephi's layout list, exporter list, filter +list, etc. — implementing the interface alone is not enough; without `@ServiceProvider`, `Lookup` +will not find it. This is also the whole mechanism: there's no separate "plugin API" beyond the SPI +you're implementing and this annotation. + +## Anatomy of a plugin module + +`mvn org.gephi:gephi-maven-plugin:generate` produces this layout under `modules//`: + +```text +modules// +├── src/ +│ └── main/ +│ ├── java/ # SPI implementation(s), e.g. org.foo.myplugin.MyLayout +│ ├── resources/ # Bundle.properties, icons +│ └── nbm/ +│ └── manifest.mf # OpenIDE-Module-* branding/description/category entries +└── pom.xml # parent = org.gephi:gephi-plugin-parent, packaging = nbm +``` + +The generated `pom.xml` sets `` to `org.gephi:gephi-plugin-parent` (see below) and +configures `nbm-maven-plugin` with the plugin's author/license and a `` list — +only packages listed there are visible to other modules/plugins, mirroring the public-package +convention used throughout core Gephi. `manifest.mf` carries the branding shown in Gephi's Plugin +Manager (`OpenIDE-Module-Name`, `-Short-Description`, `-Long-Description`, `-Display-Category`), or +alternatively an `OpenIDE-Module-Localizing-Bundle` pointer into `Bundle.properties` when the text is +too long for the manifest format. + +A plugin can also be a **suite**: several modules in the same top-level folder that split API, +implementation, and UI concerns (the same four-role convention — `XxxAPI` / `XxxPlugin` / +`XxxPluginUI` / `DesktopXxx` — used across core Gephi's own modules). This is only worth doing for +plugins complex enough to need a shared API surface between multiple sub-modules; a single module is +enough for the vast majority of plugins. + +## Dependency management: `gephi-plugin-parent` + +Every plugin's `pom.xml` inherits from `org.gephi:gephi-plugin-parent` — an artifact built from this +repo's `parent-pom` branch, published to Maven Central, and versioned alongside Gephi itself (e.g. +`0.11.3`). It supplies: + +- Java/compiler settings (JDK 17 target). +- A `` entry for every Gephi and NetBeans Platform module a plugin might + depend on (`graph-api`, `layout-api`, `org-openide-util-lookup`, etc.), so a plugin's own `pom.xml` + can declare a dependency without a `` and get the right one for the Gephi version it + targets. + +This is why the root `pom.xml` on `master` and `master-forge` looks different from +`modules/pom.xml` on `parent-pom`: the former is the reactor POM that aggregates whichever plugin +modules exist in this checkout (`pom`, lists ``); the latter is the +plugin parent POM those modules inherit build configuration and dependency versions from. + +## The `gephi-maven-plugin` build lifecycle + +[`gephi-maven-plugin`](https://github.com/gephi/gephi-maven-plugin) is the Maven plugin that drives +everything above. Its goals, bound in this repo's root `pom.xml` or invoked directly: + +| Goal | When it runs | What it does | +|---|---|---| +| `generate` | Invoked manually | Interactively scaffolds a new plugin module and adds it to `pom.xml`'s `` | +| `validate` | Bound to the `validate` phase (every `mvn package`) | Checks every listed module's manifest/pom configuration is well-formed; fails the build with a specific reason if not | +| `run` | Invoked manually | Downloads/builds the matching Gephi distribution and launches it with the current modules installed, for manual testing | +| `build-metadata` / `create-autoupdate` | Bound to the `package` phase under the `release` profile | Generates the autoupdate site (`updates.xml` + `.nbm` files) published to `metadataUrl` — this is what powers Gephi's in-app Plugin Manager and the gephi.org plugin listing | +| `migrate` | Invoked manually | Helps update an existing plugin's configuration when the target Gephi version changes | + +## Root files + +```text +.github/workflows/ # build.yml (feature branches), test-generation.yml (master), release-pom.yml (parent-pom) +modules/ # one folder per plugin/suite; empty list on master until you add one +plugins/ # static assets (images) used by the gephi.org plugin listing, not source code +pom.xml # reactor POM for this checkout's plugin modules — NOT the plugin parent POM +nbactions.xml # NetBeans IDE run/debug actions (`mvn package org.gephi:gephi-maven-plugin:run`) +``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..5de1b51d0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,348 @@ +# Contributing to gephi-plugins + +This covers code-quality expectations for plugin code and, since most activity in this repository is +reviewing plugin submissions from third parties, a checklist for reviewing those PRs. For how to set +up, build, and submit a plugin in the first place, see `README.md`. For how the repository and +Gephi's extension mechanism fit together, see `ARCHITECTURE.md`. + +## Code quality + +- Write code, comments, commit messages, and PR descriptions in English. +- Match the existing style of the file being edited; don't reformat unrelated code in the same PR. +- Remove debug leftovers before submitting: ad-hoc debug output (see "Logging" below for what to use + instead), commented-out code blocks, placeholder text left over from the `generate` template (e.g. + "Plugin catch-phrase", "Insert dependencies here"). +- Plugins pick their own license (the `generate` goal defaults to Apache 2.0) — don't carry over + core Gephi's CDDL/GPL dual-license header into plugin code. + +## Gephi platform conventions + +Core Gephi has established conventions for cross-cutting concerns. Plugins should follow them +instead of introducing their own — this keeps a plugin's runtime behavior and log/UI output +consistent with core Gephi and every other installed plugin. + +### Logging + +Declare a per-class logger field and use it — this is the pattern throughout core Gephi, not SLF4J +or Log4j: + +```java +private static final Logger LOGGER = Logger.getLogger(ProjectControllerImpl.class.getName()); +``` + +(real example: `modules/ProjectAPI/.../ProjectControllerImpl.java`). Scope the logger to your own +class, never a shared or hardcoded name: the fully-qualified class name +(`org...SomeClass`) is what makes a log line traceable back to your plugin +specifically, as opposed to an `org.gephi.*` core line or another plugin's output. + +Use `SEVERE`/`WARNING`/`INFO`/`FINE` levels appropriately; don't invent your own scale. When logging +a caught exception, pass it as the log call's `Throwable` argument so the stack trace is preserved, +e.g.: + +```java +Logger.getLogger(SQLiteDriver.class.getName()).log(Level.SEVERE, null, ex); +// or, with a message: +Logger.getLogger("").log(Level.SEVERE, "Error while setting value for property '" + getName() + "'", e); +``` + +(real examples: `modules/DBDrivers/.../SQLiteDriver.java`, `modules/FiltersAPI/.../FilterProperty.java`). +Don't use `exception.printStackTrace()` — it bypasses the logger, so the message never respects the +user's configured log level or destination. Log messages are developer-facing, plain Java strings — +don't route them through `Bundle.properties`/`NbBundle`. + +### Localization + +No hardcoded English UI strings (labels, tooltips, dialog text, user-facing error messages) in Java +code. Put the string in a `Bundle.properties` file in the same package as the class +(`src/main/resources//Bundle.properties`) and reference it with +`NbBundle.getMessage(YourClass.class, "key")`: + +```properties +# org/gephi/datalab/api/Bundle.properties +DataLaboratoryHelper.ui.okButton.text=Ok +SettingsPanel.title={0} +``` +```java +NbBundle.getMessage(DataLaboratoryHelper.class, "SettingsPanel.title", ui.getDisplayName()); +``` + +The real key convention observed throughout core Gephi (320 `Bundle.properties` files, 477 +`NbBundle.getMessage` call sites) is `ClassName.member.property`, e.g. +`DataLaboratoryHelper.ui.okButton.text` for a field/button named `okButton`'s `text`. For +parameterized text, use `{0}`, `{1}`, ... `MessageFormat` placeholders in the property value and +pass the extra arguments to `NbBundle.getMessage(Class, String, Object...)` — as in the +`SettingsPanel.title={0}` example above. + +If you build UI with the NetBeans GUI builder (a `.form` file next to the `.java` file), it +generates `ResourceString` entries in the `.form` XML itself (with a `bundle=".../Bundle.properties"` +and `key="..."` pair per field) and writes the corresponding `NbBundle.getMessage(...)` call into +the generated code for you — you still need the key/value present in `Bundle.properties`, but you +don't hand-write the `NbBundle.getMessage` call for form fields. + +### Preferences + +Persist a **per-user, cross-session, cross-project** setting (e.g. "always export in millimeters") +with `NbPreferences.forModule(YourClass.class)`: + +```java +boolean defaultMM = NbPreferences.forModule(UIExporterPDF.class).getBoolean("Default_Millimeter", false); +millimeter = NbPreferences.forModule(UIExporterPDF.class).getBoolean("Millimeter", defaultMM); +// ... +NbPreferences.forModule(UIExporterPDF.class).putBoolean("Millimeter", millimeter); +``` + +(real example: `modules/PreviewExportUI/.../UIExporterPDFPanel.java`; `AbstractExporterSettings` in +the same module wraps the same pattern for `getInt`/`putInt`/`getFloat`/`putFloat`). Don't build a +plugin-invented properties file, serialization scheme, or static field for this. + +`NbPreferences` is backed by the NetBeans user directory and is global to the user's Gephi +installation — it is the wrong place for anything scoped to a single project or workspace (layout +parameters, filter configuration, anything that should be saved and reopened *with* the project). +That belongs in a Project SPI persistence provider that serializes into the `.gephi` file instead — +see `ARCHITECTURE.md`'s Project SPI row. + +### Library dependencies and version collisions + +Before adding a library directly, check whether Gephi already wraps it in one of its `*Wrapper` +modules and depend on that Maven artifact instead: + +- `core-library-wrapper` (`org.gephi:core-library-wrapper`) — e.g. `gson`, `commons-math3`, + `jfreechart`, `trove4j`, `commons-csv`, `commons-codec`, `commons-compress`. +- `org.gephi:ui-library-wrapper` — the Flamingo ribbon UI library. +- `org.gephi:batik-wrapper` — Batik/SVG rendering. + +```xml + + org.gephi + core-library-wrapper + +``` + +Two copies of the same library on the classpath (one via the wrapper, one direct) is wasteful and +can behave inconsistently — depend on the wrapper's Maven coordinate above instead of the raw +library's. + +If a plugin needs library X, and X transitively pulls in a different version of a library Y that +Gephi already provides (directly or via a wrapper), don't let both versions coexist on the +classpath — exclude X's transitive dependency on Y. `batik-wrapper`'s own `pom.xml` does exactly +this for `batik-transcoder`: + +```xml + + org.apache.xmlgraphics + batik-transcoder + ${gephi.batik.version} + + + xml-apis + xml-apis + + + commons-logging + commons-logging + + + +``` + +Only reach for `` when there's no wrapper module to depend on instead of the raw +library. Before submitting, run `mvn dependency:tree` in your plugin's module (add +`-Dincludes=:` to filter to one suspect library) to check whether a dependency +you added is pulling in a second, different version of something Gephi already provides. + +### Icons + +Use SVG, not PNG/GIF, for toolbar, menu, and branding icons. Load one the same way core Gephi does, via +`ImageUtilities.loadImageIcon(...)` from an SPI's `getIcon()`: + +```java +@Override +public Icon getIcon() { + return ImageUtilities.loadImageIcon("DataLaboratoryPlugin/settle.svg", false); +} +``` + +The path argument is `"/.svg"`, resolved as a classpath resource — for your own +plugin, put the file at `src/main/resources//icon.svg` in that same module. +Use the mandated pixel size: `viewBox="0 0 32 32"`. + +### Suite folder/package naming + +When a plugin has multiple modules, keep folder and package names consistent across the suite. For +example, a suite named `MyPlugin` from an org `com.foo`, mirroring `ARCHITECTURE.md`'s +API/SPI/implementation/UI package-role table, would look like: + +```text +modules/ +├── MyPluginAPI/ com.foo.myplugin.api (public API interfaces) +│ com.foo.myplugin.spi (SPI interfaces, if the suite defines its own) +│ com.foo.myplugin (API implementation) +└── MyPluginUI/ com.foo.myplugin.ui (Swing panels/wizards implementing the UI) +``` + +Don't mix naming schemes within one suite — e.g. `MyPluginCore` next to `myplugin.ui` next to +`MyPluginModuleThree` — pick one convention (ideally the one above) and apply it to every module +folder and every module's base package. + +### Test utilities + +For graph fixtures in unit tests, depend on GraphAPI's (`org.gephi:graph-api`) test-jar and reuse +`org.gephi.graph.GraphGenerator`'s static factory methods rather than hand-building a `GraphModel`: + +```java +GraphModel model = GraphGenerator.generateCompleteUndirectedGraph(10); +// also available: generateNullUndirectedGraph(int), generateCyclicDirectedGraph(int), +// generatePathUndirectedGraph(int), generateStarUndirectedGraph(int), and directed variants +``` + +It's the only class GraphAPI's `pom.xml` explicitly includes in its test-jar build (its own +comment: "Only this class is intended to be re-used other modules" — other GraphAPI test classes are +excluded from the jar). Declare the dependency as: + +```xml + + org.gephi + graph-api + test + test-jar + +``` + +(real consumer example: `modules/AppearanceAPI/pom.xml`). For import fixtures, depend the same way +on `org.gephi:io-importer-api`'s test-jar (unlike GraphAPI's, it isn't filtered to one class) and +use: + +```java +GraphModel model = GraphImporter.importGraph(new File("path/to/test.gexf")); +// or, to load a fixture bundled as a classpath resource next to a test class: +GraphModel model = GraphImporter.importGraph(MyPluginTest.class, "fixture.gexf"); +``` + +to build a `GraphModel` from a test file through the real import pipeline (real consumer example: +`modules/StatisticsPlugin/pom.xml`, depending on `io-importer-api` with `test-jar`), +instead of hand-rolling either one. + +### Comments + +Keep code comments short and about the current code — what invariant holds, what a non-obvious +value means — not the history of why it got that way or what alternative was rejected. That +belongs in the commit message or PR description, not the source, where it rots as the code +evolves. For example: + +```java +// Bad: explains history/rationale, will be stale the moment someone changes this again +// We used to retry 3 times here but that caused timeouts in slow environments, so now it's 1. +int maxRetries = 1; + +// Good: states a fact about the current code +// Must stay <= server-side rate limit of 1 req/s; see the ImporterUI cap for the same constant. +int maxRetries = 1; +``` + +## Reviewing a plugin submission PR + +Third-party plugin PRs are usually incomplete in the same handful of ways. Work through this list +before approving; when something's missing, name the specific item rather than asking generically +for "more polish." + +### Build actually passes + +- `mvn clean package` succeeds from the repository root, including the `gephi-maven-plugin:validate` + goal that runs automatically at the `validate` phase — don't take a green PR description at face + value, run it. +- If the PR adds a suite (multiple modules in one plugin folder), every sub-module folder is added to + the root `pom.xml`'s `` list, not just the first one. +- The PR touches only its own plugin's folder plus its own line in root `pom.xml`'s `` list + — changes to another plugin's code, to `gephi-plugin-parent`-managed versions, or to unrelated + workflow/config files are a red flag in a plugin-submission PR. + +### `pom.xml` configuration + +- `` is `org.gephi:gephi-plugin-parent` with a `` matching this repo's + `gephi.version` property — not a hardcoded fork or an unrelated parent. +- Dependencies on Gephi/NetBeans modules are declared **without** an explicit `` — the + parent's `dependencyManagement` should supply it. An explicit version there usually means the + author copy-pasted from an old example instead of relying on the parent, and can silently drift + from the Gephi version this repo targets. +- `` lists only packages meant for other modules to consume, not implementation + packages — an empty list, or a list that includes an obvious `impl`/internal package, is worth + flagging either way (the former may be intentional, the latter usually isn't). +- For an **update** to an existing plugin, the `` was actually bumped from the previous + release — this is easy to miss and the autoupdate site keys off it. +- `author`, and `licenseName`/`licenseFile` (or an equivalent license declaration), are filled in on + the `nbm-maven-plugin` configuration — not left as generated placeholders. + +### Manifest / branding + +- `src/main/nbm/manifest.mf` (or the `Bundle.properties` it points to via + `OpenIDE-Module-Localizing-Bundle`) has real values for module name, short description, long + description, and category — not leftover template placeholders. +- The declared category (`Layout`, `Export`, `Import`, `Data Laboratory`, `Filter`, `Generator`, + `Metric`, `Preview`, `Tool`, `Appearance`) actually matches the SPI the plugin implements (see + next section) — a layout algorithm categorized as "Tool" will be hard for users to find. + +### SPI implementation correctness + +- The class implementing the plugin's functionality is annotated `@ServiceProvider(service = ...)` + for the correct SPI interface. Without the annotation, Gephi's `Lookup` will not discover it and + the plugin will silently do nothing at runtime — this is a common, easy-to-miss failure mode that + the build will not catch. See `ARCHITECTURE.md`'s "How Gephi can be extended" section for the SPI + list. +- In a suite, UI-only code (Swing panels, wizards) lives in the UI/plugin-UI module, not mixed into + the same module as the core SPI implementation. +- If the plugin implements a cancellable long-running operation (a `Generator`, `Importer`, + `Statistics`, or other `LongTask`), check that `cancel()` actually stops the work instead of being + a stub that always returns `false` — a no-op cancel passes `mvn package` cleanly and only shows up + as an unresponsive UI at runtime. +- Any code that reads or writes the graph inside such a task must release its lock + (`Graph.readUnlock()`/`writeUnlock()`) on every exit path, including the cancellation path — a leak + here breaks graph consistency for the whole session, not just this plugin's feature (see + `ARCHITECTURE.md`'s locking model). + +### Platform conventions + +See "Gephi platform conventions" above for the reasoning; on review, check that the plugin: + +- Logs via `java.util.logging` with a per-class logger — not `System.out`, not SLF4J/Log4j. +- Has no hardcoded English UI strings — routed through `Bundle.properties` + `NbBundle.getMessage`. +- Persists settings via `NbPreferences.forModule(...)`, not a homemade scheme. +- Depends on a `*Wrapper` module instead of re-adding a library Gephi already wraps, and uses + `` rather than silently duplicating a library at a different version — `mvn + dependency:tree` on the plugin's module is the fast way to check. +- Uses SVG icons loaded via `ImageUtilities.loadImageIcon(...)`, not PNG/GIF. +- Uses consistent folder/package naming across suite modules, if any. +- Reuses `GraphGenerator`/`GraphImporter` from GraphAPI/ImportAPI's test-jars in tests rather than + reinventing fixture construction. +- Has comments that state facts about the current code, not rationale or history. + +### Licensing and attribution + +- A license file is actually included and matches what's declared in `pom.xml` (e.g. a plugin + declaring GPLv3 ships a `gpl-3.0.txt` or equivalent, not just the pom field). +- Any bundled third-party library, native binary, or copy-pasted code is disclosed and its license is + compatible with the plugin's declared license — don't assume silence means it's fine. + +### File hygiene + +- No build artifacts committed (`target/`, `*.class`, stray `*.jar` outside a deliberately vendored + native dependency). +- No IDE metadata committed (`.idea/`, `.classpath`, `.project`, `*.iml`) unless the existing plugin + already tracks it as a convention. +- No secrets, tokens, or credentials anywhere in the diff. + +### Tests + +- Unit tests are included where the plugin's logic is non-trivial (e.g. algorithm implementations), + using the `org-netbeans-modules-nbjunit` dependency per `README.md`'s testing section. +- Tests are not disabled or skipped (`@Ignore`, `-DskipTests` baked into the module's own `pom.xml`) + to force a green build. + +### PR completeness + +- `.github/pull_request_template.md`'s checkboxes reflect what was actually done, not left checked + by default. +- The "What is the purpose of this plugin?" and "How to test your plugin in Gephi?" sections are + filled in with real, reproducible content — not left as the template's empty numbered list. +- The PR targets the `master-forge` branch for a plugin submission or update, not `master` (see + README's "Submit a plugin"). A PR against `master` touching only `modules/` is itself worth + questioning. diff --git a/README.md b/README.md index 8d0c16b9e..a29b2f4f5 100644 --- a/README.md +++ b/README.md @@ -194,8 +194,7 @@ It's the same thing. We say module because Gephi is a modular application and is This error appears when you try to run a module. To run Gephi with your plugin you need to run the `gephi-plugins` project, not your module. -## Best practices +## Contributing -### Code quality - -- Write your code in English, so it can be best reviewed and maintained. \ No newline at end of file +See [CONTRIBUTING.md](CONTRIBUTING.md) for code-quality expectations and, for maintainers, the +checklist used to review plugin submission PRs. \ No newline at end of file