From c9447d83a2434b87899a031893db793d7af5375f Mon Sep 17 00:00:00 2001 From: Yuan Huang Date: Mon, 25 May 2026 14:30:07 +0800 Subject: [PATCH 1/5] fix: add missing expires_at in global_tokens_test INSERT statements The global_token table (migration 00045) defines expires_at as NOT NULL, but the test was inserting rows without providing this column, causing NotNullViolation errors that cascaded into all 14 test failures. Add expires_at = NOW() + INTERVAL '30 days' to the three direct INSERT statements in global_tokens_test.py. --- infrabox/test/api/global_tokens_test.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/infrabox/test/api/global_tokens_test.py b/infrabox/test/api/global_tokens_test.py index 1f86232d..69a6b90d 100644 --- a/infrabox/test/api/global_tokens_test.py +++ b/infrabox/test/api/global_tokens_test.py @@ -41,8 +41,8 @@ def test_list_tokens_initially_empty(self): def test_list_tokens_only_returns_own_tokens(self): # Insert a token owned by another user TestClient.execute(""" - INSERT INTO global_token (id, description, scope_push, scope_pull, user_id) - VALUES (%s, 'other token', false, true, %s) + INSERT INTO global_token (id, description, scope_push, scope_pull, user_id, expires_at) + VALUES (%s, 'other token', false, true, %s, NOW() + INTERVAL '30 days') """, [str(uuid.uuid4()), self.other_user_id]) r = TestClient.get(self.URL, TestClient.get_user_authorization(self.user_id)) @@ -119,8 +119,8 @@ def test_delete_own_token_removes_from_db(self): def test_cannot_delete_other_users_token(self): other_token_id = str(uuid.uuid4()) TestClient.execute(""" - INSERT INTO global_token (id, description, scope_push, scope_pull, user_id) - VALUES (%s, 'not yours', false, true, %s) + INSERT INTO global_token (id, description, scope_push, scope_pull, user_id, expires_at) + VALUES (%s, 'not yours', false, true, %s, NOW() + INTERVAL '30 days') """, [other_token_id, self.other_user_id]) r = TestClient.delete(self.TOKEN_URL % other_token_id, @@ -180,8 +180,8 @@ def test_access_log_returns_entries(self): def test_access_log_enforces_ownership(self): other_token_id = str(uuid.uuid4()) TestClient.execute(""" - INSERT INTO global_token (id, description, scope_push, scope_pull, user_id) - VALUES (%s, 'other log token', false, true, %s) + INSERT INTO global_token (id, description, scope_push, scope_pull, user_id, expires_at) + VALUES (%s, 'other log token', false, true, %s, NOW() + INTERVAL '30 days') """, [other_token_id, self.other_user_id]) TestClient.execute(""" INSERT INTO global_token_access_log (token_id, path, method, status_code) @@ -196,4 +196,4 @@ def test_access_log_nonexistent_token_returns_404(self): fake_id = str(uuid.uuid4()) r = TestClient.get(self.ACCESS_LOG_URL % fake_id, headers=TestClient.get_user_authorization(self.user_id)) - self.assertEqual(r['status'], 404) + self.assertEqual(r['status'], 404) \ No newline at end of file From 34ade8ffdb8e5fcb63400fafaaf2141edfedd0e0 Mon Sep 17 00:00:00 2001 From: Yuan Huang Date: Mon, 25 May 2026 14:52:27 +0800 Subject: [PATCH 2/5] fix: upgrade dashboard build Dockerfile from node:8.9 to node:20 The package.json engines field requires node>=20.0.0 and npm>=10.0.0, but the CI Dockerfile was still using node:8.9-alpine. The build.js version check rejects the old runtime, causing the build to fail. --- infrabox/deploy/build-dashboard-client/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infrabox/deploy/build-dashboard-client/Dockerfile b/infrabox/deploy/build-dashboard-client/Dockerfile index 607b02cd..b543c86e 100644 --- a/infrabox/deploy/build-dashboard-client/Dockerfile +++ b/infrabox/deploy/build-dashboard-client/Dockerfile @@ -1,3 +1,3 @@ -FROM node:8.9-alpine +FROM node:20-alpine -CMD /infrabox/context/src/dashboard-client/build.sh +CMD /infrabox/context/src/dashboard-client/build.sh \ No newline at end of file From 215ed8e4bc79467c762c157b4772e43876422ab0 Mon Sep 17 00:00:00 2001 From: Yuan Huang Date: Tue, 26 May 2026 14:15:57 +0800 Subject: [PATCH 3/5] perf: replace cp -r with tar pipe for node_modules cache The cp -r of ~50,000 small files in node_modules was causing the CI job to timeout (1 hour). Using tar pipe for sequential bulk I/O reduces the copy time from minutes to seconds. --- src/dashboard-client/build.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dashboard-client/build.sh b/src/dashboard-client/build.sh index 646f7dc6..da813645 100755 --- a/src/dashboard-client/build.sh +++ b/src/dashboard-client/build.sh @@ -3,7 +3,7 @@ cp -r /infrabox/context/src/dashboard-client /dashboard echo "## Link cache" mkdir -p /infrabox/cache/node_modules -cp -r /infrabox/cache/node_modules /dashboard +tar -C /infrabox/cache -cf - node_modules | tar -C /dashboard -xf - cd /dashboard @@ -17,9 +17,9 @@ npm run build echo "## Copy to cache" rm -rf /infrabox/cache/node_modules -cp -r /dashboard/node_modules /infrabox/cache +tar -C /dashboard -cf - node_modules | tar -C /infrabox/cache -xf - echo "## Copy to output" cp -r /dashboard/dist /infrabox/output -echo "## done" +echo "## done" \ No newline at end of file From e0c29f2ee5c3bfc01f53b363082349f3b3ffa3f0 Mon Sep 17 00:00:00 2001 From: Yuan Huang Date: Tue, 26 May 2026 16:14:09 +0800 Subject: [PATCH 4/5] fix: remove node_modules from /infrabox/cache to prevent timeout The real bottleneck was not cp -r in build.sh but job.py's post-build step that compresses and uploads /infrabox/cache via snappy+tar to the API server. With node:20's much larger node_modules (~200MB+), this compression/upload exceeds the 1-hour job timeout. Solution: stop writing node_modules back to /infrabox/cache. Instead, use mv to restore cached node_modules at the start (fast), and don't write it back (npm install with warm cache only takes ~20s anyway). This eliminates the expensive cache upload entirely. --- src/dashboard-client/build.sh | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/dashboard-client/build.sh b/src/dashboard-client/build.sh index da813645..27b8245d 100755 --- a/src/dashboard-client/build.sh +++ b/src/dashboard-client/build.sh @@ -2,8 +2,11 @@ cp -r /infrabox/context/src/dashboard-client /dashboard echo "## Link cache" -mkdir -p /infrabox/cache/node_modules -tar -C /infrabox/cache -cf - node_modules | tar -C /dashboard -xf - +if [ -d /infrabox/cache/node_modules ]; then + mv /infrabox/cache/node_modules /dashboard/node_modules +else + mkdir -p /dashboard/node_modules +fi cd /dashboard @@ -15,10 +18,6 @@ npm install echo "## build" npm run build -echo "## Copy to cache" -rm -rf /infrabox/cache/node_modules -tar -C /dashboard -cf - node_modules | tar -C /infrabox/cache -xf - - echo "## Copy to output" cp -r /dashboard/dist /infrabox/output From 448007819bb5fc2a91c24552c688673eafda7d1c Mon Sep 17 00:00:00 2001 From: Yuan Huang Date: Wed, 27 May 2026 10:13:44 +0800 Subject: [PATCH 5/5] fix: force process.exit(0) after webpack build to prevent Node.js 20 hang webpack 3.12.0 leaves open handles (internal timers/fs watchers) under Node.js 20, causing the process to never exit naturally after a successful build. The container hangs, docker run waits forever, and the InfraBox job hits the 1-hour timeout. The failure path already calls process.exit(1) explicitly; mirror that for the success path with process.exit(0). Root cause introduced by 34ade8ff (node:8.9 -> node:20-alpine upgrade). --- src/dashboard-client/build/build.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dashboard-client/build/build.js b/src/dashboard-client/build/build.js index 30f036a1..ac27aeef 100644 --- a/src/dashboard-client/build/build.js +++ b/src/dashboard-client/build/build.js @@ -37,5 +37,6 @@ rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { ' Tip: built files are meant to be served over an HTTP server.\n' + ' Opening index.html over file:// won\'t work.\n' )) + process.exit(0) }) })