When running gh commands in this project via an automated agent environment, ensure you bypass the default GITHUB_TOKEN environment variable. The agent environment may have an invalid GITHUB_TOKEN set, which gh prioritizes over valid keyring credentials, resulting in an HTTP 401: Bad credentials error.
Workaround: Prefix gh commands with env -u GITHUB_TOKEN to force the CLI to use the valid keyring authentication.
Example:
env -u GITHUB_TOKEN gh pr create --title "..." --body "..."Any new feature, bugfix, or improvement must be developed in a separate branch that starts with either feature/ or bugfix/ and has a meaningful but short name (e.g., feature/lock-contention-resolution or bugfix/fix-upload-timeout).
When adding new features or preparing a release, the CHANGELOG.md file must be updated to describe the new functionality added in this version. Use a release version header (e.g., ## [1.0.0-3.2]) instead of ## [Unreleased]. Derive this next release version from the SNAPSHOT version declared in the pom.xml file by removing the -SNAPSHOT suffix. Make sure to not add duplicate headers.
When performing a release, please strictly follow the instructions outlined in the docs/RELEASE.md documentation file.
- Java Version: The project is configured for Java 17 (or newer) to align with Spring Boot 3.x requirements.
- Jakarta EE / Servlets: Always use
jakarta.servlet.*package imports instead of the legacyjavax.servlet.*packages.
- The
UploadInfoclass is stored on disk serialized. If you modify fields inUploadInfo, you must preserve theserialVersionUID = -8751200491586638308Lto ensure pre-existing uploads on disk do not triggerInvalidClassExceptionupon deserialization. - Backward compatibility is paramount for this project. Breaking changes should only be done if all other options lead to ugly code and design. Breaking changes require a new major version.
- When expanding interfaces like
UploadLockingServiceorUploadStorageService, always use Javadefaultmethods to avoid breaking custom third-party implementations.
The deduplication mechanism links duplicate uploads (child) to the original upload (parent) using the duplicatesUploadId field in UploadInfo.
- Read Operations: Methods that read data (e.g.,
getUploadedBytes,copyUploadToinDiskStorageService) should dynamically resolveduplicatesUploadIdto the parent upload ID if it is set. - Write/Modify Operations: Methods that write or truncate data (e.g.,
append,removeLastNumberOfBytesinDiskStorageService) must not resolveduplicatesUploadIdrecursively. They must only operate on the target upload's own physical files to guarantee parent files are never modified or truncated when handling child upload errors.
Completed parent uploads are indexed by checksum under the <storagePath>/checksums/<algorithm>/<checksum_value> file path containing the target UploadId.
- Index lookup includes a self-cleaning check: if the index points to an upload that is null or whose data file is missing (e.g., due to expiration), the index file is deleted on the fly, keeping the file system clean without needing a separate index sweeper.
- Child uploads (duplicates) are never indexed.
- On parent termination, the parent's index entry is explicitly deleted.
- Do not use
ThreadLocalvariables or thread-local request context to pass state between components. Always pass parameters explicitly or use request wrapping.
- Unit test coverage must remain high. All new code (including background watchdog threads, helper methods, stream wrappers, and retry logic) must be thoroughly unit tested.
- After finalizing any implementation, you MUST check the unit test coverage on new/modified lines by running:
If any added or modified lines are reported as uncovered (❌) or partially covered (
mvn verify -Pcheck-coverage -Djacoco.compare.branch=master
⚠️ ), you must add extra unit tests to cover them before submitting.
- Avoid code duplication wherever possible. Consolidate repetitive logic (such as path generation, header parsing, and lock acquisition/release) into reusable helper functions or utility classes. Keep file path resolutions consolidated.
- All new classes, interfaces, and non-obvious code blocks (e.g., watchdog lifecycle, stream wrapping logic, thread-safety mechanisms) must have detailed comments describing their purpose, behavior, and design decisions.