diff --git a/.github/workflows/dev-branch-images.yml b/.github/workflows/dev-branch-images.yml index b7ad470314..9d40cd3fc4 100644 --- a/.github/workflows/dev-branch-images.yml +++ b/.github/workflows/dev-branch-images.yml @@ -10,9 +10,14 @@ on: - 'client/**' - 'packages/**' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: runs-on: ubuntu-latest + timeout-minutes: 130 strategy: matrix: include: @@ -69,4 +74,4 @@ jobs: ${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.image_name }}:${{ github.sha }} ${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.image_name }}:latest platforms: linux/amd64,linux/arm64 - target: ${{ matrix.target }} \ No newline at end of file + target: ${{ matrix.target }} diff --git a/.github/workflows/dev-images.yml b/.github/workflows/dev-images.yml index 41d427c6c8..a6417556aa 100644 --- a/.github/workflows/dev-images.yml +++ b/.github/workflows/dev-images.yml @@ -13,6 +13,7 @@ on: jobs: build: runs-on: ubuntu-latest + timeout-minutes: 130 strategy: matrix: include: diff --git a/Dockerfile b/Dockerfile index 8fd104b5a5..ce3ec093d7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,6 +16,8 @@ RUN uv --version # Set configurable max-old-space-size with default ARG NODE_MAX_OLD_SPACE_SIZE=6144 +ARG NPM_CI_TIMEOUT_SECONDS=1500 +ARG NPM_CI_ATTEMPTS=2 RUN mkdir -p /app && chown node:node /app WORKDIR /app @@ -37,7 +39,17 @@ RUN \ npm config set fetch-retry-maxtimeout 600000 ; \ npm config set fetch-retries 5 ; \ npm config set fetch-retry-mintimeout 15000 ; \ - npm ci --no-audit + attempt=1 ; \ + until timeout "$NPM_CI_TIMEOUT_SECONDS" npm ci --no-audit ; do \ + status=$? ; \ + if [ "$attempt" -ge "$NPM_CI_ATTEMPTS" ]; then \ + exit "$status" ; \ + fi ; \ + echo "npm ci --no-audit failed with exit code $status; retrying attempt $((attempt + 1))/$NPM_CI_ATTEMPTS" ; \ + attempt=$((attempt + 1)) ; \ + npm cache clean --force || true ; \ + sleep 10 ; \ + done COPY --chown=node:node . . diff --git a/Dockerfile.multi b/Dockerfile.multi index 778a9dba9f..a53a16646a 100644 --- a/Dockerfile.multi +++ b/Dockerfile.multi @@ -6,6 +6,8 @@ ARG NODE_MAX_OLD_SPACE_SIZE=6144 # Base for all builds FROM node:20-alpine AS base-min +ARG NPM_CI_TIMEOUT_SECONDS=1500 +ARG NPM_CI_ATTEMPTS=2 RUN apk upgrade --no-cache RUN apk add --no-cache jemalloc # Set environment variable to use jemalloc @@ -26,8 +28,20 @@ COPY api/package*.json ./api/ # Install all dependencies for every build FROM base-min AS base +ARG NPM_CI_TIMEOUT_SECONDS=1500 +ARG NPM_CI_ATTEMPTS=2 WORKDIR /app -RUN npm ci +RUN attempt=1; \ + until timeout "$NPM_CI_TIMEOUT_SECONDS" npm ci; do \ + status=$?; \ + if [ "$attempt" -ge "$NPM_CI_ATTEMPTS" ]; then \ + exit "$status"; \ + fi; \ + echo "npm ci failed with exit code $status; retrying attempt $((attempt + 1))/$NPM_CI_ATTEMPTS"; \ + attempt=$((attempt + 1)); \ + npm cache clean --force || true; \ + sleep 10; \ + done # Build `data-provider` package FROM base AS data-provider-build @@ -69,12 +83,24 @@ RUN npm run build # API setup (including client dist) FROM base-min AS api-build +ARG NPM_CI_TIMEOUT_SECONDS=1500 +ARG NPM_CI_ATTEMPTS=2 # Add `uv` for extended MCP support COPY --from=ghcr.io/astral-sh/uv:0.6.13 /uv /uvx /bin/ RUN uv --version WORKDIR /app # Install only production deps -RUN npm ci --omit=dev +RUN attempt=1; \ + until timeout "$NPM_CI_TIMEOUT_SECONDS" npm ci --omit=dev; do \ + status=$?; \ + if [ "$attempt" -ge "$NPM_CI_ATTEMPTS" ]; then \ + exit "$status"; \ + fi; \ + echo "npm ci --omit=dev failed with exit code $status; retrying attempt $((attempt + 1))/$NPM_CI_ATTEMPTS"; \ + attempt=$((attempt + 1)); \ + npm cache clean --force || true; \ + sleep 10; \ + done COPY api ./api COPY config ./config COPY --from=data-provider-build /app/packages/data-provider/dist ./packages/data-provider/dist