Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 142 additions & 27 deletions .github/workflows/docker-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,118 @@

name: Docker release - tika-server and tika-grpc

# Auto-trigger on tag push for GA-style version tags only. The convention is:
# release:prepare creates `X.Y.Z-rcN` for the vote (workflow stays silent),
# vote passes, the release manager pushes a separate `X.Y.Z` tag pointing
# at the same commit, and *that* push triggers this workflow.
# Manual rebuilds (CVE in base image, plugin refresh) use workflow_dispatch
# with an explicit build_number.
#
# GH Actions doesn't allow combining `tags` (include) and `tags-ignore` on
# a single event, so the filter is expressed as `tags-ignore` only. Any tag
# without a hyphen or underscore fires the workflow; this rejects prerelease
# tags (`4.0.0-rc1`, `4.0.0-alpha-1`, `4.0.0-BETA`) and branch-style tags
# (`branch_4x`). The `Compute tags` step has a separate prerelease check
# (`*-*`) that gates `:latest` defense-in-depth for the workflow_dispatch
# path where the auto-trigger filter isn't in play.
on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+*'
tags-ignore:
- '*-*' # anything with a hyphen is a prerelease/non-GA tag
- '*_*' # anything with an underscore is a branch/non-version tag
workflow_dispatch:
inputs:
tag:
description: 'Tika release tag (e.g. 4.0.0-alpha-1). Must already exist as a git tag.'
required: true
build_number:
description: 'Docker build number for this Tika tag (1 for first build, increment on rebuilds).'
required: true
default: '1'
source_ref:
description: 'Git ref to build from. Defaults to `tag`. Override only for Dockerfile-update rebuilds where the source has changed since the original tag was cut.'
required: false

# Resolve the effective tag and build number from either trigger source.
# `inputs.*` is populated only by workflow_dispatch; on a tag push, fall
# back to the tag's short name (e.g. `4.0.0`) and build_number=1.
env:
TAG: ${{ inputs.tag || github.ref_name }}
BUILD: ${{ inputs.build_number || '1' }}

jobs:
# Gating job for push triggers: refuse to publish if the tag isn't shaped
# like a GA version (digit+ . digit+ . digit+). The `tags-ignore` filter at
# the `on:` level already blocks anything with `-` or `_`, but it doesn't
# reject other oddities like `wip`, `foo`, or `test`. This job is the
# belt-and-suspenders to those (which were the suspenders).
#
# workflow_dispatch trigger is permissive — humans pick the tag (which can
# be alpha/beta/RC) — so the strict check is push-only.
validate-tag:
runs-on: ubuntu-latest
steps:
- name: Reject non-GA-style tags on push triggers
run: |
if [[ "${{ github.event_name }}" != "push" ]]; then
echo "workflow_dispatch trigger — skipping strict tag-shape validation."
exit 0
fi
tag='${{ github.ref_name }}'
if [[ ! "$tag" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error title=Non-GA tag::Refusing to publish: tag '$tag' is not a GA-style X.Y.Z."
echo "::error::For prerelease publishes (alpha/BETA/RC), use workflow_dispatch with an explicit tag and build_number."
exit 1
fi
echo "Tag '$tag' is GA-style. Proceeding."

release-tika-server:
needs: validate-tag
runs-on: ubuntu-latest
timeout-minutes: 60

steps:
- uses: actions/checkout@v6
with:
ref: ${{ inputs.source_ref || env.TAG }}
fetch-depth: 0 # full history so we can push a provenance tag at the end

- name: Extract version from tag
id: version
# Compute the tag set for each image. Three tags per image at minimum:
# apache/tika:<tag> (mutable; moves on each rebuild)
# apache/tika:<tag>-<build> (immutable; one per rebuild)
# apache/tika:latest (only for non-prerelease tags)
# The grpc image always pushes :latest (no 3.x incumbent to protect).
- name: Compute tags
id: tags
run: |
TAG_NAME="${GITHUB_REF#refs/tags/}"
echo "tag=${TAG_NAME}" >> "$GITHUB_OUTPUT"
tag='${{ env.TAG }}'
build='${{ env.BUILD }}'
minimal="apache/tika:${tag}
apache/tika:${tag}-${build}"
full="apache/tika:${tag}-full
apache/tika:${tag}-${build}-full"
grpc="apache/tika-grpc:${tag}
apache/tika-grpc:${tag}-${build}
apache/tika-grpc:latest"
# Any hyphen in the tag = prerelease (alpha/beta/rc/SNAPSHOT/etc.,
# in any case). Mirrors the `tags-ignore: ['*-*']` rule on the
# auto-trigger so manual workflow_dispatch behaves the same way.
case "$tag" in
*-*)
echo "Prerelease tag $tag — skipping :latest for apache/tika."
;;
*)
minimal="${minimal}
apache/tika:latest"
full="${full}
apache/tika:latest-full"
;;
esac
{
echo "minimal<<EOF"; echo "$minimal"; echo "EOF"
echo "full<<EOF"; echo "$full"; echo "EOF"
echo "grpc<<EOF"; echo "$grpc"; echo "EOF"
} >> "$GITHUB_OUTPUT"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
Expand All @@ -52,38 +146,45 @@ jobs:
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
file: tika-server/docker-build/minimal/Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/s390x
platforms: linux/amd64,linux/arm64,linux/s390x
push: true
build-args: |
TIKA_VERSION=${{ steps.version.outputs.tag }}
tags: |
apache/tika:${{ steps.version.outputs.tag }}
apache/tika:latest
TIKA_VERSION=${{ env.TAG }}
tags: ${{ steps.tags.outputs.minimal }}

