Skip to content

fix(setup): read a file:/// repository from the local file system - #3510

Merged
marevol merged 1 commit into
mainfrom
fix/setup-file-repository
Sep 27, 2026
Merged

marevol merged 1 commit into
mainfrom
fix/setup-file-repository

Conversation

@marevol

@marevol marevol commented Sep 27, 2026

Copy link
Copy Markdown
Contributor

Summary

On a server with no route to the Internet, plugins and themes can be installed by copying the Maven repository onto the server and pointing --repository at the copy. fess-setup install plugin ... --repository file:///path/to/repository/org/codelibs/fess/ instead fails with a stack trace (IllegalArgumentException: invalid URI scheme file) and exit code 1. Other failures print one error: ... line.

Cause

Every read in fess-setup goes through Downloader, and Downloader always uses java.net.http.HttpClient. That client only accepts http and https and throws an unchecked IllegalArgumentException for any other scheme. FessSetup.run catches only SetupException and IOException, so the exception reaches the user as a stack trace. A --repository with no scheme at all, such as a plain path, fails the same way.

Fix

Downloader reads a file: URI from the local file system and treats it like HTTP:

  • A missing file counts as a 404. The optional .sha1 lookup (readStringIfPresent) and the "try the next source" logic (downloadIfPresent) therefore behave as they do over HTTP.
  • A directory is read as the index a web server would list for it, so list plugins --repository file:///... works too.
  • The artifact is copied through the same code as an HTTP download, including the .part file and the existing-size check. The HTTP body-writing code was moved into a shared helper for this.

maven-metadata.xml, the snapshot build metadata, the plugin jar, the theme archive and its theme-index.txt, and the published SHA-1 are all read through Downloader. Version resolution, installation and checksum verification therefore work against a file: repository without other changes. --repository already disables the GitHub download source, so nothing else goes to the network.

Downloader now refuses any scheme other than http, https and file, and any --repository with no scheme, with a SetupException. The user gets the usual single error: ... line instead of a stack trace. A file: URI that does not name an absolute local path is reported the same way.

The install plugin help mentions that a file:/// URL can name a local copy of the repository.

Tests

  • DownloaderTest: reading a file: URL; a missing file fails readString/download and returns null from readStringIfPresent/downloadIfPresent; a directory is listed as an index; copying reports progress and leaves no .part file; an unsupported scheme and a URI with no scheme are refused with SetupException.
  • FileRepositoryTest (new) builds a repository layout in a temporary directory (org/codelibs/fess/<artifact>/maven-metadata.xml, <version>/<artifact>-<version>.jar and .jar.sha1) and runs fess-setup against it:
    • install plugin fess-ds-git:15.9.0 --repository file:///... installs the jar and verifies its checksum without a warning.
    • A wrong .sha1 fails the install with error: Checksum mismatch ... and leaves no jar.
    • A version that is not in the repository fails with one error: line.
    • --repository ftp://... fails with one error: line.
    • Version resolution reads maven-metadata.xml from the file: repository.
    • list plugins lists the plugins in the repository directory and leaves out non-plugin artifacts.
  • Without the Downloader change, 14 of these tests fail.
  • mvn test -Dtest='org.codelibs.fess.setup.*Test': 226 tests, all pass.

A server with no route to the Internet cannot reach the Maven repository,
so the natural way to install plugins there is to copy the repository
onto it and point --repository at the copy. `fess-setup install plugin
... --repository file:///path` instead died with a stack trace from
IllegalArgumentException: invalid URI scheme file, because every read
went through java.net.http.HttpClient, which only takes http and https
and throws an unchecked exception for anything else, which the command
line does not catch.

Downloader now reads a file: URI from the local file system and treats
it the way it treats HTTP: a missing file is what a 404 is, so the
optional .sha1 lookup and the "try the next source" logic behave the
same, and a directory reads as the index a web server would list for it,
so list plugins works against the copy too. The metadata, the jar or
theme archive and its checksum all go through Downloader, so resolving a
version, installing and verifying the SHA-1 work unchanged. The body
copy is shared with the HTTP path, including the .part file and the
existing-size check.

Any other scheme, or a --repository with no scheme at all, is now
refused with a SetupException, which reaches the user as the usual
one-line "error: ..." instead of a stack trace.
@marevol marevol added this to the 15.9.0 milestone Sep 27, 2026
@marevol marevol added the task label Sep 27, 2026
@marevol marevol self-assigned this Sep 27, 2026
@marevol
marevol merged commit 07bc3a9 into main Sep 27, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant