diff --git a/.github/workflows/build-branch.yml b/.github/workflows/build-branch.yml index 8ad71e7d6..00fc16531 100644 --- a/.github/workflows/build-branch.yml +++ b/.github/workflows/build-branch.yml @@ -397,7 +397,6 @@ jobs: with: tag_name: ${{ env.REL_VERSION }} name: ${{ env.REL_VERSION }} - target_commitish: ${{ github.sha }} draft: false prerelease: ${{ env.IS_PRERELEASE }} generate_release_notes: true diff --git a/apps/admin/Dockerfile.admin b/apps/admin/Dockerfile.admin index 19ad2c392..63b30a1c3 100644 --- a/apps/admin/Dockerfile.admin +++ b/apps/admin/Dockerfile.admin @@ -13,7 +13,7 @@ RUN corepack enable pnpm FROM base AS builder -RUN pnpm add -g turbo@2.9.4 +RUN pnpm add -g turbo@2.8.12 COPY . . diff --git a/apps/api/plane/app/views/issue/base.py b/apps/api/plane/app/views/issue/base.py index bb331802c..98a59b648 100644 --- a/apps/api/plane/app/views/issue/base.py +++ b/apps/api/plane/app/views/issue/base.py @@ -1118,7 +1118,7 @@ class IssueBulkUpdateDateEndpoint(BaseAPIView): epoch = int(timezone.now().timestamp()) # Fetch all relevant issues in a single query - issues = list(Issue.objects.filter(id__in=issue_ids, workspace__slug=slug, project_id=project_id)) + issues = list(Issue.objects.filter(id__in=issue_ids)) issues_dict = {str(issue.id): issue for issue in issues} issues_to_update = [] diff --git a/apps/api/plane/app/views/project/member.py b/apps/api/plane/app/views/project/member.py index 7dfe70900..1ad7639fb 100644 --- a/apps/api/plane/app/views/project/member.py +++ b/apps/api/plane/app/views/project/member.py @@ -226,36 +226,21 @@ class ProjectMemberViewSet(BaseViewSet): is_active=True, ) - if "role" in request.data: - # Only Admins can modify roles - if requested_project_member.role < ROLE.ADMIN.value and not is_workspace_admin: - return Response( - {"error": "You do not have permission to update roles"}, - status=status.HTTP_403_FORBIDDEN, - ) + if workspace_role in [5] and int(request.data.get("role", project_member.role)) in [15, 20]: + return Response( + {"error": "You cannot add a user with role higher than the workspace role"}, + status=status.HTTP_400_BAD_REQUEST, + ) - # Cannot modify a member whose role is equal to or higher than your own - if project_member.role >= requested_project_member.role and not is_workspace_admin: - return Response( - {"error": "You cannot update the role of a member with a role equal to or higher than your own"}, - status=status.HTTP_403_FORBIDDEN, - ) - - new_role = int(request.data.get("role")) - - # Cannot assign a role equal to or higher than your own - if new_role >= requested_project_member.role and not is_workspace_admin: - return Response( - {"error": "You cannot assign a role equal to or higher than your own"}, - status=status.HTTP_403_FORBIDDEN, - ) - - # Cannot assign a role higher than the target's workspace role - if workspace_role in [5] and new_role in [15, 20]: - return Response( - {"error": "You cannot add a user with role higher than the workspace role"}, - status=status.HTTP_400_BAD_REQUEST, - ) + if ( + "role" in request.data + and int(request.data.get("role", project_member.role)) > requested_project_member.role + and not is_workspace_admin + ): + return Response( + {"error": "You cannot update a role that is higher than your own role"}, + status=status.HTTP_400_BAD_REQUEST, + ) serializer = ProjectMemberSerializer(project_member, data=request.data, partial=True) diff --git a/apps/api/plane/bgtasks/work_item_link_task.py b/apps/api/plane/bgtasks/work_item_link_task.py index 5cf0fbb19..442396c7f 100644 --- a/apps/api/plane/bgtasks/work_item_link_task.py +++ b/apps/api/plane/bgtasks/work_item_link_task.py @@ -13,7 +13,7 @@ from bs4 import BeautifulSoup from urllib.parse import urlparse, urljoin import base64 import ipaddress -from typing import Dict, Any, Tuple +from typing import Dict, Any from typing import Optional from plane.db.models import IssueLink from plane.utils.exception_logger import log_exception @@ -66,52 +66,6 @@ def validate_url_ip(url: str) -> None: MAX_REDIRECTS = 5 -def safe_get( - url: str, - headers: Optional[Dict[str, str]] = None, - timeout: int = 1, -) -> Tuple[requests.Response, str]: - """ - Perform a GET request that validates every redirect hop against private IPs. - Prevents SSRF by ensuring no redirect lands on a private/internal address. - - Args: - url: The URL to fetch - headers: Optional request headers - timeout: Request timeout in seconds - - Returns: - A tuple of (final Response object, final URL after redirects) - - Raises: - ValueError: If any URL in the redirect chain points to a private IP - requests.RequestException: On network errors - RuntimeError: If max redirects exceeded - """ - validate_url_ip(url) - - current_url = url - response = requests.get( - current_url, headers=headers, timeout=timeout, allow_redirects=False - ) - - redirect_count = 0 - while response.is_redirect: - if redirect_count >= MAX_REDIRECTS: - raise RuntimeError(f"Too many redirects for URL: {url}") - redirect_url = response.headers.get("Location") - if not redirect_url: - break - current_url = urljoin(current_url, redirect_url) - validate_url_ip(current_url) - redirect_count += 1 - response = requests.get( - current_url, headers=headers, timeout=timeout, allow_redirects=False - ) - - return response, current_url - - def crawl_work_item_link_title_and_favicon(url: str) -> Dict[str, Any]: """ Crawls a URL to extract the title and favicon. @@ -132,8 +86,26 @@ def crawl_work_item_link_title_and_favicon(url: str) -> Dict[str, Any]: title = None final_url = url + validate_url_ip(final_url) + try: - response, final_url = safe_get(url, headers=headers) + # Manually follow redirects to validate each URL before requesting + redirect_count = 0 + response = requests.get(final_url, headers=headers, timeout=1, allow_redirects=False) + + while response.is_redirect and redirect_count < MAX_REDIRECTS: + redirect_url = response.headers.get("Location") + if not redirect_url: + break + # Resolve relative redirects against current URL + final_url = urljoin(final_url, redirect_url) + # Validate the redirect target BEFORE making the request + validate_url_ip(final_url) + redirect_count += 1 + response = requests.get(final_url, headers=headers, timeout=1, allow_redirects=False) + + if redirect_count >= MAX_REDIRECTS: + logger.warning(f"Too many redirects for URL: {url}") soup = BeautifulSoup(response.content, "html.parser") title_tag = soup.find("title") @@ -141,10 +113,8 @@ def crawl_work_item_link_title_and_favicon(url: str) -> Dict[str, Any]: except requests.RequestException as e: logger.warning(f"Failed to fetch HTML for title: {str(e)}") - except (ValueError, RuntimeError) as e: - logger.warning(f"URL validation failed: {str(e)}") - # Fetch and encode favicon using final URL (after redirects) for correct relative href resolution + # Fetch and encode favicon using final URL (after redirects) favicon_base64 = fetch_and_encode_favicon(headers, soup, final_url) # Prepare result @@ -234,7 +204,9 @@ def fetch_and_encode_favicon( "favicon_base64": f"data:image/svg+xml;base64,{DEFAULT_FAVICON}", } - response, _ = safe_get(favicon_url, headers=headers) + validate_url_ip(favicon_url) + + response = requests.get(favicon_url, headers=headers, timeout=1) # Get content type content_type = response.headers.get("content-type", "image/x-icon") diff --git a/apps/api/plane/tests/unit/bg_tasks/test_work_item_link_task.py b/apps/api/plane/tests/unit/bg_tasks/test_work_item_link_task.py deleted file mode 100644 index 2838260e8..000000000 --- a/apps/api/plane/tests/unit/bg_tasks/test_work_item_link_task.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (c) 2023-present Plane Software, Inc. and contributors -# SPDX-License-Identifier: AGPL-3.0-only -# See the LICENSE file for details. - -import pytest -from unittest.mock import patch, MagicMock -from plane.bgtasks.work_item_link_task import safe_get, validate_url_ip - - -def _make_response(status_code=200, headers=None, is_redirect=False, content=b""): - """Create a mock requests.Response.""" - resp = MagicMock() - resp.status_code = status_code - resp.is_redirect = is_redirect - resp.headers = headers or {} - resp.content = content - return resp - - -@pytest.mark.unit -class TestValidateUrlIp: - """Test validate_url_ip blocks private/internal IPs.""" - - def test_rejects_private_ip(self): - with patch("plane.bgtasks.work_item_link_task.socket.getaddrinfo") as mock_dns: - mock_dns.return_value = [(None, None, None, None, ("192.168.1.1", 0))] - with pytest.raises(ValueError, match="private/internal"): - validate_url_ip("http://example.com") - - def test_rejects_loopback(self): - with patch("plane.bgtasks.work_item_link_task.socket.getaddrinfo") as mock_dns: - mock_dns.return_value = [(None, None, None, None, ("127.0.0.1", 0))] - with pytest.raises(ValueError, match="private/internal"): - validate_url_ip("http://example.com") - - def test_rejects_non_http_scheme(self): - with pytest.raises(ValueError, match="Only HTTP and HTTPS"): - validate_url_ip("file:///etc/passwd") - - def test_allows_public_ip(self): - with patch("plane.bgtasks.work_item_link_task.socket.getaddrinfo") as mock_dns: - mock_dns.return_value = [(None, None, None, None, ("93.184.216.34", 0))] - validate_url_ip("https://example.com") # Should not raise - - -@pytest.mark.unit -class TestSafeGet: - """Test safe_get follows redirects safely and blocks SSRF.""" - - @patch("plane.bgtasks.work_item_link_task.requests.get") - @patch("plane.bgtasks.work_item_link_task.validate_url_ip") - def test_returns_response_for_non_redirect(self, mock_validate, mock_get): - final_resp = _make_response(status_code=200, content=b"OK") - mock_get.return_value = final_resp - - response, final_url = safe_get("https://example.com") - - assert response is final_resp - assert final_url == "https://example.com" - mock_validate.assert_called_once_with("https://example.com") - - @patch("plane.bgtasks.work_item_link_task.requests.get") - @patch("plane.bgtasks.work_item_link_task.validate_url_ip") - def test_follows_redirect_and_validates_each_hop(self, mock_validate, mock_get): - redirect_resp = _make_response( - status_code=301, - is_redirect=True, - headers={"Location": "https://other.com/page"}, - ) - final_resp = _make_response(status_code=200, content=b"OK") - mock_get.side_effect = [redirect_resp, final_resp] - - response, final_url = safe_get("https://example.com") - - assert response is final_resp - assert final_url == "https://other.com/page" - # Should validate both the initial URL and the redirect target - assert mock_validate.call_count == 2 - mock_validate.assert_any_call("https://example.com") - mock_validate.assert_any_call("https://other.com/page") - - @patch("plane.bgtasks.work_item_link_task.requests.get") - @patch("plane.bgtasks.work_item_link_task.validate_url_ip") - def test_blocks_redirect_to_private_ip(self, mock_validate, mock_get): - redirect_resp = _make_response( - status_code=302, - is_redirect=True, - headers={"Location": "http://192.168.1.1:8080"}, - ) - mock_get.return_value = redirect_resp - # First call (initial URL) succeeds, second call (redirect target) fails - mock_validate.side_effect = [None, ValueError("Access to private/internal networks is not allowed")] - - with pytest.raises(ValueError, match="private/internal"): - safe_get("https://evil.com/redirect") - - @patch("plane.bgtasks.work_item_link_task.requests.get") - @patch("plane.bgtasks.work_item_link_task.validate_url_ip") - def test_raises_on_too_many_redirects(self, mock_validate, mock_get): - redirect_resp = _make_response( - status_code=302, - is_redirect=True, - headers={"Location": "https://example.com/loop"}, - ) - mock_get.return_value = redirect_resp - - with pytest.raises(RuntimeError, match="Too many redirects"): - safe_get("https://example.com/start") - - @patch("plane.bgtasks.work_item_link_task.requests.get") - @patch("plane.bgtasks.work_item_link_task.validate_url_ip") - def test_succeeds_at_exact_max_redirects(self, mock_validate, mock_get): - """After exactly MAX_REDIRECTS hops, if the final response is 200, it should succeed.""" - redirect_resp = _make_response( - status_code=302, - is_redirect=True, - headers={"Location": "https://example.com/next"}, - ) - final_resp = _make_response(status_code=200, content=b"OK") - # 5 redirects then a 200 - mock_get.side_effect = [redirect_resp] * 5 + [final_resp] - - response, final_url = safe_get("https://example.com/start") - - assert response is final_resp - assert not response.is_redirect diff --git a/apps/live/Dockerfile.live b/apps/live/Dockerfile.live index 801afca67..58d3f58c7 100644 --- a/apps/live/Dockerfile.live +++ b/apps/live/Dockerfile.live @@ -15,7 +15,7 @@ RUN apk update RUN apk add --no-cache libc6-compat # Set working directory WORKDIR /app -ARG TURBO_VERSION=2.9.4 +ARG TURBO_VERSION=2.8.12 RUN corepack enable pnpm && pnpm add -g turbo@${TURBO_VERSION} COPY . . RUN turbo prune --scope=live --docker diff --git a/apps/space/Dockerfile.space b/apps/space/Dockerfile.space index 60d4a155a..10bc612f6 100644 --- a/apps/space/Dockerfile.space +++ b/apps/space/Dockerfile.space @@ -13,7 +13,7 @@ RUN corepack enable pnpm FROM base AS builder -RUN pnpm add -g turbo@2.9.4 +RUN pnpm add -g turbo@2.8.12 COPY . . diff --git a/apps/web/Dockerfile.web b/apps/web/Dockerfile.web index 38af19e74..ab1a40462 100644 --- a/apps/web/Dockerfile.web +++ b/apps/web/Dockerfile.web @@ -14,7 +14,7 @@ RUN apk add --no-cache libc6-compat # Set working directory WORKDIR /app -ARG TURBO_VERSION=2.9.4 +ARG TURBO_VERSION=2.8.12 RUN corepack enable pnpm && pnpm add -g turbo@${TURBO_VERSION} COPY . . diff --git a/apps/web/core/components/inbox/modals/snooze-issue-modal.tsx b/apps/web/core/components/inbox/modals/snooze-issue-modal.tsx index 1ad83a6b0..708458efb 100644 --- a/apps/web/core/components/inbox/modals/snooze-issue-modal.tsx +++ b/apps/web/core/components/inbox/modals/snooze-issue-modal.tsx @@ -26,13 +26,7 @@ export function InboxIssueSnoozeModal(props: InboxIssueSnoozeModalProps) { const { t } = useTranslation(); return ( - +
| undefined, payload: Partial) => { - if (!workspaceSlug || !data?.project_id || !data?.id) return; - // return if user is not trying to change the cycle, i.e - // - cycle_id is not present in payload - // - cycle_id is the same as the current cycle id - if (!("cycle_id" in payload) || isEqual(data?.cycle_id, payload.cycle_id)) return; - - const slug = workspaceSlug.toString(); - - // Removing the cycle - const currentCycleId = data?.cycle_id; - if (currentCycleId && payload.cycle_id === null) { - await issues.removeIssueFromCycle(slug, data.project_id, currentCycleId, data.id); - fetchCycleDetails(slug, data.project_id, currentCycleId).catch((error) => { - console.error(error); - }); - } - - // Adding the cycle - const newCycleId = payload.cycle_id; - if (newCycleId && newCycleId !== "" && (payload.cycle_id !== cycleId || storeType !== EIssuesStoreType.CYCLE)) { - await addIssueToCycle(data as TBaseIssue, newCycleId); - } - }; - - const handleModuleChange = async (data: Partial, payload: Partial) => { - if (!workspaceSlug || !data?.project_id || !data?.id) return; - // return if user is not trying to change the module, i.e - // - module_ids is not present in payload - // - module_ids is not an array - // - module_ids is the same as the current module ids - if ( - !("module_ids" in payload) || - !Array.isArray(payload.module_ids) || - isEqual(data?.module_ids, payload.module_ids) - ) - return; - - const updatedModuleIds = xor(data.module_ids, payload.module_ids); - const modulesToAdd: string[] = []; - const modulesToRemove: string[] = []; - - for (const moduleId of updatedModuleIds) { - if (data.module_ids?.includes(moduleId)) { - modulesToRemove.push(moduleId); - } else { - modulesToAdd.push(moduleId); - } - } - // update modules if there are modules to add or remove - if (modulesToAdd.length > 0 || modulesToRemove.length > 0) { - await issues.changeModulesInIssue( - workspaceSlug.toString(), - data.project_id, - data.id, - modulesToAdd, - modulesToRemove - ); - } - }; - const handleUpdateIssue = async (payload: Partial): Promise => { if (!workspaceSlug || !payload.project_id || !data?.id) return; @@ -328,10 +267,41 @@ export const CreateUpdateIssueModalBase = observer(function CreateUpdateIssueMod if (isDraft) await draftIssues.updateIssue(workspaceSlug.toString(), data.id, payload); else if (updateIssue) await updateIssue(payload.project_id, data.id, payload); - // Run cycle, module, and property changes sequentially to avoid - // optimistic store writes from racing against each other. - await handleCycleChange(data, payload); - await handleModuleChange(data, payload); + // check if we should add/remove issue to/from cycle + if ( + payload.cycle_id && + payload.cycle_id !== "" && + (payload.cycle_id !== cycleId || storeType !== EIssuesStoreType.CYCLE) + ) { + await addIssueToCycle(data as TBaseIssue, payload.cycle_id); + } + if (data.cycle_id && !payload.cycle_id && data.project_id) { + await issues.removeIssueFromCycle(workspaceSlug.toString(), data.project_id, data.cycle_id, data.id); + fetchCycleDetails(workspaceSlug.toString(), data.project_id, data.cycle_id); + } + + if (data.module_ids && payload.module_ids && data.project_id) { + const updatedModuleIds = xor(data.module_ids, payload.module_ids); + const modulesToAdd: string[] = []; + const modulesToRemove: string[] = []; + + for (const moduleId of updatedModuleIds) { + if (data.module_ids.includes(moduleId)) { + modulesToRemove.push(moduleId); + } else { + modulesToAdd.push(moduleId); + } + } + await issues.changeModulesInIssue( + workspaceSlug.toString(), + data.project_id, + data.id, + modulesToAdd, + modulesToRemove + ); + } + + // add other property values await handleCreateUpdatePropertyValues({ issueId: data.id, issueTypeId: payload.type_id, diff --git a/deployments/aio/community/Dockerfile b/deployments/aio/community/Dockerfile index 642c9bac6..47e1227d9 100644 --- a/deployments/aio/community/Dockerfile +++ b/deployments/aio/community/Dockerfile @@ -6,12 +6,12 @@ FROM --platform=$BUILDPLATFORM tonistiigi/binfmt AS binfmt # ************************************************** FROM node:22-alpine AS node -FROM makeplane/plane-frontend:${PLANE_VERSION} AS web-img -FROM makeplane/plane-backend:${PLANE_VERSION} AS backend-img -FROM makeplane/plane-space:${PLANE_VERSION} AS space-img -FROM makeplane/plane-admin:${PLANE_VERSION} AS admin-img -FROM makeplane/plane-live:${PLANE_VERSION} AS live-img -FROM makeplane/plane-proxy:${PLANE_VERSION} AS proxy-img +FROM artifacts.plane.so/makeplane/plane-frontend:${PLANE_VERSION} AS web-img +FROM artifacts.plane.so/makeplane/plane-backend:${PLANE_VERSION} AS backend-img +FROM artifacts.plane.so/makeplane/plane-space:${PLANE_VERSION} AS space-img +FROM artifacts.plane.so/makeplane/plane-admin:${PLANE_VERSION} AS admin-img +FROM artifacts.plane.so/makeplane/plane-live:${PLANE_VERSION} AS live-img +FROM artifacts.plane.so/makeplane/plane-proxy:${PLANE_VERSION} AS proxy-img # ************************************************** # STAGE 1: Runner diff --git a/deployments/aio/community/README.md b/deployments/aio/community/README.md index 8e945a826..96aab6737 100644 --- a/deployments/aio/community/README.md +++ b/deployments/aio/community/README.md @@ -59,7 +59,7 @@ docker run --name plane-aio --rm -it \ -e AWS_ACCESS_KEY_ID=your-access-key \ -e AWS_SECRET_ACCESS_KEY=your-secret-key \ -e AWS_S3_BUCKET_NAME=your-bucket \ - makeplane/plane-aio-community:latest + artifacts.plane.so/makeplane/plane-aio-community:latest ``` ### Example with IP Address @@ -78,7 +78,7 @@ docker run --name myaio --rm -it \ -e AWS_S3_BUCKET_NAME=plane-app \ -e AWS_S3_ENDPOINT_URL=http://${MYIP}:19000 \ -e FILE_SIZE_LIMIT=10485760 \ - makeplane/plane-aio-community:latest + artifacts.plane.so/makeplane/plane-aio-community:latest ``` ## Configuration Options diff --git a/deployments/aio/community/supervisor.conf b/deployments/aio/community/supervisor.conf index 4474ee3b5..33b12985e 100644 --- a/deployments/aio/community/supervisor.conf +++ b/deployments/aio/community/supervisor.conf @@ -18,8 +18,8 @@ priority=10 [program:space] -directory=/app/space/apps/space -command=sh -c "npx react-router-serve ./build/server/index.js" +directory=/app/space/apps/space/build/server +command=sh -c "npx react-router-serve index.js" autostart=true autorestart=true stdout_logfile=/app/logs/access/space.log diff --git a/deployments/cli/community/docker-compose.yml b/deployments/cli/community/docker-compose.yml index 2ed44f037..d4de33a74 100644 --- a/deployments/cli/community/docker-compose.yml +++ b/deployments/cli/community/docker-compose.yml @@ -61,7 +61,7 @@ x-app-env: &app-env services: web: - image: makeplane/plane-frontend:${APP_RELEASE:-stable} + image: artifacts.plane.so/makeplane/plane-frontend:${APP_RELEASE:-stable} deploy: replicas: ${WEB_REPLICAS:-1} restart_policy: @@ -71,7 +71,7 @@ services: - worker space: - image: makeplane/plane-space:${APP_RELEASE:-stable} + image: artifacts.plane.so/makeplane/plane-space:${APP_RELEASE:-stable} deploy: replicas: ${SPACE_REPLICAS:-1} restart_policy: @@ -82,7 +82,7 @@ services: - web admin: - image: makeplane/plane-admin:${APP_RELEASE:-stable} + image: artifacts.plane.so/makeplane/plane-admin:${APP_RELEASE:-stable} deploy: replicas: ${ADMIN_REPLICAS:-1} restart_policy: @@ -92,7 +92,7 @@ services: - web live: - image: makeplane/plane-live:${APP_RELEASE:-stable} + image: artifacts.plane.so/makeplane/plane-live:${APP_RELEASE:-stable} environment: <<: [*live-env, *redis-env] deploy: @@ -104,7 +104,7 @@ services: - web api: - image: makeplane/plane-backend:${APP_RELEASE:-stable} + image: artifacts.plane.so/makeplane/plane-backend:${APP_RELEASE:-stable} command: ./bin/docker-entrypoint-api.sh deploy: replicas: ${API_REPLICAS:-1} @@ -120,7 +120,7 @@ services: - plane-mq worker: - image: makeplane/plane-backend:${APP_RELEASE:-stable} + image: artifacts.plane.so/makeplane/plane-backend:${APP_RELEASE:-stable} command: ./bin/docker-entrypoint-worker.sh deploy: replicas: ${WORKER_REPLICAS:-1} @@ -137,7 +137,7 @@ services: - plane-mq beat-worker: - image: makeplane/plane-backend:${APP_RELEASE:-stable} + image: artifacts.plane.so/makeplane/plane-backend:${APP_RELEASE:-stable} command: ./bin/docker-entrypoint-beat.sh deploy: replicas: ${BEAT_WORKER_REPLICAS:-1} @@ -154,7 +154,7 @@ services: - plane-mq migrator: - image: makeplane/plane-backend:${APP_RELEASE:-stable} + image: artifacts.plane.so/makeplane/plane-backend:${APP_RELEASE:-stable} command: ./bin/docker-entrypoint-migrator.sh deploy: replicas: 1 @@ -216,7 +216,7 @@ services: # Comment this if you already have a reverse proxy running proxy: - image: makeplane/plane-proxy:${APP_RELEASE:-stable} + image: artifacts.plane.so/makeplane/plane-proxy:${APP_RELEASE:-stable} deploy: replicas: 1 restart_policy: diff --git a/deployments/cli/community/install.sh b/deployments/cli/community/install.sh index accd89c81..582aa8394 100755 --- a/deployments/cli/community/install.sh +++ b/deployments/cli/community/install.sh @@ -5,7 +5,7 @@ SCRIPT_DIR=$PWD SERVICE_FOLDER=plane-app PLANE_INSTALL_DIR=$PWD/$SERVICE_FOLDER export APP_RELEASE=stable -export DOCKERHUB_USER=makeplane +export DOCKERHUB_USER=artifacts.plane.so/makeplane export PULL_POLICY=${PULL_POLICY:-if_not_present} export GH_REPO=makeplane/plane export RELEASE_DOWNLOAD_URL="https://github.com/$GH_REPO/releases/download" @@ -690,7 +690,7 @@ if [ -f "$DOCKER_ENV_PATH" ]; then CUSTOM_BUILD=$(getEnvValue "CUSTOM_BUILD" "$DOCKER_ENV_PATH") if [ -z "$DOCKERHUB_USER" ]; then - DOCKERHUB_USER=makeplane + DOCKERHUB_USER=artifacts.plane.so/makeplane updateEnvFile "DOCKERHUB_USER" "$DOCKERHUB_USER" "$DOCKER_ENV_PATH" fi diff --git a/deployments/swarm/community/swarm.sh b/deployments/swarm/community/swarm.sh index 02f170f4d..d496c25e6 100755 --- a/deployments/swarm/community/swarm.sh +++ b/deployments/swarm/community/swarm.sh @@ -5,7 +5,7 @@ SERVICE_FOLDER=plane-app SCRIPT_DIR=$PWD PLANE_INSTALL_DIR=$PWD/$SERVICE_FOLDER export APP_RELEASE="stable" -export DOCKERHUB_USER=makeplane +export DOCKERHUB_USER=artifacts.plane.so/makeplane export GH_REPO=makeplane/plane export RELEASE_DOWNLOAD_URL="https://github.com/$GH_REPO/releases/download" @@ -595,7 +595,7 @@ if [ -f "$DOCKER_ENV_PATH" ]; then APP_RELEASE=$(getEnvValue "APP_RELEASE" "$DOCKER_ENV_PATH") if [ -z "$DOCKERHUB_USER" ]; then - DOCKERHUB_USER=makeplane + DOCKERHUB_USER=artifacts.plane.so/makeplane updateEnvFile "DOCKERHUB_USER" "$DOCKERHUB_USER" "$DOCKER_ENV_PATH" fi diff --git a/package.json b/package.json index ec1fdbc00..ffe45b22f 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "lint-staged": "16.2.7", "oxfmt": "0.35.0", "oxlint": "1.51.0", - "turbo": "2.9.4" + "turbo": "2.8.12" }, "lint-staged": { "*.{js,jsx,ts,tsx,cjs,mjs,cts,mts,json,css,md}": [ @@ -75,8 +75,7 @@ "picomatch": "2.3.2", "yaml@1": "1.10.3", "yaml@2": "2.8.3", - "path-to-regexp": "0.1.13", - "defu": "6.1.5" + "path-to-regexp": "0.1.13" }, "onlyBuiltDependencies": [ "@parcel/watcher", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e048c47f8..7929b2669 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -103,7 +103,7 @@ overrides: qs: 6.14.2 diff: 5.2.2 webpack: 5.104.1 - lodash-es: 4.18.0 + lodash-es: 4.17.23 '@isaacs/brace-expansion': 5.0.1 lodash: 4.17.23 markdown-it: 14.1.1 @@ -119,7 +119,6 @@ overrides: yaml@1: 1.10.3 yaml@2: 2.8.3 path-to-regexp: 0.1.13 - defu: 6.1.5 importers: @@ -138,8 +137,8 @@ importers: specifier: 1.51.0 version: 1.51.0 turbo: - specifier: 2.9.4 - version: 2.9.4 + specifier: 2.8.12 + version: 2.8.12 apps/admin: dependencies: @@ -195,8 +194,8 @@ importers: specifier: ^5.1.31 version: 5.1.31 lodash-es: - specifier: 4.18.0 - version: 4.18.0 + specifier: 4.17.23 + version: 4.17.23 lucide-react: specifier: 'catalog:' version: 0.469.0(react@18.3.1) @@ -473,8 +472,8 @@ importers: specifier: ^5.1.31 version: 5.1.31 lodash-es: - specifier: 4.18.0 - version: 4.18.0 + specifier: 4.17.23 + version: 4.17.23 lucide-react: specifier: 'catalog:' version: 0.469.0(react@18.3.1) @@ -645,8 +644,8 @@ importers: specifier: ^5.1.31 version: 5.1.31 lodash-es: - specifier: 4.18.0 - version: 4.18.0 + specifier: 4.17.23 + version: 4.17.23 lucide-react: specifier: 'catalog:' version: 0.469.0(react@18.3.1) @@ -938,8 +937,8 @@ importers: specifier: ^4.3.2 version: 4.3.2 lodash-es: - specifier: 4.18.0 - version: 4.18.0 + specifier: 4.17.23 + version: 4.17.23 lowlight: specifier: ^3.0.0 version: 3.3.0 @@ -1036,8 +1035,8 @@ importers: specifier: ^10.7.11 version: 10.7.16 lodash-es: - specifier: 4.18.0 - version: 4.18.0 + specifier: 4.17.23 + version: 4.17.23 mobx: specifier: 'catalog:' version: 6.12.0 @@ -1217,8 +1216,8 @@ importers: specifier: workspace:* version: link:../utils lodash-es: - specifier: 4.18.0 - version: 4.18.0 + specifier: 4.17.23 + version: 4.17.23 mobx: specifier: 'catalog:' version: 6.12.0 @@ -1333,8 +1332,8 @@ importers: specifier: ^2.0.0 version: 2.1.1 lodash-es: - specifier: 4.18.0 - version: 4.18.0 + specifier: 4.17.23 + version: 4.17.23 lucide-react: specifier: 'catalog:' version: 0.469.0(react@18.3.1) @@ -1457,8 +1456,8 @@ importers: specifier: ^10.1.2 version: 10.1.2 lodash-es: - specifier: 4.18.0 - version: 4.18.0 + specifier: 4.17.23 + version: 4.17.23 lucide-react: specifier: 'catalog:' version: 0.469.0(react@18.3.1) @@ -4189,36 +4188,6 @@ packages: '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - '@turbo/darwin-64@2.9.4': - resolution: {integrity: sha512-ZSlPqJ5Vqg/wgVw8P3AOVCIosnbBilOxLq7TMz3MN/9U46DUYfdG2jtfevNDufyxyrg98pcPs/GBgDRaaids6g==} - cpu: [x64] - os: [darwin] - - '@turbo/darwin-arm64@2.9.4': - resolution: {integrity: sha512-9cjTWe4OiNlFMSRggPNh+TJlRs7MS5FWrHc96MOzft5vESWjjpvaadYPv5ykDW7b45mVHOF2U/W+48LoX9USWw==} - cpu: [arm64] - os: [darwin] - - '@turbo/linux-64@2.9.4': - resolution: {integrity: sha512-Cl1GjxqBXQ+r9KKowmXG+lhD1gclLp48/SE7NxL//66iaMytRw0uiphWGOkccD92iPiRjHLRUaA9lOTtgr5OCA==} - cpu: [x64] - os: [linux] - - '@turbo/linux-arm64@2.9.4': - resolution: {integrity: sha512-j2hPAKVmGNN2EsKigEWD+43y9m7zaPhNAs6ptsyfq0u7evHHBAXAwOfv86OEMg/gvC+pwGip0i1CIm1bR1vYug==} - cpu: [arm64] - os: [linux] - - '@turbo/windows-64@2.9.4': - resolution: {integrity: sha512-1jWPjCe9ZRmsDTXE7uzqfySNQspnUx0g6caqvwps+k/sc+fm9hC/4zRQKlXZLbVmP3Xxp601Ju71boegHdnYGw==} - cpu: [x64] - os: [win32] - - '@turbo/windows-arm64@2.9.4': - resolution: {integrity: sha512-dlko15TQVu/BFYmIY018Y3covWMRQlUgAkD+OOk+Rokcfj6VY02Vv4mCfT/Zns6B4q8jGbOd6IZhnCFYsE8Viw==} - cpu: [arm64] - os: [win32] - '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} @@ -5290,8 +5259,8 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} - defu@6.1.5: - resolution: {integrity: sha512-pwdBJxJuJXmqrLO6s0VBmfbRz+G7FUzkjldAsdi9Yrv86mPyzq0ll1o8+8gB4Gsr6GJHbK1Lh3ngllgTInDCjA==} + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} @@ -6441,9 +6410,8 @@ packages: resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lodash-es@4.18.0: - resolution: {integrity: sha512-koAgswPPA+UTaPN64Etp+PGP+WT6oqOS2NMi5yDkMaiGw9qY4VxQbQF0mtKMyr4BlTznWyzePV5UpECTJQmSUA==} - deprecated: Bad release. Please use lodash-es@4.17.23 instead. + lodash-es@4.17.23: + resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} @@ -8254,8 +8222,38 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - turbo@2.9.4: - resolution: {integrity: sha512-wZ/kMcZCuK5oEp7sXSSo/5fzKjP9I2EhoiarZjyCm2Ixk0WxFrC/h0gF3686eHHINoFQOOSWgB/pGfvkR8rkgQ==} + turbo-darwin-64@2.8.12: + resolution: {integrity: sha512-EiHJmW2MeQQx+21x8hjMHw/uPhXt9PIxvDrxzOtyVwrXzL0tQmsxtO4qHf2l7uA+K6PUJ4+TjY1MHZDuCvWXrw==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@2.8.12: + resolution: {integrity: sha512-cbqqGN0vd7ly2TeuaM8k9AK9u1CABO4kBA5KPSqovTiLL3sORccn/mZzJSbvQf0EsYRfU34MgW5FotfwW3kx8Q==} + cpu: [arm64] + os: [darwin] + + turbo-linux-64@2.8.12: + resolution: {integrity: sha512-jXKw9j4r4q6s0goSXuKI3aKbQK2qiNeP25lGGEnq018TM6SWRW1CCpPMxyG91aCKrub7wDm/K45sGNT4ZFBcFQ==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@2.8.12: + resolution: {integrity: sha512-BRJCMdyXjyBoL0GYpvj9d2WNfMHwc3tKmJG5ATn2Efvil9LsiOsd/93/NxDqW0jACtHFNVOPnd/CBwXRPiRbwA==} + cpu: [arm64] + os: [linux] + + turbo-windows-64@2.8.12: + resolution: {integrity: sha512-vyFOlpFFzQFkikvSVhVkESEfzIopgs2J7J1rYvtSwSHQ4zmHxkC95Q8Kjkus8gg+8X2mZyP1GS5jirmaypGiPw==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@2.8.12: + resolution: {integrity: sha512-9nRnlw5DF0LkJClkIws1evaIF36dmmMEO84J5Uj4oQ8C0QTHwlH7DNe5Kq2Jdmu8GXESCNDNuUYG8Cx6W/vm3g==} + cpu: [arm64] + os: [win32] + + turbo@2.8.12: + resolution: {integrity: sha512-auUAMLmi0eJhxDhQrxzvuhfEbICnVt0CTiYQYY8WyRJ5nwCDZxD0JG8bCSxT4nusI2CwJzmZAay5BfF6LmK7Hw==} hasBin: true tween-functions@1.2.0: @@ -11369,24 +11367,6 @@ snapshots: '@tokenizer/token@0.3.0': {} - '@turbo/darwin-64@2.9.4': - optional: true - - '@turbo/darwin-arm64@2.9.4': - optional: true - - '@turbo/linux-64@2.9.4': - optional: true - - '@turbo/linux-arm64@2.9.4': - optional: true - - '@turbo/windows-64@2.9.4': - optional: true - - '@turbo/windows-arm64@2.9.4': - optional: true - '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 @@ -12555,7 +12535,7 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 - defu@6.1.5: {} + defu@6.1.4: {} delayed-stream@1.0.0: {} @@ -13773,7 +13753,7 @@ snapshots: dependencies: p-locate: 6.0.0 - lodash-es@4.18.0: {} + lodash-es@4.17.23: {} lodash.debounce@4.0.8: {} @@ -15041,7 +15021,7 @@ snapshots: dependencies: '@icons/material': 0.2.4(react@18.3.1) lodash: 4.17.23 - lodash-es: 4.18.0 + lodash-es: 4.17.23 material-colors: 1.2.6 prop-types: 15.8.1 react: 18.3.1 @@ -16047,14 +16027,32 @@ snapshots: tslib@2.8.1: {} - turbo@2.9.4: + turbo-darwin-64@2.8.12: + optional: true + + turbo-darwin-arm64@2.8.12: + optional: true + + turbo-linux-64@2.8.12: + optional: true + + turbo-linux-arm64@2.8.12: + optional: true + + turbo-windows-64@2.8.12: + optional: true + + turbo-windows-arm64@2.8.12: + optional: true + + turbo@2.8.12: optionalDependencies: - '@turbo/darwin-64': 2.9.4 - '@turbo/darwin-arm64': 2.9.4 - '@turbo/linux-64': 2.9.4 - '@turbo/linux-arm64': 2.9.4 - '@turbo/windows-64': 2.9.4 - '@turbo/windows-arm64': 2.9.4 + turbo-darwin-64: 2.8.12 + turbo-darwin-arm64: 2.8.12 + turbo-linux-64: 2.8.12 + turbo-linux-arm64: 2.8.12 + turbo-windows-64: 2.8.12 + turbo-windows-arm64: 2.8.12 tween-functions@1.2.0: {} @@ -16081,7 +16079,7 @@ snapshots: unconfig@7.5.0: dependencies: '@quansync/fs': 1.0.0 - defu: 6.1.5 + defu: 6.1.4 jiti: 2.6.1 quansync: 1.0.0 unconfig-core: 7.5.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b20c3be19..011d59c67 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -21,7 +21,7 @@ catalog: "@types/react": 18.3.11 axios: 1.13.5 express: 4.22.0 - lodash-es: 4.18.0 + lodash-es: 4.17.23 lucide-react: 0.469.0 mobx-react: 9.1.1 mobx-utils: 6.0.8