- name: Build and push tika-server full
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
file: tika-server/docker-build/full/Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/s390x
platforms: linux/amd64,linux/arm64,linux/s390x
push: true
build-args: |
TIKA_VERSION=${{ steps.version.outputs.tag }}
tags: |
apache/tika:${{ steps.version.outputs.tag }}-full
apache/tika:latest-full
TIKA_VERSION=${{ env.TAG }}
tags: ${{ steps.tags.outputs.full }}

# After a successful publish, push a `<tag>-<build_number>` git tag for
# provenance. Skipped on build_number=1 because the original `<tag>` already
# marks the source state of build 1. Lives in the server job (not the grpc
# job) to avoid both jobs racing to push the same tag.
- name: Push provenance git tag
if: ${{ env.BUILD != '1' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag "${TAG}-${BUILD}"
git push origin "${TAG}-${BUILD}"

release-tika-grpc:
needs: validate-tag
runs-on: ubuntu-latest
timeout-minutes: 120

steps:
- uses: actions/checkout@v6

- name: Extract version from tag
id: version
run: |
TAG_NAME="${GITHUB_REF#refs/tags/}"
echo "tag=${TAG_NAME}" >> "$GITHUB_OUTPUT"
with:
ref: ${{ inputs.source_ref || env.TAG }}

- name: Set up JDK 17
uses: actions/setup-java@v5
Expand All @@ -107,9 +208,22 @@ jobs:
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Compute grpc tags
id: grpc_tags
run: |
tag='${{ env.TAG }}'
build='${{ env.BUILD }}'
{
echo "tags<<EOF"
echo "apache/tika-grpc:${tag}"
echo "apache/tika-grpc:${tag}-${build}"
echo "apache/tika-grpc:latest"
echo "EOF"
} >> "$GITHUB_OUTPUT"

- name: Prepare tika-grpc Docker build context
run: |
TIKA_VERSION="${{ steps.version.outputs.tag }}"
TIKA_VERSION='${{ env.TAG }}'
OUT_DIR=target/tika-grpc-docker

mkdir -p "${OUT_DIR}/libs/tika-grpc" "${OUT_DIR}/plugins" "${OUT_DIR}/config" "${OUT_DIR}/bin"
Expand Down Expand Up @@ -151,7 +265,8 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
build-args: |
VERSION=${{ steps.version.outputs.tag }}
tags: |
apache/tika-grpc:${{ steps.version.outputs.tag }}
apache/tika-grpc:latest
VERSION=${{ env.TAG }}
# apache/tika-grpc is new in 4.x with no prior `:latest` to protect, so
# we track latest from the start (unlike apache/tika the server image,
# whose :latest stays on 3.x until 4.0.0 GA).
tags: ${{ steps.grpc_tags.outputs.tags }}
4 changes: 2 additions & 2 deletions .github/workflows/docker-snapshot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ jobs:
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
context: target/tika-server-minimal-docker
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/s390x
platforms: linux/amd64,linux/arm64,linux/s390x
push: true
build-args: |
TIKA_VERSION=${{ steps.version.outputs.tika_version }}
Expand Down Expand Up @@ -157,7 +157,7 @@ jobs:
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
context: target/tika-server-full-docker
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/s390x
platforms: linux/amd64,linux/arm64,linux/s390x
push: true
build-args: |
TIKA_VERSION=${{ steps.version.outputs.tika_version }}
Expand Down
1 change: 1 addition & 0 deletions docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
** xref:advanced/spooling.adoc[Spooling]
** xref:advanced/embedded-documents.adoc[Embedded Document Metadata]
** xref:advanced/local-vlm-server.adoc[Running a Local VLM Server]
** xref:advanced/integration-testing/run-uat-script.adoc[Tika-Server REST UAT Script]
* xref:developers/index.adoc[Developers]
** xref:developers/serialization.adoc[Serialization and Configuration]
* xref:faq.adoc[FAQ]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

= Tika-Server REST UAT Script

A portable shell script that exercises the tika-server REST surface against an
already-running server. The same script is used as the docker image smoke
test, the e2e integration test, and as part of the source-release
verification.

== Where it lives

[source]
----
release-tools/uat/
├── run-uat.sh # the script
└── test-files/
├── testPDF.pdf
├── testHTML.html
└── test_recursive_embedded.docx
----

== What it covers

Roughly 25 REST endpoint checks across the default-mode endpoints, header
behavior, and error handling — the same surface enumerated in the manual
walkthrough at xref:advanced/integration-testing/tika-server.adoc[Tika-Server
Integration Testing], translated to bash + curl assertions.

Coverage includes:

* `/version`, `/parsers`, `/detectors`, `/mime-types` (introspection)
* `/detect/stream` (mime detection)
* `/tika`, `/tika/text`, `/tika/xml`, `/tika/json` (parse)
* `/meta`, `/meta/{field}` (metadata)
* `/rmeta`, `/rmeta/text` (recursive metadata)
* `/unpack/all` (embedded extraction; verifies the response is a valid zip)
* `/language/stream`
* `/meta/form`, `/rmeta/form` (multipart variants)
* `enableUnsecureFeatures=false` gating: `/meta/config`, `/rmeta/config`,
`/tika/config` all return 403
* `X-Tika-OCRskipOcr` header, `Content-Disposition` filename
* 404 / 405 error handling

Two checks (T18d, T27) are currently disabled with inline comments pointing
at tika-core behavior anomalies that need fixing — re-enable them when those
land.

== Running it

The script takes a URL pointing at a running tika-server. It does *not* start
or stop the server itself.

[source,bash]
----
release-tools/uat/run-uat.sh [host]
# default host: http://localhost:9998
----

Exit code: `0` on all-pass, `1` on any failure. Failed checks print the
expected pattern and a truncated response body.

=== Against the unpacked bin.zip distribution

[source,bash]
----
unzip tika-server-standard-<VERSION>-bin.zip -d /tmp/tika-server-dist
cd /tmp/tika-server-dist
java -jar tika-server.jar -p 9998 -h localhost &
sleep 12
~/path/to/tika/release-tools/uat/run-uat.sh
----

=== Against the Docker image

The `docker-tool.sh test-uat` subcommand wraps starting the container, waiting
for `/version`, running the UAT, and stopping the container:

[source,bash]
----
cd tika-server/docker-build
./docker-tool.sh test-uat <DOCKER_VERSION>
----

=== As part of the e2e tests (CI)

The Maven module `tika-e2e-tests/tika-server` unpacks the bin.zip, forks
`java -jar tika-server.jar`, and invokes this script via
`org.apache.tika.server.e2e.RunUatSmokeTest`. The CI workflow
`.github/workflows/main-jdk17-build.yml` runs this automatically on every PR
via `mvn -pl tika-e2e-tests -am clean verify -Pe2e`.

== When to use it

* *Pre-vote release verification.* Unpack
`tika-server-standard-<VERSION>-bin.zip` from `dist/dev` and run the UAT
against it. Catches packaging regressions before the vote thread starts.
* *Pre-publish docker verification.* Run via `docker-tool.sh test-uat` after
building a new image and before tagging it for release.
* *Local development sanity check.* When changing anything in
`tika-server-core` or the bin.zip assembly descriptor, run the UAT against
the build output to confirm you didn't regress endpoint behavior.
* *Adding new endpoints.* When a new REST endpoint lands, add a corresponding
check to the script so future regressions get caught.

== Platform notes

The script is bash + curl + unzip. It's skipped automatically on Windows by
the e2e test (no bash). On Linux/macOS it runs as-is. No external dependencies
beyond the standard tooling.
Loading
Loading