|
| 1 | +# Contributor guide for coding agents |
| 2 | + |
| 3 | +This file is for agents contributing to this repository. If you are *using* the SDK in another |
| 4 | +project, read [`docs/`](docs/README.md) instead — task-oriented pages for agents using the SDK. |
| 5 | + |
| 6 | +## Commands |
| 7 | + |
| 8 | +Use JDK 8 or 11; CI runs on JDK 8. The Gradle 6.0.1 wrapper fails on JDK 17 and later before any |
| 9 | +task runs. |
| 10 | + |
| 11 | +```bash |
| 12 | +./gradlew :cloudinary-core:jar :cloudinary-http5:jar :cloudinary-taglib:jar # build the published modules |
| 13 | +./gradlew :cloudinary-core:test # unit tests, no cloud needed |
| 14 | +./gradlew createTestSubAccount -PmoduleName=http5 # throwaway cloud -> tools/cloudinary_url.txt |
| 15 | +source tools/cloudinary_url.txt # sets CLOUDINARY_URL |
| 16 | +./gradlew ciTest -p cloudinary-http5 -DCLOUDINARY_URL="$CLOUDINARY_URL" # integration tests, as CI runs them |
| 17 | +``` |
| 18 | + |
| 19 | +Gradle passes `-DCLOUDINARY_URL` to the test JVM as the `CLOUDINARY_URL` environment variable |
| 20 | +(`java_shared.gradle`). `tools/cloudinary_url.txt` is git-ignored and contains live credentials; |
| 21 | +never commit it or paste its contents anywhere. |
| 22 | + |
| 23 | +## Testing |
| 24 | + |
| 25 | +- `cloudinary-core` tests run offline. `cloudinary-http5` tests need a live product environment. |
| 26 | + `cloudinary-taglib` has no tests. |
| 27 | +- Use a fresh sub-account for each integration run. Assets and metadata fields left by earlier |
| 28 | + runs can fail unrelated tests. |
| 29 | +- `java` snippets in `docs/` must compile against the built jars, and links in `docs/` must |
| 30 | + resolve. |
| 31 | + |
| 32 | +## Project structure |
| 33 | + |
| 34 | +| Path | Published | Purpose | |
| 35 | +|---|---|---| |
| 36 | +| `cloudinary-core/` | Yes | URL/transformation building, parameter serialisation. No HTTP client, no runtime dependencies. | |
| 37 | +| `cloudinary-http5/` | Yes | Apache HttpClient 5 transport. **This is what users depend on.** | |
| 38 | +| `cloudinary-taglib/` | Yes | JSP tags. Targets `javax.*`, not Jakarta EE 9+. | |
| 39 | +| `cloudinary-test-common/` | Yes | Shared test helpers. Not for application code. | |
| 40 | +| `docs/` | No | Task pages for agents *using* the SDK. | |
| 41 | +| `examples/` | No | One runnable program per doc page where the task is genuinely runnable. | |
| 42 | +| `samples/` | No | Legacy sample applications. Not in the build. | |
| 43 | +| `tools/` | No | Release and test-cloud scripts. | |
| 44 | + |
| 45 | +`settings.gradle` includes the four modules above. |
| 46 | + |
| 47 | +`org.cloudinary.json` inside `cloudinary-core` is a vendored JSON implementation. It exists to |
| 48 | +keep the core dependency-free. Do not replace it with Jackson or Gson. |
| 49 | + |
| 50 | +`docs/` and `examples/` are not packaged into any jar; the published `-javadoc.jar` is the |
| 51 | +documentation attached to each artifact. |
| 52 | + |
| 53 | +Keep examples uniform: one entry point taking no arguments, no CLI parsing, constants at the top, |
| 54 | +and one error-handling block mapping every failure to a non-zero exit with a readable message. |
| 55 | + |
| 56 | +## Code style |
| 57 | + |
| 58 | +- The build targets **Java 8** (`sourceCompatibility = 1.8` in `java_shared.gradle`). Do not use |
| 59 | + newer language features in `src/main`. |
| 60 | +- There is no linter, formatter, or static-analysis tool. Match the surrounding code. |
| 61 | +- Options are untyped `Map`s built with `ObjectUtils.asMap(k, v, ...)`; responses are `Map`s keyed |
| 62 | + by the raw API field names (`public_id`, `secure_url`, `asset_id`). There are no typed response |
| 63 | + objects. |
| 64 | +- `Cloudinary` is safe to share as a singleton. `Url` is a **mutable** builder and is not |
| 65 | + thread-safe; call `cloudinary.url()` per URL. |
| 66 | + |
| 67 | +## Docs |
| 68 | + |
| 69 | +- Docs describe this SDK. Link Cloudinary documentation in `.md` form |
| 70 | + (`https://cloudinary.com/documentation/<page>.md`); `README.md` may link HTML. Do not recommend |
| 71 | + third-party packages. |
| 72 | +- Prefer `asset_id` (immutable) over `public_id` (changes on rename) when showing how to store a |
| 73 | + reference. Five `Api` methods accept an asset ID; nothing in `Uploader` does. |
| 74 | +- `README.md` shows the released version, which the release script updates. Pages in `docs/` use |
| 75 | + `X.Y.Z`, because the release script does not touch them. |
| 76 | + |
| 77 | +## Git workflow |
| 78 | + |
| 79 | +- Branch from `master`; keep changes focused; one topic per pull request. |
| 80 | +- Do not edit `CHANGELOG.md` or bump the version. Releases run `tools/update_version.sh <version>`, |
| 81 | + which rewrites `Cloudinary.VERSION` in `Cloudinary.java`, `version=` in `gradle.properties`, and |
| 82 | + the version in `README.md`. It reads the `VERSION` line with a regex; do not reformat it. |
| 83 | +- Keep secrets out of everything — code, tests, fixtures, docs, commit messages. Grep the diff |
| 84 | + for `cloudinary://` with real values before pushing. |
| 85 | +- Never commit `tools/cloudinary_url.txt`, `/out/`, or media left behind by running examples. |
| 86 | + |
| 87 | +## Boundaries |
| 88 | + |
| 89 | +**Always** |
| 90 | +- Verify claims in `docs/` against the source and, where possible, by running them. Do not copy |
| 91 | + another Cloudinary SDK's docs; behaviour differs between the SDKs. |
| 92 | +- Keep `docs/` and `examples/` consistent with the code they document. |
| 93 | +- Keep API secrets out of examples, docs, tests, and fixtures. |
| 94 | + |
| 95 | +**Ask first** |
| 96 | +- Changing the published artifact list, packaging, or what ships inside a jar. |
| 97 | +- Adding a dependency. `cloudinary-core` has no runtime dependencies. |
| 98 | +- Changing public API signatures or behaviour. Document current behaviour and raise an issue |
| 99 | + before changing it. This includes these behaviours, which `docs/` describes and callers may |
| 100 | + depend on: |
| 101 | + 1. Error types differ by entry point: `java.lang.RuntimeException` carrying a message for |
| 102 | + uploads (`AbstractUploaderStrategy.processResponse`), typed `ApiException` subclasses for |
| 103 | + the Admin API, and `java.lang.UnknownError` (an `Error`, not an `Exception`) when no HTTP |
| 104 | + adapter is present. |
| 105 | + 2. `EnumMetadataField` has a package-private constructor and no factory, unlike |
| 106 | + `SetMetadataField`. Application code uses `new MetadataField<String>(MetadataFieldType.ENUM)`. |
| 107 | + 3. `ProgressCallback` overloads compile on `cloudinary-http5` and throw |
| 108 | + `IllegalArgumentException: Progress callback is not supported` when called. |
| 109 | + 4. `Url.resourcType(String)` is retained alongside `resourceType(String)` for compatibility. |
| 110 | +- Touching `publish.gradle`, `gradle.properties`, `MAVEN_CENTRAL_PUBLISHING_GUIDE.md`, or the |
| 111 | + release script. |
| 112 | +- Adding a typed response object, a linter, or a formatter. |
| 113 | +- Modifying the legacy sample apps in `samples/`. |
| 114 | + |
| 115 | +**Never** |
| 116 | +- Commit credentials or real account identifiers. |
| 117 | +- Document a Cloudinary platform capability as an SDK method unless this library implements it |
| 118 | + (see `docs/platform-capabilities.md`). |
0 commit comments