diff --git a/.github/workflows/npm-test.yml b/.github/workflows/npm-test.yml index a82f498dd3..ca4a003b70 100644 --- a/.github/workflows/npm-test.yml +++ b/.github/workflows/npm-test.yml @@ -8,38 +8,69 @@ on: jobs: build: runs-on: ubuntu-20.04 + + strategy: + + matrix: + node-version: + - '16.15.0' # prior pinned Node version supported by kpi + - '20.17.0' # version pinned for kpi release + - '20' # latest available v20 + - '22' # latest available v22 + fail-fast: false # Let each job finish + steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - # We need this particular version, as npm 8.5.5 is the last version - # that works with our package.json :sadface:. - node-version: '16.15.0' + node-version: ${{ matrix.node-version }} + check-latest: true # download newer semver match if available cache: 'npm' + + - name: Identify resolved node version + id: resolved-node-version + run: echo "NODE_VERSION=$(node --version)" >> "$GITHUB_OUTPUT" + - name: Add "Node ${{ steps.resolved-node-version.outputs.NODE_VERSION }}" to summary + run: echo "${{ matrix.node-version }} → **${{ steps.resolved-node-version.outputs.NODE_VERSION }}**" >> "$GITHUB_STEP_SUMMARY" + + # Set up Chrome, for the unit tests - uses: browser-actions/setup-chrome@latest - run: chrome --version - - name: Check for cached node_modules + + # Cache node_modules, keyed on os, node version, package-lock, and patches + - uses: actions/cache@v4 + name: Check for cached node_modules id: cache-nodemodules - uses: actions/cache@v3 env: cache-name: cache-node-modules with: path: node_modules - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json', 'patches/**/*.patch') }} - - name: Install JavaScript dependencies (npm install) - if: steps.cache-nodemodules.outputs.cache-hit != 'true' - run: npm install - - name: Run copy-fonts (if using cached node_modules) - if: steps.cache-nodemodules.outputs.cache-hit == 'true' + key: ${{ runner.os }}-build-${{ env.cache-name }}-node-v${{ steps.resolved-node-version.outputs.NODE_VERSION }}-${{ hashFiles('**/package-lock.json', 'patches/**/*.patch') }} + + # Cache hit: node_modules is copied from a previous run. Run copy-fonts + - if: steps.cache-nodemodules.outputs.cache-hit == 'true' + name: Run copy-fonts (if using cached node_modules) run: npm run copy-fonts + + # Cache miss: Run npm install, which does copy-fonts as post-install step + - if: steps.cache-nodemodules.outputs.cache-hit != 'true' + name: Install JavaScript dependencies (npm install) + run: npm install + + # Build the app! - name: Build Prod run: SKIP_TS_CHECK=true npm run build + + # Run TypeScript Checks and ESLint - name: Check TypeScript # Separated for visibility run: npm run check-types - name: Check ESLint, errors only run: npm run lint -- --quiet + + # Unit Tests - name: Build Tests run: npx webpack --config webpack/test.config.js + - name: Run Tests, with mocha-chrome run: npx mocha-chrome test/tests.html --chrome-launcher.connectionPollInterval=5000 # This step takes less than 1 minute if it succeeds, but will hang for diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 5bc93e6559..54034ca43f 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -38,9 +38,9 @@ jobs: ports: - 6379:6379 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install pip-tools diff --git a/.node-version b/.node-version index 6f0223c1c0..016e34baf1 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -v16.15.0 \ No newline at end of file +v20.17.0 diff --git a/.nvmrc b/.nvmrc index 6f0223c1c0..016e34baf1 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16.15.0 \ No newline at end of file +v20.17.0 diff --git a/Dockerfile b/Dockerfile index 92fa432353..95392965e9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.10 as build-python +FROM python:3.10 AS build-python ENV VIRTUAL_ENV=/opt/venv \ TMP_DIR=/srv/tmp @@ -10,7 +10,7 @@ COPY ./dependencies/pip/requirements.txt "${TMP_DIR}/pip_dependencies.txt" RUN pip-sync "${TMP_DIR}/pip_dependencies.txt" 1>/dev/null -from python:3.10-slim +FROM python:3.10-slim ENV DEBIAN_FRONTEND=noninteractive ENV LANG=en_US.UTF-8 @@ -48,14 +48,8 @@ RUN mkdir -p "${NGINX_STATIC_DIR}" && \ # jnm (or the current on-call sysadmin). Thanks. RUN apt-get -qq update && \ - apt-get -qq -y install ca-certificates curl gnupg && \ - mkdir -p /etc/apt/keyrings && \ - curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \ - | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ - echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_16.x nodistro main" \ - | tee /etc/apt/sources.list.d/nodesource.list && \ - apt-get -qq update && \ - apt-get -qq -y install openjdk-17-jre && \ + apt-get -qq -y install curl && \ + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \ apt-get -qq -y install --no-install-recommends \ ffmpeg \ gdal-bin \ @@ -65,7 +59,9 @@ RUN apt-get -qq update && \ less \ libproj-dev \ locales \ - nodejs=$(apt-cache show nodejs | grep -F 'Version: 16.15.0' | cut -f 2 -d ' ') \ + # pin an exact Node version for stability. update this regularly. + nodejs=$(apt-cache show nodejs | grep -F 'Version: 20.17.0' | cut -f 2 -d ' ') \ + openjdk-17-jre \ postgresql-client \ procps \ rsync \ @@ -109,14 +105,14 @@ WORKDIR ${KPI_SRC_DIR}/ RUN rm -rf ${KPI_NODE_PATH} && \ mkdir -p "${TMP_DIR}/.npm" && \ npm config set cache "${TMP_DIR}/.npm" --global && \ - npm install -g npm@8.5.5 && \ + # to pin an exact npm version, see 'git blame' here npm install -g check-dependencies@1 && \ rm -rf "${KPI_SRC_DIR}/jsapp/fonts" && \ rm -rf "${KPI_SRC_DIR}/jsapp/compiled" && \ npm install --quiet && \ npm cache clean --force -ENV PATH $PATH:${KPI_NODE_PATH}/.bin +ENV PATH=$PATH:${KPI_NODE_PATH}/.bin ###################### # Build client code. # diff --git a/dependencies/pip/dev_requirements.txt b/dependencies/pip/dev_requirements.txt index a0d327ba18..c46972c3c4 100644 --- a/dependencies/pip/dev_requirements.txt +++ b/dependencies/pip/dev_requirements.txt @@ -4,7 +4,7 @@ # # pip-compile dependencies/pip/dev_requirements.in # --e git+https://github.com/kobotoolbox/django-digest@43f3100a7e257942c313ad79057e6a0b1612a74a#egg=django_digest +-e git+https://github.com/kobotoolbox/django-digest@3995226ed8e5bd1cb32c640aae970f8c104f6156#egg=django_digest # via -r dependencies/pip/requirements.in -e git+https://github.com/trevoriancox/django-dont-vary-on.git@01a804122b7ddcdc22f50b40993f91c27b03bef6#egg=django-dont-vary-on # via -r dependencies/pip/requirements.in diff --git a/dependencies/pip/requirements.in b/dependencies/pip/requirements.in index da85d168d3..86365bcaea 100644 --- a/dependencies/pip/requirements.in +++ b/dependencies/pip/requirements.in @@ -7,7 +7,7 @@ # More up-to-date version of django-digest than PyPI seems to have. # Also, python-digest is an unlisted dependency thereof. -e git+https://github.com/dimagi/python-digest@5c94bb74516b977b60180ee832765c0695ff2b56#egg=python_digest --e git+https://github.com/kobotoolbox/django-digest@43f3100a7e257942c313ad79057e6a0b1612a74a#egg=django_digest +-e git+https://github.com/kobotoolbox/django-digest@3995226ed8e5bd1cb32c640aae970f8c104f6156#egg=django_digest # ssrf protect -e git+https://github.com/kobotoolbox/ssrf-protect@9b97d3f0fd8f737a38dd7a6b64efeffc03ab3cdd#egg=ssrf_protect diff --git a/dependencies/pip/requirements.txt b/dependencies/pip/requirements.txt index 3299d51d17..d81ea2e13e 100644 --- a/dependencies/pip/requirements.txt +++ b/dependencies/pip/requirements.txt @@ -4,7 +4,7 @@ # # pip-compile dependencies/pip/requirements.in # --e git+https://github.com/kobotoolbox/django-digest@43f3100a7e257942c313ad79057e6a0b1612a74a#egg=django_digest +-e git+https://github.com/kobotoolbox/django-digest@3995226ed8e5bd1cb32c640aae970f8c104f6156#egg=django_digest # via -r dependencies/pip/requirements.in -e git+https://github.com/trevoriancox/django-dont-vary-on.git@01a804122b7ddcdc22f50b40993f91c27b03bef6#egg=django-dont-vary-on # via -r dependencies/pip/requirements.in diff --git a/jsapp/js/account/accountSettingsRoute.tsx b/jsapp/js/account/accountSettingsRoute.tsx index 4594141490..ca97f40000 100644 --- a/jsapp/js/account/accountSettingsRoute.tsx +++ b/jsapp/js/account/accountSettingsRoute.tsx @@ -142,9 +142,8 @@ const AccountSettings = observer(() => { + + -
+
{content}   {supportArticle && ( diff --git a/jsapp/js/components/newFeatureDialog.module.scss b/jsapp/js/components/newFeatureDialog.module.scss index 276a089e90..af2597b6e9 100644 --- a/jsapp/js/components/newFeatureDialog.module.scss +++ b/jsapp/js/components/newFeatureDialog.module.scss @@ -1,5 +1,6 @@ @use 'scss/colors'; @use 'scss/sizes'; +@use 'scss/mixins'; .root { position: relative; @@ -51,3 +52,12 @@ color: lighten(colors.$kobo-blue, 20%); text-decoration: underline; } + +.closeButton { + @include mixins.buttonReset; + color: colors.$kobo-white; + + &:hover { + opacity: 0.8; + } +} diff --git a/jsapp/js/components/permissions/copyTeamPermissions.component.tsx b/jsapp/js/components/permissions/copyTeamPermissions.component.tsx index 9062827bab..775dd168db 100644 --- a/jsapp/js/components/permissions/copyTeamPermissions.component.tsx +++ b/jsapp/js/components/permissions/copyTeamPermissions.component.tsx @@ -168,9 +168,8 @@ export default class CopyTeamPermissions extends React.Component< />
diff --git a/jsapp/js/components/permissions/userPermissionRow.component.tsx b/jsapp/js/components/permissions/userPermissionRow.component.tsx index bb6be083a0..281d3bb391 100644 --- a/jsapp/js/components/permissions/userPermissionRow.component.tsx +++ b/jsapp/js/components/permissions/userPermissionRow.component.tsx @@ -166,28 +166,26 @@ export default class UserPermissionRow extends React.Component< )} {!this.props.isUserOwner && !this.props.isPendingOwner && ( - +
{this.renderPermissions(this.props.permissions)} {this.props.userCanEditPerms && ( <>
)} diff --git a/jsapp/js/components/processing/analysis/analysisHeader.component.tsx b/jsapp/js/components/processing/analysis/analysisHeader.component.tsx index d5848ad68c..9a1e376e6d 100644 --- a/jsapp/js/components/processing/analysis/analysisHeader.component.tsx +++ b/jsapp/js/components/processing/analysis/analysisHeader.component.tsx @@ -66,8 +66,7 @@ export default function AnalysisHeader() { hideOnMenuClick triggerContent={