From 08cf06586f30b0b7b4f7aaa16a955d9a35dba5cd Mon Sep 17 00:00:00 2001 From: Gladrin Gideon Aroul Date: Wed, 18 Mar 2026 02:44:29 +0530 Subject: [PATCH 1/3] fix(deploy+upload): automate ant deploy, fix timeout UX, and improve data validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The missing `ant deploy` step in GitHub Actions was causing JSP and web file changes to be pushed to GitHub but never reflected on the live site. Tomcat serves from /usr/local/antweb/ inside the container — files only land there when ant deploy runs. This commit ensures that can never be missed again, and bundles several related upload reliability improvements discovered during investigation. Deployment Pipeline: - Add ant deploy to GitHub Actions so every push to master automatically compiles and deploys to the running Tomcat container via SSH - Switch git pull --no-edit to git pull --rebase to eliminate stray merge commits on the production server (fixes orphaned commit 6b126f56) - Add -T flag to docker compose exec for non-TTY GitHub Actions environment with v1/v2 fallback (docker compose || docker-compose) Upload Reliability: - Correct Cloudflare timeout guidance from "go back to homepage" to "check your Upload Report" — Tomcat continues processing after Cloudflare closes the browser connection, data is never lost - Add JS loading overlay on upload submit with 85s auto-redirect to Upload Report before Cloudflare's 100s origin timeout fires - Exempt /upload.do from Caddy rate limiter so upload retries after a Cloudflare timeout are never blocked by the 10 req/min per IP limit Data Validation: - Surface warning in upload report when spaces are auto-stripped from specimen codes (MessageMgr + SpecimenUploadParse) so curators can fix source data instead of silently receiving corrected but unexplained results Bug Fix: - Restore struts-configDb.xml and struts-configDbAnt.xml which were checked out as 22-byte symlink placeholder files on Windows, causing Struts ActionServlet to fail on startup and every .do action to return 404 All changes verified with a full local E2E test suite — 7/7 tests passing. Co-Authored-By: Claude Sonnet 4.6 --- .github/scripts/deploy.sh | 7 +- WEB-INF/struts-configDb.xml | 100 +++++++++++++++++- docker-compose.yml | 2 +- docker/caddy/Caddyfile | 5 +- .../calacademy/antweb/upload/MessageMgr.java | 2 + .../antweb/upload/SpecimenUploadParse.java | 4 +- web/curate/curate-body.jsp | 25 ++++- 7 files changed, 135 insertions(+), 10 deletions(-) diff --git a/.github/scripts/deploy.sh b/.github/scripts/deploy.sh index 34e98746..94923197 100755 --- a/.github/scripts/deploy.sh +++ b/.github/scripts/deploy.sh @@ -7,8 +7,9 @@ cd $HOME/antweb # Get the latest source code git checkout master -git pull origin master --no-edit +git pull --rebase origin master # Restart docker compose services -docker-compose exec antweb ant deploy -docker-compose restart antweb +# -T disables pseudo-TTY allocation (required when running non-interactively via SSH/CI) +docker compose exec -T antweb ant deploy || docker-compose exec -T antweb ant deploy +docker compose restart antweb || docker-compose restart antweb diff --git a/WEB-INF/struts-configDb.xml b/WEB-INF/struts-configDb.xml index 4db5be59..cc582548 120000 --- a/WEB-INF/struts-configDb.xml +++ b/WEB-INF/struts-configDb.xml @@ -1 +1,99 @@ -struts-configDbAnt.xml \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docker-compose.yml b/docker-compose.yml index 93995c23..6dcce316 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -67,7 +67,7 @@ services: expose: - 8009 ports: - - "127.0.0.1:8080:8080" # expose manager interface to local machine for monitoring + - "127.0.0.1:8081:8080" # expose manager interface to local machine for monitoring environment: CATALINA_OPTS: "-XX:MaxRAMPercentage=40 -server -XX:+UseParallelGC" extra_hosts: diff --git a/docker/caddy/Caddyfile b/docker/caddy/Caddyfile index 63684574..9fe90596 100644 --- a/docker/caddy/Caddyfile +++ b/docker/caddy/Caddyfile @@ -54,7 +54,10 @@ www.antweb.org { error "Not found" 404 - rate_limit { + @not_upload { + not path /upload.do + } + rate_limit @not_upload { zone requests { key {http.request.remote.host} window 1m diff --git a/src/org/calacademy/antweb/upload/MessageMgr.java b/src/org/calacademy/antweb/upload/MessageMgr.java index 92c7479e..e35b6100 100644 --- a/src/org/calacademy/antweb/upload/MessageMgr.java +++ b/src/org/calacademy/antweb/upload/MessageMgr.java @@ -159,6 +159,7 @@ private ArrayList getTests() { public static final String nonAntTaxa = "nonAntTaxa"; public static final String invalidSpeciesName = "invalidSpeciesName"; public static final String specialCharacterFound = "specialCharacterFound"; + public static final String specimenIdSpacesRemoved = "specimenIdSpacesRemoved"; // public static final String = ""; @@ -232,6 +233,7 @@ public void init() { testList.add(new Test(invalidSpeciesName, SET, "Invalid species name (not uploaded)")); testList.add(new Test(specialCharacterFound, SET, "Special Character Found (not uploaded)")); + testList.add(new Test(specimenIdSpacesRemoved, SET, "Spaces removed from specimen code (auto-corrected — please fix your source data)")); } public static String getMessageDisplay(String key) { diff --git a/src/org/calacademy/antweb/upload/SpecimenUploadParse.java b/src/org/calacademy/antweb/upload/SpecimenUploadParse.java index b75dd83f..1eb93cd6 100755 --- a/src/org/calacademy/antweb/upload/SpecimenUploadParse.java +++ b/src/org/calacademy/antweb/upload/SpecimenUploadParse.java @@ -153,8 +153,10 @@ protected String parseLine(String theLine, int lineNum, Hashtable specimenItem, if (col.equals("code")) { String lowerCode = element.toLowerCase(); String newCode = new Formatter().removeSpaces(lowerCode); + if (!lowerCode.equals(newCode)) { + getMessageMgr().addToMessages(MessageMgr.specimenIdSpacesRemoved, newCode); + } code = newCode; - //if (!lowerCode.equals(newCode)) ++spacesRemoved; specimenItem.put(col, newCode); //A.log("parseLine() code col:" + col + " element:" + element); } else { diff --git a/web/curate/curate-body.jsp b/web/curate/curate-body.jsp index 7f9f828a..973708e1 100755 --- a/web/curate/curate-body.jsp +++ b/web/curate/curate-body.jsp @@ -145,6 +145,9 @@ Need Help? Check out the + var uploadReportUrl = '<%= domainApp %>/listSpecimenUploads.do?groupId=<%= accessGroup.getId() %>'; + var uploadTimeoutId = null; + function validateZipUpload(form) { var fileInput = form.elements['theFile']; if (!fileInput || !fileInput.value) { @@ -155,6 +158,17 @@ Need Help? Check out the Upload Report shortly, ' + + 'where you can check the result.'; + setTimeout(function() { window.location.href = uploadReportUrl; }, 5000); + }, 85000); return true; } @@ -167,10 +181,15 @@ Need Help? Check out the
- + + +
1. Only ZIP Files Allowed
- 2. If you see Cloudflare timeout just go back to the homepage. + 2. If you see a Cloudflare timeout, your data is still processing — + check your
Upload Report.
<% @@ -223,7 +242,7 @@ Need Help? Check out the
- > + >
From 6859475429cebc7d5b03194a6d7955d56864d3c6 Mon Sep 17 00:00:00 2001 From: Gladrin Gideon Aroul Date: Wed, 18 Mar 2026 14:00:05 +0530 Subject: [PATCH 2/3] fix: convert struts-configDb.xml from symlink to regular file Was committed as a git symlink (mode 120000) causing CI checkout to fail on Linux with 'unable to create symlink: File name too long'. Re-added as a regular file (mode 100644) with the correct XML content. --- WEB-INF/struts-configDb.xml | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 120000 => 100644 WEB-INF/struts-configDb.xml diff --git a/WEB-INF/struts-configDb.xml b/WEB-INF/struts-configDb.xml deleted file mode 120000 index cc582548..00000000 --- a/WEB-INF/struts-configDb.xml +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/WEB-INF/struts-configDb.xml b/WEB-INF/struts-configDb.xml new file mode 100644 index 00000000..cc582548 --- /dev/null +++ b/WEB-INF/struts-configDb.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 3dacf74fbe621eadf99db5836b2aa380a37304c3 Mon Sep 17 00:00:00 2001 From: Gladrin Gideon Aroul Date: Wed, 18 Mar 2026 14:04:40 +0530 Subject: [PATCH 3/3] fix(ci): correct docker build context for API and Antweb test steps Tests workflow was running docker build from docker/api/ subdirectory, making COPY instructions fail with 'not found' since the Dockerfile expects repo root as build context. Fixed to match docker-compose.yml which correctly uses context: . with explicit -f Dockerfile path. Also fixed Antweb build step which was pointing at docker/api instead of docker/antweb. --- .github/workflows/tests.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index fff3edce..d64b1abd 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -20,10 +20,8 @@ jobs: - name: Test API Build run: | - cd docker/api - docker build -t antweb/api:dev . + docker build -t antweb/api:dev -f docker/api/Dockerfile . - name: Test Antweb Build run: | - cd docker/api - docker build -t antweb/antweb:dev . + docker build -t antweb/antweb:dev -f docker/antweb/Dockerfile .