Skip to content
Draft
39 changes: 39 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "FSCrawler",
"image": "mcr.microsoft.com/devcontainers/java:25",
"features": {
"ghcr.io/devcontainers/features/java:1": {
"version": "none",
"installMaven": true,
"installGradle": false
},
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {
"moby": true
}
},
"forwardPorts": [9200, 5601],
"portsAttributes": {
"9200": {
"label": "Elasticsearch",
"onAutoForward": "notify"
},
"5601": {
"label": "Kibana (optional)",
"onAutoForward": "silent"
}
},
"postCreateCommand": "bash .devcontainer/post-create.sh",
"customizations": {
"vscode": {
"extensions": [
"vscjava.vscode-java-pack",
"vscjava.vscode-maven"
],
"settings": {
"java.configuration.updateBuildConfiguration": "automatic",
"java.compile.nullAnalysis.mode": "automatic"
}
}
},
"remoteUser": "vscode"
}
27 changes: 27 additions & 0 deletions .devcontainer/extract-es-version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
POM="${1:-$ROOT_DIR/pom.xml}"

if [[ ! -f "$POM" ]]; then
echo "pom.xml not found: $POM" >&2
exit 1
fi

# Prefer xmllint if available; fall back to sed for the first elasticsearch.version property.
version=""
if command -v xmllint >/dev/null 2>&1; then
version="$(xmllint --xpath "string(/*[local-name()='project']/*[local-name()='properties']/*[local-name()='elasticsearch.version'])" "$POM" 2>/dev/null || true)"
fi

if [[ -z "$version" ]]; then
version="$(sed -n 's/.*<elasticsearch\.version>\([^<][^<]*\)<\/elasticsearch\.version>.*/\1/p' "$POM" | head -n 1)"
fi

if [[ -z "$version" ]]; then
echo "Could not read elasticsearch.version from $POM" >&2
exit 1
fi

printf '%s\n' "$version"
78 changes: 78 additions & 0 deletions .devcontainer/post-create.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"

ES_DIR="$ROOT_DIR/IGNORE_ME/elastic-start-local"
ES_URL="${ES_LOCAL_URL:-http://localhost:9200}"
ES_PASSWORD="${ES_LOCAL_PASSWORD:-changeme}"

echo "==> FSCrawler Dev Container post-create"

mkdir -p "$ROOT_DIR/IGNORE_ME"

es_up() {
curl -fsS -u "elastic:${ES_PASSWORD}" "$ES_URL" >/dev/null 2>&1 \
|| curl -fsS "$ES_URL" >/dev/null 2>&1
}

if es_up; then
echo "==> Elasticsearch already reachable at $ES_URL — skipping start-local"
else
if ! command -v docker >/dev/null 2>&1; then
echo "ERROR: docker CLI not found. Docker-outside-of-Docker is required for start-local." >&2
exit 1
fi
if ! docker info >/dev/null 2>&1; then
echo "ERROR: cannot talk to Docker daemon (socket / permissions)." >&2
exit 1
fi

ES_VERSION="$("$ROOT_DIR/.devcontainer/extract-es-version.sh")"
echo "==> Starting Elasticsearch ${ES_VERSION} via start-local (ES-only) under IGNORE_ME/"

# If a previous install exists, prefer start.sh; otherwise bootstrap.
if [[ -x "$ES_DIR/start.sh" ]]; then
(cd "$ES_DIR" && ./start.sh)
else
curl -fsSL https://elastic.co/start-local \
| ES_LOCAL_PASSWORD="$ES_PASSWORD" ES_LOCAL_DIR="$ES_DIR" \
sh -s -- -v "$ES_VERSION" --esonly
fi

echo "==> Waiting for Elasticsearch at $ES_URL"
for i in $(seq 1 60); do
if es_up; then
echo "==> Elasticsearch is up"
break
fi
if [[ "$i" -eq 60 ]]; then
echo "ERROR: Elasticsearch did not become ready in time" >&2
exit 1
fi
sleep 2
done
fi

echo "==> Warming Maven dependencies (dependency:go-offline)"
mvn -q dependency:go-offline -DskipTests

echo
echo "==> Ready."
echo " Build: mvn clean package -DskipTests -Ddocker.skip"
echo " ITs vs start-local:"
if [[ -f "$ES_DIR/.env" ]]; then
# shellcheck disable=SC1090
set -a
# shellcheck disable=SC1091
source "$ES_DIR/.env"
set +a
echo " source IGNORE_ME/elastic-start-local/.env"
echo " mvn verify -pl fr.pilato.elasticsearch.crawler:fscrawler-it \\"
echo " -Dtests.cluster.url=http://localhost:9200 \\"
echo " -Dtests.cluster.apiKey=\"\$ES_LOCAL_API_KEY\""
else
echo " (start-local .env not found yet — re-run post-create or start-local)"
fi
echo " Optional Kibana: re-run start-local without --esonly (same ES_LOCAL_DIR / password / version)."
58 changes: 58 additions & 0 deletions docs/source/dev/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Thanks to [JetBrains](https://www.jetbrains.com/?from=FSCrawler) for the Intelli

[![Jet Brains](../jetbrains.png)](https://www.jetbrains.com/?from=FSCrawler)

[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dadoonet/fscrawler)

```{contents}
:backlinks: entry
```
Expand All @@ -19,6 +21,62 @@ git clone git@github.com:dadoonet/fscrawler.git
cd fscrawler
```

## Dev Containers and GitHub Codespaces

You can develop FSCrawler in [VS Code](https://code.visualstudio.com/) /
[Cursor](https://cursor.com/) Dev Containers or in
[GitHub Codespaces](https://github.com/features/codespaces) using the shared
configuration under `.devcontainer/`.

### Prerequisites (local Dev Containers)

* Docker Desktop (or another Docker engine) on the host
* VS Code / Cursor with the Dev Containers extension

Open the repository and use **Reopen in Container**. For Codespaces, use the
badge above or open a codespace from the GitHub UI.

### What the container provides

* JDK 25 and Maven
* Docker-outside-of-Docker (required for Elastic [start-local](https://github.com/elastic/start-local))
* After create, `post-create.sh`:
* Starts Elasticsearch **9.x** (version from `<elasticsearch.version>` in the root `pom.xml`) with start-local **ES-only** under `IGNORE_ME/elastic-start-local/`
* Uses password `changeme` for the `elastic` user
* Warms the local Maven repository (`dependency:go-offline`)

Elasticsearch is then available at `http://localhost:9200`.

### Build and test inside the container

```shell
mvn clean package -DskipTests -Ddocker.skip
```

To run integration tests against the start-local cluster instead of Testcontainers:

```shell
source IGNORE_ME/elastic-start-local/.env
mvn verify -pl fr.pilato.elasticsearch.crawler:fscrawler-it \
-Dtests.cluster.url=http://localhost:9200 \
-Dtests.cluster.apiKey="$ES_LOCAL_API_KEY"
```

### Optional Kibana

By default only Elasticsearch is started (`--esonly`). To also run Kibana, re-run
start-local **without** `--esonly`, using the same directory, password, and version,
for example:

```shell
ES_VERSION="$(./.devcontainer/extract-es-version.sh)"
curl -fsSL https://elastic.co/start-local \
| ES_LOCAL_PASSWORD=changeme ES_LOCAL_DIR="$PWD/IGNORE_ME/elastic-start-local" \
sh -s -- -v "$ES_VERSION"
```

Kibana listens on `http://localhost:5601` when enabled.

## Build the artifact

To build the project, run:
Expand Down
Loading
Loading