From cdfb2025d5169d26aff819a5b171632bd9782ea8 Mon Sep 17 00:00:00 2001 From: "cvat-bot[bot]" <147643061+cvat-bot[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 13:36:49 +0000 Subject: [PATCH 01/15] Update develop after v2.21.2 --- cvat-cli/requirements/base.txt | 2 +- cvat-cli/src/cvat_cli/version.py | 2 +- cvat-sdk/gen/generate.sh | 2 +- cvat/__init__.py | 2 +- cvat/schema.yml | 2 +- docker-compose.yml | 18 +++++++++--------- helm-chart/values.yaml | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cvat-cli/requirements/base.txt b/cvat-cli/requirements/base.txt index c307b6c96895..e9be53974d91 100644 --- a/cvat-cli/requirements/base.txt +++ b/cvat-cli/requirements/base.txt @@ -1,3 +1,3 @@ -cvat-sdk~=2.21.2 +cvat-sdk~=2.22.0 Pillow>=10.3.0 setuptools>=70.0.0 # not directly required, pinned by Snyk to avoid a vulnerability diff --git a/cvat-cli/src/cvat_cli/version.py b/cvat-cli/src/cvat_cli/version.py index 528bf553abd1..b2829a54b105 100644 --- a/cvat-cli/src/cvat_cli/version.py +++ b/cvat-cli/src/cvat_cli/version.py @@ -1 +1 @@ -VERSION = "2.21.2" +VERSION = "2.22.0" diff --git a/cvat-sdk/gen/generate.sh b/cvat-sdk/gen/generate.sh index b85479836a7f..ca9a08be98fe 100755 --- a/cvat-sdk/gen/generate.sh +++ b/cvat-sdk/gen/generate.sh @@ -8,7 +8,7 @@ set -e GENERATOR_VERSION="v6.0.1" -VERSION="2.21.2" +VERSION="2.22.0" LIB_NAME="cvat_sdk" LAYER1_LIB_NAME="${LIB_NAME}/api_client" DST_DIR="$(cd "$(dirname -- "$0")/.." && pwd)" diff --git a/cvat/__init__.py b/cvat/__init__.py index 33ddf9c436cf..d72cb8e0099c 100644 --- a/cvat/__init__.py +++ b/cvat/__init__.py @@ -4,6 +4,6 @@ from cvat.utils.version import get_version -VERSION = (2, 21, 2, 'final', 0) +VERSION = (2, 22, 0, 'alpha', 0) __version__ = get_version(VERSION) diff --git a/cvat/schema.yml b/cvat/schema.yml index b05b2989d53b..4bda991cbb12 100644 --- a/cvat/schema.yml +++ b/cvat/schema.yml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: title: CVAT REST API - version: 2.21.2 + version: 2.22.0 description: REST API for Computer Vision Annotation Tool (CVAT) termsOfService: https://www.google.com/policies/terms/ contact: diff --git a/docker-compose.yml b/docker-compose.yml index 45399e0c39ea..0d3f802c82f5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -79,7 +79,7 @@ services: cvat_server: container_name: cvat_server - image: cvat/server:${CVAT_VERSION:-v2.21.2} + image: cvat/server:${CVAT_VERSION:-dev} restart: always depends_on: <<: *backend-deps @@ -113,7 +113,7 @@ services: cvat_utils: container_name: cvat_utils - image: cvat/server:${CVAT_VERSION:-v2.21.2} + image: cvat/server:${CVAT_VERSION:-dev} restart: always depends_on: *backend-deps environment: @@ -130,7 +130,7 @@ services: cvat_worker_import: container_name: cvat_worker_import - image: cvat/server:${CVAT_VERSION:-v2.21.2} + image: cvat/server:${CVAT_VERSION:-dev} restart: always depends_on: *backend-deps environment: @@ -146,7 +146,7 @@ services: cvat_worker_export: container_name: cvat_worker_export - image: cvat/server:${CVAT_VERSION:-v2.21.2} + image: cvat/server:${CVAT_VERSION:-dev} restart: always depends_on: *backend-deps environment: @@ -162,7 +162,7 @@ services: cvat_worker_annotation: container_name: cvat_worker_annotation - image: cvat/server:${CVAT_VERSION:-v2.21.2} + image: cvat/server:${CVAT_VERSION:-dev} restart: always depends_on: *backend-deps environment: @@ -178,7 +178,7 @@ services: cvat_worker_webhooks: container_name: cvat_worker_webhooks - image: cvat/server:${CVAT_VERSION:-v2.21.2} + image: cvat/server:${CVAT_VERSION:-dev} restart: always depends_on: *backend-deps environment: @@ -194,7 +194,7 @@ services: cvat_worker_quality_reports: container_name: cvat_worker_quality_reports - image: cvat/server:${CVAT_VERSION:-v2.21.2} + image: cvat/server:${CVAT_VERSION:-dev} restart: always depends_on: *backend-deps environment: @@ -210,7 +210,7 @@ services: cvat_worker_analytics_reports: container_name: cvat_worker_analytics_reports - image: cvat/server:${CVAT_VERSION:-v2.21.2} + image: cvat/server:${CVAT_VERSION:-dev} restart: always depends_on: *backend-deps environment: @@ -226,7 +226,7 @@ services: cvat_ui: container_name: cvat_ui - image: cvat/ui:${CVAT_VERSION:-v2.21.2} + image: cvat/ui:${CVAT_VERSION:-dev} restart: always depends_on: - cvat_server diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml index fc1e841bec43..05d74e906e98 100644 --- a/helm-chart/values.yaml +++ b/helm-chart/values.yaml @@ -129,7 +129,7 @@ cvat: additionalVolumeMounts: [] replicas: 1 image: cvat/server - tag: v2.21.2 + tag: dev imagePullPolicy: Always permissionFix: enabled: true @@ -153,7 +153,7 @@ cvat: frontend: replicas: 1 image: cvat/ui - tag: v2.21.2 + tag: dev imagePullPolicy: Always labels: {} # test: test From 15124c8b1d066264e99e50fafc48eea147a3bb3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:19:45 +0300 Subject: [PATCH 02/15] Bump http-proxy-middleware from 2.0.6 to 2.0.7 (#8588) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 111a2b86f925..1f42b7b8dccc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6294,9 +6294,9 @@ http-proxy-agent@^5.0.0: debug "4" http-proxy-middleware@^2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" - integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + version "2.0.7" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz#915f236d92ae98ef48278a95dedf17e991936ec6" + integrity sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA== dependencies: "@types/http-proxy" "^1.17.8" http-proxy "^1.18.1" From 8b8140e3a7f5213865cd934d50c37a3f2670321c Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Thu, 24 Oct 2024 17:33:51 +0300 Subject: [PATCH 03/15] Remove unnecessary Docker tagging in CI workflows (#8562) Rather than retag the Docker images, it seems like we can just build them with correct tags to start with. --- .github/workflows/full.yml | 9 ++------- .github/workflows/main.yml | 17 ++++++----------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/.github/workflows/full.yml b/.github/workflows/full.yml index c6369340b5c3..b3097a01ec8c 100644 --- a/.github/workflows/full.yml +++ b/.github/workflows/full.yml @@ -55,7 +55,7 @@ jobs: cache-from: type=local,src=/tmp/cvat_cache_server context: . file: Dockerfile - tags: cvat/server + tags: cvat/server:${{ env.CVAT_VERSION }} outputs: type=docker,dest=/tmp/cvat_server/image.tar - name: CVAT UI. Build and push @@ -64,7 +64,7 @@ jobs: cache-from: type=local,src=/tmp/cvat_cache_ui context: . file: Dockerfile.ui - tags: cvat/ui + tags: cvat/ui:${{ env.CVAT_VERSION }} outputs: type=docker,dest=/tmp/cvat_ui/image.tar - name: CVAT SDK. Build @@ -126,8 +126,6 @@ jobs: run: | docker load --input /tmp/cvat_server/image.tar docker load --input /tmp/cvat_ui/image.tar - docker tag cvat/server:latest cvat/server:${CVAT_VERSION} - docker tag cvat/ui:latest cvat/ui:${CVAT_VERSION} docker image ls -a - name: Verify API schema @@ -203,7 +201,6 @@ jobs: - name: Load Docker server image run: | docker load --input /tmp/cvat_server/image.tar - docker tag cvat/server:latest cvat/server:${CVAT_VERSION} docker image ls -a - name: Running OPA tests @@ -280,8 +277,6 @@ jobs: run: | docker load --input /tmp/cvat_server/image.tar docker load --input /tmp/cvat_ui/image.tar - docker tag cvat/server:latest cvat/server:${CVAT_VERSION} - docker tag cvat/ui:latest cvat/ui:${CVAT_VERSION} docker image ls -a - name: Run CVAT instance diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0c9211b0c4a5..4cd353fd85fe 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -67,7 +67,7 @@ jobs: cache-from: type=local,src=/tmp/cvat_cache_server context: . file: Dockerfile - tags: cvat/server + tags: cvat/server:${{ env.CVAT_VERSION }} outputs: type=docker,dest=/tmp/cvat_server/image.tar - name: Instrumentation of the code then rebuilding the CVAT UI @@ -81,7 +81,7 @@ jobs: cache-from: type=local,src=/tmp/cvat_cache_ui context: . file: Dockerfile.ui - tags: cvat/ui + tags: cvat/ui:${{ env.CVAT_VERSION }} outputs: type=docker,dest=/tmp/cvat_ui/image.tar - name: CVAT SDK. Build @@ -95,7 +95,7 @@ jobs: id: verify_schema run: | docker load --input /tmp/cvat_server/image.tar - docker run --rm cvat/server bash \ + docker run --rm "cvat/server:${CVAT_VERSION}" bash \ -c 'python manage.py spectacular' > cvat/schema-expected.yml if ! git diff --no-index cvat/schema.yml cvat/schema-expected.yml; then @@ -109,7 +109,7 @@ jobs: - name: Verify migrations run: | - docker run --rm cvat/server bash \ + docker run --rm "cvat/server:${CVAT_VERSION}" bash \ -c 'python manage.py makemigrations --check' - name: Upload CVAT server artifact @@ -156,8 +156,6 @@ jobs: run: | docker load --input /tmp/cvat_server/image.tar docker load --input /tmp/cvat_ui/image.tar - docker tag cvat/server:latest cvat/server:${CVAT_VERSION} - docker tag cvat/ui:latest cvat/ui:${CVAT_VERSION} docker image ls -a - name: Generate SDK @@ -221,7 +219,6 @@ jobs: - name: Load Docker server image run: | docker load --input /tmp/cvat_server/image.tar - docker tag cvat/server:latest cvat/server:${CVAT_VERSION} docker image ls -a - name: Running OPA tests @@ -304,8 +301,6 @@ jobs: run: | docker load --input /tmp/cvat_server/image.tar docker load --input /tmp/cvat_ui/image.tar - docker tag cvat/server:latest cvat/server:${CVAT_VERSION} - docker tag cvat/ui:latest cvat/ui:${CVAT_VERSION} docker image ls -a - name: Run CVAT instance @@ -426,10 +421,10 @@ jobs: SERVER_IMAGE_REPO: ${{ secrets.DOCKERHUB_WORKSPACE }}/server UI_IMAGE_REPO: ${{ secrets.DOCKERHUB_WORKSPACE }}/ui run: | - docker tag cvat/server:latest "${SERVER_IMAGE_REPO}:dev" + docker tag "cvat/server:${CVAT_VERSION}" "${SERVER_IMAGE_REPO}:dev" docker push "${SERVER_IMAGE_REPO}:dev" - docker tag cvat/ui:latest "${UI_IMAGE_REPO}:dev" + docker tag "cvat/ui:${CVAT_VERSION}" "${UI_IMAGE_REPO}:dev" docker push "${UI_IMAGE_REPO}:dev" codecov: From 88e8db0b373d2794eb667ceb13859440593c5cc0 Mon Sep 17 00:00:00 2001 From: Dmitrii Lavrukhin Date: Mon, 28 Oct 2024 16:38:18 +0400 Subject: [PATCH 04/15] how to grant analytics access (#8590) --- site/content/en/docs/administration/advanced/analytics.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/site/content/en/docs/administration/advanced/analytics.md b/site/content/en/docs/administration/advanced/analytics.md index 36425c341a32..445c212f9687 100644 --- a/site/content/en/docs/administration/advanced/analytics.md +++ b/site/content/en/docs/administration/advanced/analytics.md @@ -18,6 +18,10 @@ and enhance user satisfaction. CVAT analytics are available from the top menu. +Superusers and users with administrator role have access to analytics. +Permission to access analytics can also be granted when editing a user +on admin page by `Has access to analytics` checkbox. + ![CVAT Analytics](/images/analytics_menu.jpg) > Note: CVAT analytics and monitoring are available only for on-prem solution. From 37586c08387aa672ac02c77004f938823611a95d Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Tue, 29 Oct 2024 09:07:56 +0200 Subject: [PATCH 05/15] Update the Docker image handling code in `schedule.yml` (#8596) ### Motivation and context It currently uses an auxiliary Docker Hub account to temporarily store the images, but all other workflows have long ago switched to using workflow artifacts. Switch this workflow too, by replacing all image-handling code with the equivalents from `main.yml`. The main reason to do this is to fix a bug in the workflow: the `unit_testing` job tags the `cvat/*` images as `:latest`, but the Compose file wants the `:dev` tag. So the images end up being redownloaded from Docker Hub. There are also a few other reasons why the new version is better: * It avoids unnecessary complexity, viz. the usage of Docker Hub, an external service. * It's consistent with the other pipelines, which should make future changes easier. * It's much less code. ### How has this been tested? Test run: . You can see that at no point it pulls the `cvat/server` or `cvat/ui` images from GitHub. ### Checklist - [x] I submit my changes into the `develop` branch - ~~[ ] I have created a changelog fragment~~ - ~~[ ] I have updated the documentation accordingly~~ - ~~[ ] I have added tests to cover my changes~~ - ~~[ ] I have linked related issues (see [GitHub docs]( https://help.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword))~~ - ~~[ ] I have increased versions of npm packages if it is necessary ([cvat-canvas](https://github.com/cvat-ai/cvat/tree/develop/cvat-canvas#versioning), [cvat-core](https://github.com/cvat-ai/cvat/tree/develop/cvat-core#versioning), [cvat-data](https://github.com/cvat-ai/cvat/tree/develop/cvat-data#versioning) and [cvat-ui](https://github.com/cvat-ai/cvat/tree/develop/cvat-ui#versioning))~~ ### License - [x] I submit _my code changes_ under the same [MIT License]( https://github.com/cvat-ai/cvat/blob/develop/LICENSE) that covers the project. Feel free to contact the maintainers if that's a concern. ## Summary by CodeRabbit - **Chores** - Updated workflow configuration to streamline artifact management for image builds. - Removed Docker Hub interactions for building and testing images. - Introduced a new environment variable `CVAT_VERSION` for tagging images. --- .github/workflows/schedule.yml | 131 ++++++++++++--------------------- 1 file changed, 47 insertions(+), 84 deletions(-) diff --git a/.github/workflows/schedule.yml b/.github/workflows/schedule.yml index c2071cd85d13..b71833882cf0 100644 --- a/.github/workflows/schedule.yml +++ b/.github/workflows/schedule.yml @@ -5,9 +5,8 @@ on: workflow_dispatch: env: - SERVER_IMAGE_TEST_REPO: cvat_server - UI_IMAGE_TEST_REPO: instrumentation_cvat_ui CYPRESS_VERIFY_TIMEOUT: 180000 # https://docs.cypress.io/guides/guides/command-line#cypress-verify + CVAT_VERSION: "local" jobs: check_updates: @@ -48,12 +47,6 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_CI_USERNAME }} - password: ${{ secrets.DOCKERHUB_CI_TOKEN }} - - name: CVAT server. Getting cache from the default branch uses: actions/cache@v4 with: @@ -66,34 +59,23 @@ jobs: path: /tmp/cvat_cache_ui key: ${{ runner.os }}-build-ui-${{ needs.search_cache.outputs.sha }} - - name: CVAT server. Extract metadata (tags, labels) for Docker - id: meta-server - uses: docker/metadata-action@master - with: - images: ${{ secrets.DOCKERHUB_CI_WORKSPACE }}/${{ env.SERVER_IMAGE_TEST_REPO }} - tags: - type=raw,value=nightly - - - name: CVAT UI. Extract metadata (tags, labels) for Docker - id: meta-ui - uses: docker/metadata-action@master - with: - images: ${{ secrets.DOCKERHUB_CI_WORKSPACE }}/${{ env.UI_IMAGE_TEST_REPO }} - tags: - type=raw,value=nightly - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + - name: Create artifact directories + run: | + mkdir /tmp/cvat_server + mkdir /tmp/cvat_ui + mkdir /tmp/cvat_sdk + - name: CVAT server. Build and push uses: docker/build-push-action@v6 with: cache-from: type=local,src=/tmp/cvat_cache_server context: . file: Dockerfile - push: true - tags: ${{ steps.meta-server.outputs.tags }} - labels: ${{ steps.meta-server.outputs.labels }} + tags: cvat/server:${{ env.CVAT_VERSION }} + outputs: type=docker,dest=/tmp/cvat_server/image.tar - name: CVAT UI. Build and push uses: docker/build-push-action@v6 @@ -101,9 +83,20 @@ jobs: cache-from: type=local,src=/tmp/cvat_cache_ui context: . file: Dockerfile.ui - push: true - tags: ${{ steps.meta-ui.outputs.tags }} - labels: ${{ steps.meta-ui.outputs.labels }} + tags: cvat/ui:${{ env.CVAT_VERSION }} + outputs: type=docker,dest=/tmp/cvat_ui/image.tar + + - name: Upload CVAT server artifact + uses: actions/upload-artifact@v4 + with: + name: cvat_server + path: /tmp/cvat_server/image.tar + + - name: Upload CVAT UI artifact + uses: actions/upload-artifact@v4 + with: + name: cvat_ui + path: /tmp/cvat_ui/image.tar unit_testing: needs: build @@ -115,41 +108,23 @@ jobs: with: python-version: '3.8' - - name: Getting CVAT UI cache from the default branch - uses: actions/cache@v4 + - name: Download CVAT server image + uses: actions/download-artifact@v4 with: - path: /tmp/cvat_cache_ui - key: ${{ runner.os }}-build-ui-${{ needs.search_cache.outputs.sha }} + name: cvat_server + path: /tmp/cvat_server/ - - name: Building CVAT UI image - uses: docker/build-push-action@v6 + - name: Download CVAT UI images + uses: actions/download-artifact@v4 with: - context: . - file: ./Dockerfile.ui - cache-from: type=local,src=/tmp/cvat_cache_ui - tags: cvat/ui:latest - load: true + name: cvat_ui + path: /tmp/cvat_ui/ - - name: CVAT server. Extract metadata (tags, labels) for Docker - id: meta-server - uses: docker/metadata-action@master - with: - images: ${{ secrets.DOCKERHUB_CI_WORKSPACE }}/${{ env.SERVER_IMAGE_TEST_REPO }} - tags: - type=raw,value=nightly - - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_CI_USERNAME }} - password: ${{ secrets.DOCKERHUB_CI_TOKEN }} - - - name: Pull CVAT server image + - name: Load Docker images run: | - docker pull ${{ steps.meta-server.outputs.tags }} - docker tag ${{ steps.meta-server.outputs.tags }} cvat/server:local - docker tag ${{ steps.meta-server.outputs.tags }} cvat/server:latest - docker tag cvat/ui:latest cvat/ui:local + docker load --input /tmp/cvat_server/image.tar + docker load --input /tmp/cvat_ui/image.tar + docker image ls -a - name: OPA tests run: | @@ -210,35 +185,23 @@ jobs: with: node-version: '16.x' - - name: Login to Docker Hub - uses: docker/login-action@v3 + - name: Download CVAT server image + uses: actions/download-artifact@v4 with: - username: ${{ secrets.DOCKERHUB_CI_USERNAME }} - password: ${{ secrets.DOCKERHUB_CI_TOKEN }} + name: cvat_server + path: /tmp/cvat_server/ - - name: CVAT server. Extract metadata (tags, labels) for Docker - id: meta-server - uses: docker/metadata-action@master + - name: Download CVAT UI image + uses: actions/download-artifact@v4 with: - images: ${{ secrets.DOCKERHUB_CI_WORKSPACE }}/${{ env.SERVER_IMAGE_TEST_REPO }} - tags: - type=raw,value=nightly + name: cvat_ui + path: /tmp/cvat_ui/ - - name: CVAT UI. Extract metadata (tags, labels) for Docker - id: meta-ui - uses: docker/metadata-action@master - with: - images: ${{ secrets.DOCKERHUB_CI_USERNAME }}/${{ env.UI_IMAGE_TEST_REPO }} - tags: - type=raw,value=nightly - - - name: Pull CVAT UI image + - name: Load Docker images run: | - docker pull ${{ steps.meta-server.outputs.tags }} - docker tag ${{ steps.meta-server.outputs.tags }} cvat/server:dev - - docker pull ${{ steps.meta-ui.outputs.tags }} - docker tag ${{ steps.meta-ui.outputs.tags }} cvat/ui:dev + docker load --input /tmp/cvat_server/image.tar + docker load --input /tmp/cvat_ui/image.tar + docker image ls -a - name: Run CVAT instance run: | From e27f93e0c56d57a513afb45a2670b8b2f5e5e24d Mon Sep 17 00:00:00 2001 From: Kirill Lakhov Date: Tue, 29 Oct 2024 11:54:18 +0300 Subject: [PATCH 06/15] Fix UI crush on failed request status check (#8575) ### Motivation and context When a status check for `request` is failing for any reason, eg something happened on the server and the request failed with 50X error. The requests page crushes with an error: `Cannot read property 'target' of undefined` : ![image](https://github.com/user-attachments/assets/e41ea391-c03e-47cd-9ab0-1e06583d366c) Now the error message is shown: ![image](https://github.com/user-attachments/assets/e9976657-2fc8-4be8-94b4-1d5453b9d9ab) ### How has this been tested? ### Checklist - [x] I submit my changes into the `develop` branch - [x] I have created a changelog fragment - ~~[ ] I have updated the documentation accordingly~~ - [ ] I have added tests to cover my changes - ~~[ ] I have linked related issues (see [GitHub docs]( https://help.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword))~~ - [x] I have increased versions of npm packages if it is necessary ([cvat-canvas](https://github.com/cvat-ai/cvat/tree/develop/cvat-canvas#versioning), [cvat-core](https://github.com/cvat-ai/cvat/tree/develop/cvat-core#versioning), [cvat-data](https://github.com/cvat-ai/cvat/tree/develop/cvat-data#versioning) and [cvat-ui](https://github.com/cvat-ai/cvat/tree/develop/cvat-ui#versioning)) ### License - [x] I submit _my code changes_ under the same [MIT License]( https://github.com/cvat-ai/cvat/blob/develop/LICENSE) that covers the project. Feel free to contact the maintainers if that's a concern. ## Summary by CodeRabbit ## Release Notes - **New Features** - Enhanced request handling with improved initialization and error management for tasks and jobs. - Added `listenToCreate` method in the Task class for better task creation management. - Introduced `generateInitialRequest` function to standardize request initialization across imports and exports. - **Bug Fixes** - Improved clarity of status messages for failed requests in the RequestCard component. - **Documentation** - Updated method signatures across various action files for better clarity and consistency. - **Chores** - Refined import statements and type exports in the core wrapper to streamline functionality. --- ...121638_klakhov_fix_request_status_crush.md | 4 ++ cvat-core/package.json | 2 +- cvat-core/src/core-types.ts | 10 +++- cvat-core/src/request.ts | 52 ++++++++++++++----- cvat-core/src/requests-manager.ts | 17 +++--- cvat-core/src/server-proxy.ts | 46 ++++++++++------ cvat-core/src/server-response-types.ts | 13 ++--- cvat-core/src/session-implementation.ts | 4 +- cvat-core/src/session.ts | 3 +- cvat-ui/src/actions/requests-actions.ts | 17 ++++-- cvat-ui/src/actions/requests-async-actions.ts | 6 ++- cvat-ui/src/actions/tasks-actions.ts | 10 ++-- .../components/requests-page/request-card.tsx | 8 ++- cvat-ui/src/cvat-core-wrapper.ts | 5 +- 14 files changed, 137 insertions(+), 60 deletions(-) create mode 100644 changelog.d/20241022_121638_klakhov_fix_request_status_crush.md diff --git a/changelog.d/20241022_121638_klakhov_fix_request_status_crush.md b/changelog.d/20241022_121638_klakhov_fix_request_status_crush.md new file mode 100644 index 000000000000..082b59a70d4f --- /dev/null +++ b/changelog.d/20241022_121638_klakhov_fix_request_status_crush.md @@ -0,0 +1,4 @@ +### Fixed + +- Requests page crush with `Cannot read property 'target' of undefined` error + () diff --git a/cvat-core/package.json b/cvat-core/package.json index 782d74c15b65..a769b74bf78c 100644 --- a/cvat-core/package.json +++ b/cvat-core/package.json @@ -1,6 +1,6 @@ { "name": "cvat-core", - "version": "15.2.0", + "version": "15.2.1", "type": "module", "description": "Part of Computer Vision Tool which presents an interface for client-side integration", "main": "src/api.ts", diff --git a/cvat-core/src/core-types.ts b/cvat-core/src/core-types.ts index e44a354cb5bd..c05b7b6ba4a5 100644 --- a/cvat-core/src/core-types.ts +++ b/cvat-core/src/core-types.ts @@ -2,7 +2,9 @@ // // SPDX-License-Identifier: MIT -import { ModelKind, ModelReturnType, ShapeType } from './enums'; +import { + ModelKind, ModelReturnType, RQStatus, ShapeType, +} from './enums'; export interface ModelAttribute { name: string; @@ -54,4 +56,10 @@ export interface SerializedModel { updated_date?: string; } +export interface UpdateStatusData { + status: RQStatus; + progress: number; + message: string; +} + export type PaginatedResource = T[] & { count: number }; diff --git a/cvat-core/src/request.ts b/cvat-core/src/request.ts index 66ae49b4c96b..1935f78b2f0c 100644 --- a/cvat-core/src/request.ts +++ b/cvat-core/src/request.ts @@ -6,10 +6,10 @@ import { RQStatus } from './enums'; import User from './user'; import { SerializedRequest } from './server-response-types'; -type Operation = { +export type RequestOperation = { target: string; type: string; - format: string; + format: string | null; jobID: number | null; taskID: number | null; projectID: number | null; @@ -44,9 +44,7 @@ export class Request { this.#finishedDate = initialData.finished_date; this.#expiryDate = initialData.expiry_date; - if (initialData.owner) { - this.#owner = new User(initialData.owner); - } + this.#owner = new User(initialData.owner); } get id(): string { @@ -57,7 +55,7 @@ export class Request { return this.#status.toLowerCase() as RQStatus; } - get progress(): number { + get progress(): number | undefined { return this.#progress; } @@ -65,7 +63,7 @@ export class Request { return this.#message; } - get operation(): Operation { + get operation(): RequestOperation { return { target: this.#operation.target, type: this.#operation.type, @@ -77,11 +75,11 @@ export class Request { }; } - get url(): string { + get url(): string | undefined { return this.#resultUrl; } - get resultID(): number { + get resultID(): number | undefined { return this.#resultID; } @@ -89,19 +87,49 @@ export class Request { return this.#createdDate; } - get startedDate(): string { + get startedDate(): string | undefined { return this.#startedDate; } - get finishedDate(): string { + get finishedDate(): string | undefined { return this.#finishedDate; } - get expiryDate(): string { + get expiryDate(): string | undefined { return this.#expiryDate; } get owner(): User { return this.#owner; } + + public toJSON(): SerializedRequest { + const result: SerializedRequest = { + id: this.#id, + status: this.#status, + operation: { + target: this.#operation.target, + type: this.#operation.type, + format: this.#operation.format, + job_id: this.#operation.job_id, + task_id: this.#operation.task_id, + project_id: this.#operation.project_id, + function_id: this.#operation.function_id, + }, + progress: this.#progress, + message: this.#message, + result_url: this.#resultUrl, + result_id: this.#resultID, + created_date: this.#createdDate, + started_date: this.#startedDate, + finished_date: this.#finishedDate, + expiry_date: this.#expiryDate, + owner: { + id: this.#owner.id, + username: this.#owner.username, + }, + }; + + return result; + } } diff --git a/cvat-core/src/requests-manager.ts b/cvat-core/src/requests-manager.ts index c348923e68bc..711073988955 100644 --- a/cvat-core/src/requests-manager.ts +++ b/cvat-core/src/requests-manager.ts @@ -122,14 +122,15 @@ class RequestsManager { } } catch (error) { if (requestID in this.listening) { - const { onUpdate } = this.listening[requestID]; - - onUpdate - .forEach((update) => update(new Request({ - id: requestID, - status: RQStatus.FAILED, - message: `Could not get a status of the request ${requestID}. ${error.toString()}`, - }))); + const { onUpdate, request } = this.listening[requestID]; + if (request) { + onUpdate + .forEach((update) => update(new Request({ + ...request.toJSON(), + status: RQStatus.FAILED, + message: `Could not get a status of the request ${requestID}. ${error.toString()}`, + }))); + } reject(error); } } diff --git a/cvat-core/src/server-proxy.ts b/cvat-core/src/server-proxy.ts index eb9c15ce64b9..37f2337c0e52 100644 --- a/cvat-core/src/server-proxy.ts +++ b/cvat-core/src/server-proxy.ts @@ -21,7 +21,7 @@ import { SerializedQualityReportData, APIQualityReportsFilter, SerializedAnalyticsReport, APIAnalyticsReportFilter, SerializedRequest, SerializedJobValidationLayout, SerializedTaskValidationLayout, } from './server-response-types'; -import { PaginatedResource } from './core-types'; +import { PaginatedResource, UpdateStatusData } from './core-types'; import { Request } from './request'; import { Storage } from './storage'; import { SerializedEvent } from './event'; @@ -1069,7 +1069,7 @@ type LongProcessListener = Record, taskDataSpec: any, - onUpdate: (request: Request) => void, + onUpdate: (request: Request | UpdateStatusData) => void, ): Promise<{ taskID: number, rqID: string }> { const { backendAPI, origin } = config; // keep current default params to 'freeze" them during this request @@ -1104,11 +1104,11 @@ async function createTask( let response = null; - onUpdate(new Request({ + onUpdate({ status: RQStatus.UNKNOWN, progress: 0, message: 'CVAT is creating your task', - })); + }); try { response = await Axios.post(`${backendAPI}/tasks`, taskSpec, { @@ -1118,11 +1118,11 @@ async function createTask( throw generateError(errorData); } - onUpdate(new Request({ + onUpdate({ status: RQStatus.UNKNOWN, progress: 0, message: 'CVAT is uploading task data to the server', - })); + }); async function bulkUpload(taskId, files) { const fileBulks = files.reduce((fileGroups, file) => { @@ -1142,11 +1142,11 @@ async function createTask( taskData.append(`client_files[${idx}]`, element); } const percentage = totalSentSize / totalSize; - onUpdate(new Request({ + onUpdate({ status: RQStatus.UNKNOWN, progress: percentage, message: 'CVAT is uploading task data to the server', - })); + }); await Axios.post(`${backendAPI}/tasks/${taskId}/data`, taskData, { ...params, headers: { 'Upload-Multiple': true }, @@ -1170,11 +1170,11 @@ async function createTask( const uploadConfig = { endpoint: `${origin}${backendAPI}/tasks/${response.data.id}/data/`, onUpdate: (percentage) => { - onUpdate(new Request({ + onUpdate({ status: RQStatus.UNKNOWN, progress: percentage, message: 'CVAT is uploading task data to the server', - })); + }); }, chunkSize, totalSize, @@ -2250,16 +2250,32 @@ async function getRequestsList(): Promise> } } +// Temporary solution for server availability problems +const retryTimeouts = [5000, 10000, 15000]; async function getRequestStatus(rqID: string): Promise { const { backendAPI } = config; + let retryCount = 0; + let lastError = null; - try { - const response = await Axios.get(`${backendAPI}/requests/${rqID}`); + while (retryCount < 3) { + try { + const response = await Axios.get(`${backendAPI}/requests/${rqID}`); - return response.data; - } catch (errorData) { - throw generateError(errorData); + return response.data; + } catch (errorData) { + lastError = generateError(errorData); + const { response } = errorData; + if (response && [502, 503, 504].includes(response.status)) { + const timeout = retryTimeouts[retryCount]; + await new Promise((resolve) => { setTimeout(resolve, timeout); }); + retryCount++; + } else { + throw generateError(errorData); + } + } } + + throw lastError; } async function cancelRequest(requestID): Promise { diff --git a/cvat-core/src/server-response-types.ts b/cvat-core/src/server-response-types.ts index af6cd760ed40..4bf7a482bccb 100644 --- a/cvat-core/src/server-response-types.ts +++ b/cvat-core/src/server-response-types.ts @@ -504,25 +504,26 @@ export interface SerializedAPISchema { } export interface SerializedRequest { - id?: string; + id: string; + message: string; status: string; - operation?: { + operation: { target: string; type: string; - format: string; + format: string | null; job_id: number | null; task_id: number | null; project_id: number | null; + function_id: string | null; }; progress?: number; - message: string; result_url?: string; result_id?: number; - created_date?: string; + created_date: string; started_date?: string; finished_date?: string; expiry_date?: string; - owner?: any; + owner: any; } export interface SerializedJobValidationLayout { diff --git a/cvat-core/src/session-implementation.ts b/cvat-core/src/session-implementation.ts index 369d0c9d5393..1c2194250155 100644 --- a/cvat-core/src/session-implementation.ts +++ b/cvat-core/src/session-implementation.ts @@ -756,12 +756,12 @@ export function implementTask(Task: typeof TaskClass): typeof TaskClass { const { taskID, rqID } = await serverProxy.tasks.create( taskSpec, taskDataSpec, - options?.requestStatusCallback || (() => {}), + options?.updateStatusCallback || (() => {}), ); await requestsManager.listen(rqID, { callback: (request: Request) => { - options?.requestStatusCallback(request); + options?.updateStatusCallback(request); if (request.status === RQStatus.FAILED) { serverProxy.tasks.delete(taskID, config.organization.organizationSlug || null); } diff --git a/cvat-core/src/session.ts b/cvat-core/src/session.ts index 8ecef7e0e632..1164ae0c07de 100644 --- a/cvat-core/src/session.ts +++ b/cvat-core/src/session.ts @@ -29,6 +29,7 @@ import logger from './logger'; import Issue from './issue'; import ObjectState from './object-state'; import { JobValidationLayout, TaskValidationLayout } from './validation-layout'; +import { UpdateStatusData } from './core-types'; function buildDuplicatedAPI(prototype) { Object.defineProperties(prototype, { @@ -1141,7 +1142,7 @@ export class Task extends Session { async save( fields: Record = {}, - options?: { requestStatusCallback?: (request: Request) => void }, + options?: { updateStatusCallback?: (updateData: Request | UpdateStatusData) => void }, ): Promise { const result = await PluginRegistry.apiWrapper.call(this, Task.prototype.save, fields, options); return result; diff --git a/cvat-ui/src/actions/requests-actions.ts b/cvat-ui/src/actions/requests-actions.ts index 6a62e7cecf7c..1f3972746e7e 100644 --- a/cvat-ui/src/actions/requests-actions.ts +++ b/cvat-ui/src/actions/requests-actions.ts @@ -3,12 +3,21 @@ // SPDX-License-Identifier: MIT import { ActionUnion, createAction } from 'utils/redux'; -import { RequestsQuery, RequestsState } from 'reducers'; +import { CombinedState, RequestsQuery, RequestsState } from 'reducers'; import { Request, ProjectOrTaskOrJob, getCore, RQStatus, } from 'cvat-core-wrapper'; +import { Store } from 'redux'; +import { getCVATStore } from 'cvat-store'; const core = getCore(); +let store: null | Store = null; +function getStore(): Store { + if (store === null) { + store = getCVATStore(); + } + return store; +} export enum RequestsActionsTypes { GET_REQUESTS = 'GET_REQUESTS', @@ -79,7 +88,7 @@ export function updateRequestProgress(request: Request, dispatch: (action: Reque ); } -export function shouldListenForProgress(rqID: string | undefined, state: RequestsState): boolean { +export function shouldListenForProgress(rqID: string | void, state: RequestsState): boolean { return ( typeof rqID === 'string' && (!state.requests[rqID] || [RQStatus.FINISHED, RQStatus.FAILED].includes(state.requests[rqID]?.status)) @@ -89,13 +98,13 @@ export function shouldListenForProgress(rqID: string | undefined, state: Request export function listen( requestID: string, dispatch: (action: RequestsActions) => void, - initialRequest?: Request, ) : Promise { + const { requests } = getStore().getState().requests; return core.requests .listen(requestID, { callback: (updatedRequest) => { updateRequestProgress(updatedRequest, dispatch); }, - initialRequest, + initialRequest: requests[requestID], }); } diff --git a/cvat-ui/src/actions/requests-async-actions.ts b/cvat-ui/src/actions/requests-async-actions.ts index 04a5ffd0a5c5..06a137eafd28 100644 --- a/cvat-ui/src/actions/requests-async-actions.ts +++ b/cvat-ui/src/actions/requests-async-actions.ts @@ -8,7 +8,9 @@ import { getCore, RQStatus, Request, Project, Task, Job, } from 'cvat-core-wrapper'; import { listenExportBackupAsync, listenExportDatasetAsync } from './export-actions'; -import { RequestInstanceType, listen, requestsActions } from './requests-actions'; +import { + RequestInstanceType, listen, requestsActions, +} from './requests-actions'; import { listenImportBackupAsync, listenImportDatasetAsync } from './import-actions'; const core = getCore(); @@ -28,6 +30,7 @@ export function getRequestsAsync(query: RequestsQuery): ThunkAction { try { const requests = await core.requests.list(); + dispatch(requestsActions.getRequestsSuccess(requests)); requests .filter((request: Request) => [RQStatus.STARTED, RQStatus.QUEUED].includes(request.status)) @@ -80,7 +83,6 @@ export function getRequestsAsync(query: RequestsQuery): ThunkAction { } } }); - dispatch(requestsActions.getRequestsSuccess(requests)); } catch (error) { dispatch(requestsActions.getRequestsFailed(error)); } diff --git a/cvat-ui/src/actions/tasks-actions.ts b/cvat-ui/src/actions/tasks-actions.ts index 60e4da022ef2..d15f033f1e6f 100644 --- a/cvat-ui/src/actions/tasks-actions.ts +++ b/cvat-ui/src/actions/tasks-actions.ts @@ -6,7 +6,7 @@ import { AnyAction } from 'redux'; import { TasksQuery, StorageLocation } from 'reducers'; import { - getCore, RQStatus, Storage, Task, + getCore, RQStatus, Storage, Task, UpdateStatusData, Request, } from 'cvat-core-wrapper'; import { filterNull } from 'utils/filter-null'; import { ThunkDispatch, ThunkAction } from 'utils/redux'; @@ -274,10 +274,10 @@ ThunkAction { taskInstance.remoteFiles = data.files.remote; try { const savedTask = await taskInstance.save(extras, { - requestStatusCallback(request) { - let { message } = request; + updateStatusCallback(updateData: Request | UpdateStatusData) { + let { message } = updateData; + const { status, progress } = updateData; let helperMessage = ''; - const { status, progress } = request; if (!message) { if ([RQStatus.QUEUED, RQStatus.STARTED].includes(status)) { message = 'CVAT queued the task to import'; @@ -291,7 +291,7 @@ ThunkAction { } } onProgress?.(`${message} ${progress ? `${Math.floor(progress * 100)}%` : ''}. ${helperMessage}`); - if (request.id) updateRequestProgress(request, dispatch); + if (updateData instanceof Request) updateRequestProgress(updateData, dispatch); }, }); diff --git a/cvat-ui/src/components/requests-page/request-card.tsx b/cvat-ui/src/components/requests-page/request-card.tsx index 980af114563a..52c109e3822c 100644 --- a/cvat-ui/src/components/requests-page/request-card.tsx +++ b/cvat-ui/src/components/requests-page/request-card.tsx @@ -100,11 +100,15 @@ function constructTimestamps(request: Request): JSX.Element { ); } case RQStatus.FAILED: { - return ( + return (request.startedDate ? ( {`Started by ${request.owner.username} on ${started}`} - ); + ) : ( + + {`Enqueued by ${request.owner.username} on ${created}`} + + )); } case RQStatus.STARTED: { return ( diff --git a/cvat-ui/src/cvat-core-wrapper.ts b/cvat-ui/src/cvat-core-wrapper.ts index 275cedcc8ab9..94b70373a1c7 100644 --- a/cvat-ui/src/cvat-core-wrapper.ts +++ b/cvat-ui/src/cvat-core-wrapper.ts @@ -17,6 +17,7 @@ import { import { SerializedAttribute, SerializedLabel, SerializedAPISchema, } from 'cvat-core/src/server-response-types'; +import { UpdateStatusData } from 'cvat-core/src/core-types'; import { Job, Task } from 'cvat-core/src/session'; import Project from 'cvat-core/src/project'; import QualityReport, { QualitySummary } from 'cvat-core/src/quality-report'; @@ -41,7 +42,7 @@ import { Dumper } from 'cvat-core/src/annotation-formats'; import { Event } from 'cvat-core/src/event'; import { APIWrapperEnterOptions } from 'cvat-core/src/plugins'; import BaseSingleFrameAction, { ActionParameterType, FrameSelectionType } from 'cvat-core/src/annotations-actions'; -import { Request } from 'cvat-core/src/request'; +import { Request, RequestOperation } from 'cvat-core/src/request'; const cvat: CVATCore = _cvat; @@ -120,4 +121,6 @@ export type { CVATCore, SerializedAPISchema, ProjectOrTaskOrJob, + RequestOperation, + UpdateStatusData, }; From 74dea3b290128b7ff3b33cad7fd4e8dcc6ca69c7 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 29 Oct 2024 11:08:59 +0200 Subject: [PATCH 07/15] Exception 'Canvas is busy' when change frame during drag/resize a track (#8598) --- changelog.d/20241028_140908_sekachev.bs.md | 4 ++ changelog.d/20241028_140945_sekachev.bs.md | 4 ++ cvat-canvas/src/typescript/canvasView.ts | 55 +++++++++++++++++++- cvat-ui/src/utils/is-able-to-change-frame.ts | 6 +-- 4 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 changelog.d/20241028_140908_sekachev.bs.md create mode 100644 changelog.d/20241028_140945_sekachev.bs.md diff --git a/changelog.d/20241028_140908_sekachev.bs.md b/changelog.d/20241028_140908_sekachev.bs.md new file mode 100644 index 000000000000..507346e3d051 --- /dev/null +++ b/changelog.d/20241028_140908_sekachev.bs.md @@ -0,0 +1,4 @@ +### Fixed + +- Exception 'Canvas is busy' when change frame during drag/resize a track + () diff --git a/changelog.d/20241028_140945_sekachev.bs.md b/changelog.d/20241028_140945_sekachev.bs.md new file mode 100644 index 000000000000..fd401e390a15 --- /dev/null +++ b/changelog.d/20241028_140945_sekachev.bs.md @@ -0,0 +1,4 @@ +### Fixed + +- A shape gets shifted if auto save triggered during dragging + () diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 480a5d3aea52..ab9a96682746 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -245,6 +245,53 @@ export class CanvasViewImpl implements CanvasView, Listener { this.canvas.dispatchEvent(event); } + private resetViewPosition(clientID: number): void { + const drawnState = this.drawnStates[clientID]; + const drawnShape = this.svgShapes[clientID]; + + if (drawnState && drawnShape) { + const { shapeType, points } = drawnState; + const translatedPoints: number[] = this.translateToCanvas(points); + const stringified = stringifyPoints(translatedPoints); + if (shapeType === 'cuboid') { + drawnShape.attr('points', stringified); + } else if (['polygon', 'polyline', 'points'].includes(shapeType)) { + (drawnShape as SVG.PolyLine | SVG.Polygon).plot(stringified); + if (shapeType === 'points') { + this.selectize(false, drawnShape); + this.setupPoints(drawnShape as SVG.PolyLine, drawnState); + } + } else if (shapeType === 'rectangle') { + const [xtl, ytl, xbr, ybr] = translatedPoints; + drawnShape.rotate(0); + drawnShape.size(xbr - xtl, ybr - ytl).move(xtl, ytl); + drawnShape.rotate(drawnState.rotation); + } else if (shapeType === 'ellipse') { + const [cx, cy, rightX, topY] = translatedPoints; + const [rx, ry] = [rightX - cx, cy - topY]; + drawnShape.rotate(0); + drawnShape.size(rx * 2, ry * 2).center(cx, cy); + drawnShape.rotate(drawnState.rotation); + } else if (shapeType === 'skeleton') { + drawnShape.rotate(0); + for (const child of (drawnShape as SVG.G).children()) { + if (child.type === 'circle') { + const childClientID = child.attr('data-client-id'); + const element = drawnState.elements.find((el: any) => el.clientID === childClientID); + const [x, y] = this.translateToCanvas(element.points); + child.center(x, y); + } + } + drawnShape.rotate(drawnState.rotation); + } else if (shapeType === 'mask') { + const [left, top] = points.slice(-4); + drawnShape.move(this.geometry.offset + left, this.geometry.offset + top); + } else { + throw new Error('Not implemented'); + } + } + } + private onInteraction = ( shapes: InteractionResult[] | null, shapesUpdated = true, @@ -1114,6 +1161,7 @@ export class CanvasViewImpl implements CanvasView, Listener { } }).on('dragend', (e: CustomEvent): void => { if (aborted) { + this.resetViewPosition(state.clientID); return; } @@ -1172,6 +1220,7 @@ export class CanvasViewImpl implements CanvasView, Listener { this.draggableShape = null; aborted = true; // disable internal drag events of SVG.js + // call chain is (mouseup -> SVG.handler.end -> SVG.handler.drag -> dragend) window.dispatchEvent(new MouseEvent('mouseup')); }); } else { @@ -1303,6 +1352,7 @@ export class CanvasViewImpl implements CanvasView, Listener { }) .on('resizedone', (): void => { if (aborted) { + this.resetViewPosition(state.clientID); return; } @@ -1359,7 +1409,8 @@ export class CanvasViewImpl implements CanvasView, Listener { onResizeEnd(); aborted = true; this.resizableShape = null; - // disable internal drag events of SVG.js + // disable internal resize events of SVG.js + // call chain is (mouseup -> SVG.handler.end -> SVG.handler.resize-> resizeend) window.dispatchEvent(new MouseEvent('mouseup')); }); } else { @@ -3401,7 +3452,7 @@ export class CanvasViewImpl implements CanvasView, Listener { return skeleton; } - private setupPoints(basicPolyline: SVG.PolyLine, state: any): any { + private setupPoints(basicPolyline: SVG.PolyLine, state: any | DrawnState): any { this.selectize(true, basicPolyline); const group: SVG.G = basicPolyline diff --git a/cvat-ui/src/utils/is-able-to-change-frame.ts b/cvat-ui/src/utils/is-able-to-change-frame.ts index b3029c4b2670..3cbc127a8a86 100644 --- a/cvat-ui/src/utils/is-able-to-change-frame.ts +++ b/cvat-ui/src/utils/is-able-to-change-frame.ts @@ -17,15 +17,15 @@ export default function isAbleToChangeFrame(frame?: number): boolean { return false; } - const frameInTheJob = true; + let frameInTheJob = true; if (typeof frame === 'number') { if (meta.includedFrames) { // frame argument comes in job coordinates // hovewer includedFrames contains absolute data values - return meta.includedFrames.includes(meta.getDataFrameNumber(frame - job.startFrame)); + frameInTheJob = meta.includedFrames.includes(meta.getDataFrameNumber(frame - job.startFrame)); } - return frame >= job.startFrame && frame <= job.stopFrame; + frameInTheJob = frame >= job.startFrame && frame <= job.stopFrame; } return canvas.isAbleToChangeFrame() && frameInTheJob && !state.annotation.player.navigationBlocked; From 251054b2230599088fc2f5448b6823ca818ae780 Mon Sep 17 00:00:00 2001 From: Kirill Lakhov Date: Tue, 29 Oct 2024 12:58:12 +0300 Subject: [PATCH 08/15] Improved Ground truth tags (#8586) ### Motivation and context Fixed several issues: - GT tags are rendered with `(GT)` mark inside of GT job which is not correct - GT tags can not be deleted via `x` button inside the GT job Other improvements: - Refactored `frame-tags` component code - Improved styles for gt/non-gt tags, as they are too close: ![image](https://github.com/user-attachments/assets/a5b1c055-4ee0-4469-ada2-21a6a70146ee) -> ![image](https://github.com/user-attachments/assets/49eb4625-1690-4865-b94f-973cee351183) Related #8547 ### How has this been tested? ### Checklist - [x] I submit my changes into the `develop` branch - [ ] I have created a changelog fragment - [ ] I have updated the documentation accordingly - [ ] I have added tests to cover my changes - [ ] I have linked related issues (see [GitHub docs]( https://help.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword)) - [ ] I have increased versions of npm packages if it is necessary ([cvat-canvas](https://github.com/cvat-ai/cvat/tree/develop/cvat-canvas#versioning), [cvat-core](https://github.com/cvat-ai/cvat/tree/develop/cvat-core#versioning), [cvat-data](https://github.com/cvat-ai/cvat/tree/develop/cvat-data#versioning) and [cvat-ui](https://github.com/cvat-ai/cvat/tree/develop/cvat-ui#versioning)) ### License - [x] I submit _my code changes_ under the same [MIT License]( https://github.com/cvat-ai/cvat/blob/develop/LICENSE) that covers the project. Feel free to contact the maintainers if that's a concern. ## Summary by CodeRabbit - **New Features** - Enhanced state management in the FrameTags component for improved performance and readability. - Introduced a new CSS class to manage spacing in the tag annotation workspace. - **Bug Fixes** - Updated tag filtering logic to improve accuracy in displaying tags. - **Refactor** - Simplified component structure by utilizing hooks for state management. - Optimized rendering logic and performance with memoization. --- ...20241023_120236_klakhov_improve_gt_tags.md | 7 ++ .../tag-annotation-workspace/frame-tags.tsx | 86 +++++++------------ .../tag-annotation-workspace/styles.scss | 4 + 3 files changed, 40 insertions(+), 57 deletions(-) create mode 100644 changelog.d/20241023_120236_klakhov_improve_gt_tags.md diff --git a/changelog.d/20241023_120236_klakhov_improve_gt_tags.md b/changelog.d/20241023_120236_klakhov_improve_gt_tags.md new file mode 100644 index 000000000000..79c1e724a35a --- /dev/null +++ b/changelog.d/20241023_120236_klakhov_improve_gt_tags.md @@ -0,0 +1,7 @@ +### Fixed + +- Tags in ground truth job were displayed as `tag (GT)` + () + +- Tags in ground truth job couldn't be deleted via `x` button + () diff --git a/cvat-ui/src/components/annotation-page/tag-annotation-workspace/frame-tags.tsx b/cvat-ui/src/components/annotation-page/tag-annotation-workspace/frame-tags.tsx index 145ec6c1c9b5..4c286122ceb1 100644 --- a/cvat-ui/src/components/annotation-page/tag-annotation-workspace/frame-tags.tsx +++ b/cvat-ui/src/components/annotation-page/tag-annotation-workspace/frame-tags.tsx @@ -1,64 +1,34 @@ // Copyright (C) 2022 Intel Corporation +// Copyright (C) 2024 CVAT.ai Corporation // // SPDX-License-Identifier: MIT import './styles.scss'; -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import Tag from 'antd/lib/tag'; -import { connect } from 'react-redux'; -import { Action } from 'redux'; -import { ThunkDispatch } from 'redux-thunk'; +import { shallowEqual, useDispatch, useSelector } from 'react-redux'; import { removeObject as removeObjectAction, } from 'actions/annotation-actions'; -import { CombinedState, ObjectType, Workspace } from 'reducers'; -import { - QualityConflict, ObjectState, AnnotationConflict, getCore, -} from 'cvat-core-wrapper'; +import { CombinedState, ObjectType } from 'reducers'; +import { ObjectState, AnnotationConflict } from 'cvat-core-wrapper'; import { filterAnnotations } from 'utils/filter-annotations'; -const core = getCore(); - -interface StateToProps { - highlightedConflict: QualityConflict | null; - states: ObjectState[]; - workspace: Workspace; -} - -interface DispatchToProps { - removeObject(objectState: any): void; -} - -function mapStateToProps(state: CombinedState): StateToProps { - const { - annotation: { - annotations: { highlightedConflict, states }, - workspace, - }, - } = state; +function FrameTags(): JSX.Element { + const dispatch = useDispatch(); - return { highlightedConflict, states, workspace }; -} - -function mapDispatchToProps(dispatch: ThunkDispatch): DispatchToProps { - return { - removeObject(objectState: ObjectState): void { - dispatch(removeObjectAction(objectState, false)); - }, - }; -} - -function FrameTags(props: StateToProps & DispatchToProps): JSX.Element { - const { - highlightedConflict, states, workspace, removeObject, - } = props; + const { highlightedConflict, states, workspace } = useSelector((state: CombinedState) => ({ + highlightedConflict: state.annotation.annotations.highlightedConflict, + states: state.annotation.annotations.states, + workspace: state.annotation.workspace, + }), shallowEqual); - const [frameTags, setFrameTags] = useState([] as ObjectState[]); + const [frameTags, setFrameTags] = useState([]); const onRemoveState = (objectState: ObjectState): void => { - removeObject(objectState); + dispatch(removeObjectAction(objectState, false)); }; useEffect(() => { @@ -67,16 +37,20 @@ function FrameTags(props: StateToProps & DispatchToProps): JSX.Element { ); }, [states]); + const tagClassName = useCallback((tag: ObjectState): string => { + const tagHighlighted = (highlightedConflict?.annotationConflicts || []) + .find((conflict: AnnotationConflict) => conflict.serverID === tag.serverID); + return tagHighlighted ? 'cvat-frame-tag-highlighted' : 'cvat-frame-tag'; + }, [highlightedConflict]); + return ( <> -
+
{frameTags - .filter((tag: any) => tag.source !== core.enums.Source.GT) - .map((tag: any) => ( + .filter((tag: ObjectState) => !tag.isGroundTruth) + .map((tag: ObjectState) => ( conflict.serverID === tag.serverID).length !== 0 ? 'cvat-frame-tag-highlighted' : 'cvat-frame-tag' - } + className={tagClassName(tag)} color={tag.label.color} onClose={() => { onRemoveState(tag); @@ -88,14 +62,12 @@ function FrameTags(props: StateToProps & DispatchToProps): JSX.Element { ))}
-
+
{frameTags - .filter((tag: any) => tag.source === core.enums.Source.GT) - .map((tag: any) => ( + .filter((tag: ObjectState) => tag.isGroundTruth) + .map((tag: ObjectState) => ( conflict.serverID === tag.serverID).length !== 0 ? 'cvat-frame-tag-highlighted' : 'cvat-frame-tag' - } + className={tagClassName(tag)} color={tag.label.color} onClose={() => { onRemoveState(tag); @@ -112,4 +84,4 @@ function FrameTags(props: StateToProps & DispatchToProps): JSX.Element { ); } -export default connect(mapStateToProps, mapDispatchToProps)(FrameTags); +export default React.memo(FrameTags); diff --git a/cvat-ui/src/components/annotation-page/tag-annotation-workspace/styles.scss b/cvat-ui/src/components/annotation-page/tag-annotation-workspace/styles.scss index 8ac9962e01e8..0740e3c9046d 100644 --- a/cvat-ui/src/components/annotation-page/tag-annotation-workspace/styles.scss +++ b/cvat-ui/src/components/annotation-page/tag-annotation-workspace/styles.scss @@ -96,3 +96,7 @@ transform: scale(1.1); } + +.cvat-canvas-annotation-frame-tags { + margin-bottom: $grid-unit-size; +} From 99821567cef2b6f4acc5233067e11a0904ff81fa Mon Sep 17 00:00:00 2001 From: Maria Khrustaleva Date: Tue, 29 Oct 2024 13:26:36 +0100 Subject: [PATCH 09/15] Drop python3.8 support (#8360) https://devguide.python.org/versions/#unsupported-versions --- .github/workflows/full.yml | 2 +- .github/workflows/main.yml | 2 +- .github/workflows/schedule.yml | 2 +- changelog.d/20241029_113229_maria_drop_python_3_8_support.md | 4 ++++ cvat-cli/setup.py | 2 +- cvat-sdk/gen/generator-config.yml | 2 +- pyproject.toml | 2 +- site/content/en/docs/api_sdk/cli/_index.md | 2 +- site/content/en/docs/api_sdk/sdk/_index.md | 2 +- 9 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 changelog.d/20241029_113229_maria_drop_python_3_8_support.md diff --git a/.github/workflows/full.yml b/.github/workflows/full.yml index b3097a01ec8c..e587e26aa1b8 100644 --- a/.github/workflows/full.yml +++ b/.github/workflows/full.yml @@ -102,7 +102,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.9' - name: Download CVAT server image uses: actions/download-artifact@v4 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4cd353fd85fe..f4e3f11d1052 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -138,7 +138,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.9' - name: Download CVAT server image uses: actions/download-artifact@v4 diff --git a/.github/workflows/schedule.yml b/.github/workflows/schedule.yml index b71833882cf0..bf74b30df047 100644 --- a/.github/workflows/schedule.yml +++ b/.github/workflows/schedule.yml @@ -106,7 +106,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.9' - name: Download CVAT server image uses: actions/download-artifact@v4 diff --git a/changelog.d/20241029_113229_maria_drop_python_3_8_support.md b/changelog.d/20241029_113229_maria_drop_python_3_8_support.md new file mode 100644 index 000000000000..ca57734aa1dc --- /dev/null +++ b/changelog.d/20241029_113229_maria_drop_python_3_8_support.md @@ -0,0 +1,4 @@ +### Removed + +- Dropped support for Python 3.8 since its EOL was on 2024-10-07 + () diff --git a/cvat-cli/setup.py b/cvat-cli/setup.py index 454ce2f00956..05b20a9165e1 100644 --- a/cvat-cli/setup.py +++ b/cvat-cli/setup.py @@ -56,7 +56,7 @@ def parse_requirements(filename=BASE_REQUIREMENTS_FILE): "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], - python_requires=">=3.8", + python_requires=">=3.9", install_requires=BASE_REQUIREMENTS, entry_points={ "console_scripts": [ diff --git a/cvat-sdk/gen/generator-config.yml b/cvat-sdk/gen/generator-config.yml index 26e78cb8a3a9..82f46fda971a 100644 --- a/cvat-sdk/gen/generator-config.yml +++ b/cvat-sdk/gen/generator-config.yml @@ -4,7 +4,7 @@ additionalProperties: packageName: "cvat_sdk.api_client" initRequiredVars: true generateSourceCodeOnly: false - generatorLanguageVersion: '>=3.8' + generatorLanguageVersion: '>=3.9' globalProperties: generateAliasAsModel: true apiTests: false diff --git a/pyproject.toml b/pyproject.toml index 581552a67ebc..6d0772451578 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,4 +6,4 @@ skip_gitignore = true # align tool behavior with Black [tool.black] line-length = 100 -target-version = ['py38'] +target-version = ['py39'] diff --git a/site/content/en/docs/api_sdk/cli/_index.md b/site/content/en/docs/api_sdk/cli/_index.md index f17d712717b2..541e29f30611 100644 --- a/site/content/en/docs/api_sdk/cli/_index.md +++ b/site/content/en/docs/api_sdk/cli/_index.md @@ -30,7 +30,7 @@ To install an [official release of CVAT CLI](https://pypi.org/project/cvat-cli/) pip install cvat-cli ``` -We support Python versions 3.8 and higher. +We support Python versions 3.9 and higher. ## Usage diff --git a/site/content/en/docs/api_sdk/sdk/_index.md b/site/content/en/docs/api_sdk/sdk/_index.md index 4c133a7b0231..e9683583ab0e 100644 --- a/site/content/en/docs/api_sdk/sdk/_index.md +++ b/site/content/en/docs/api_sdk/sdk/_index.md @@ -48,7 +48,7 @@ To use the PyTorch adapter, request the `pytorch` extra: pip install "cvat-sdk[pytorch]" ``` -We support Python versions 3.8 and higher. +We support Python versions 3.9 and higher. ## Usage From bce96eaafd0dc1dab6d59044aba6e15f7ca3163e Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Tue, 29 Oct 2024 18:18:15 +0200 Subject: [PATCH 10/15] Reblacken code after #8360 (#8609) Starting with Python 3.9, `with` allows parenthesized context managers, which black will use when needed. --- cvat-sdk/cvat_sdk/core/downloading.py | 11 +++++++++-- tests/python/shared/fixtures/init.py | 7 ++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/cvat-sdk/cvat_sdk/core/downloading.py b/cvat-sdk/cvat_sdk/core/downloading.py index c2be936c9aa4..2e8263373350 100644 --- a/cvat-sdk/cvat_sdk/core/downloading.py +++ b/cvat-sdk/cvat_sdk/core/downloading.py @@ -58,8 +58,15 @@ def download_file( except ValueError: file_size = None - with atomic_writer(output_path, "wb") as fd, pbar.task( - total=file_size, desc="Downloading", unit_scale=True, unit="B", unit_divisor=1024 + with ( + atomic_writer(output_path, "wb") as fd, + pbar.task( + total=file_size, + desc="Downloading", + unit_scale=True, + unit="B", + unit_divisor=1024, + ), ): while True: chunk = response.read(amt=CHUNK_SIZE, decode_content=False) diff --git a/tests/python/shared/fixtures/init.py b/tests/python/shared/fixtures/init.py index aa1192a0acf5..b29c5c30528b 100644 --- a/tests/python/shared/fixtures/init.py +++ b/tests/python/shared/fixtures/init.py @@ -300,9 +300,10 @@ def dump_db(): def create_compose_files(container_name_files): for filename in container_name_files: - with open(filename.with_name(filename.name.replace(".tests", "")), "r") as dcf, open( - filename, "w" - ) as ndcf: + with ( + open(filename.with_name(filename.name.replace(".tests", "")), "r") as dcf, + open(filename, "w") as ndcf, + ): dc_config = yaml.safe_load(dcf) for service_name, service_config in dc_config["services"].items(): From 4d4d8fc0ff618eecf18ed2f9406eceb5f94f321c Mon Sep 17 00:00:00 2001 From: Kirill Lakhov Date: Wed, 30 Oct 2024 13:03:53 +0300 Subject: [PATCH 11/15] Added regression test for requests page (#8608) --- tests/cypress/e2e/features/requests_page.js | 19 +++++++++++++++++++ tests/cypress/support/commands.js | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/cypress/e2e/features/requests_page.js b/tests/cypress/e2e/features/requests_page.js index 3cdf187a9825..8622092b89d8 100644 --- a/tests/cypress/e2e/features/requests_page.js +++ b/tests/cypress/e2e/features/requests_page.js @@ -357,5 +357,24 @@ context('Requests page', () => { }); }); }); + + it('Export task. Request for status fails, UI is not crushing', () => { + cy.intercept('GET', '/api/requests/**', { + statusCode: 500, + body: 'Network error', + }); + + cy.exportTask({ + type: 'annotations', + format: exportFormat, + archiveCustomName: annotationsArchiveNameLocal, + }); + + cy.contains('Could not export dataset').should('be.visible'); + cy.closeNotification('.ant-notification-notice-error'); + + cy.contains('.cvat-header-button', 'Requests').should('be.visible').click(); + cy.get('.cvat-requests-page').should('be.visible'); + }); }); }); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 76f7ffe2640c..91322e9c3695 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -1272,7 +1272,7 @@ Cypress.Commands.add('exportTask', ({ cy.get('.cvat-cloud-storage-select-provider').click(); } } - cy.contains('button', 'OK').click(); + cy.contains('.cvat-modal-export-task button', 'OK').click(); cy.get('.cvat-notification-notice-export-task-start').should('be.visible'); cy.closeNotification('.cvat-notification-notice-export-task-start'); }); From 9ecafb6029fb427f0041f849b60c91adac7011c6 Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Wed, 30 Oct 2024 12:31:57 +0200 Subject: [PATCH 12/15] Make the `--insecure` CLI option test less hacky (#8601) Test it by starting an HTTPS server (that forwards all requests to CVAT) and attempting to access that server via the CLI. This allows us to test that the option actually works end-to-end, and avoids dependencies on CLI internals. The new test requires `main` to catch the exception that is raised in the case of a certificate error, so implement that. --- .../20241029_134200_roman_cli_refactor.md | 4 ++ cvat-cli/src/cvat_cli/__main__.py | 3 +- tests/python/cli/self-signed.crt | 9 +++ tests/python/cli/self-signed.key | 3 + tests/python/cli/test_cli.py | 40 ++++++------- tests/python/cli/util.py | 56 ++++++++++++++++++- 6 files changed, 94 insertions(+), 21 deletions(-) create mode 100644 changelog.d/20241029_134200_roman_cli_refactor.md create mode 100644 tests/python/cli/self-signed.crt create mode 100644 tests/python/cli/self-signed.key diff --git a/changelog.d/20241029_134200_roman_cli_refactor.md b/changelog.d/20241029_134200_roman_cli_refactor.md new file mode 100644 index 000000000000..9cb76e2d52a5 --- /dev/null +++ b/changelog.d/20241029_134200_roman_cli_refactor.md @@ -0,0 +1,4 @@ +### Changed + +- CLI no longer prints the stack trace in case of HTTP errors + () diff --git a/cvat-cli/src/cvat_cli/__main__.py b/cvat-cli/src/cvat_cli/__main__.py index 2448587245f9..b18c8d8bb751 100755 --- a/cvat-cli/src/cvat_cli/__main__.py +++ b/cvat-cli/src/cvat_cli/__main__.py @@ -9,6 +9,7 @@ from types import SimpleNamespace from typing import List +import urllib3.exceptions from cvat_sdk import exceptions from cvat_sdk.core.client import Client, Config @@ -70,7 +71,7 @@ def main(args: List[str] = None): try: cli = CLI(client=client, credentials=parsed_args.auth) actions[parsed_args.action](cli, **vars(action_args)) - except exceptions.ApiException as e: + except (exceptions.ApiException, urllib3.exceptions.HTTPError) as e: logger.critical(e) return 1 diff --git a/tests/python/cli/self-signed.crt b/tests/python/cli/self-signed.crt new file mode 100644 index 000000000000..815373bf286c --- /dev/null +++ b/tests/python/cli/self-signed.crt @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIBPDCB76ADAgECAhQksQwFGcyVwF0+gIOPMPBB+/NjNTAFBgMrZXAwFDESMBAG +A1UEAwwJbG9jYWxob3N0MB4XDTI0MTAyODEyMTkyNFoXDTI0MTAyOTEyMTkyNFow +FDESMBAGA1UEAwwJbG9jYWxob3N0MCowBQYDK2VwAyEAzGOv96vkrHr0GPcWL7vN +8mgR4XMg9ItNpJ2nbMmjYCKjUzBRMB0GA1UdDgQWBBR6Hn0aG/ZGAJjY9HIUK7El +84qAgzAfBgNVHSMEGDAWgBR6Hn0aG/ZGAJjY9HIUK7El84qAgzAPBgNVHRMBAf8E +BTADAQH/MAUGAytlcANBAMj2zWdIa8oOiEtUWFMv+KYf1kyP1lUnlcC2xUpOj8d3 +kRYtlRX4E7F5zzzgKgNpbanRAg72qnqPiFAFCGVAhgY= +-----END CERTIFICATE----- diff --git a/tests/python/cli/self-signed.key b/tests/python/cli/self-signed.key new file mode 100644 index 000000000000..f81d8519bcac --- /dev/null +++ b/tests/python/cli/self-signed.key @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIKe5zj/UrVJ/LySjKm9BBVHXziqFIwJ6w+HuTHnldCLo +-----END PRIVATE KEY----- diff --git a/tests/python/cli/test_cli.py b/tests/python/cli/test_cli.py index 364c7011e7ca..d6b19cfe0a3c 100644 --- a/tests/python/cli/test_cli.py +++ b/tests/python/cli/test_cli.py @@ -10,7 +10,6 @@ import packaging.version as pv import pytest -from cvat_cli.cli import CLI from cvat_sdk import Client, make_client from cvat_sdk.api_client import exceptions, models from cvat_sdk.core.proxies.tasks import ResourceType, Task @@ -20,7 +19,7 @@ from shared.utils.config import BASE_URL, USER_PASS from shared.utils.helpers import generate_image_file -from .util import generate_images, run_cli +from .util import generate_images, https_reverse_proxy, run_cli class TestCLI: @@ -243,23 +242,26 @@ def mocked_version(_): assert "Server version '0' is not compatible with SDK version" in caplog.text @pytest.mark.parametrize("verify", [True, False]) - def test_can_control_ssl_verification_with_arg(self, monkeypatch, verify: bool): - # TODO: Very hacky implementation, improve it, if possible - class MyException(Exception): - pass - - normal_init = CLI.__init__ - - def my_init(self, *args, **kwargs): - normal_init(self, *args, **kwargs) - raise MyException(self.client.api_client.configuration.verify_ssl) - - monkeypatch.setattr(CLI, "__init__", my_init) - - with pytest.raises(MyException) as capture: - self.run_cli(*(["--insecure"] if not verify else []), "ls") - - assert capture.value.args[0] == verify + def test_can_control_ssl_verification_with_arg(self, verify: bool): + with https_reverse_proxy() as proxy_url: + if verify: + insecure_args = [] + else: + insecure_args = ["--insecure"] + + run_cli( + self, + f"--auth={self.user}:{self.password}", + f"--server-host={proxy_url}", + *insecure_args, + "ls", + expected_code=1 if verify else 0, + ) + stdout = self.stdout.getvalue() + + if not verify: + for line in stdout.splitlines(): + int(line) def test_can_control_organization_context(self): org = "cli-test-org" diff --git a/tests/python/cli/util.py b/tests/python/cli/util.py index 034d5d073ace..f90f2bb6b73e 100644 --- a/tests/python/cli/util.py +++ b/tests/python/cli/util.py @@ -3,10 +3,17 @@ # SPDX-License-Identifier: MIT +import contextlib +import http.server +import ssl +import threading import unittest from pathlib import Path -from typing import Any, List, Union +from typing import Any, Dict, Iterator, List, Union +import requests + +from shared.utils.config import BASE_URL from shared.utils.helpers import generate_image_file @@ -29,3 +36,50 @@ def generate_images(dst_dir: Path, count: int) -> List[Path]: filename.write_bytes(generate_image_file(filename.name).getvalue()) filenames.append(filename) return filenames + + +@contextlib.contextmanager +def https_reverse_proxy() -> Iterator[str]: + ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2 + cert_dir = Path(__file__).parent + ssl_context.load_cert_chain(cert_dir / "self-signed.crt", cert_dir / "self-signed.key") + + with http.server.HTTPServer(("localhost", 0), _ProxyHttpRequestHandler) as proxy_server: + proxy_server.socket = ssl_context.wrap_socket( + proxy_server.socket, + server_side=True, + ) + server_thread = threading.Thread(target=proxy_server.serve_forever) + server_thread.start() + try: + yield f"https://localhost:{proxy_server.server_port}" + finally: + proxy_server.shutdown() + server_thread.join() + + +class _ProxyHttpRequestHandler(http.server.BaseHTTPRequestHandler): + def do_GET(self): + response = requests.get(**self._shared_request_args()) + self._translate_response(response) + + def do_POST(self): + body_length = int(self.headers["Content-Length"]) + + response = requests.post(data=self.rfile.read(body_length), **self._shared_request_args()) + self._translate_response(response) + + def _shared_request_args(self) -> Dict[str, Any]: + headers = {k.lower(): v for k, v in self.headers.items()} + del headers["host"] + + return {"url": BASE_URL + self.path, "headers": headers, "timeout": 60, "stream": True} + + def _translate_response(self, response: requests.Response) -> None: + self.send_response(response.status_code) + for key, value in response.headers.items(): + self.send_header(key, value) + self.end_headers() + # Need to use raw here to prevent requests from handling Content-Encoding. + self.wfile.write(response.raw.read()) From 4dd7f2207dd6b1933eaa0e4c9c36df12db55d19f Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 30 Oct 2024 15:16:26 +0200 Subject: [PATCH 13/15] Regression test for UI crash when navigation during resize (#8615) --- tests/cypress.config.js | 4 ++ .../e2e/actions_objects/regression_tests.js | 67 ++++++++++++------- tests/cypress/e2e/features/masks_basics.js | 6 ++ ..._error_canvas_is_busy_at_resize_element.js | 54 --------------- tests/cypress_canvas3d.config.js | 4 ++ 5 files changed, 58 insertions(+), 77 deletions(-) delete mode 100644 tests/cypress/e2e/issues_prs/issue_1922_error_canvas_is_busy_at_resize_element.js diff --git a/tests/cypress.config.js b/tests/cypress.config.js index da1f20d8dee1..17f157c59c8d 100644 --- a/tests/cypress.config.js +++ b/tests/cypress.config.js @@ -1,3 +1,7 @@ +// Copyright (C) 2024 CVAT.ai Corporation +// +// SPDX-License-Identifier: MIT + const { defineConfig } = require('cypress'); const baseConfig = require('./cypress.base.config'); diff --git a/tests/cypress/e2e/actions_objects/regression_tests.js b/tests/cypress/e2e/actions_objects/regression_tests.js index 8cf00b90e0c1..7bf11c7b0d7b 100644 --- a/tests/cypress/e2e/actions_objects/regression_tests.js +++ b/tests/cypress/e2e/actions_objects/regression_tests.js @@ -9,9 +9,9 @@ context('Regression tests', () => { let jobID = null; const taskPayload = { - name: 'Test annotations actions', + name: 'Regression tests', labels: [{ - name: 'label 1', + name: 'car', attributes: [], type: 'any', }], @@ -29,12 +29,9 @@ context('Regression tests', () => { }; const rectanglePayload = { - frame: 99, - objectType: 'shape', shapeType: 'rectangle', - points: [250, 64, 491, 228], occluded: false, - labelName: 'label 1', + labelName: taskPayload.labels[0].name, }; before(() => { @@ -45,41 +42,65 @@ context('Regression tests', () => { taskID = response.taskID; [jobID] = response.jobIDs; - cy.headlessCreateObjects([rectanglePayload], jobID); - cy.visit(`/tasks/${taskID}/jobs/${jobID}`); + cy.headlessCreateObjects([ + { + ...rectanglePayload, frame: 99, points: [250, 64, 491, 228], objectType: 'shape', + }, + { + ...rectanglePayload, frame: 0, points: [10, 10, 30, 30], objectType: 'track', + }, + ], jobID); }); }); - describe('Regression tests', () => { + describe('UI does not crash', () => { + beforeEach(() => { + cy.visit(`/tasks/${taskID}/jobs/${jobID}`); + cy.get('.cvat-canvas-container').should('not.exist'); + cy.get('.cvat-canvas-container').should('exist').and('be.visible'); + }); + it('UI does not crash if to activate an object while frame fetching', () => { - cy.reload(); cy.intercept('GET', '/api/jobs/**/data?**', (req) => { req.continue((res) => { - res.setDelay(1000); + res.setDelay(3000); }); }).as('delayedRequest'); + cy.get('.cvat-player-last-button').click(); - cy.get('#cvat_canvas_shape_1').trigger('mousemove'); - cy.get('#cvat_canvas_shape_1').should('not.have.class', 'cvat_canvas_shape_activated'); + cy.get('#cvat-objects-sidebar-state-item-1').trigger('mousemove'); + cy.get('#cvat-objects-sidebar-state-item-1').should('not.have.class', 'cvat-objects-sidebar-state-active-item'); cy.wait('@delayedRequest'); cy.get('#cvat_canvas_shape_1').trigger('mousemove'); cy.get('#cvat_canvas_shape_1').should('have.class', 'cvat_canvas_shape_activated'); }); + + it('UI does not crash if to navigate during an element resizing (issue 1922)', { scrollBehavior: false }, () => { + cy.get('#cvat_canvas_shape_2').then(([el]) => { + const rect = el.getBoundingClientRect(); + + cy.get('body').trigger('mousemove', rect.x + rect.width / 2, rect.y + rect.height / 2); + cy.get('#cvat_canvas_shape_2').should('have.class', 'cvat_canvas_shape_activated'); + + cy.get('body').trigger('mousedown', rect.right, rect.bottom, { button: 0 }); + cy.get('body').trigger('mousemove', rect.right + 100, rect.bottom + 100); + + cy.get('body').type('f'); // go to next frame + cy.get('body').trigger('mouseup'); + + // Page with the error is missing + cy.get('.cvat-global-boundary').should('not.exist'); + cy.checkFrameNum(0); + }); + }); }); after(() => { + if (taskID !== null) { + cy.headlessDeleteTask(taskID); + } cy.logout(); - cy.getAuthKey().then((response) => { - const authKey = response.body.key; - cy.request({ - method: 'DELETE', - url: `/api/tasks/${taskID}`, - headers: { - Authorization: `Token ${authKey}`, - }, - }); - }); }); }); diff --git a/tests/cypress/e2e/features/masks_basics.js b/tests/cypress/e2e/features/masks_basics.js index b39d6ea769d7..3e119e97f039 100644 --- a/tests/cypress/e2e/features/masks_basics.js +++ b/tests/cypress/e2e/features/masks_basics.js @@ -156,6 +156,12 @@ context('Manipulations with masks', { scrollBehavior: false }, () => { cy.interactAnnotationObjectMenu('#cvat-objects-sidebar-state-item-1', 'Edit'); cy.drawMask(editingActions); + + // Check issue fixed in https://github.com/cvat-ai/cvat/pull/8598 + // Frames navigation should not work during editing + cy.get('.cvat-player-next-button').click(); + cy.checkFrameNum(0); + cy.finishMaskDrawing(); }); diff --git a/tests/cypress/e2e/issues_prs/issue_1922_error_canvas_is_busy_at_resize_element.js b/tests/cypress/e2e/issues_prs/issue_1922_error_canvas_is_busy_at_resize_element.js deleted file mode 100644 index 53b5606f457b..000000000000 --- a/tests/cypress/e2e/issues_prs/issue_1922_error_canvas_is_busy_at_resize_element.js +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2020-2022 Intel Corporation -// Copyright (C) 2023 CVAT.ai Corporation -// -// SPDX-License-Identifier: MIT - -/// - -import { taskName, labelName } from '../../support/const'; - -context('Check error canvas is busy at resize element', () => { - const issueId = '1922'; - const createRectangleShape2Points = { - points: 'By 2 Points', - type: 'Shape', - labelName, - firstX: 100, - firstY: 100, - secondX: 300, - secondY: 300, - }; - - before(() => { - cy.openTaskJob(taskName); - }); - - describe(`Testing issue "${issueId}"`, () => { - it('Create an object in first frame', () => { - cy.createRectangle(createRectangleShape2Points); - }); - - it('Go to next frame and create an object in second frame', () => { - cy.get('.cvat-player-next-button').click(); - cy.createRectangle(createRectangleShape2Points); - }); - - it('Switching mode of button on "back with a filter"', () => { - cy.get('.cvat-player-previous-button').rightclick(); - cy.get('.cvat-player-previous-filtered-inlined-button').click(); - }); - - it('Resize element on second frame and go to previous frame at resizing element', () => { - const { secondX, secondY } = createRectangleShape2Points; - cy.get('.cvat-canvas-container').trigger('mousemove', secondX - 10, secondY - 10); // activate second shape - cy.get('.cvat-canvas-container').trigger('mousedown', secondX, secondY, { button: 0 }); - cy.get('.cvat-canvas-container').trigger('mousemove', secondX + 100, secondY + 100); - cy.get('body').type('d'); // go to previous frame - cy.get('body').trigger('mouseup'); - }); - - it('Page with the error is missing', () => { - cy.get('.cvat-global-boundary').should('not.exist'); - }); - }); -}); diff --git a/tests/cypress_canvas3d.config.js b/tests/cypress_canvas3d.config.js index e1cd5ede69f2..f542fe78bde9 100644 --- a/tests/cypress_canvas3d.config.js +++ b/tests/cypress_canvas3d.config.js @@ -1,3 +1,7 @@ +// Copyright (C) 2024 CVAT.ai Corporation +// +// SPDX-License-Identifier: MIT + const { defineConfig } = require('cypress'); const baseConfig = require('./cypress.base.config'); From 23e1e0a8539d75bc2367566d2a5138e12a29a64a Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Wed, 30 Oct 2024 15:31:54 +0200 Subject: [PATCH 14/15] Fix return type annotations for functions used with @contextmanager (#8617) Some of them are annotated with an `Iterator` return type. However... It just occurred to me that `@contextmanager` cannot work with a function that returns a plain iterator, since it relies on the generator class's `throw` method. `contextmanager` is defined in typeshed as accepting an iterator-returning function, but that appears to be a bug: . Change all such annotations to a `Generator` type instead. Some annotations are also broken in other ways; fix them too. --- cvat-sdk/cvat_sdk/core/client.py | 4 ++-- cvat-sdk/cvat_sdk/core/progress.py | 4 ++-- cvat-sdk/cvat_sdk/core/utils.py | 4 ++-- cvat/apps/engine/media_extractors.py | 4 +++- tests/python/cli/util.py | 4 ++-- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cvat-sdk/cvat_sdk/core/client.py b/cvat-sdk/cvat_sdk/core/client.py index add7ccb5f3d3..0ae0b88ecad9 100644 --- a/cvat-sdk/cvat_sdk/core/client.py +++ b/cvat-sdk/cvat_sdk/core/client.py @@ -10,7 +10,7 @@ from contextlib import contextmanager, suppress from pathlib import Path from time import sleep -from typing import Any, Dict, Iterator, Optional, Sequence, Tuple, TypeVar +from typing import Any, Dict, Generator, Optional, Sequence, Tuple, TypeVar import attrs import packaging.specifiers as specifiers @@ -121,7 +121,7 @@ def organization_slug(self, org_slug: Optional[str]): self.api_client.default_headers[self._ORG_SLUG_HEADER] = org_slug @contextmanager - def organization_context(self, slug: str) -> Iterator[None]: + def organization_context(self, slug: str) -> Generator[None, None, None]: prev_slug = self.organization_slug self.organization_slug = slug try: diff --git a/cvat-sdk/cvat_sdk/core/progress.py b/cvat-sdk/cvat_sdk/core/progress.py index 7fd2d13a2cd2..fd844de722a0 100644 --- a/cvat-sdk/cvat_sdk/core/progress.py +++ b/cvat-sdk/cvat_sdk/core/progress.py @@ -6,7 +6,7 @@ from __future__ import annotations import contextlib -from typing import ContextManager, Iterable, Optional, TypeVar +from typing import Generator, Iterable, Optional, TypeVar T = TypeVar("T") @@ -26,7 +26,7 @@ class ProgressReporter: """ @contextlib.contextmanager - def task(self, **kwargs) -> ContextManager[None]: + def task(self, **kwargs) -> Generator[None, None, None]: """ Returns a context manager that represents a long-running task for which progress can be reported. diff --git a/cvat-sdk/cvat_sdk/core/utils.py b/cvat-sdk/cvat_sdk/core/utils.py index 0706a2eec613..1ef434e3ad5b 100644 --- a/cvat-sdk/cvat_sdk/core/utils.py +++ b/cvat-sdk/cvat_sdk/core/utils.py @@ -13,7 +13,7 @@ BinaryIO, ContextManager, Dict, - Iterator, + Generator, Literal, Sequence, TextIO, @@ -43,7 +43,7 @@ def atomic_writer( @contextlib.contextmanager def atomic_writer( path: Union[os.PathLike, str], mode: Literal["w", "wb"], encoding: str = "UTF-8" -) -> Iterator[IO]: +) -> Generator[IO, None, None]: """ Returns a context manager that, when entered, returns a handle to a temporary file opened with the specified `mode` and `encoding`. If the context manager diff --git a/cvat/apps/engine/media_extractors.py b/cvat/apps/engine/media_extractors.py index a64637359ff5..c923083b18b3 100644 --- a/cvat/apps/engine/media_extractors.py +++ b/cvat/apps/engine/media_extractors.py @@ -539,7 +539,9 @@ def extract(self): class _AvVideoReading: @contextmanager - def read_av_container(self, source: Union[str, io.BytesIO]) -> av.container.InputContainer: + def read_av_container( + self, source: Union[str, io.BytesIO] + ) -> Generator[av.container.InputContainer, None, None]: if isinstance(source, io.BytesIO): source.seek(0) # required for re-reading diff --git a/tests/python/cli/util.py b/tests/python/cli/util.py index f90f2bb6b73e..ff1173fa4a8d 100644 --- a/tests/python/cli/util.py +++ b/tests/python/cli/util.py @@ -9,7 +9,7 @@ import threading import unittest from pathlib import Path -from typing import Any, Dict, Iterator, List, Union +from typing import Any, Dict, Generator, List, Union import requests @@ -39,7 +39,7 @@ def generate_images(dst_dir: Path, count: int) -> List[Path]: @contextlib.contextmanager -def https_reverse_proxy() -> Iterator[str]: +def https_reverse_proxy() -> Generator[str, None, None]: ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2 cert_dir = Path(__file__).parent From f1c7a1e3d5e67bf5044c6dd23ae288909cbd0a3c Mon Sep 17 00:00:00 2001 From: "cvat-bot[bot]" <147643061+cvat-bot[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 09:44:59 +0000 Subject: [PATCH 15/15] Prepare release v2.21.3 --- CHANGELOG.md | 30 +++++++++++++++++++ ...121638_klakhov_fix_request_status_crush.md | 4 --- ...20241023_120236_klakhov_improve_gt_tags.md | 7 ----- changelog.d/20241028_140908_sekachev.bs.md | 4 --- changelog.d/20241028_140945_sekachev.bs.md | 4 --- ...29_113229_maria_drop_python_3_8_support.md | 4 --- .../20241029_134200_roman_cli_refactor.md | 4 --- cvat-cli/requirements/base.txt | 2 +- cvat-cli/src/cvat_cli/version.py | 2 +- cvat-sdk/gen/generate.sh | 2 +- cvat/__init__.py | 2 +- cvat/schema.yml | 2 +- docker-compose.yml | 18 +++++------ helm-chart/values.yaml | 4 +-- 14 files changed, 46 insertions(+), 43 deletions(-) delete mode 100644 changelog.d/20241022_121638_klakhov_fix_request_status_crush.md delete mode 100644 changelog.d/20241023_120236_klakhov_improve_gt_tags.md delete mode 100644 changelog.d/20241028_140908_sekachev.bs.md delete mode 100644 changelog.d/20241028_140945_sekachev.bs.md delete mode 100644 changelog.d/20241029_113229_maria_drop_python_3_8_support.md delete mode 100644 changelog.d/20241029_134200_roman_cli_refactor.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b9a4fd9584c..42d2893080be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 + +## \[2.21.3\] - 2024-10-31 + +### Changed + +- CLI no longer prints the stack trace in case of HTTP errors + () + +### Removed + +- Dropped support for Python 3.8 since its EOL was on 2024-10-07 + () + +### Fixed + +- Requests page crush with `Cannot read property 'target' of undefined` error + () + +- Tags in ground truth job were displayed as `tag (GT)` + () + +- Tags in ground truth job couldn't be deleted via `x` button + () + +- Exception 'Canvas is busy' when change frame during drag/resize a track + () + +- A shape gets shifted if auto save triggered during dragging + () + ## \[2.21.2\] - 2024-10-24 diff --git a/changelog.d/20241022_121638_klakhov_fix_request_status_crush.md b/changelog.d/20241022_121638_klakhov_fix_request_status_crush.md deleted file mode 100644 index 082b59a70d4f..000000000000 --- a/changelog.d/20241022_121638_klakhov_fix_request_status_crush.md +++ /dev/null @@ -1,4 +0,0 @@ -### Fixed - -- Requests page crush with `Cannot read property 'target' of undefined` error - () diff --git a/changelog.d/20241023_120236_klakhov_improve_gt_tags.md b/changelog.d/20241023_120236_klakhov_improve_gt_tags.md deleted file mode 100644 index 79c1e724a35a..000000000000 --- a/changelog.d/20241023_120236_klakhov_improve_gt_tags.md +++ /dev/null @@ -1,7 +0,0 @@ -### Fixed - -- Tags in ground truth job were displayed as `tag (GT)` - () - -- Tags in ground truth job couldn't be deleted via `x` button - () diff --git a/changelog.d/20241028_140908_sekachev.bs.md b/changelog.d/20241028_140908_sekachev.bs.md deleted file mode 100644 index 507346e3d051..000000000000 --- a/changelog.d/20241028_140908_sekachev.bs.md +++ /dev/null @@ -1,4 +0,0 @@ -### Fixed - -- Exception 'Canvas is busy' when change frame during drag/resize a track - () diff --git a/changelog.d/20241028_140945_sekachev.bs.md b/changelog.d/20241028_140945_sekachev.bs.md deleted file mode 100644 index fd401e390a15..000000000000 --- a/changelog.d/20241028_140945_sekachev.bs.md +++ /dev/null @@ -1,4 +0,0 @@ -### Fixed - -- A shape gets shifted if auto save triggered during dragging - () diff --git a/changelog.d/20241029_113229_maria_drop_python_3_8_support.md b/changelog.d/20241029_113229_maria_drop_python_3_8_support.md deleted file mode 100644 index ca57734aa1dc..000000000000 --- a/changelog.d/20241029_113229_maria_drop_python_3_8_support.md +++ /dev/null @@ -1,4 +0,0 @@ -### Removed - -- Dropped support for Python 3.8 since its EOL was on 2024-10-07 - () diff --git a/changelog.d/20241029_134200_roman_cli_refactor.md b/changelog.d/20241029_134200_roman_cli_refactor.md deleted file mode 100644 index 9cb76e2d52a5..000000000000 --- a/changelog.d/20241029_134200_roman_cli_refactor.md +++ /dev/null @@ -1,4 +0,0 @@ -### Changed - -- CLI no longer prints the stack trace in case of HTTP errors - () diff --git a/cvat-cli/requirements/base.txt b/cvat-cli/requirements/base.txt index e9be53974d91..063da9c864b4 100644 --- a/cvat-cli/requirements/base.txt +++ b/cvat-cli/requirements/base.txt @@ -1,3 +1,3 @@ -cvat-sdk~=2.22.0 +cvat-sdk~=2.21.3 Pillow>=10.3.0 setuptools>=70.0.0 # not directly required, pinned by Snyk to avoid a vulnerability diff --git a/cvat-cli/src/cvat_cli/version.py b/cvat-cli/src/cvat_cli/version.py index b2829a54b105..3899bcb7cd96 100644 --- a/cvat-cli/src/cvat_cli/version.py +++ b/cvat-cli/src/cvat_cli/version.py @@ -1 +1 @@ -VERSION = "2.22.0" +VERSION = "2.21.3" diff --git a/cvat-sdk/gen/generate.sh b/cvat-sdk/gen/generate.sh index ca9a08be98fe..c506564999b6 100755 --- a/cvat-sdk/gen/generate.sh +++ b/cvat-sdk/gen/generate.sh @@ -8,7 +8,7 @@ set -e GENERATOR_VERSION="v6.0.1" -VERSION="2.22.0" +VERSION="2.21.3" LIB_NAME="cvat_sdk" LAYER1_LIB_NAME="${LIB_NAME}/api_client" DST_DIR="$(cd "$(dirname -- "$0")/.." && pwd)" diff --git a/cvat/__init__.py b/cvat/__init__.py index d72cb8e0099c..ac47316ad0a1 100644 --- a/cvat/__init__.py +++ b/cvat/__init__.py @@ -4,6 +4,6 @@ from cvat.utils.version import get_version -VERSION = (2, 22, 0, 'alpha', 0) +VERSION = (2, 21, 3, 'final', 0) __version__ = get_version(VERSION) diff --git a/cvat/schema.yml b/cvat/schema.yml index 4bda991cbb12..3af7944889b4 100644 --- a/cvat/schema.yml +++ b/cvat/schema.yml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: title: CVAT REST API - version: 2.22.0 + version: 2.21.3 description: REST API for Computer Vision Annotation Tool (CVAT) termsOfService: https://www.google.com/policies/terms/ contact: diff --git a/docker-compose.yml b/docker-compose.yml index 0d3f802c82f5..b51e38fce7d5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -79,7 +79,7 @@ services: cvat_server: container_name: cvat_server - image: cvat/server:${CVAT_VERSION:-dev} + image: cvat/server:${CVAT_VERSION:-v2.21.3} restart: always depends_on: <<: *backend-deps @@ -113,7 +113,7 @@ services: cvat_utils: container_name: cvat_utils - image: cvat/server:${CVAT_VERSION:-dev} + image: cvat/server:${CVAT_VERSION:-v2.21.3} restart: always depends_on: *backend-deps environment: @@ -130,7 +130,7 @@ services: cvat_worker_import: container_name: cvat_worker_import - image: cvat/server:${CVAT_VERSION:-dev} + image: cvat/server:${CVAT_VERSION:-v2.21.3} restart: always depends_on: *backend-deps environment: @@ -146,7 +146,7 @@ services: cvat_worker_export: container_name: cvat_worker_export - image: cvat/server:${CVAT_VERSION:-dev} + image: cvat/server:${CVAT_VERSION:-v2.21.3} restart: always depends_on: *backend-deps environment: @@ -162,7 +162,7 @@ services: cvat_worker_annotation: container_name: cvat_worker_annotation - image: cvat/server:${CVAT_VERSION:-dev} + image: cvat/server:${CVAT_VERSION:-v2.21.3} restart: always depends_on: *backend-deps environment: @@ -178,7 +178,7 @@ services: cvat_worker_webhooks: container_name: cvat_worker_webhooks - image: cvat/server:${CVAT_VERSION:-dev} + image: cvat/server:${CVAT_VERSION:-v2.21.3} restart: always depends_on: *backend-deps environment: @@ -194,7 +194,7 @@ services: cvat_worker_quality_reports: container_name: cvat_worker_quality_reports - image: cvat/server:${CVAT_VERSION:-dev} + image: cvat/server:${CVAT_VERSION:-v2.21.3} restart: always depends_on: *backend-deps environment: @@ -210,7 +210,7 @@ services: cvat_worker_analytics_reports: container_name: cvat_worker_analytics_reports - image: cvat/server:${CVAT_VERSION:-dev} + image: cvat/server:${CVAT_VERSION:-v2.21.3} restart: always depends_on: *backend-deps environment: @@ -226,7 +226,7 @@ services: cvat_ui: container_name: cvat_ui - image: cvat/ui:${CVAT_VERSION:-dev} + image: cvat/ui:${CVAT_VERSION:-v2.21.3} restart: always depends_on: - cvat_server diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml index 05d74e906e98..d0906952f96d 100644 --- a/helm-chart/values.yaml +++ b/helm-chart/values.yaml @@ -129,7 +129,7 @@ cvat: additionalVolumeMounts: [] replicas: 1 image: cvat/server - tag: dev + tag: v2.21.3 imagePullPolicy: Always permissionFix: enabled: true @@ -153,7 +153,7 @@ cvat: frontend: replicas: 1 image: cvat/ui - tag: dev + tag: v2.21.3 imagePullPolicy: Always labels: {} # test: test