@@ -5,36 +5,29 @@ project, read [`docs/`](docs/README.md) instead — task-oriented pages for agen
55
66## Commands
77
8- ** Use JDK 11. ** The Gradle 6.0.1 wrapper cannot run on a modern JVM: under JDK 17+ it fails
9- before any task with ` Could not initialize class org.codehaus.groovy.reflection.ReflectionCache ` .
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 .
1010
1111``` bash
12- export JAVA_HOME=/path/to/jdk-11
13- ./gradlew :cloudinary-core:jar :cloudinary-http5:jar # build the two artifacts users depend on
14- ./gradlew :cloudinary-core:test # unit tests, no cloud needed
15- ./gradlew :cloudinary-http5:test -DCLOUDINARY_URL= " cloudinary://key:secret@cloud " # live integration tests
16- ./gradlew createTestSubAccount -PmoduleName= http5 # throwaway cloud -> tools/cloudinary_url.txt
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
1717```
1818
19- ` CLOUDINARY_URL ` is read as a ** system property ** by the tests (see ` java_shared.gradle ` ), not
20- from the environment. ` tools/cloudinary_url.txt ` is git-ignored and ** contains live
21- credentials — never commit it or paste its contents anywhere.**
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.
2222
2323## Testing
2424
25- - ` cloudinary-core ` tests are offline. Everything else needs a live product environment.
26- - Do not share one product environment between doc verification and the integration suite.
27- Doc probes leave mandatory metadata fields and pending moderated assets behind, and
28- ` ApiTest.testGetResourcesWithMetadata ` then fails for reasons unrelated to the branch. Use a
29- fresh sub-account for each.
30- - For docs-only changes, run the suite against the base branch and compare failure counts
31- rather than asserting "tests pass". The integration suite takes about 3.5 minutes plus a
32- throwaway cloud.
33- - Before committing a ` docs/ ` change, two cheap checks: every ` java ` snippet compiles against
34- the built jars (wrap fragments in a harness with the ambient names — ` cloudinary ` , ` file ` ,
35- ` publicId ` — declared as fields, then ` javac ` ; this catches invented methods, which is the
36- whole point), and every internal link, cross-page anchor, and external URL resolves. GitHub
37- ** preserves underscores** in heading slugs, so a naive slugifier reports false positives.
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.
3831
3932## Project structure
4033
@@ -44,108 +37,76 @@ credentials — never commit it or paste its contents anywhere.**
4437| ` cloudinary-http5/ ` | Yes | Apache HttpClient 5 transport. ** This is what users depend on.** |
4538| ` cloudinary-taglib/ ` | Yes | JSP tags. Targets ` javax.* ` , not Jakarta EE 9+. |
4639| ` cloudinary-test-common/ ` | Yes | Shared test helpers. Not for application code. |
47- | ` docs/ ` | No | Task pages for agents * using* the SDK. Every claim produced by running something. |
40+ | ` docs/ ` | No | Task pages for agents * using* the SDK. |
4841| ` examples/ ` | No | One runnable program per doc page where the task is genuinely runnable. |
4942| ` samples/ ` | No | Legacy sample applications. Not in the build. |
5043| ` tools/ ` | No | Release and test-cloud scripts. |
5144
52- ` settings.gradle ` includes only the four modules. The ` cloudinary-http42/43/44 ` directories
53- present in a working copy are ** untracked leftovers** from the 1.x line — not in git, not in the
54- build. Do not add them back and do not "tidy" them as part of unrelated work.
45+ ` settings.gradle ` includes the four modules above.
5546
5647` org.cloudinary.json ` inside ` cloudinary-core ` is a vendored JSON implementation. It exists to
5748keep the core dependency-free. Do not replace it with Jackson or Gson.
5849
59- ** ` docs/ ` is deliberately not packaged into any jar.** A survey of 15 widely-used Java libraries
60- on Maven Central — Guava, OkHttp, Jackson, Gson, commons-lang3, SLF4J, Retrofit, AWS SDK v2,
61- google-cloud-storage, Stripe, Twilio, SendGrid, java-jwt, Spring Core, jsoup — found that only
62- jsoup ships any Markdown: its 3 KB project README in ` META-INF/ ` . Everything else carries
63- manifests, licences, and machine-consumed config only. Embedding these pages would grow
64- ` cloudinary-core ` by 27%, from 175 KB to 223 KB, for files no runtime reads, and retrieval
65- through ` unzip -p ` is slower than reading the repository or Cloudinary's ` llms.txt ` . Java's
66- convention for documentation attached to an artifact is the ` -javadoc.jar ` , which this SDK
67- already publishes. Reversing this needs a reason better than "other Cloudinary SDKs do it".
68-
69- ` examples/ ` is likewise not shipped: loose ` .java ` files inside a library artifact are dead
70- weight, and each doc page carries its full flow inline. Keep examples uniform — one entry point
71- taking no arguments, no CLI parsing, constants at the top, one error-handling block mapping every
72- failure to a non-zero exit with a readable message and no stack trace.
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.
7355
7456## Code style
7557
7658- The build targets ** Java 8** (` sourceCompatibility = 1.8 ` in ` java_shared.gradle ` ). Do not use
7759 newer language features in ` src/main ` .
78- - There is ** no linter, formatter, or static-analysis tool** , deliberately. Match the surrounding
79- code and do not introduce one as a side effect.
60+ - There is no linter, formatter, or static-analysis tool. Match the surrounding code.
8061- Options are untyped ` Map ` s built with ` ObjectUtils.asMap(k, v, ...) ` ; responses are ` Map ` s keyed
8162 by the raw API field names (` public_id ` , ` secure_url ` , ` asset_id ` ). There are no typed response
8263 objects.
8364- ` Cloudinary ` is safe to share as a singleton. ` Url ` is a ** mutable** builder and is not
8465 thread-safe; call ` cloudinary.url() ` per URL.
85- - In docs, prefer ` asset_id ` (immutable) over ` public_id ` (changes on rename) when showing how to
86- store a reference. Only six ` Api ` methods accept an asset ID; nothing in ` Uploader ` does.
87- - Docs describe ** this SDK** , not how it differs from other Cloudinary SDKs. Link Cloudinary docs
88- in ` .md ` form (` https://cloudinary.com/documentation/<page>.md ` ); the repository ` README.md `
89- may link HTML, since people read it. Do not recommend third-party packages — community
90- Java/Spring integrations exist and are not maintained by Cloudinary.
91- - Docs are reference documentation: state what the API does and how to use it. Do not frame
92- documented behaviour as a bug or an oversight, and do not tell the reader what they might
93- wrongly assume.
94- - Docs and README carry ** no version literal** . Install snippets use ` X.Y.Z ` and point at the
95- Maven Central badge and a ` maven-metadata.xml ` one-liner. The literal once drifted to a
96- never-published ` 2.3.1 ` while Central was at ` 2.4.0 ` , and nothing rewrites ` docs/ ` at release.
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.
9776
9877## Git workflow
9978
10079- Branch from ` master ` ; keep changes focused; one topic per pull request.
101- - Do not edit ` CHANGELOG.md ` or bump the version. Releases are a separate process:
102- ` tools/update_version.sh ` rewrites ` Cloudinary.VERSION ` in ` Cloudinary.java ` and ` version= `
103- in ` gradle.properties ` . ** It greps the ` VERSION ` line with a regex; do not reformat it.** Its
104- ` sed ` over ` README.md ` is now a harmless no-op. ` Cloudinary.VERSION ` feeds the ` User-Agent ` and
105- the ` _a= ` analytics parameter, so drift misreports SDK usage rather than breaking visibly.
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.
10683- Keep secrets out of everything — code, tests, fixtures, docs, commit messages. Grep the diff
10784 for ` cloudinary:// ` with real values before pushing.
10885- Never commit ` tools/cloudinary_url.txt ` , ` /out/ ` , or media left behind by running examples.
10986
11087## Boundaries
11188
11289** Always**
113- - Produce every claim in ` docs/ ` by running something — not by reading source, and not by
114- copying another Cloudinary SDK's docs. Behaviour differs between the SDKs.
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.
11592- Keep ` docs/ ` and ` examples/ ` consistent with the code they document.
11693- Keep API secrets out of examples, docs, tests, and fixtures.
11794
11895** Ask first**
119- - Changing the published artifact list, packaging, or what ships inside a jar — including
120- moving ` docs/ ` into ` src/main/resources ` .
121- - Adding a dependency. ` cloudinary-core ` has no runtime dependencies at all, which is a feature
122- for consumers.
123- - Changing public API signatures or behaviour. This is a widely deployed library; a behaviour
124- change can break callers. Document current behaviour and raise an issue before changing it.
125- - Changing any of these documented behaviours. Each is part of the public contract as observed
126- by execution and described in ` docs/ ` ; changing one is a behaviour change for existing
127- callers:
128- 1 . ` Configuration.from() ` parses with ` java.net.URI ` , so a cloud name containing an
129- underscore yields ` null ` cloud name, key, and secret (` getHost() ` and ` getUserInfo() `
130- return ` null ` for a hostname with an underscore). Such environments are configured with a
131- ` Map ` . An alternative implementation would parse ` getAuthority() ` directly.
132- 2 . Error types differ by entry point: ` java.lang.RuntimeException ` carrying a message for
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
133102 uploads (` AbstractUploaderStrategy.processResponse ` ), typed ` ApiException ` subclasses for
134103 the Admin API, and ` java.lang.UnknownError ` (an ` Error ` , not an ` Exception ` ) when no HTTP
135104 adapter is present.
136- 3 . ` Api.CLOUDINARY_API_ERROR_CLASSES ` maps HTTP 400/401/403/404/409/420/500 to typed
137- exceptions; other status codes become ` GeneralError ` . This includes 429, which is not
138- routed to ` RateLimited ` .
139- 4 . ` EnumMetadataField ` has a package-private constructor and no factory, unlike
105+ 2 . ` EnumMetadataField ` has a package-private constructor and no factory, unlike
140106 ` SetMetadataField ` . Application code uses ` new MetadataField<String>(MetadataFieldType.ENUM) ` .
141- 5 . ` ProgressCallback ` overloads compile on ` cloudinary-http5 ` and throw
142- ` IllegalArgumentException: Progress callback is not supported ` when called. Progress
143- reporting is available in the Android SDK.
144- 6 . ` Url.resourcType(String) ` is retained alongside ` resourceType(String) ` for compatibility.
145- Both work.
146- 7 . ` Cloudinary.VERSION ` and ` version= ` in ` gradle.properties ` are maintained separately and
147- can drift; during 2.3.x they read ` 2.3.1 ` and ` 2.3.2 ` . Both read ` 2.5.0 ` at the time of
148- writing.
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.
149110- Touching ` publish.gradle ` , ` gradle.properties ` , ` MAVEN_CENTRAL_PUBLISHING_GUIDE.md ` , or the
150111 release script.
151112- Adding a typed response object, a linter, or a formatter.
0 commit comments