From 2370491335672259b0363ef24b12aeb6c1896e69 Mon Sep 17 00:00:00 2001 From: Robert Ozimek Date: Thu, 8 Oct 2026 02:06:42 -0400 Subject: [PATCH 01/22] Add GitLab integration with two-way issue sync Signed-off-by: Robert Ozimek --- common/config/rush/pnpm-lock.yaml | 650 ++++++++++++++++++ desktop/package.json | 3 + desktop/src/ui/platform.ts | 5 + desktop/src/ui/types.ts | 1 + dev/docker-compose.yaml | 20 + dev/prod/package.json | 3 + dev/prod/public/config.json | 1 + dev/prod/src/platform.ts | 7 + foundations/core/packages/core/src/classes.ts | 1 + models/all/package.json | 2 + models/all/src/index.ts | 15 + pods/front/src/__start.ts | 1 + rush.json | 40 ++ scripts/fast-start.sh | 0 .../src/__tests__/gitlab-social-id.test.ts | 68 ++ server/account/src/__tests__/utils.test.ts | 7 + .../src/collections/postgres/migrations.ts | 28 +- server/account/src/operations.ts | 2 +- server/account/src/serviceOperations.ts | 2 +- server/account/src/utils.ts | 1 + server/server-pipeline/package.json | 4 + .../src/internationalization.ts | 3 + server/server-pipeline/src/serverPlugins.ts | 2 + services/gitlab/gitlab-assets/.eslintrc.js | 7 + .../gitlab/gitlab-assets/assets/icons.svg | 9 + services/gitlab/gitlab-assets/config/rig.json | 5 + services/gitlab/gitlab-assets/jest.config.js | 7 + services/gitlab/gitlab-assets/lang/cs.json | 53 ++ services/gitlab/gitlab-assets/lang/de.json | 53 ++ services/gitlab/gitlab-assets/lang/en.json | 53 ++ services/gitlab/gitlab-assets/lang/es.json | 53 ++ services/gitlab/gitlab-assets/lang/fr.json | 53 ++ services/gitlab/gitlab-assets/lang/it.json | 53 ++ services/gitlab/gitlab-assets/lang/ko.json | 53 ++ services/gitlab/gitlab-assets/lang/pl.json | 53 ++ services/gitlab/gitlab-assets/lang/pt-br.json | 53 ++ services/gitlab/gitlab-assets/lang/pt.json | 53 ++ services/gitlab/gitlab-assets/lang/ru.json | 53 ++ services/gitlab/gitlab-assets/lang/sp.json | 53 ++ services/gitlab/gitlab-assets/lang/tr.json | 53 ++ services/gitlab/gitlab-assets/lang/zh.json | 53 ++ services/gitlab/gitlab-assets/package.json | 67 ++ .../gitlab-assets/src/__tests__/lang.test.ts | 34 + services/gitlab/gitlab-assets/src/index.ts | 10 + services/gitlab/gitlab-assets/tsconfig.json | 13 + services/gitlab/gitlab-resources/.eslintrc.js | 4 + services/gitlab/gitlab-resources/.prettierrc | 22 + .../gitlab/gitlab-resources/config/rig.json | 5 + .../gitlab/gitlab-resources/jest.config.js | 7 + services/gitlab/gitlab-resources/package.json | 55 ++ .../gitlab/gitlab-resources/postcss.config.js | 5 + .../src/__tests__/errors.test.ts | 39 ++ .../src/__tests__/state.test.ts | 195 ++++++ .../src/components/Configure.svelte | 9 + .../src/components/Connect.svelte | 171 +++++ .../src/components/ConnectApp.svelte | 124 ++++ .../src/components/ConnectProject.svelte | 121 ++++ .../src/components/GitlabIcon.svelte | 11 + .../src/components/GitlabRepositories.svelte | 87 +++ .../src/components/IntegrationState.svelte | 53 ++ .../src/components/SetupApp.svelte | 334 +++++++++ .../gitlab/gitlab-resources/src/errors.ts | 24 + services/gitlab/gitlab-resources/src/index.ts | 25 + .../gitlab/gitlab-resources/src/plugin.ts | 67 ++ services/gitlab/gitlab-resources/src/state.ts | 177 +++++ services/gitlab/gitlab-resources/src/utils.ts | 48 ++ .../gitlab/gitlab-resources/svelte.config.js | 10 + .../gitlab/gitlab-resources/tsconfig.json | 11 + services/gitlab/gitlab/.eslintrc.js | 7 + services/gitlab/gitlab/.npmignore | 4 + services/gitlab/gitlab/config/rig.json | 4 + services/gitlab/gitlab/jest.config.js | 7 + services/gitlab/gitlab/package.json | 44 ++ services/gitlab/gitlab/src/index.ts | 187 +++++ services/gitlab/gitlab/tsconfig.json | 12 + services/gitlab/model-gitlab/.eslintrc.js | 7 + services/gitlab/model-gitlab/.npmignore | 4 + services/gitlab/model-gitlab/config/rig.json | 5 + services/gitlab/model-gitlab/jest.config.js | 7 + services/gitlab/model-gitlab/package.json | 49 ++ services/gitlab/model-gitlab/src/index.ts | 312 +++++++++ services/gitlab/model-gitlab/src/plugin.ts | 16 + services/gitlab/model-gitlab/tsconfig.json | 12 + services/gitlab/pod-gitlab/.eslintrc.js | 7 + services/gitlab/pod-gitlab/.gitignore | 1 + services/gitlab/pod-gitlab/.npmignore | 4 + services/gitlab/pod-gitlab/Dockerfile | 8 + services/gitlab/pod-gitlab/Readme.md | 37 + services/gitlab/pod-gitlab/config/rig.json | 4 + services/gitlab/pod-gitlab/jest.config.js | 7 + services/gitlab/pod-gitlab/package.json | 78 +++ .../src/__tests__/api-issues.test.ts | 115 ++++ .../pod-gitlab/src/__tests__/api.test.ts | 86 +++ .../pod-gitlab/src/__tests__/apps.test.ts | 200 ++++++ .../pod-gitlab/src/__tests__/caller.test.ts | 117 ++++ .../pod-gitlab/src/__tests__/config.test.ts | 78 +++ .../src/__tests__/helpers/fixtures.ts | 124 ++++ .../src/__tests__/helpers/memory.ts | 131 ++++ .../src/__tests__/helpers/provider.ts | 97 +++ .../pod-gitlab/src/__tests__/issues.test.ts | 286 ++++++++ .../pod-gitlab/src/__tests__/keys.test.ts | 31 + .../pod-gitlab/src/__tests__/markdown.test.ts | 36 + .../pod-gitlab/src/__tests__/merge.test.ts | 59 ++ .../pod-gitlab/src/__tests__/notes.test.ts | 188 +++++ .../pod-gitlab/src/__tests__/oauth.test.ts | 101 +++ .../pod-gitlab/src/__tests__/persons.test.ts | 111 +++ .../pod-gitlab/src/__tests__/platform.test.ts | 110 +++ .../src/__tests__/repositories.test.ts | 80 +++ .../pod-gitlab/src/__tests__/routes.test.ts | 95 +++ .../pod-gitlab/src/__tests__/runner.test.ts | 39 ++ .../pod-gitlab/src/__tests__/service.test.ts | 533 ++++++++++++++ .../pod-gitlab/src/__tests__/status.test.ts | 41 ++ .../pod-gitlab/src/__tests__/users.test.ts | 207 ++++++ .../pod-gitlab/src/__tests__/webhook.test.ts | 59 ++ .../pod-gitlab/src/__tests__/worker.test.ts | 211 ++++++ services/gitlab/pod-gitlab/src/apps.ts | 181 +++++ services/gitlab/pod-gitlab/src/caller.ts | 76 ++ services/gitlab/pod-gitlab/src/client.ts | 32 + services/gitlab/pod-gitlab/src/config.ts | 84 +++ services/gitlab/pod-gitlab/src/gitlab/api.ts | 163 +++++ .../gitlab/pod-gitlab/src/gitlab/oauth.ts | 78 +++ .../gitlab/pod-gitlab/src/gitlab/types.ts | 94 +++ services/gitlab/pod-gitlab/src/index.ts | 55 ++ .../pod-gitlab/src/markdown/extensions.ts | 19 + .../gitlab/pod-gitlab/src/markdown/index.ts | 58 ++ .../gitlab/pod-gitlab/src/repositories.ts | 114 +++ services/gitlab/pod-gitlab/src/routes.ts | 70 ++ services/gitlab/pod-gitlab/src/server.ts | 175 +++++ services/gitlab/pod-gitlab/src/service.ts | 514 ++++++++++++++ services/gitlab/pod-gitlab/src/state.ts | 63 ++ services/gitlab/pod-gitlab/src/sync/errors.ts | 12 + services/gitlab/pod-gitlab/src/sync/issues.ts | 408 +++++++++++ services/gitlab/pod-gitlab/src/sync/keys.ts | 43 ++ services/gitlab/pod-gitlab/src/sync/merge.ts | 48 ++ services/gitlab/pod-gitlab/src/sync/notes.ts | 229 ++++++ .../gitlab/pod-gitlab/src/sync/persons.ts | 135 ++++ services/gitlab/pod-gitlab/src/sync/runner.ts | 23 + services/gitlab/pod-gitlab/src/sync/status.ts | 42 ++ services/gitlab/pod-gitlab/src/sync/types.ts | 55 ++ .../gitlab/pod-gitlab/src/sync/versions.ts | 4 + services/gitlab/pod-gitlab/src/users.ts | 153 +++++ services/gitlab/pod-gitlab/src/webhook.ts | 63 ++ .../gitlab/pod-gitlab/src/worker/factory.ts | 63 ++ .../gitlab/pod-gitlab/src/worker/platform.ts | 119 ++++ .../gitlab/pod-gitlab/src/worker/worker.ts | 488 +++++++++++++ services/gitlab/pod-gitlab/tsconfig.json | 13 + .../gitlab/server-gitlab-model/.eslintrc.js | 7 + .../gitlab/server-gitlab-model/.npmignore | 4 + .../server-gitlab-model/config/rig.json | 5 + .../gitlab/server-gitlab-model/jest.config.js | 7 + .../gitlab/server-gitlab-model/package.json | 41 ++ .../gitlab/server-gitlab-model/src/index.ts | 29 + .../gitlab/server-gitlab-model/tsconfig.json | 12 + .../server-gitlab-resources/.eslintrc.js | 7 + .../gitlab/server-gitlab-resources/.npmignore | 4 + .../server-gitlab-resources/config/rig.json | 4 + .../server-gitlab-resources/jest.config.js | 7 + .../server-gitlab-resources/package.json | 47 ++ .../src/__tests__/triggers.test.ts | 122 ++++ .../server-gitlab-resources/src/index.ts | 145 ++++ .../server-gitlab-resources/tsconfig.json | 12 + services/gitlab/server-gitlab/.eslintrc.js | 7 + services/gitlab/server-gitlab/.npmignore | 4 + services/gitlab/server-gitlab/config/rig.json | 4 + services/gitlab/server-gitlab/jest.config.js | 7 + services/gitlab/server-gitlab/package.json | 42 ++ services/gitlab/server-gitlab/src/index.ts | 21 + services/gitlab/server-gitlab/tsconfig.json | 12 + 168 files changed, 11543 insertions(+), 3 deletions(-) mode change 100644 => 100755 scripts/fast-start.sh create mode 100644 server/account/src/__tests__/gitlab-social-id.test.ts create mode 100644 services/gitlab/gitlab-assets/.eslintrc.js create mode 100644 services/gitlab/gitlab-assets/assets/icons.svg create mode 100644 services/gitlab/gitlab-assets/config/rig.json create mode 100644 services/gitlab/gitlab-assets/jest.config.js create mode 100644 services/gitlab/gitlab-assets/lang/cs.json create mode 100644 services/gitlab/gitlab-assets/lang/de.json create mode 100644 services/gitlab/gitlab-assets/lang/en.json create mode 100644 services/gitlab/gitlab-assets/lang/es.json create mode 100644 services/gitlab/gitlab-assets/lang/fr.json create mode 100644 services/gitlab/gitlab-assets/lang/it.json create mode 100644 services/gitlab/gitlab-assets/lang/ko.json create mode 100644 services/gitlab/gitlab-assets/lang/pl.json create mode 100644 services/gitlab/gitlab-assets/lang/pt-br.json create mode 100644 services/gitlab/gitlab-assets/lang/pt.json create mode 100644 services/gitlab/gitlab-assets/lang/ru.json create mode 100644 services/gitlab/gitlab-assets/lang/sp.json create mode 100644 services/gitlab/gitlab-assets/lang/tr.json create mode 100644 services/gitlab/gitlab-assets/lang/zh.json create mode 100644 services/gitlab/gitlab-assets/package.json create mode 100644 services/gitlab/gitlab-assets/src/__tests__/lang.test.ts create mode 100644 services/gitlab/gitlab-assets/src/index.ts create mode 100644 services/gitlab/gitlab-assets/tsconfig.json create mode 100644 services/gitlab/gitlab-resources/.eslintrc.js create mode 100644 services/gitlab/gitlab-resources/.prettierrc create mode 100644 services/gitlab/gitlab-resources/config/rig.json create mode 100644 services/gitlab/gitlab-resources/jest.config.js create mode 100644 services/gitlab/gitlab-resources/package.json create mode 100644 services/gitlab/gitlab-resources/postcss.config.js create mode 100644 services/gitlab/gitlab-resources/src/__tests__/errors.test.ts create mode 100644 services/gitlab/gitlab-resources/src/__tests__/state.test.ts create mode 100644 services/gitlab/gitlab-resources/src/components/Configure.svelte create mode 100644 services/gitlab/gitlab-resources/src/components/Connect.svelte create mode 100644 services/gitlab/gitlab-resources/src/components/ConnectApp.svelte create mode 100644 services/gitlab/gitlab-resources/src/components/ConnectProject.svelte create mode 100644 services/gitlab/gitlab-resources/src/components/GitlabIcon.svelte create mode 100644 services/gitlab/gitlab-resources/src/components/GitlabRepositories.svelte create mode 100644 services/gitlab/gitlab-resources/src/components/IntegrationState.svelte create mode 100644 services/gitlab/gitlab-resources/src/components/SetupApp.svelte create mode 100644 services/gitlab/gitlab-resources/src/errors.ts create mode 100644 services/gitlab/gitlab-resources/src/index.ts create mode 100644 services/gitlab/gitlab-resources/src/plugin.ts create mode 100644 services/gitlab/gitlab-resources/src/state.ts create mode 100644 services/gitlab/gitlab-resources/src/utils.ts create mode 100644 services/gitlab/gitlab-resources/svelte.config.js create mode 100644 services/gitlab/gitlab-resources/tsconfig.json create mode 100644 services/gitlab/gitlab/.eslintrc.js create mode 100644 services/gitlab/gitlab/.npmignore create mode 100644 services/gitlab/gitlab/config/rig.json create mode 100644 services/gitlab/gitlab/jest.config.js create mode 100644 services/gitlab/gitlab/package.json create mode 100644 services/gitlab/gitlab/src/index.ts create mode 100644 services/gitlab/gitlab/tsconfig.json create mode 100644 services/gitlab/model-gitlab/.eslintrc.js create mode 100644 services/gitlab/model-gitlab/.npmignore create mode 100644 services/gitlab/model-gitlab/config/rig.json create mode 100644 services/gitlab/model-gitlab/jest.config.js create mode 100644 services/gitlab/model-gitlab/package.json create mode 100644 services/gitlab/model-gitlab/src/index.ts create mode 100644 services/gitlab/model-gitlab/src/plugin.ts create mode 100644 services/gitlab/model-gitlab/tsconfig.json create mode 100644 services/gitlab/pod-gitlab/.eslintrc.js create mode 100644 services/gitlab/pod-gitlab/.gitignore create mode 100644 services/gitlab/pod-gitlab/.npmignore create mode 100644 services/gitlab/pod-gitlab/Dockerfile create mode 100644 services/gitlab/pod-gitlab/Readme.md create mode 100644 services/gitlab/pod-gitlab/config/rig.json create mode 100644 services/gitlab/pod-gitlab/jest.config.js create mode 100644 services/gitlab/pod-gitlab/package.json create mode 100644 services/gitlab/pod-gitlab/src/__tests__/api-issues.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/api.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/apps.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/caller.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/config.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/helpers/fixtures.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/helpers/memory.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/helpers/provider.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/issues.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/keys.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/markdown.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/merge.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/notes.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/oauth.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/persons.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/platform.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/repositories.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/routes.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/runner.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/service.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/status.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/users.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/webhook.test.ts create mode 100644 services/gitlab/pod-gitlab/src/__tests__/worker.test.ts create mode 100644 services/gitlab/pod-gitlab/src/apps.ts create mode 100644 services/gitlab/pod-gitlab/src/caller.ts create mode 100644 services/gitlab/pod-gitlab/src/client.ts create mode 100644 services/gitlab/pod-gitlab/src/config.ts create mode 100644 services/gitlab/pod-gitlab/src/gitlab/api.ts create mode 100644 services/gitlab/pod-gitlab/src/gitlab/oauth.ts create mode 100644 services/gitlab/pod-gitlab/src/gitlab/types.ts create mode 100644 services/gitlab/pod-gitlab/src/index.ts create mode 100644 services/gitlab/pod-gitlab/src/markdown/extensions.ts create mode 100644 services/gitlab/pod-gitlab/src/markdown/index.ts create mode 100644 services/gitlab/pod-gitlab/src/repositories.ts create mode 100644 services/gitlab/pod-gitlab/src/routes.ts create mode 100644 services/gitlab/pod-gitlab/src/server.ts create mode 100644 services/gitlab/pod-gitlab/src/service.ts create mode 100644 services/gitlab/pod-gitlab/src/state.ts create mode 100644 services/gitlab/pod-gitlab/src/sync/errors.ts create mode 100644 services/gitlab/pod-gitlab/src/sync/issues.ts create mode 100644 services/gitlab/pod-gitlab/src/sync/keys.ts create mode 100644 services/gitlab/pod-gitlab/src/sync/merge.ts create mode 100644 services/gitlab/pod-gitlab/src/sync/notes.ts create mode 100644 services/gitlab/pod-gitlab/src/sync/persons.ts create mode 100644 services/gitlab/pod-gitlab/src/sync/runner.ts create mode 100644 services/gitlab/pod-gitlab/src/sync/status.ts create mode 100644 services/gitlab/pod-gitlab/src/sync/types.ts create mode 100644 services/gitlab/pod-gitlab/src/sync/versions.ts create mode 100644 services/gitlab/pod-gitlab/src/users.ts create mode 100644 services/gitlab/pod-gitlab/src/webhook.ts create mode 100644 services/gitlab/pod-gitlab/src/worker/factory.ts create mode 100644 services/gitlab/pod-gitlab/src/worker/platform.ts create mode 100644 services/gitlab/pod-gitlab/src/worker/worker.ts create mode 100644 services/gitlab/pod-gitlab/tsconfig.json create mode 100644 services/gitlab/server-gitlab-model/.eslintrc.js create mode 100644 services/gitlab/server-gitlab-model/.npmignore create mode 100644 services/gitlab/server-gitlab-model/config/rig.json create mode 100644 services/gitlab/server-gitlab-model/jest.config.js create mode 100644 services/gitlab/server-gitlab-model/package.json create mode 100644 services/gitlab/server-gitlab-model/src/index.ts create mode 100644 services/gitlab/server-gitlab-model/tsconfig.json create mode 100644 services/gitlab/server-gitlab-resources/.eslintrc.js create mode 100644 services/gitlab/server-gitlab-resources/.npmignore create mode 100644 services/gitlab/server-gitlab-resources/config/rig.json create mode 100644 services/gitlab/server-gitlab-resources/jest.config.js create mode 100644 services/gitlab/server-gitlab-resources/package.json create mode 100644 services/gitlab/server-gitlab-resources/src/__tests__/triggers.test.ts create mode 100644 services/gitlab/server-gitlab-resources/src/index.ts create mode 100644 services/gitlab/server-gitlab-resources/tsconfig.json create mode 100644 services/gitlab/server-gitlab/.eslintrc.js create mode 100644 services/gitlab/server-gitlab/.npmignore create mode 100644 services/gitlab/server-gitlab/config/rig.json create mode 100644 services/gitlab/server-gitlab/jest.config.js create mode 100644 services/gitlab/server-gitlab/package.json create mode 100644 services/gitlab/server-gitlab/src/index.ts create mode 100644 services/gitlab/server-gitlab/tsconfig.json diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index b091421b53..03000970be 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -246,6 +246,15 @@ importers: '@hcengineering/github-resources': specifier: workspace:^0.7.0 version: link:../services/github/github-resources + '@hcengineering/gitlab': + specifier: workspace:^0.7.0 + version: link:../services/gitlab/gitlab + '@hcengineering/gitlab-assets': + specifier: workspace:^0.7.0 + version: link:../services/gitlab/gitlab-assets + '@hcengineering/gitlab-resources': + specifier: workspace:^0.7.0 + version: link:../services/gitlab/gitlab-resources '@hcengineering/global-profile': specifier: workspace:^0.7.0 version: link:../plugins/global-profile @@ -1303,6 +1312,15 @@ importers: '@hcengineering/github-resources': specifier: workspace:^0.7.0 version: link:../../services/github/github-resources + '@hcengineering/gitlab': + specifier: workspace:^0.7.0 + version: link:../../services/gitlab/gitlab + '@hcengineering/gitlab-assets': + specifier: workspace:^0.7.0 + version: link:../../services/gitlab/gitlab-assets + '@hcengineering/gitlab-resources': + specifier: workspace:^0.7.0 + version: link:../../services/gitlab/gitlab-resources '@hcengineering/global-profile': specifier: workspace:^0.7.0 version: link:../../plugins/global-profile @@ -6016,6 +6034,9 @@ importers: '@hcengineering/model-github': specifier: workspace:^0.7.0 version: link:../../services/github/model-github + '@hcengineering/model-gitlab': + specifier: workspace:^0.7.0 + version: link:../../services/gitlab/model-gitlab '@hcengineering/model-gmail': specifier: workspace:^0.7.0 version: link:../gmail @@ -6232,6 +6253,9 @@ importers: '@hcengineering/server-github-model': specifier: workspace:^0.7.0 version: link:../../services/github/server-github-model + '@hcengineering/server-gitlab-model': + specifier: workspace:^0.7.0 + version: link:../../services/gitlab/server-gitlab-model devDependencies: '@hcengineering/platform-rig': specifier: workspace:^0.7.21 @@ -36795,6 +36819,12 @@ importers: '@hcengineering/github-assets': specifier: workspace:^0.7.0 version: link:../../services/github/github-assets + '@hcengineering/gitlab': + specifier: workspace:^0.7.0 + version: link:../../services/gitlab/gitlab + '@hcengineering/gitlab-assets': + specifier: workspace:^0.7.0 + version: link:../../services/gitlab/gitlab-assets '@hcengineering/gmail': specifier: workspace:^0.7.0 version: link:../../plugins/gmail @@ -36960,6 +36990,12 @@ importers: '@hcengineering/server-github-resources': specifier: workspace:^0.7.0 version: link:../../services/github/server-github-resources + '@hcengineering/server-gitlab': + specifier: workspace:^0.7.0 + version: link:../../services/gitlab/server-gitlab + '@hcengineering/server-gitlab-resources': + specifier: workspace:^0.7.0 + version: link:../../services/gitlab/server-gitlab-resources '@hcengineering/server-gmail': specifier: workspace:^0.7.0 version: link:../../server-plugins/gmail @@ -39635,6 +39671,620 @@ importers: specifier: ^5.9.3 version: 5.9.3 + ../../services/gitlab/gitlab: + dependencies: + '@hcengineering/core': + specifier: workspace:^0.7.26 + version: link:../../../foundations/core/packages/core + '@hcengineering/platform': + specifier: workspace:^0.7.20 + version: link:../../../foundations/core/packages/platform + '@hcengineering/preference': + specifier: workspace:^0.7.0 + version: link:../../../plugins/preference + '@hcengineering/tracker': + specifier: workspace:^0.7.0 + version: link:../../../plugins/tracker + '@hcengineering/ui': + specifier: workspace:^0.7.0 + version: link:../../../packages/ui + devDependencies: + '@hcengineering/platform-rig': + specifier: workspace:^0.7.21 + version: link:../../../foundations/utils/packages/platform-rig + '@types/jest': + specifier: ^29.5.5 + version: 29.5.14 + '@typescript-eslint/eslint-plugin': + specifier: ^6.21.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^6.21.0 + version: 6.21.0(eslint@8.57.1)(typescript@5.9.3) + eslint: + specifier: ^8.54.0 + version: 8.57.1 + eslint-config-standard-with-typescript: + specifier: ^40.0.0 + version: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) + eslint-plugin-import: + specifier: ^2.26.0 + version: 2.32.0(eslint@8.57.1) + eslint-plugin-n: + specifier: ^15.4.0 + version: 15.7.0(eslint@8.57.1) + eslint-plugin-promise: + specifier: ^6.1.1 + version: 6.6.0(eslint@8.57.1) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@24.13.6)(ts-node@10.9.2(@types/node@24.13.6)(typescript@5.9.3)) + prettier: + specifier: ^3.6.2 + version: 3.6.2 + ts-jest: + specifier: ^29.1.1 + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0)(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + ../../services/gitlab/gitlab-assets: + dependencies: + '@hcengineering/gitlab': + specifier: workspace:^0.7.0 + version: link:../gitlab + '@hcengineering/platform': + specifier: workspace:^0.7.20 + version: link:../../../foundations/core/packages/platform + devDependencies: + '@hcengineering/platform-rig': + specifier: workspace:^0.7.21 + version: link:../../../foundations/utils/packages/platform-rig + '@types/jest': + specifier: ^29.5.5 + version: 29.5.14 + '@types/node': + specifier: ^24.13.3 + version: 24.13.6 + '@typescript-eslint/eslint-plugin': + specifier: ^6.21.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^6.21.0 + version: 6.21.0(eslint@8.57.1)(typescript@5.9.3) + eslint: + specifier: ^8.54.0 + version: 8.57.1 + eslint-config-standard-with-typescript: + specifier: ^40.0.0 + version: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) + eslint-plugin-import: + specifier: ^2.26.0 + version: 2.32.0(eslint@8.57.1) + eslint-plugin-n: + specifier: ^15.4.0 + version: 15.7.0(eslint@8.57.1) + eslint-plugin-promise: + specifier: ^6.1.1 + version: 6.6.0(eslint@8.57.1) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@24.13.6)(ts-node@10.9.2(@types/node@24.13.6)(typescript@5.9.3)) + prettier: + specifier: ^3.6.2 + version: 3.6.2 + ts-jest: + specifier: ^29.1.1 + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@24.13.6))(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + ../../services/gitlab/gitlab-resources: + dependencies: + '@hcengineering/account-client': + specifier: workspace:^0.7.25 + version: link:../../../foundations/core/packages/account-client + '@hcengineering/analytics': + specifier: workspace:^0.7.19 + version: link:../../../foundations/core/packages/analytics + '@hcengineering/core': + specifier: workspace:^0.7.26 + version: link:../../../foundations/core/packages/core + '@hcengineering/gitlab': + specifier: workspace:^0.7.0 + version: link:../gitlab + '@hcengineering/login': + specifier: workspace:^0.7.0 + version: link:../../../plugins/login + '@hcengineering/platform': + specifier: workspace:^0.7.20 + version: link:../../../foundations/core/packages/platform + '@hcengineering/presentation': + specifier: workspace:^0.7.0 + version: link:../../../packages/presentation + '@hcengineering/setting': + specifier: workspace:^0.7.0 + version: link:../../../plugins/setting + '@hcengineering/setting-resources': + specifier: workspace:^0.7.0 + version: link:../../../plugins/setting-resources + '@hcengineering/tracker': + specifier: workspace:^0.7.0 + version: link:../../../plugins/tracker + '@hcengineering/ui': + specifier: workspace:^0.7.0 + version: link:../../../packages/ui + '@hcengineering/view-resources': + specifier: workspace:^0.7.0 + version: link:../../../plugins/view-resources + svelte: + specifier: ^4.2.20 + version: 4.2.20 + devDependencies: + '@hcengineering/platform-rig': + specifier: workspace:^0.7.21 + version: link:../../../foundations/utils/packages/platform-rig + '@types/jest': + specifier: ^29.5.5 + version: 29.5.14 + '@typescript-eslint/eslint-plugin': + specifier: ^6.21.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^6.21.0 + version: 6.21.0(eslint@8.57.1)(typescript@5.9.3) + eslint: + specifier: ^8.54.0 + version: 8.57.1 + eslint-config-standard-with-typescript: + specifier: ^40.0.0 + version: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) + eslint-plugin-import: + specifier: ^2.26.0 + version: 2.32.0(eslint@8.57.1) + eslint-plugin-n: + specifier: ^15.4.0 + version: 15.7.0(eslint@8.57.1) + eslint-plugin-promise: + specifier: ^6.1.1 + version: 6.6.0(eslint@8.57.1) + eslint-plugin-svelte: + specifier: ^2.35.1 + version: 2.46.1(eslint@8.57.1)(svelte@4.2.20)(ts-node@10.9.2(@types/node@24.13.6)(typescript@5.9.3)) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@24.13.6)(ts-node@10.9.2(@types/node@24.13.6)(typescript@5.9.3)) + prettier: + specifier: ^3.6.2 + version: 3.6.2 + prettier-plugin-svelte: + specifier: ^3.4.0 + version: 3.4.0(prettier@3.6.2)(svelte@4.2.20) + sass: + specifier: ^1.80.0 + version: 1.93.3 + svelte-check: + specifier: ^3.6.9 + version: 3.8.6(@babel/core@7.28.5)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@24.13.6)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.3)(svelte@4.2.20) + svelte-eslint-parser: + specifier: ^0.33.1 + version: 0.33.1(svelte@4.2.20) + svelte-loader: + specifier: ^3.2.0 + version: 3.2.4(svelte@4.2.20) + svelte-preprocess: + specifier: ^5.1.4 + version: 5.1.4(@babel/core@7.28.5)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@24.13.6)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.3)(svelte@4.2.20)(typescript@5.9.3) + ts-jest: + specifier: ^29.1.1 + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@24.13.6)(ts-node@10.9.2(@types/node@24.13.6)(typescript@5.9.3)))(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + ../../services/gitlab/model-gitlab: + dependencies: + '@hcengineering/contact': + specifier: workspace:^0.7.0 + version: link:../../../plugins/contact + '@hcengineering/core': + specifier: workspace:^0.7.26 + version: link:../../../foundations/core/packages/core + '@hcengineering/gitlab': + specifier: workspace:^0.7.0 + version: link:../gitlab + '@hcengineering/gitlab-resources': + specifier: workspace:^0.7.0 + version: link:../gitlab-resources + '@hcengineering/model': + specifier: workspace:^0.7.17 + version: link:../../../foundations/core/packages/model + '@hcengineering/model-contact': + specifier: workspace:^0.7.0 + version: link:../../../models/contact + '@hcengineering/model-core': + specifier: workspace:^0.7.0 + version: link:../../../models/core + '@hcengineering/model-preference': + specifier: workspace:^0.7.0 + version: link:../../../models/preference + '@hcengineering/model-tracker': + specifier: workspace:^0.7.0 + version: link:../../../models/tracker + '@hcengineering/platform': + specifier: workspace:^0.7.20 + version: link:../../../foundations/core/packages/platform + '@hcengineering/preference': + specifier: workspace:^0.7.0 + version: link:../../../plugins/preference + '@hcengineering/setting': + specifier: workspace:^0.7.0 + version: link:../../../plugins/setting + '@hcengineering/ui': + specifier: workspace:^0.7.0 + version: link:../../../packages/ui + devDependencies: + '@hcengineering/platform-rig': + specifier: workspace:^0.7.21 + version: link:../../../foundations/utils/packages/platform-rig + '@types/jest': + specifier: ^29.5.5 + version: 29.5.14 + '@types/node': + specifier: ^24.13.3 + version: 24.13.6 + '@typescript-eslint/eslint-plugin': + specifier: ^6.21.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^6.21.0 + version: 6.21.0(eslint@8.57.1)(typescript@5.9.3) + eslint: + specifier: ^8.54.0 + version: 8.57.1 + eslint-config-standard-with-typescript: + specifier: ^40.0.0 + version: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) + eslint-plugin-import: + specifier: ^2.26.0 + version: 2.32.0(eslint@8.57.1) + eslint-plugin-n: + specifier: ^15.4.0 + version: 15.7.0(eslint@8.57.1) + eslint-plugin-promise: + specifier: ^6.1.1 + version: 6.6.0(eslint@8.57.1) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@24.13.6)(ts-node@10.9.2(@types/node@24.13.6)(typescript@5.9.3)) + prettier: + specifier: ^3.6.2 + version: 3.6.2 + ts-jest: + specifier: ^29.1.1 + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@24.13.6))(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + ../../services/gitlab/pod-gitlab: + dependencies: + '@hcengineering/account-client': + specifier: workspace:^0.7.25 + version: link:../../../foundations/core/packages/account-client + '@hcengineering/activity': + specifier: workspace:^0.7.0 + version: link:../../../plugins/activity + '@hcengineering/analytics': + specifier: workspace:^0.7.19 + version: link:../../../foundations/core/packages/analytics + '@hcengineering/analytics-service': + specifier: workspace:^0.7.19 + version: link:../../../foundations/core/packages/analytics-service + '@hcengineering/chunter': + specifier: workspace:^0.7.0 + version: link:../../../plugins/chunter + '@hcengineering/client': + specifier: workspace:^0.7.19 + version: link:../../../foundations/core/packages/client + '@hcengineering/client-resources': + specifier: workspace:^0.7.19 + version: link:../../../foundations/core/packages/client-resources + '@hcengineering/collaborator-client': + specifier: workspace:^0.7.18 + version: link:../../../foundations/core/packages/collaborator-client + '@hcengineering/contact': + specifier: workspace:^0.7.0 + version: link:../../../plugins/contact + '@hcengineering/core': + specifier: workspace:^0.7.26 + version: link:../../../foundations/core/packages/core + '@hcengineering/gitlab': + specifier: workspace:^0.7.0 + version: link:../gitlab + '@hcengineering/platform': + specifier: workspace:^0.7.20 + version: link:../../../foundations/core/packages/platform + '@hcengineering/server-client': + specifier: workspace:^0.7.16 + version: link:../../../foundations/server/packages/client + '@hcengineering/server-core': + specifier: workspace:^0.7.19 + version: link:../../../foundations/server/packages/core + '@hcengineering/server-token': + specifier: workspace:^0.7.18 + version: link:../../../foundations/core/packages/token + '@hcengineering/task': + specifier: workspace:^0.7.0 + version: link:../../../plugins/task + '@hcengineering/text': + specifier: workspace:^0.7.19 + version: link:../../../foundations/core/packages/text + '@hcengineering/text-markdown': + specifier: workspace:^0.7.21 + version: link:../../../foundations/core/packages/text-markdown + '@hcengineering/tracker': + specifier: workspace:^0.7.0 + version: link:../../../plugins/tracker + body-parser: + specifier: ^1.20.3 + version: 1.20.3 + cors: + specifier: ^2.8.5 + version: 2.8.5 + express: + specifier: ^4.21.2 + version: 4.21.2 + ws: + specifier: ^8.21.0 + version: 8.21.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) + devDependencies: + '@hcengineering/platform-rig': + specifier: workspace:^0.7.21 + version: link:../../../foundations/utils/packages/platform-rig + '@types/body-parser': + specifier: ~1.19.2 + version: 1.19.6 + '@types/cors': + specifier: ^2.8.12 + version: 2.8.19 + '@types/express': + specifier: ^4.17.13 + version: 4.17.25 + '@types/jest': + specifier: ^29.5.5 + version: 29.5.14 + '@types/node': + specifier: ^24.13.3 + version: 24.13.6 + '@types/ws': + specifier: ^8.18.1 + version: 8.18.1 + '@typescript-eslint/eslint-plugin': + specifier: ^6.21.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^6.21.0 + version: 6.21.0(eslint@8.57.1)(typescript@5.9.3) + cross-env: + specifier: ~7.0.3 + version: 7.0.3 + esbuild: + specifier: ^0.25.10 + version: 0.25.12 + eslint: + specifier: ^8.54.0 + version: 8.57.1 + eslint-config-standard-with-typescript: + specifier: ^40.0.0 + version: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) + eslint-plugin-import: + specifier: ^2.26.0 + version: 2.32.0(eslint@8.57.1) + eslint-plugin-n: + specifier: ^15.4.0 + version: 15.7.0(eslint@8.57.1) + eslint-plugin-promise: + specifier: ^6.1.1 + version: 6.6.0(eslint@8.57.1) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@24.13.6)(ts-node@10.9.2(@types/node@24.13.6)(typescript@5.9.3)) + prettier: + specifier: ^3.6.2 + version: 3.6.2 + ts-jest: + specifier: ^29.1.1 + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@24.13.6)(ts-node@10.9.2(@types/node@24.13.6)(typescript@5.9.3)))(typescript@5.9.3) + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@24.13.6)(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + ../../services/gitlab/server-gitlab: + dependencies: + '@hcengineering/platform': + specifier: workspace:^0.7.20 + version: link:../../../foundations/core/packages/platform + '@hcengineering/server-core': + specifier: workspace:^0.7.19 + version: link:../../../foundations/server/packages/core + devDependencies: + '@hcengineering/platform-rig': + specifier: workspace:^0.7.21 + version: link:../../../foundations/utils/packages/platform-rig + '@types/jest': + specifier: ^29.5.5 + version: 29.5.14 + '@types/node': + specifier: ^24.13.3 + version: 24.13.6 + '@typescript-eslint/eslint-plugin': + specifier: ^6.21.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^6.21.0 + version: 6.21.0(eslint@8.57.1)(typescript@5.9.3) + eslint: + specifier: ^8.54.0 + version: 8.57.1 + eslint-config-standard-with-typescript: + specifier: ^40.0.0 + version: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) + eslint-plugin-import: + specifier: ^2.26.0 + version: 2.32.0(eslint@8.57.1) + eslint-plugin-n: + specifier: ^15.4.0 + version: 15.7.0(eslint@8.57.1) + eslint-plugin-promise: + specifier: ^6.1.1 + version: 6.6.0(eslint@8.57.1) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@24.13.6)(ts-node@10.9.2(@types/node@24.13.6)(typescript@5.9.3)) + prettier: + specifier: ^3.6.2 + version: 3.6.2 + ts-jest: + specifier: ^29.1.1 + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@24.13.6))(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + ../../services/gitlab/server-gitlab-model: + dependencies: + '@hcengineering/core': + specifier: workspace:^0.7.26 + version: link:../../../foundations/core/packages/core + '@hcengineering/model': + specifier: workspace:^0.7.17 + version: link:../../../foundations/core/packages/model + '@hcengineering/server-core': + specifier: workspace:^0.7.19 + version: link:../../../foundations/server/packages/core + '@hcengineering/server-gitlab': + specifier: workspace:^0.7.0 + version: link:../server-gitlab + '@hcengineering/tracker': + specifier: workspace:^0.7.0 + version: link:../../../plugins/tracker + devDependencies: + '@hcengineering/platform-rig': + specifier: workspace:^0.7.21 + version: link:../../../foundations/utils/packages/platform-rig + '@types/jest': + specifier: ^29.5.5 + version: 29.5.14 + '@types/node': + specifier: ^24.13.3 + version: 24.13.6 + '@typescript-eslint/eslint-plugin': + specifier: ^6.21.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^6.21.0 + version: 6.21.0(eslint@8.57.1)(typescript@5.9.3) + eslint: + specifier: ^8.54.0 + version: 8.57.1 + eslint-config-standard-with-typescript: + specifier: ^40.0.0 + version: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) + eslint-plugin-import: + specifier: ^2.26.0 + version: 2.32.0(eslint@8.57.1) + eslint-plugin-n: + specifier: ^15.4.0 + version: 15.7.0(eslint@8.57.1) + eslint-plugin-promise: + specifier: ^6.1.1 + version: 6.6.0(eslint@8.57.1) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@24.13.6)(ts-node@10.9.2(@types/node@24.13.6)(typescript@5.9.3)) + prettier: + specifier: ^3.6.2 + version: 3.6.2 + ts-jest: + specifier: ^29.1.1 + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@24.13.6))(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + ../../services/gitlab/server-gitlab-resources: + dependencies: + '@hcengineering/chunter': + specifier: workspace:^0.7.0 + version: link:../../../plugins/chunter + '@hcengineering/core': + specifier: workspace:^0.7.26 + version: link:../../../foundations/core/packages/core + '@hcengineering/gitlab': + specifier: workspace:^0.7.0 + version: link:../gitlab + '@hcengineering/platform': + specifier: workspace:^0.7.20 + version: link:../../../foundations/core/packages/platform + '@hcengineering/server-core': + specifier: workspace:^0.7.19 + version: link:../../../foundations/server/packages/core + '@hcengineering/server-gitlab': + specifier: workspace:^0.7.0 + version: link:../server-gitlab + '@hcengineering/tracker': + specifier: workspace:^0.7.0 + version: link:../../../plugins/tracker + devDependencies: + '@hcengineering/platform-rig': + specifier: workspace:^0.7.21 + version: link:../../../foundations/utils/packages/platform-rig + '@types/jest': + specifier: ^29.5.5 + version: 29.5.14 + '@types/node': + specifier: ^24.13.3 + version: 24.13.6 + '@typescript-eslint/eslint-plugin': + specifier: ^6.21.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^6.21.0 + version: 6.21.0(eslint@8.57.1)(typescript@5.9.3) + eslint: + specifier: ^8.54.0 + version: 8.57.1 + eslint-config-standard-with-typescript: + specifier: ^40.0.0 + version: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) + eslint-plugin-import: + specifier: ^2.26.0 + version: 2.32.0(eslint@8.57.1) + eslint-plugin-n: + specifier: ^15.4.0 + version: 15.7.0(eslint@8.57.1) + eslint-plugin-promise: + specifier: ^6.1.1 + version: 6.6.0(eslint@8.57.1) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@24.13.6)(ts-node@10.9.2(@types/node@24.13.6)(typescript@5.9.3)) + prettier: + specifier: ^3.6.2 + version: 3.6.2 + ts-jest: + specifier: ^29.1.1 + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@24.13.6))(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + ../../services/gmail/pod-gmail: dependencies: '@hcengineering/account-client': diff --git a/desktop/package.json b/desktop/package.json index c6d740a1ae..b0dc32e51f 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -185,6 +185,9 @@ "@hcengineering/github": "workspace:^0.7.0", "@hcengineering/github-assets": "workspace:^0.7.0", "@hcengineering/github-resources": "workspace:^0.7.0", + "@hcengineering/gitlab": "workspace:^0.7.0", + "@hcengineering/gitlab-assets": "workspace:^0.7.0", + "@hcengineering/gitlab-resources": "workspace:^0.7.0", "@hcengineering/desktop-preferences": "workspace:^0.7.0", "@hcengineering/desktop-preferences-assets": "workspace:^0.7.0", "@hcengineering/desktop-preferences-resources": "workspace:^0.7.0", diff --git a/desktop/src/ui/platform.ts b/desktop/src/ui/platform.ts index 3250b8607c..e0d3ffe51c 100644 --- a/desktop/src/ui/platform.ts +++ b/desktop/src/ui/platform.ts @@ -163,6 +163,8 @@ import { ipcMainExposed } from './typesUtils' import github, { githubId } from '@hcengineering/github' import '@hcengineering/github-assets' +import gitlab, { gitlabId } from '@hcengineering/gitlab' +import '@hcengineering/gitlab-assets' import { preferenceId } from '@hcengineering/preference' import { uiId } from '@hcengineering/ui/src/plugin' @@ -258,6 +260,7 @@ function configureI18n (): void { addStringsLoader(documentId, async (lang: string) => await import(`@hcengineering/document-assets/lang/${lang}.json`)) addStringsLoader(timeId, async (lang: string) => await import(`@hcengineering/time-assets/lang/${lang}.json`)) addStringsLoader(githubId, async (lang: string) => await import(`@hcengineering/github-assets/lang/${lang}.json`)) + addStringsLoader(gitlabId, async (lang: string) => await import(`@hcengineering/gitlab-assets/lang/${lang}.json`)) addStringsLoader( documentsId, async (lang: string) => await import(`@hcengineering/controlled-documents-assets/lang/${lang}.json`) @@ -369,6 +372,7 @@ export async function configurePlatform (onWorkbenchConnect?: () => Promise Promise await import('@hcengineering/recorder-resources')) addLocation(presenceId, async () => await import('@hcengineering/presence-resources')) addLocation(githubId, async () => await import(/* webpackChunkName: "github" */ '@hcengineering/github-resources')) + addLocation(gitlabId, async () => await import(/* webpackChunkName: "gitlab" */ '@hcengineering/gitlab-resources')) addLocation( desktopPreferencesId, async () => diff --git a/desktop/src/ui/types.ts b/desktop/src/ui/types.ts index 74a2e143b2..c72c91994c 100644 --- a/desktop/src/ui/types.ts +++ b/desktop/src/ui/types.ts @@ -40,6 +40,7 @@ export interface Config { GITHUB_APP: string GITHUB_CLIENTID: string GITHUB_URL: string + GITLAB_URL?: string GMAIL_URL: string INITIAL_URL: string LINK_PREVIEW_URL?: string diff --git a/dev/docker-compose.yaml b/dev/docker-compose.yaml index 7bd59d7873..9876b05f8e 100644 --- a/dev/docker-compose.yaml +++ b/dev/docker-compose.yaml @@ -282,6 +282,7 @@ services: - COLLABORATOR_URL=ws://huly.local:3078 - STORAGE_CONFIG=${STORAGE_CONFIG} - GITHUB_URL=http://huly.local:3500 + - GITLAB_URL=http://huly.local:3600 - PRINT_URL=http://huly.local:4005 - SIGN_URL=http://huly.local:4006 # - ANALYTICS_COLLECTOR_URL=http://huly.local:4017 @@ -399,6 +400,25 @@ services: - ACCOUNTS_URL=http://huly.local:3000 - OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces - FRONT_URL=http://huly.local:8087 + gitlab: + image: platformcollective/gitlab + extra_hosts: + - 'huly.local:host-gateway' + restart: unless-stopped + ports: + - 3600:3600 + environment: + - PORT=3600 + - SERVER_SECRET=secret + - ACCOUNTS_URL=http://huly.local:3000 + - FRONT_URL=http://huly.local:8087 + - COLLABORATOR_URL=ws://huly.local:3078 + # OAuth callback; register the same URL in the GitLab application. Empty = {FRONT_URL}/gitlab + - GITLAB_REDIRECT_URI=${GITLAB_REDIRECT_URI:-} + - WEBHOOK_BASE_URL=${GITLAB_WEBHOOK_BASE_URL:-http://huly.local:3600} + - WEBHOOK_SECRET=${GITLAB_WEBHOOK_SECRET:-secret} + - GITLAB_ALLOW_INSECURE_HOSTS=true + - OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces sign: image: platformcollective/sign extra_hosts: diff --git a/dev/prod/package.json b/dev/prod/package.json index 4df37adc3f..fb6885c54d 100644 --- a/dev/prod/package.json +++ b/dev/prod/package.json @@ -124,6 +124,9 @@ "@hcengineering/github": "workspace:^0.7.0", "@hcengineering/github-assets": "workspace:^0.7.0", "@hcengineering/github-resources": "workspace:^0.7.0", + "@hcengineering/gitlab": "workspace:^0.7.0", + "@hcengineering/gitlab-assets": "workspace:^0.7.0", + "@hcengineering/gitlab-resources": "workspace:^0.7.0", "@hcengineering/gmail": "workspace:^0.7.0", "@hcengineering/gmail-assets": "workspace:^0.7.0", "@hcengineering/gmail-resources": "workspace:^0.7.0", diff --git a/dev/prod/public/config.json b/dev/prod/public/config.json index 8a3b72d286..26ac455fb5 100644 --- a/dev/prod/public/config.json +++ b/dev/prod/public/config.json @@ -12,6 +12,7 @@ "GITHUB_APP": "uberflow-dev", "GITHUB_CLIENTID": "Iv1.43f9cac43bd68617", "GITHUB_URL": "http://huly.local:3500", + "GITLAB_URL": "http://huly.local:3600", "LAST_NAME_FIRST": "true", "PRINT_URL": "http://huly.local:4005", "SIGN_URL": "http://huly.local:4006", diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index 8844cc7104..52b65c7879 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -74,6 +74,7 @@ import workbench, { workbenchId } from '@hcengineering/workbench' import { mailId } from '@hcengineering/mail' import { chatId } from '@hcengineering/chat' import github, { githubId } from '@hcengineering/github' +import gitlab, { gitlabId } from '@hcengineering/gitlab' import { bitrixId } from '@hcengineering/bitrix' import { inboxId } from '@hcengineering/inbox' import { achievementId } from '@hcengineering/achievement' @@ -136,6 +137,7 @@ import '@hcengineering/chat-assets' import '@hcengineering/inbox-assets' import '@hcengineering/mail-assets' import '@hcengineering/github-assets' +import '@hcengineering/gitlab-assets' import '@hcengineering/achievement-assets' import '@hcengineering/communication-assets' import '@hcengineering/emoji-assets' @@ -173,6 +175,7 @@ export interface Config { GITHUB_APP?: string GITHUB_CLIENTID?: string GITHUB_URL: string + GITLAB_URL?: string LOVE_ENDPOINT?: string LIVEKIT_WS?: string SIGN_URL?: string @@ -364,6 +367,7 @@ function configureI18n(): void { addStringsLoader(documentId, async (lang: string) => await import(`@hcengineering/document-assets/lang/${lang}.json`)) addStringsLoader(timeId, async (lang: string) => await import(`@hcengineering/time-assets/lang/${lang}.json`)) addStringsLoader(githubId, async (lang: string) => await import(`@hcengineering/github-assets/lang/${lang}.json`)) + addStringsLoader(gitlabId, async (lang: string) => await import(`@hcengineering/gitlab-assets/lang/${lang}.json`)) addStringsLoader( documentsId, async (lang: string) => await import(`@hcengineering/controlled-documents-assets/lang/${lang}.json`) @@ -527,6 +531,7 @@ export async function configurePlatform() { setMetadata(github.metadata.GithubApplication, config.GITHUB_APP ?? '') setMetadata(github.metadata.GithubClientID, config.GITHUB_CLIENTID ?? '') setMetadata(github.metadata.GithubURL, config.GITHUB_URL) + setMetadata(gitlab.metadata.GitlabURL, config.GITLAB_URL ?? '') setMetadata(rekoni.metadata.RekoniUrl, config.REKONI_URL) @@ -562,6 +567,7 @@ export async function configurePlatform() { [loginId, login.component.LoginApp], [onboardId, onboard.component.OnboardApp], [githubId, github.component.ConnectApp], + [gitlabId, gitlab.component.ConnectApp], [calendarId, calendar.component.ConnectApp], [guestId, guest.component.GuestApp], [globalProfileRoute, globalProfile.component.GlobalProfileApp], @@ -647,6 +653,7 @@ export async function configurePlatform() { async () => await import(/* webpackChunkName: "document" */ '@hcengineering/document-resources') ) addLocation(githubId, async () => await import(/* webpackChunkName: "github" */ '@hcengineering/github-resources')) + addLocation(gitlabId, async () => await import(/* webpackChunkName: "gitlab" */ '@hcengineering/gitlab-resources')) addLocation( questionsId, async () => await import(/* webpackChunkName: "training" */ '@hcengineering/questions-resources') diff --git a/foundations/core/packages/core/src/classes.ts b/foundations/core/packages/core/src/classes.ts index 11049aa3fe..c16df47941 100644 --- a/foundations/core/packages/core/src/classes.ts +++ b/foundations/core/packages/core/src/classes.ts @@ -1010,6 +1010,7 @@ export interface WorkspaceMemberInfo { export enum SocialIdType { EMAIL = 'email', GITHUB = 'github', + GITLAB = 'gitlab', GOOGLE = 'google', PHONE = 'phone', OIDC = 'oidc', diff --git a/models/all/package.json b/models/all/package.json index 0547dbb11b..11d8bbaeeb 100644 --- a/models/all/package.json +++ b/models/all/package.json @@ -105,6 +105,8 @@ "@hcengineering/model-server-document": "workspace:^0.7.0", "@hcengineering/model-github": "workspace:^0.7.0", "@hcengineering/server-github-model": "workspace:^0.7.0", + "@hcengineering/server-gitlab-model": "workspace:^0.7.0", + "@hcengineering/model-gitlab": "workspace:^0.7.0", "@hcengineering/model-love": "workspace:^0.7.0", "@hcengineering/model-server-love": "workspace:^0.7.0", "@hcengineering/model-print": "workspace:^0.7.0", diff --git a/models/all/src/index.ts b/models/all/src/index.ts index e483fe8848..2dd62d39a5 100644 --- a/models/all/src/index.ts +++ b/models/all/src/index.ts @@ -89,6 +89,8 @@ import { serverDocumentId, createModel as serverDocumentModel } from '@hcenginee import github, { githubId, createModel as githubModel } from '@hcengineering/model-github' import { serverGithubId, createModel as serverGithubModel } from '@hcengineering/server-github-model' +import { serverGitlabId, createModel as serverGitlabModel } from '@hcengineering/server-gitlab-model' +import gitlab, { gitlabId, createModel as gitlabModel } from '@hcengineering/model-gitlab' import { analyticsCollectorId, createModel as analyticsCollectorModel } from '@hcengineering/model-analytics-collector' import { exportId, createModel as exportModel } from '@hcengineering/model-export' @@ -387,6 +389,18 @@ export default function buildModel (): Builder { classFilter: defaultFilter } ], + [ + gitlabModel, + gitlabId, + { + label: gitlab.string.ConfigLabel, + description: gitlab.string.ConfigDescription, + enabled: false, + beta: true, + icon: gitlab.icon.Gitlab, + classFilter: defaultFilter + } + ], [ loveModel, loveId, @@ -561,6 +575,7 @@ export default function buildModel (): Builder { [serverActivityModel, serverActivityId], [serverDocumentModel, serverDocumentId], [serverGithubModel, serverGithubId], + [serverGitlabModel, serverGitlabId], [serverLoveModel, serverLoveId], [serverTimeModel, serverTimeId], [serverGuestModel, serverGuestId], diff --git a/pods/front/src/__start.ts b/pods/front/src/__start.ts index fc8cd76377..60a6072172 100644 --- a/pods/front/src/__start.ts +++ b/pods/front/src/__start.ts @@ -32,6 +32,7 @@ startFront(metricsContext, { INTERCOM_APP_ID: process.env.INTERCOM_APP_ID ?? '', INTERCOM_API_URL: process.env.INTERCOM_API_URL ?? '', GITHUB_URL: process.env.GITHUB_URL ?? '', + GITLAB_URL: process.env.GITLAB_URL ?? '', LIVEKIT_WS: process.env.LIVEKIT_WS ?? '', LOVE_ENDPOINT: process.env.LOVE_ENDPOINT ?? '', SIGN_URL: process.env.SIGN_URL ?? '', diff --git a/rush.json b/rush.json index b46ef7ebb6..00f7e0ba96 100644 --- a/rush.json +++ b/rush.json @@ -2181,6 +2181,46 @@ "projectFolder": "services/github/server-github-model", "shouldPublish": false }, + { + "packageName": "@hcengineering/gitlab", + "projectFolder": "services/gitlab/gitlab", + "shouldPublish": false + }, + { + "packageName": "@hcengineering/gitlab-assets", + "projectFolder": "services/gitlab/gitlab-assets", + "shouldPublish": false + }, + { + "packageName": "@hcengineering/pod-gitlab", + "projectFolder": "services/gitlab/pod-gitlab", + "shouldPublish": false + }, + { + "packageName": "@hcengineering/model-gitlab", + "projectFolder": "services/gitlab/model-gitlab", + "shouldPublish": false + }, + { + "packageName": "@hcengineering/gitlab-resources", + "projectFolder": "services/gitlab/gitlab-resources", + "shouldPublish": false + }, + { + "packageName": "@hcengineering/server-gitlab", + "projectFolder": "services/gitlab/server-gitlab", + "shouldPublish": false + }, + { + "packageName": "@hcengineering/server-gitlab-resources", + "projectFolder": "services/gitlab/server-gitlab-resources", + "shouldPublish": false + }, + { + "packageName": "@hcengineering/server-gitlab-model", + "projectFolder": "services/gitlab/server-gitlab-model", + "shouldPublish": false + }, { "packageName": "@hcengineering/desktop-preferences", "projectFolder": "plugins/desktop-preferences", diff --git a/scripts/fast-start.sh b/scripts/fast-start.sh old mode 100644 new mode 100755 diff --git a/server/account/src/__tests__/gitlab-social-id.test.ts b/server/account/src/__tests__/gitlab-social-id.test.ts new file mode 100644 index 0000000000..874fa2c394 --- /dev/null +++ b/server/account/src/__tests__/gitlab-social-id.test.ts @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: EPL-2.0 +jest.mock('@hcengineering/platform', () => { + const actual = jest.requireActual('@hcengineering/platform') + return { ...actual, ...actual.default, getMetadata: jest.fn() } +}) + +jest.mock('@hcengineering/server-token', () => ({ + decodeTokenVerbose: jest.fn(), + decodeToken: jest.fn(), + generateToken: jest.fn() +})) + +/* eslint-disable import/first */ +import { type MeasureContext, type PersonId, type PersonUuid, SocialIdType } from '@hcengineering/core' +import { decodeTokenVerbose } from '@hcengineering/server-token' +import { getMigrations } from '../collections/postgres/migrations' +import { ensurePerson } from '../operations' +import { addSocialIdToPerson } from '../serviceOperations' +import type { AccountDB } from '../types' +import * as utils from '../utils' + +const ctx = { error: jest.fn(), info: jest.fn(), warn: jest.fn() } as unknown as MeasureContext + +describe('GitLab social ids', () => { + beforeEach(() => { + jest.clearAllMocks() + ;(decodeTokenVerbose as jest.Mock).mockReturnValue({ account: 'svc', extra: { service: 'gitlab' } }) + }) + + it('has a gitlab social id type', () => { + expect(SocialIdType.GITLAB).toBe('gitlab') + }) + + it('lets the gitlab service ensure a person without a workspace role check', async () => { + const db = { + socialId: { findOne: jest.fn(async () => ({ _id: 'sid-1' as PersonId, personUuid: 'p-1' as PersonUuid })) }, + getWorkspaceRole: jest.fn() + } as unknown as AccountDB + const result = await ensurePerson(ctx, db, null, 'token', { + socialType: SocialIdType.GITLAB, + socialValue: '42@gitlab.com', + firstName: 'Alice', + lastName: '' + }) + expect(result).toEqual({ uuid: 'p-1', socialId: 'sid-1' }) + expect((db as any).getWorkspaceRole).not.toHaveBeenCalled() + }) + + it('lets the gitlab service add a social id to a person', async () => { + const spy = jest.spyOn(utils, 'addSocialIdBase').mockResolvedValue('sid-2' as PersonId) + const result = await addSocialIdToPerson(ctx, {} as unknown as AccountDB, null, 'token', { + person: 'p-1' as PersonUuid, + type: SocialIdType.GITLAB, + value: '42@gitlab.com', + confirmed: true, + displayValue: 'alice' + }) + expect(result).toBe('sid-2') + expect(spy).toHaveBeenCalledWith(expect.anything(), 'p-1', SocialIdType.GITLAB, '42@gitlab.com', true, 'alice') + spy.mockRestore() + }) + + it.each(['postgres', 'cockroach'] as const)('migrates the %s social_id_type enum', (flavor) => { + const migration = getMigrations('global_account', flavor).find(([key]) => key === 'account_db_v30_add_gitlab_social_id_type') + expect(migration).toBeDefined() + expect(migration?.[1]).toContain("'gitlab'") + }) +}) diff --git a/server/account/src/__tests__/utils.test.ts b/server/account/src/__tests__/utils.test.ts index 0d1243f02c..24900fafff 100644 --- a/server/account/src/__tests__/utils.test.ts +++ b/server/account/src/__tests__/utils.test.ts @@ -56,6 +56,7 @@ import { cleanExpiredOtp, getWorkspaces, verifyAllowedServices, + integrationServices, getPersonName, getInviteEmail, getFrontUrl, @@ -2536,4 +2537,10 @@ describe('account utils', () => { }) }) }) + + describe('integrationServices', () => { + it('allows the gitlab service to manage integrations', () => { + expect(integrationServices).toContain('gitlab') + }) + }) }) diff --git a/server/account/src/collections/postgres/migrations.ts b/server/account/src/collections/postgres/migrations.ts index e46c18af01..7a7c206af0 100644 --- a/server/account/src/collections/postgres/migrations.ts +++ b/server/account/src/collections/postgres/migrations.ts @@ -86,7 +86,8 @@ export function getMigrations (ns: string, flavor: DBFlavor): [string, string][] getV26Migration(ns, flavor), getV27Migration(ns, flavor), getV28Migration(ns, flavor), - getV29Migration(ns, flavor) + getV29Migration(ns, flavor), + getV30Migration(ns, flavor) ] } @@ -885,3 +886,28 @@ function getV29Migration (ns: string, flavor: DBFlavor): [string, string] { ` ] } + +function getV30Migration (ns: string, flavor: DBFlavor): [string, string] { + // For PostgreSQL, we need to check if the value exists before adding it + const addValueSql = + flavor === 'postgres' + ? ` + -- Add gitlab value to social_id_type enum (PostgreSQL) + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_enum + WHERE enumlabel = 'gitlab' + AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'social_id_type' AND typnamespace = (SELECT oid FROM pg_namespace WHERE nspname = '${ns}')) + ) THEN + ALTER TYPE ${ns}.social_id_type ADD VALUE 'gitlab'; + END IF; + END $$; + ` + : ` + -- Add gitlab value to social_id_type enum (CockroachDB) + ALTER TYPE ${ns}.social_id_type ADD VALUE IF NOT EXISTS 'gitlab'; + ` + + return ['account_db_v30_add_gitlab_social_id_type', addValueSql] +} diff --git a/server/account/src/operations.ts b/server/account/src/operations.ts index 494459b229..f0fe331e32 100644 --- a/server/account/src/operations.ts +++ b/server/account/src/operations.ts @@ -2577,7 +2577,7 @@ export async function ensurePerson ( ): Promise<{ uuid: PersonUuid, socialId: PersonId }> { const { account, workspace, extra } = decodeTokenVerbose(ctx, token) const allowedService = verifyAllowedServices( - ['tool', 'workspace', 'schedule', 'mail', 'github', 'hulygram'], + ['tool', 'workspace', 'schedule', 'mail', 'github', 'gitlab', 'hulygram'], extra, false ) diff --git a/server/account/src/serviceOperations.ts b/server/account/src/serviceOperations.ts index f13af03345..254a54eab3 100644 --- a/server/account/src/serviceOperations.ts +++ b/server/account/src/serviceOperations.ts @@ -618,7 +618,7 @@ export async function addSocialIdToPerson ( if (extra?.admin !== 'true') { verifyAllowedServices( - ['github', 'telegram-bot', 'gmail', 'tool', 'workspace', 'hulygram', 'google-calendar', 'ai-assistant'], + ['github', 'gitlab', 'telegram-bot', 'gmail', 'tool', 'workspace', 'hulygram', 'google-calendar', 'ai-assistant'], extra ) } diff --git a/server/account/src/utils.ts b/server/account/src/utils.ts index 6020df3aab..99e490b091 100644 --- a/server/account/src/utils.ts +++ b/server/account/src/utils.ts @@ -2059,6 +2059,7 @@ export async function setTimezone ( // Move to config? export const integrationServices = [ 'github', + 'gitlab', 'telegram-bot', 'hulygram', 'mailbox', diff --git a/server/server-pipeline/package.json b/server/server-pipeline/package.json index 295b890128..b461d24cc0 100644 --- a/server/server-pipeline/package.json +++ b/server/server-pipeline/package.json @@ -71,6 +71,8 @@ "@hcengineering/server-calendar-resources": "workspace:^0.7.0", "@hcengineering/server-github": "workspace:^0.7.0", "@hcengineering/server-github-resources": "workspace:^0.7.0", + "@hcengineering/server-gitlab": "workspace:^0.7.0", + "@hcengineering/server-gitlab-resources": "workspace:^0.7.0", "@hcengineering/server-gmail": "workspace:^0.7.0", "@hcengineering/server-gmail-resources": "workspace:^0.7.0", "@hcengineering/server-preference": "workspace:^0.7.0", @@ -170,6 +172,8 @@ "@hcengineering/training": "workspace:^0.7.0", "@hcengineering/github": "workspace:^0.7.0", "@hcengineering/github-assets": "workspace:^0.7.0", + "@hcengineering/gitlab": "workspace:^0.7.0", + "@hcengineering/gitlab-assets": "workspace:^0.7.0", "@hcengineering/server-ai-bot": "workspace:^0.7.0", "@hcengineering/server-ai-bot-resources": "workspace:^0.7.0", "@hcengineering/time": "workspace:^0.7.0", diff --git a/server/server-pipeline/src/internationalization.ts b/server/server-pipeline/src/internationalization.ts index e24082a7c4..160dc4b95e 100644 --- a/server/server-pipeline/src/internationalization.ts +++ b/server/server-pipeline/src/internationalization.ts @@ -11,6 +11,7 @@ import { documentId } from '@hcengineering/document' import { exportId } from '@hcengineering/export' import { driveId } from '@hcengineering/drive' import { githubId } from '@hcengineering/github' +import { gitlabId } from '@hcengineering/gitlab' import { gmailId } from '@hcengineering/gmail' import { hrId } from '@hcengineering/hr' import { inventoryId } from '@hcengineering/inventory' @@ -54,6 +55,7 @@ import documentEn from '@hcengineering/document-assets/lang/en.json' import exportEn from '@hcengineering/export-assets/lang/en.json' import driveEn from '@hcengineering/drive-assets/lang/en.json' import githubEn from '@hcengineering/github-assets/lang/en.json' +import gitlabEn from '@hcengineering/gitlab-assets/lang/en.json' import gmailEn from '@hcengineering/gmail-assets/lang/en.json' import hrEn from '@hcengineering/hr-assets/lang/en.json' import inventoryEn from '@hcengineering/inventory-assets/lang/en.json' @@ -119,6 +121,7 @@ export function registerStringLoaders (): void { addStringsLoader(productsId, async (lang: string) => productsEn) addStringsLoader(trainingId, async (lang: string) => trainingEn) addStringsLoader(githubId, async (lang: string) => githubEn) + addStringsLoader(gitlabId, async (lang: string) => gitlabEn) addStringsLoader(timeId, async (lang: string) => timeEn) addStringsLoader(surveyId, async (lang: string) => surveyEn) addStringsLoader(chatId, async (lang: string) => chatEn) diff --git a/server/server-pipeline/src/serverPlugins.ts b/server/server-pipeline/src/serverPlugins.ts index 36298ce9d5..fca468e5f0 100644 --- a/server/server-pipeline/src/serverPlugins.ts +++ b/server/server-pipeline/src/serverPlugins.ts @@ -10,6 +10,7 @@ import { serverDocumentsId } from '@hcengineering/server-controlled-documents' import { serverDocumentId } from '@hcengineering/server-document' import { serverDriveId } from '@hcengineering/server-drive' import { serverGithubId } from '@hcengineering/server-github' +import { serverGitlabId } from '@hcengineering/server-gitlab' import { serverGmailId } from '@hcengineering/server-gmail' import { serverGuestId } from '@hcengineering/server-guest' import { serverHrId } from '@hcengineering/server-hr' @@ -59,6 +60,7 @@ export function registerServerPlugins (): void { addLocation(serverDocumentsId, () => import('@hcengineering/server-controlled-documents-resources')) addLocation(serverTrainingId, () => import('@hcengineering/server-training-resources')) addLocation(serverGithubId, () => import('@hcengineering/server-github-resources')) + addLocation(serverGitlabId, () => import('@hcengineering/server-gitlab-resources')) addLocation(serverAiBotId, () => import('@hcengineering/server-ai-bot-resources')) addLocation(serverProcessId, () => import('@hcengineering/server-process-resources')) } diff --git a/services/gitlab/gitlab-assets/.eslintrc.js b/services/gitlab/gitlab-assets/.eslintrc.js new file mode 100644 index 0000000000..e73094bc7e --- /dev/null +++ b/services/gitlab/gitlab-assets/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/assets/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/services/gitlab/gitlab-assets/assets/icons.svg b/services/gitlab/gitlab-assets/assets/icons.svg new file mode 100644 index 0000000000..50a7b7ee20 --- /dev/null +++ b/services/gitlab/gitlab-assets/assets/icons.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/services/gitlab/gitlab-assets/config/rig.json b/services/gitlab/gitlab-assets/config/rig.json new file mode 100644 index 0000000000..b75800b9b7 --- /dev/null +++ b/services/gitlab/gitlab-assets/config/rig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig", + "rigProfile": "assets" +} diff --git a/services/gitlab/gitlab-assets/jest.config.js b/services/gitlab/gitlab-assets/jest.config.js new file mode 100644 index 0000000000..2cfd408b67 --- /dev/null +++ b/services/gitlab/gitlab-assets/jest.config.js @@ -0,0 +1,7 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + roots: ["./src"], + coverageReporters: ["text-summary", "html"] +} diff --git a/services/gitlab/gitlab-assets/lang/cs.json b/services/gitlab/gitlab-assets/lang/cs.json new file mode 100644 index 0000000000..29787aacc4 --- /dev/null +++ b/services/gitlab/gitlab-assets/lang/cs.json @@ -0,0 +1,53 @@ +{ + "string": { + "Gitlab": "GitLab", + "GitlabDesc": "Connect your GitLab projects to sync issues, merge requests, and project updates", + "ConfigLabel": "GitLab", + "ConfigDescription": "Extension for GitLab integration", + "Authorize": "Authorize {title} with GitLab", + "ReAuthorize": "Re-authorize {title} with GitLab", + "Authorized": "Connected as {login}", + "NotAuthorized": "Authorize {title} to access your GitLab account. Projects where you have the Maintainer role will be listed here.", + "Repositories": "GitLab projects", + "NoRepositories": "No projects with the Maintainer role were found for this account.", + "RefreshRepositories": "Refresh projects", + "LinkToProject": "Link to {title} project", + "Unlink": "Unlink", + "LinkedTo": "Linked to", + "Unavailable": "Unavailable", + "Processing": "Processing...", + "AutoClose": "This tab will close in {time} seconds", + "RequestFailed": "Request failed. Please try again.", + "CloseTab": "Close tab", + "KeepSettingsOpen": "Keep the {title} settings tab open while connecting GitLab, then try again", + "Disconnect": "Disconnect", + "Repository": "GitLab project", + "SetupTitle": "GitLab application", + "SetupWhere": "Choose where to register the application in GitLab:", + "SetupUserOwned": "User-owned: in your user settings", + "SetupGroupOwned": "Group-owned (recommended for teams): in the group's Settings > Applications", + "SetupInstanceWide": "Instance-wide (self-managed administrators): in the Admin area", + "SetupTrustedHint": "Tick Trusted to skip the consent screen for users.", + "SetupNameAndRedirect": "Name: {title}. Redirect URI:", + "SetupConfidentialScopes": "Tick Confidential, select the scopes {scopes}, then save the application.", + "SetupCopyCredentials": "Copy the Application ID and Secret below. GitLab shows the secret only once.", + "GitlabUrl": "GitLab URL", + "SelfManaged": "Self-managed GitLab", + "ApplicationId": "Application ID", + "ApplicationSecret": "Secret", + "KeepSecretHint": "Leave empty to keep the current secret", + "Save": "Save", + "Change": "Change", + "Remove": "Remove", + "Copy": "Copy", + "Copied": "Copied", + "ConfiguredApp": "GitLab application {clientId} on {host}", + "OwnerMustConfigure": "A workspace owner needs to set up the GitLab application first.", + "InvalidGitlabUrl": "Enter an https:// URL (http:// only for localhost or *.local hosts)", + "SetupEnterUrlFirst": "Enter the URL of your GitLab instance below to see where to register the application.", + "DisconnectEveryone": "Disconnect everyone", + "DisconnectEveryoneConfirm": "Disconnect every member's GitLab account from this workspace? Their linked projects are unlinked and webhooks are removed where possible. Members can connect again afterwards.", + "GitlabIssue": "GitLab issue", + "IssueConnectedActivityInfo": "Connected to GitLab issue #{number} in {repoName}" + } +} diff --git a/services/gitlab/gitlab-assets/lang/de.json b/services/gitlab/gitlab-assets/lang/de.json new file mode 100644 index 0000000000..29787aacc4 --- /dev/null +++ b/services/gitlab/gitlab-assets/lang/de.json @@ -0,0 +1,53 @@ +{ + "string": { + "Gitlab": "GitLab", + "GitlabDesc": "Connect your GitLab projects to sync issues, merge requests, and project updates", + "ConfigLabel": "GitLab", + "ConfigDescription": "Extension for GitLab integration", + "Authorize": "Authorize {title} with GitLab", + "ReAuthorize": "Re-authorize {title} with GitLab", + "Authorized": "Connected as {login}", + "NotAuthorized": "Authorize {title} to access your GitLab account. Projects where you have the Maintainer role will be listed here.", + "Repositories": "GitLab projects", + "NoRepositories": "No projects with the Maintainer role were found for this account.", + "RefreshRepositories": "Refresh projects", + "LinkToProject": "Link to {title} project", + "Unlink": "Unlink", + "LinkedTo": "Linked to", + "Unavailable": "Unavailable", + "Processing": "Processing...", + "AutoClose": "This tab will close in {time} seconds", + "RequestFailed": "Request failed. Please try again.", + "CloseTab": "Close tab", + "KeepSettingsOpen": "Keep the {title} settings tab open while connecting GitLab, then try again", + "Disconnect": "Disconnect", + "Repository": "GitLab project", + "SetupTitle": "GitLab application", + "SetupWhere": "Choose where to register the application in GitLab:", + "SetupUserOwned": "User-owned: in your user settings", + "SetupGroupOwned": "Group-owned (recommended for teams): in the group's Settings > Applications", + "SetupInstanceWide": "Instance-wide (self-managed administrators): in the Admin area", + "SetupTrustedHint": "Tick Trusted to skip the consent screen for users.", + "SetupNameAndRedirect": "Name: {title}. Redirect URI:", + "SetupConfidentialScopes": "Tick Confidential, select the scopes {scopes}, then save the application.", + "SetupCopyCredentials": "Copy the Application ID and Secret below. GitLab shows the secret only once.", + "GitlabUrl": "GitLab URL", + "SelfManaged": "Self-managed GitLab", + "ApplicationId": "Application ID", + "ApplicationSecret": "Secret", + "KeepSecretHint": "Leave empty to keep the current secret", + "Save": "Save", + "Change": "Change", + "Remove": "Remove", + "Copy": "Copy", + "Copied": "Copied", + "ConfiguredApp": "GitLab application {clientId} on {host}", + "OwnerMustConfigure": "A workspace owner needs to set up the GitLab application first.", + "InvalidGitlabUrl": "Enter an https:// URL (http:// only for localhost or *.local hosts)", + "SetupEnterUrlFirst": "Enter the URL of your GitLab instance below to see where to register the application.", + "DisconnectEveryone": "Disconnect everyone", + "DisconnectEveryoneConfirm": "Disconnect every member's GitLab account from this workspace? Their linked projects are unlinked and webhooks are removed where possible. Members can connect again afterwards.", + "GitlabIssue": "GitLab issue", + "IssueConnectedActivityInfo": "Connected to GitLab issue #{number} in {repoName}" + } +} diff --git a/services/gitlab/gitlab-assets/lang/en.json b/services/gitlab/gitlab-assets/lang/en.json new file mode 100644 index 0000000000..29787aacc4 --- /dev/null +++ b/services/gitlab/gitlab-assets/lang/en.json @@ -0,0 +1,53 @@ +{ + "string": { + "Gitlab": "GitLab", + "GitlabDesc": "Connect your GitLab projects to sync issues, merge requests, and project updates", + "ConfigLabel": "GitLab", + "ConfigDescription": "Extension for GitLab integration", + "Authorize": "Authorize {title} with GitLab", + "ReAuthorize": "Re-authorize {title} with GitLab", + "Authorized": "Connected as {login}", + "NotAuthorized": "Authorize {title} to access your GitLab account. Projects where you have the Maintainer role will be listed here.", + "Repositories": "GitLab projects", + "NoRepositories": "No projects with the Maintainer role were found for this account.", + "RefreshRepositories": "Refresh projects", + "LinkToProject": "Link to {title} project", + "Unlink": "Unlink", + "LinkedTo": "Linked to", + "Unavailable": "Unavailable", + "Processing": "Processing...", + "AutoClose": "This tab will close in {time} seconds", + "RequestFailed": "Request failed. Please try again.", + "CloseTab": "Close tab", + "KeepSettingsOpen": "Keep the {title} settings tab open while connecting GitLab, then try again", + "Disconnect": "Disconnect", + "Repository": "GitLab project", + "SetupTitle": "GitLab application", + "SetupWhere": "Choose where to register the application in GitLab:", + "SetupUserOwned": "User-owned: in your user settings", + "SetupGroupOwned": "Group-owned (recommended for teams): in the group's Settings > Applications", + "SetupInstanceWide": "Instance-wide (self-managed administrators): in the Admin area", + "SetupTrustedHint": "Tick Trusted to skip the consent screen for users.", + "SetupNameAndRedirect": "Name: {title}. Redirect URI:", + "SetupConfidentialScopes": "Tick Confidential, select the scopes {scopes}, then save the application.", + "SetupCopyCredentials": "Copy the Application ID and Secret below. GitLab shows the secret only once.", + "GitlabUrl": "GitLab URL", + "SelfManaged": "Self-managed GitLab", + "ApplicationId": "Application ID", + "ApplicationSecret": "Secret", + "KeepSecretHint": "Leave empty to keep the current secret", + "Save": "Save", + "Change": "Change", + "Remove": "Remove", + "Copy": "Copy", + "Copied": "Copied", + "ConfiguredApp": "GitLab application {clientId} on {host}", + "OwnerMustConfigure": "A workspace owner needs to set up the GitLab application first.", + "InvalidGitlabUrl": "Enter an https:// URL (http:// only for localhost or *.local hosts)", + "SetupEnterUrlFirst": "Enter the URL of your GitLab instance below to see where to register the application.", + "DisconnectEveryone": "Disconnect everyone", + "DisconnectEveryoneConfirm": "Disconnect every member's GitLab account from this workspace? Their linked projects are unlinked and webhooks are removed where possible. Members can connect again afterwards.", + "GitlabIssue": "GitLab issue", + "IssueConnectedActivityInfo": "Connected to GitLab issue #{number} in {repoName}" + } +} diff --git a/services/gitlab/gitlab-assets/lang/es.json b/services/gitlab/gitlab-assets/lang/es.json new file mode 100644 index 0000000000..29787aacc4 --- /dev/null +++ b/services/gitlab/gitlab-assets/lang/es.json @@ -0,0 +1,53 @@ +{ + "string": { + "Gitlab": "GitLab", + "GitlabDesc": "Connect your GitLab projects to sync issues, merge requests, and project updates", + "ConfigLabel": "GitLab", + "ConfigDescription": "Extension for GitLab integration", + "Authorize": "Authorize {title} with GitLab", + "ReAuthorize": "Re-authorize {title} with GitLab", + "Authorized": "Connected as {login}", + "NotAuthorized": "Authorize {title} to access your GitLab account. Projects where you have the Maintainer role will be listed here.", + "Repositories": "GitLab projects", + "NoRepositories": "No projects with the Maintainer role were found for this account.", + "RefreshRepositories": "Refresh projects", + "LinkToProject": "Link to {title} project", + "Unlink": "Unlink", + "LinkedTo": "Linked to", + "Unavailable": "Unavailable", + "Processing": "Processing...", + "AutoClose": "This tab will close in {time} seconds", + "RequestFailed": "Request failed. Please try again.", + "CloseTab": "Close tab", + "KeepSettingsOpen": "Keep the {title} settings tab open while connecting GitLab, then try again", + "Disconnect": "Disconnect", + "Repository": "GitLab project", + "SetupTitle": "GitLab application", + "SetupWhere": "Choose where to register the application in GitLab:", + "SetupUserOwned": "User-owned: in your user settings", + "SetupGroupOwned": "Group-owned (recommended for teams): in the group's Settings > Applications", + "SetupInstanceWide": "Instance-wide (self-managed administrators): in the Admin area", + "SetupTrustedHint": "Tick Trusted to skip the consent screen for users.", + "SetupNameAndRedirect": "Name: {title}. Redirect URI:", + "SetupConfidentialScopes": "Tick Confidential, select the scopes {scopes}, then save the application.", + "SetupCopyCredentials": "Copy the Application ID and Secret below. GitLab shows the secret only once.", + "GitlabUrl": "GitLab URL", + "SelfManaged": "Self-managed GitLab", + "ApplicationId": "Application ID", + "ApplicationSecret": "Secret", + "KeepSecretHint": "Leave empty to keep the current secret", + "Save": "Save", + "Change": "Change", + "Remove": "Remove", + "Copy": "Copy", + "Copied": "Copied", + "ConfiguredApp": "GitLab application {clientId} on {host}", + "OwnerMustConfigure": "A workspace owner needs to set up the GitLab application first.", + "InvalidGitlabUrl": "Enter an https:// URL (http:// only for localhost or *.local hosts)", + "SetupEnterUrlFirst": "Enter the URL of your GitLab instance below to see where to register the application.", + "DisconnectEveryone": "Disconnect everyone", + "DisconnectEveryoneConfirm": "Disconnect every member's GitLab account from this workspace? Their linked projects are unlinked and webhooks are removed where possible. Members can connect again afterwards.", + "GitlabIssue": "GitLab issue", + "IssueConnectedActivityInfo": "Connected to GitLab issue #{number} in {repoName}" + } +} diff --git a/services/gitlab/gitlab-assets/lang/fr.json b/services/gitlab/gitlab-assets/lang/fr.json new file mode 100644 index 0000000000..29787aacc4 --- /dev/null +++ b/services/gitlab/gitlab-assets/lang/fr.json @@ -0,0 +1,53 @@ +{ + "string": { + "Gitlab": "GitLab", + "GitlabDesc": "Connect your GitLab projects to sync issues, merge requests, and project updates", + "ConfigLabel": "GitLab", + "ConfigDescription": "Extension for GitLab integration", + "Authorize": "Authorize {title} with GitLab", + "ReAuthorize": "Re-authorize {title} with GitLab", + "Authorized": "Connected as {login}", + "NotAuthorized": "Authorize {title} to access your GitLab account. Projects where you have the Maintainer role will be listed here.", + "Repositories": "GitLab projects", + "NoRepositories": "No projects with the Maintainer role were found for this account.", + "RefreshRepositories": "Refresh projects", + "LinkToProject": "Link to {title} project", + "Unlink": "Unlink", + "LinkedTo": "Linked to", + "Unavailable": "Unavailable", + "Processing": "Processing...", + "AutoClose": "This tab will close in {time} seconds", + "RequestFailed": "Request failed. Please try again.", + "CloseTab": "Close tab", + "KeepSettingsOpen": "Keep the {title} settings tab open while connecting GitLab, then try again", + "Disconnect": "Disconnect", + "Repository": "GitLab project", + "SetupTitle": "GitLab application", + "SetupWhere": "Choose where to register the application in GitLab:", + "SetupUserOwned": "User-owned: in your user settings", + "SetupGroupOwned": "Group-owned (recommended for teams): in the group's Settings > Applications", + "SetupInstanceWide": "Instance-wide (self-managed administrators): in the Admin area", + "SetupTrustedHint": "Tick Trusted to skip the consent screen for users.", + "SetupNameAndRedirect": "Name: {title}. Redirect URI:", + "SetupConfidentialScopes": "Tick Confidential, select the scopes {scopes}, then save the application.", + "SetupCopyCredentials": "Copy the Application ID and Secret below. GitLab shows the secret only once.", + "GitlabUrl": "GitLab URL", + "SelfManaged": "Self-managed GitLab", + "ApplicationId": "Application ID", + "ApplicationSecret": "Secret", + "KeepSecretHint": "Leave empty to keep the current secret", + "Save": "Save", + "Change": "Change", + "Remove": "Remove", + "Copy": "Copy", + "Copied": "Copied", + "ConfiguredApp": "GitLab application {clientId} on {host}", + "OwnerMustConfigure": "A workspace owner needs to set up the GitLab application first.", + "InvalidGitlabUrl": "Enter an https:// URL (http:// only for localhost or *.local hosts)", + "SetupEnterUrlFirst": "Enter the URL of your GitLab instance below to see where to register the application.", + "DisconnectEveryone": "Disconnect everyone", + "DisconnectEveryoneConfirm": "Disconnect every member's GitLab account from this workspace? Their linked projects are unlinked and webhooks are removed where possible. Members can connect again afterwards.", + "GitlabIssue": "GitLab issue", + "IssueConnectedActivityInfo": "Connected to GitLab issue #{number} in {repoName}" + } +} diff --git a/services/gitlab/gitlab-assets/lang/it.json b/services/gitlab/gitlab-assets/lang/it.json new file mode 100644 index 0000000000..29787aacc4 --- /dev/null +++ b/services/gitlab/gitlab-assets/lang/it.json @@ -0,0 +1,53 @@ +{ + "string": { + "Gitlab": "GitLab", + "GitlabDesc": "Connect your GitLab projects to sync issues, merge requests, and project updates", + "ConfigLabel": "GitLab", + "ConfigDescription": "Extension for GitLab integration", + "Authorize": "Authorize {title} with GitLab", + "ReAuthorize": "Re-authorize {title} with GitLab", + "Authorized": "Connected as {login}", + "NotAuthorized": "Authorize {title} to access your GitLab account. Projects where you have the Maintainer role will be listed here.", + "Repositories": "GitLab projects", + "NoRepositories": "No projects with the Maintainer role were found for this account.", + "RefreshRepositories": "Refresh projects", + "LinkToProject": "Link to {title} project", + "Unlink": "Unlink", + "LinkedTo": "Linked to", + "Unavailable": "Unavailable", + "Processing": "Processing...", + "AutoClose": "This tab will close in {time} seconds", + "RequestFailed": "Request failed. Please try again.", + "CloseTab": "Close tab", + "KeepSettingsOpen": "Keep the {title} settings tab open while connecting GitLab, then try again", + "Disconnect": "Disconnect", + "Repository": "GitLab project", + "SetupTitle": "GitLab application", + "SetupWhere": "Choose where to register the application in GitLab:", + "SetupUserOwned": "User-owned: in your user settings", + "SetupGroupOwned": "Group-owned (recommended for teams): in the group's Settings > Applications", + "SetupInstanceWide": "Instance-wide (self-managed administrators): in the Admin area", + "SetupTrustedHint": "Tick Trusted to skip the consent screen for users.", + "SetupNameAndRedirect": "Name: {title}. Redirect URI:", + "SetupConfidentialScopes": "Tick Confidential, select the scopes {scopes}, then save the application.", + "SetupCopyCredentials": "Copy the Application ID and Secret below. GitLab shows the secret only once.", + "GitlabUrl": "GitLab URL", + "SelfManaged": "Self-managed GitLab", + "ApplicationId": "Application ID", + "ApplicationSecret": "Secret", + "KeepSecretHint": "Leave empty to keep the current secret", + "Save": "Save", + "Change": "Change", + "Remove": "Remove", + "Copy": "Copy", + "Copied": "Copied", + "ConfiguredApp": "GitLab application {clientId} on {host}", + "OwnerMustConfigure": "A workspace owner needs to set up the GitLab application first.", + "InvalidGitlabUrl": "Enter an https:// URL (http:// only for localhost or *.local hosts)", + "SetupEnterUrlFirst": "Enter the URL of your GitLab instance below to see where to register the application.", + "DisconnectEveryone": "Disconnect everyone", + "DisconnectEveryoneConfirm": "Disconnect every member's GitLab account from this workspace? Their linked projects are unlinked and webhooks are removed where possible. Members can connect again afterwards.", + "GitlabIssue": "GitLab issue", + "IssueConnectedActivityInfo": "Connected to GitLab issue #{number} in {repoName}" + } +} diff --git a/services/gitlab/gitlab-assets/lang/ko.json b/services/gitlab/gitlab-assets/lang/ko.json new file mode 100644 index 0000000000..29787aacc4 --- /dev/null +++ b/services/gitlab/gitlab-assets/lang/ko.json @@ -0,0 +1,53 @@ +{ + "string": { + "Gitlab": "GitLab", + "GitlabDesc": "Connect your GitLab projects to sync issues, merge requests, and project updates", + "ConfigLabel": "GitLab", + "ConfigDescription": "Extension for GitLab integration", + "Authorize": "Authorize {title} with GitLab", + "ReAuthorize": "Re-authorize {title} with GitLab", + "Authorized": "Connected as {login}", + "NotAuthorized": "Authorize {title} to access your GitLab account. Projects where you have the Maintainer role will be listed here.", + "Repositories": "GitLab projects", + "NoRepositories": "No projects with the Maintainer role were found for this account.", + "RefreshRepositories": "Refresh projects", + "LinkToProject": "Link to {title} project", + "Unlink": "Unlink", + "LinkedTo": "Linked to", + "Unavailable": "Unavailable", + "Processing": "Processing...", + "AutoClose": "This tab will close in {time} seconds", + "RequestFailed": "Request failed. Please try again.", + "CloseTab": "Close tab", + "KeepSettingsOpen": "Keep the {title} settings tab open while connecting GitLab, then try again", + "Disconnect": "Disconnect", + "Repository": "GitLab project", + "SetupTitle": "GitLab application", + "SetupWhere": "Choose where to register the application in GitLab:", + "SetupUserOwned": "User-owned: in your user settings", + "SetupGroupOwned": "Group-owned (recommended for teams): in the group's Settings > Applications", + "SetupInstanceWide": "Instance-wide (self-managed administrators): in the Admin area", + "SetupTrustedHint": "Tick Trusted to skip the consent screen for users.", + "SetupNameAndRedirect": "Name: {title}. Redirect URI:", + "SetupConfidentialScopes": "Tick Confidential, select the scopes {scopes}, then save the application.", + "SetupCopyCredentials": "Copy the Application ID and Secret below. GitLab shows the secret only once.", + "GitlabUrl": "GitLab URL", + "SelfManaged": "Self-managed GitLab", + "ApplicationId": "Application ID", + "ApplicationSecret": "Secret", + "KeepSecretHint": "Leave empty to keep the current secret", + "Save": "Save", + "Change": "Change", + "Remove": "Remove", + "Copy": "Copy", + "Copied": "Copied", + "ConfiguredApp": "GitLab application {clientId} on {host}", + "OwnerMustConfigure": "A workspace owner needs to set up the GitLab application first.", + "InvalidGitlabUrl": "Enter an https:// URL (http:// only for localhost or *.local hosts)", + "SetupEnterUrlFirst": "Enter the URL of your GitLab instance below to see where to register the application.", + "DisconnectEveryone": "Disconnect everyone", + "DisconnectEveryoneConfirm": "Disconnect every member's GitLab account from this workspace? Their linked projects are unlinked and webhooks are removed where possible. Members can connect again afterwards.", + "GitlabIssue": "GitLab issue", + "IssueConnectedActivityInfo": "Connected to GitLab issue #{number} in {repoName}" + } +} diff --git a/services/gitlab/gitlab-assets/lang/pl.json b/services/gitlab/gitlab-assets/lang/pl.json new file mode 100644 index 0000000000..29787aacc4 --- /dev/null +++ b/services/gitlab/gitlab-assets/lang/pl.json @@ -0,0 +1,53 @@ +{ + "string": { + "Gitlab": "GitLab", + "GitlabDesc": "Connect your GitLab projects to sync issues, merge requests, and project updates", + "ConfigLabel": "GitLab", + "ConfigDescription": "Extension for GitLab integration", + "Authorize": "Authorize {title} with GitLab", + "ReAuthorize": "Re-authorize {title} with GitLab", + "Authorized": "Connected as {login}", + "NotAuthorized": "Authorize {title} to access your GitLab account. Projects where you have the Maintainer role will be listed here.", + "Repositories": "GitLab projects", + "NoRepositories": "No projects with the Maintainer role were found for this account.", + "RefreshRepositories": "Refresh projects", + "LinkToProject": "Link to {title} project", + "Unlink": "Unlink", + "LinkedTo": "Linked to", + "Unavailable": "Unavailable", + "Processing": "Processing...", + "AutoClose": "This tab will close in {time} seconds", + "RequestFailed": "Request failed. Please try again.", + "CloseTab": "Close tab", + "KeepSettingsOpen": "Keep the {title} settings tab open while connecting GitLab, then try again", + "Disconnect": "Disconnect", + "Repository": "GitLab project", + "SetupTitle": "GitLab application", + "SetupWhere": "Choose where to register the application in GitLab:", + "SetupUserOwned": "User-owned: in your user settings", + "SetupGroupOwned": "Group-owned (recommended for teams): in the group's Settings > Applications", + "SetupInstanceWide": "Instance-wide (self-managed administrators): in the Admin area", + "SetupTrustedHint": "Tick Trusted to skip the consent screen for users.", + "SetupNameAndRedirect": "Name: {title}. Redirect URI:", + "SetupConfidentialScopes": "Tick Confidential, select the scopes {scopes}, then save the application.", + "SetupCopyCredentials": "Copy the Application ID and Secret below. GitLab shows the secret only once.", + "GitlabUrl": "GitLab URL", + "SelfManaged": "Self-managed GitLab", + "ApplicationId": "Application ID", + "ApplicationSecret": "Secret", + "KeepSecretHint": "Leave empty to keep the current secret", + "Save": "Save", + "Change": "Change", + "Remove": "Remove", + "Copy": "Copy", + "Copied": "Copied", + "ConfiguredApp": "GitLab application {clientId} on {host}", + "OwnerMustConfigure": "A workspace owner needs to set up the GitLab application first.", + "InvalidGitlabUrl": "Enter an https:// URL (http:// only for localhost or *.local hosts)", + "SetupEnterUrlFirst": "Enter the URL of your GitLab instance below to see where to register the application.", + "DisconnectEveryone": "Disconnect everyone", + "DisconnectEveryoneConfirm": "Disconnect every member's GitLab account from this workspace? Their linked projects are unlinked and webhooks are removed where possible. Members can connect again afterwards.", + "GitlabIssue": "GitLab issue", + "IssueConnectedActivityInfo": "Connected to GitLab issue #{number} in {repoName}" + } +} diff --git a/services/gitlab/gitlab-assets/lang/pt-br.json b/services/gitlab/gitlab-assets/lang/pt-br.json new file mode 100644 index 0000000000..29787aacc4 --- /dev/null +++ b/services/gitlab/gitlab-assets/lang/pt-br.json @@ -0,0 +1,53 @@ +{ + "string": { + "Gitlab": "GitLab", + "GitlabDesc": "Connect your GitLab projects to sync issues, merge requests, and project updates", + "ConfigLabel": "GitLab", + "ConfigDescription": "Extension for GitLab integration", + "Authorize": "Authorize {title} with GitLab", + "ReAuthorize": "Re-authorize {title} with GitLab", + "Authorized": "Connected as {login}", + "NotAuthorized": "Authorize {title} to access your GitLab account. Projects where you have the Maintainer role will be listed here.", + "Repositories": "GitLab projects", + "NoRepositories": "No projects with the Maintainer role were found for this account.", + "RefreshRepositories": "Refresh projects", + "LinkToProject": "Link to {title} project", + "Unlink": "Unlink", + "LinkedTo": "Linked to", + "Unavailable": "Unavailable", + "Processing": "Processing...", + "AutoClose": "This tab will close in {time} seconds", + "RequestFailed": "Request failed. Please try again.", + "CloseTab": "Close tab", + "KeepSettingsOpen": "Keep the {title} settings tab open while connecting GitLab, then try again", + "Disconnect": "Disconnect", + "Repository": "GitLab project", + "SetupTitle": "GitLab application", + "SetupWhere": "Choose where to register the application in GitLab:", + "SetupUserOwned": "User-owned: in your user settings", + "SetupGroupOwned": "Group-owned (recommended for teams): in the group's Settings > Applications", + "SetupInstanceWide": "Instance-wide (self-managed administrators): in the Admin area", + "SetupTrustedHint": "Tick Trusted to skip the consent screen for users.", + "SetupNameAndRedirect": "Name: {title}. Redirect URI:", + "SetupConfidentialScopes": "Tick Confidential, select the scopes {scopes}, then save the application.", + "SetupCopyCredentials": "Copy the Application ID and Secret below. GitLab shows the secret only once.", + "GitlabUrl": "GitLab URL", + "SelfManaged": "Self-managed GitLab", + "ApplicationId": "Application ID", + "ApplicationSecret": "Secret", + "KeepSecretHint": "Leave empty to keep the current secret", + "Save": "Save", + "Change": "Change", + "Remove": "Remove", + "Copy": "Copy", + "Copied": "Copied", + "ConfiguredApp": "GitLab application {clientId} on {host}", + "OwnerMustConfigure": "A workspace owner needs to set up the GitLab application first.", + "InvalidGitlabUrl": "Enter an https:// URL (http:// only for localhost or *.local hosts)", + "SetupEnterUrlFirst": "Enter the URL of your GitLab instance below to see where to register the application.", + "DisconnectEveryone": "Disconnect everyone", + "DisconnectEveryoneConfirm": "Disconnect every member's GitLab account from this workspace? Their linked projects are unlinked and webhooks are removed where possible. Members can connect again afterwards.", + "GitlabIssue": "GitLab issue", + "IssueConnectedActivityInfo": "Connected to GitLab issue #{number} in {repoName}" + } +} diff --git a/services/gitlab/gitlab-assets/lang/pt.json b/services/gitlab/gitlab-assets/lang/pt.json new file mode 100644 index 0000000000..29787aacc4 --- /dev/null +++ b/services/gitlab/gitlab-assets/lang/pt.json @@ -0,0 +1,53 @@ +{ + "string": { + "Gitlab": "GitLab", + "GitlabDesc": "Connect your GitLab projects to sync issues, merge requests, and project updates", + "ConfigLabel": "GitLab", + "ConfigDescription": "Extension for GitLab integration", + "Authorize": "Authorize {title} with GitLab", + "ReAuthorize": "Re-authorize {title} with GitLab", + "Authorized": "Connected as {login}", + "NotAuthorized": "Authorize {title} to access your GitLab account. Projects where you have the Maintainer role will be listed here.", + "Repositories": "GitLab projects", + "NoRepositories": "No projects with the Maintainer role were found for this account.", + "RefreshRepositories": "Refresh projects", + "LinkToProject": "Link to {title} project", + "Unlink": "Unlink", + "LinkedTo": "Linked to", + "Unavailable": "Unavailable", + "Processing": "Processing...", + "AutoClose": "This tab will close in {time} seconds", + "RequestFailed": "Request failed. Please try again.", + "CloseTab": "Close tab", + "KeepSettingsOpen": "Keep the {title} settings tab open while connecting GitLab, then try again", + "Disconnect": "Disconnect", + "Repository": "GitLab project", + "SetupTitle": "GitLab application", + "SetupWhere": "Choose where to register the application in GitLab:", + "SetupUserOwned": "User-owned: in your user settings", + "SetupGroupOwned": "Group-owned (recommended for teams): in the group's Settings > Applications", + "SetupInstanceWide": "Instance-wide (self-managed administrators): in the Admin area", + "SetupTrustedHint": "Tick Trusted to skip the consent screen for users.", + "SetupNameAndRedirect": "Name: {title}. Redirect URI:", + "SetupConfidentialScopes": "Tick Confidential, select the scopes {scopes}, then save the application.", + "SetupCopyCredentials": "Copy the Application ID and Secret below. GitLab shows the secret only once.", + "GitlabUrl": "GitLab URL", + "SelfManaged": "Self-managed GitLab", + "ApplicationId": "Application ID", + "ApplicationSecret": "Secret", + "KeepSecretHint": "Leave empty to keep the current secret", + "Save": "Save", + "Change": "Change", + "Remove": "Remove", + "Copy": "Copy", + "Copied": "Copied", + "ConfiguredApp": "GitLab application {clientId} on {host}", + "OwnerMustConfigure": "A workspace owner needs to set up the GitLab application first.", + "InvalidGitlabUrl": "Enter an https:// URL (http:// only for localhost or *.local hosts)", + "SetupEnterUrlFirst": "Enter the URL of your GitLab instance below to see where to register the application.", + "DisconnectEveryone": "Disconnect everyone", + "DisconnectEveryoneConfirm": "Disconnect every member's GitLab account from this workspace? Their linked projects are unlinked and webhooks are removed where possible. Members can connect again afterwards.", + "GitlabIssue": "GitLab issue", + "IssueConnectedActivityInfo": "Connected to GitLab issue #{number} in {repoName}" + } +} diff --git a/services/gitlab/gitlab-assets/lang/ru.json b/services/gitlab/gitlab-assets/lang/ru.json new file mode 100644 index 0000000000..84c816e8f8 --- /dev/null +++ b/services/gitlab/gitlab-assets/lang/ru.json @@ -0,0 +1,53 @@ +{ + "string": { + "Gitlab": "GitLab", + "GitlabDesc": "Подключите проекты GitLab для синхронизации задач, merge request и обновлений проектов", + "ConfigLabel": "GitLab", + "ConfigDescription": "Расширение для интеграции с GitLab", + "Authorize": "Авторизовать {title} в GitLab", + "ReAuthorize": "Повторно авторизовать {title} в GitLab", + "Authorized": "Подключено как {login}", + "NotAuthorized": "Авторизуйте {title} для доступа к вашему аккаунту GitLab. Здесь будут показаны проекты, в которых у вас роль Maintainer.", + "Repositories": "Проекты GitLab", + "NoRepositories": "Для этого аккаунта не найдено проектов с ролью Maintainer.", + "RefreshRepositories": "Обновить проекты", + "LinkToProject": "Связать с проектом {title}", + "Unlink": "Отвязать", + "LinkedTo": "Связан с", + "Unavailable": "Недоступен", + "Processing": "Обработка...", + "AutoClose": "Вкладка закроется через {time} сек.", + "RequestFailed": "Запрос не выполнен. Попробуйте ещё раз.", + "CloseTab": "Закрыть вкладку", + "KeepSettingsOpen": "Не закрывайте вкладку настроек {title} во время подключения GitLab и попробуйте снова", + "Disconnect": "Отключить", + "Repository": "Проект GitLab", + "SetupTitle": "Приложение GitLab", + "SetupWhere": "Выберите, где зарегистрировать приложение в GitLab:", + "SetupUserOwned": "Пользовательское: в настройках вашего профиля", + "SetupGroupOwned": "Групповое (рекомендуется для команд): в разделе Settings > Applications группы", + "SetupInstanceWide": "Для всего инстанса (администраторы self-managed): в разделе Admin area", + "SetupTrustedHint": "Отметьте Trusted, чтобы пользователи не видели экран подтверждения доступа.", + "SetupNameAndRedirect": "Имя: {title}. Redirect URI:", + "SetupConfidentialScopes": "Отметьте Confidential, выберите scopes {scopes} и сохраните приложение.", + "SetupCopyCredentials": "Скопируйте Application ID и Secret в поля ниже. GitLab показывает секрет только один раз.", + "GitlabUrl": "URL GitLab", + "SelfManaged": "Собственный сервер GitLab (self-managed)", + "ApplicationId": "Application ID", + "ApplicationSecret": "Секрет", + "KeepSecretHint": "Оставьте пустым, чтобы сохранить текущий секрет", + "Save": "Сохранить", + "Change": "Изменить", + "Remove": "Удалить", + "Copy": "Копировать", + "Copied": "Скопировано", + "ConfiguredApp": "Приложение GitLab {clientId} на {host}", + "OwnerMustConfigure": "Сначала владелец рабочего пространства должен настроить приложение GitLab.", + "InvalidGitlabUrl": "Введите URL с https:// (http:// только для localhost или хостов *.local)", + "SetupEnterUrlFirst": "Введите ниже URL вашего экземпляра GitLab, чтобы увидеть, где зарегистрировать приложение.", + "DisconnectEveryone": "Отключить всех", + "DisconnectEveryoneConfirm": "Отключить аккаунты GitLab всех участников от этого рабочего пространства? Их связанные проекты будут отвязаны, а вебхуки по возможности удалены. Участники смогут подключиться снова.", + "GitlabIssue": "Задача GitLab", + "IssueConnectedActivityInfo": "Связано с задачей GitLab #{number} в проекте {repoName}" + } +} diff --git a/services/gitlab/gitlab-assets/lang/sp.json b/services/gitlab/gitlab-assets/lang/sp.json new file mode 100644 index 0000000000..29787aacc4 --- /dev/null +++ b/services/gitlab/gitlab-assets/lang/sp.json @@ -0,0 +1,53 @@ +{ + "string": { + "Gitlab": "GitLab", + "GitlabDesc": "Connect your GitLab projects to sync issues, merge requests, and project updates", + "ConfigLabel": "GitLab", + "ConfigDescription": "Extension for GitLab integration", + "Authorize": "Authorize {title} with GitLab", + "ReAuthorize": "Re-authorize {title} with GitLab", + "Authorized": "Connected as {login}", + "NotAuthorized": "Authorize {title} to access your GitLab account. Projects where you have the Maintainer role will be listed here.", + "Repositories": "GitLab projects", + "NoRepositories": "No projects with the Maintainer role were found for this account.", + "RefreshRepositories": "Refresh projects", + "LinkToProject": "Link to {title} project", + "Unlink": "Unlink", + "LinkedTo": "Linked to", + "Unavailable": "Unavailable", + "Processing": "Processing...", + "AutoClose": "This tab will close in {time} seconds", + "RequestFailed": "Request failed. Please try again.", + "CloseTab": "Close tab", + "KeepSettingsOpen": "Keep the {title} settings tab open while connecting GitLab, then try again", + "Disconnect": "Disconnect", + "Repository": "GitLab project", + "SetupTitle": "GitLab application", + "SetupWhere": "Choose where to register the application in GitLab:", + "SetupUserOwned": "User-owned: in your user settings", + "SetupGroupOwned": "Group-owned (recommended for teams): in the group's Settings > Applications", + "SetupInstanceWide": "Instance-wide (self-managed administrators): in the Admin area", + "SetupTrustedHint": "Tick Trusted to skip the consent screen for users.", + "SetupNameAndRedirect": "Name: {title}. Redirect URI:", + "SetupConfidentialScopes": "Tick Confidential, select the scopes {scopes}, then save the application.", + "SetupCopyCredentials": "Copy the Application ID and Secret below. GitLab shows the secret only once.", + "GitlabUrl": "GitLab URL", + "SelfManaged": "Self-managed GitLab", + "ApplicationId": "Application ID", + "ApplicationSecret": "Secret", + "KeepSecretHint": "Leave empty to keep the current secret", + "Save": "Save", + "Change": "Change", + "Remove": "Remove", + "Copy": "Copy", + "Copied": "Copied", + "ConfiguredApp": "GitLab application {clientId} on {host}", + "OwnerMustConfigure": "A workspace owner needs to set up the GitLab application first.", + "InvalidGitlabUrl": "Enter an https:// URL (http:// only for localhost or *.local hosts)", + "SetupEnterUrlFirst": "Enter the URL of your GitLab instance below to see where to register the application.", + "DisconnectEveryone": "Disconnect everyone", + "DisconnectEveryoneConfirm": "Disconnect every member's GitLab account from this workspace? Their linked projects are unlinked and webhooks are removed where possible. Members can connect again afterwards.", + "GitlabIssue": "GitLab issue", + "IssueConnectedActivityInfo": "Connected to GitLab issue #{number} in {repoName}" + } +} diff --git a/services/gitlab/gitlab-assets/lang/tr.json b/services/gitlab/gitlab-assets/lang/tr.json new file mode 100644 index 0000000000..29787aacc4 --- /dev/null +++ b/services/gitlab/gitlab-assets/lang/tr.json @@ -0,0 +1,53 @@ +{ + "string": { + "Gitlab": "GitLab", + "GitlabDesc": "Connect your GitLab projects to sync issues, merge requests, and project updates", + "ConfigLabel": "GitLab", + "ConfigDescription": "Extension for GitLab integration", + "Authorize": "Authorize {title} with GitLab", + "ReAuthorize": "Re-authorize {title} with GitLab", + "Authorized": "Connected as {login}", + "NotAuthorized": "Authorize {title} to access your GitLab account. Projects where you have the Maintainer role will be listed here.", + "Repositories": "GitLab projects", + "NoRepositories": "No projects with the Maintainer role were found for this account.", + "RefreshRepositories": "Refresh projects", + "LinkToProject": "Link to {title} project", + "Unlink": "Unlink", + "LinkedTo": "Linked to", + "Unavailable": "Unavailable", + "Processing": "Processing...", + "AutoClose": "This tab will close in {time} seconds", + "RequestFailed": "Request failed. Please try again.", + "CloseTab": "Close tab", + "KeepSettingsOpen": "Keep the {title} settings tab open while connecting GitLab, then try again", + "Disconnect": "Disconnect", + "Repository": "GitLab project", + "SetupTitle": "GitLab application", + "SetupWhere": "Choose where to register the application in GitLab:", + "SetupUserOwned": "User-owned: in your user settings", + "SetupGroupOwned": "Group-owned (recommended for teams): in the group's Settings > Applications", + "SetupInstanceWide": "Instance-wide (self-managed administrators): in the Admin area", + "SetupTrustedHint": "Tick Trusted to skip the consent screen for users.", + "SetupNameAndRedirect": "Name: {title}. Redirect URI:", + "SetupConfidentialScopes": "Tick Confidential, select the scopes {scopes}, then save the application.", + "SetupCopyCredentials": "Copy the Application ID and Secret below. GitLab shows the secret only once.", + "GitlabUrl": "GitLab URL", + "SelfManaged": "Self-managed GitLab", + "ApplicationId": "Application ID", + "ApplicationSecret": "Secret", + "KeepSecretHint": "Leave empty to keep the current secret", + "Save": "Save", + "Change": "Change", + "Remove": "Remove", + "Copy": "Copy", + "Copied": "Copied", + "ConfiguredApp": "GitLab application {clientId} on {host}", + "OwnerMustConfigure": "A workspace owner needs to set up the GitLab application first.", + "InvalidGitlabUrl": "Enter an https:// URL (http:// only for localhost or *.local hosts)", + "SetupEnterUrlFirst": "Enter the URL of your GitLab instance below to see where to register the application.", + "DisconnectEveryone": "Disconnect everyone", + "DisconnectEveryoneConfirm": "Disconnect every member's GitLab account from this workspace? Their linked projects are unlinked and webhooks are removed where possible. Members can connect again afterwards.", + "GitlabIssue": "GitLab issue", + "IssueConnectedActivityInfo": "Connected to GitLab issue #{number} in {repoName}" + } +} diff --git a/services/gitlab/gitlab-assets/lang/zh.json b/services/gitlab/gitlab-assets/lang/zh.json new file mode 100644 index 0000000000..29787aacc4 --- /dev/null +++ b/services/gitlab/gitlab-assets/lang/zh.json @@ -0,0 +1,53 @@ +{ + "string": { + "Gitlab": "GitLab", + "GitlabDesc": "Connect your GitLab projects to sync issues, merge requests, and project updates", + "ConfigLabel": "GitLab", + "ConfigDescription": "Extension for GitLab integration", + "Authorize": "Authorize {title} with GitLab", + "ReAuthorize": "Re-authorize {title} with GitLab", + "Authorized": "Connected as {login}", + "NotAuthorized": "Authorize {title} to access your GitLab account. Projects where you have the Maintainer role will be listed here.", + "Repositories": "GitLab projects", + "NoRepositories": "No projects with the Maintainer role were found for this account.", + "RefreshRepositories": "Refresh projects", + "LinkToProject": "Link to {title} project", + "Unlink": "Unlink", + "LinkedTo": "Linked to", + "Unavailable": "Unavailable", + "Processing": "Processing...", + "AutoClose": "This tab will close in {time} seconds", + "RequestFailed": "Request failed. Please try again.", + "CloseTab": "Close tab", + "KeepSettingsOpen": "Keep the {title} settings tab open while connecting GitLab, then try again", + "Disconnect": "Disconnect", + "Repository": "GitLab project", + "SetupTitle": "GitLab application", + "SetupWhere": "Choose where to register the application in GitLab:", + "SetupUserOwned": "User-owned: in your user settings", + "SetupGroupOwned": "Group-owned (recommended for teams): in the group's Settings > Applications", + "SetupInstanceWide": "Instance-wide (self-managed administrators): in the Admin area", + "SetupTrustedHint": "Tick Trusted to skip the consent screen for users.", + "SetupNameAndRedirect": "Name: {title}. Redirect URI:", + "SetupConfidentialScopes": "Tick Confidential, select the scopes {scopes}, then save the application.", + "SetupCopyCredentials": "Copy the Application ID and Secret below. GitLab shows the secret only once.", + "GitlabUrl": "GitLab URL", + "SelfManaged": "Self-managed GitLab", + "ApplicationId": "Application ID", + "ApplicationSecret": "Secret", + "KeepSecretHint": "Leave empty to keep the current secret", + "Save": "Save", + "Change": "Change", + "Remove": "Remove", + "Copy": "Copy", + "Copied": "Copied", + "ConfiguredApp": "GitLab application {clientId} on {host}", + "OwnerMustConfigure": "A workspace owner needs to set up the GitLab application first.", + "InvalidGitlabUrl": "Enter an https:// URL (http:// only for localhost or *.local hosts)", + "SetupEnterUrlFirst": "Enter the URL of your GitLab instance below to see where to register the application.", + "DisconnectEveryone": "Disconnect everyone", + "DisconnectEveryoneConfirm": "Disconnect every member's GitLab account from this workspace? Their linked projects are unlinked and webhooks are removed where possible. Members can connect again afterwards.", + "GitlabIssue": "GitLab issue", + "IssueConnectedActivityInfo": "Connected to GitLab issue #{number} in {repoName}" + } +} diff --git a/services/gitlab/gitlab-assets/package.json b/services/gitlab/gitlab-assets/package.json new file mode 100644 index 0000000000..bb9b0ebd59 --- /dev/null +++ b/services/gitlab/gitlab-assets/package.json @@ -0,0 +1,67 @@ +{ + "name": "@hcengineering/gitlab-assets", + "version": "0.7.0", + "main": "./lib/index.js", + "types": "./types/index.d.ts", + "files": [ + "lib", + "types", + "src", + "assets", + "lang" + ], + "template": "@hcengineering/assets-package", + "scripts": { + "build": "compile", + "test": "jest --passWithNoTests --silent", + "build:docs": "", + "format": "format src", + "build:watch": "compile", + "_phase:build": "compile transpile src", + "_phase:test": "jest --passWithNoTests --silent", + "_phase:format": "format src", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "@hcengineering/platform-rig": "workspace:^0.7.21", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-n": "^15.4.0", + "eslint-plugin-promise": "^6.1.1", + "eslint": "^8.54.0", + "prettier": "^3.6.2", + "@types/node": "^24.13.3", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "@types/jest": "^29.5.5", + "typescript": "^5.9.3" + }, + "dependencies": { + "@hcengineering/platform": "workspace:^0.7.20", + "@hcengineering/gitlab": "workspace:^0.7.0" + }, + "exports": { + ".": { + "types": "./types/index.d.ts", + "default": "./lib/index.js" + }, + "./lang/*.json": { + "require": "./lang/*.json", + "import": "./lang/*.json" + }, + "./lang": { + "require": "./lang", + "import": "./lang" + }, + "./assets": { + "require": "./assets", + "import": "./assets" + }, + "./assets/*.svg": { + "require": "./assets/*.svg", + "import": "./assets/*.svg" + } + } +} diff --git a/services/gitlab/gitlab-assets/src/__tests__/lang.test.ts b/services/gitlab/gitlab-assets/src/__tests__/lang.test.ts new file mode 100644 index 0000000000..f0d50c5cd0 --- /dev/null +++ b/services/gitlab/gitlab-assets/src/__tests__/lang.test.ts @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: EPL-2.0 +import { makeLocalesTest } from '@hcengineering/platform' + +it( + 'Locales are equal', + makeLocalesTest((lang) => import(`../../lang/${lang}.json`)) +) + +const locales = ['cs', 'de', 'en', 'es', 'fr', 'it', 'ko', 'pl', 'pt', 'pt-br', 'ru', 'sp', 'tr', 'zh'] + +// The settings integration card renders the description without params; a placeholder would fail formatting. +it.each(locales)('GitlabDesc has no placeholders (%s)', async (lang) => { + const strings = (await import(`../../lang/${lang}.json`)).string + expect(strings.GitlabDesc).not.toMatch(/[{}]/) +}) + +// SetupApp always passes these params; a hard-coded product name or scope list would drift from the pod. +it.each(locales)('setup strings take the title and scopes params (%s)', async (lang) => { + const strings = (await import(`../../lang/${lang}.json`)).string + expect(strings.SetupNameAndRedirect).toContain('{title}') + expect(strings.SetupNameAndRedirect).not.toContain('Huly') + expect(strings.SetupConfidentialScopes).toContain('{scopes}') + expect(strings.DisconnectEveryone).toBeTruthy() + expect(strings.DisconnectEveryoneConfirm).not.toMatch(/[{}]/) +}) + +// The pod renders this activity message with exactly these params. +it.each(locales)('issue strings exist with their params (%s)', async (lang) => { + const strings = (await import(`../../lang/${lang}.json`)).string + expect(strings.GitlabIssue).toBeTruthy() + for (const param of ['{url}', '{number}', '{repository}', '{repoName}']) { + expect(strings.IssueConnectedActivityInfo).toContain(param) + } +}) diff --git a/services/gitlab/gitlab-assets/src/index.ts b/services/gitlab/gitlab-assets/src/index.ts new file mode 100644 index 0000000000..1c860bd227 --- /dev/null +++ b/services/gitlab/gitlab-assets/src/index.ts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: EPL-2.0 + +import { loadMetadata } from '@hcengineering/platform' +import gitlab from '@hcengineering/gitlab' + +const icons = require('../assets/icons.svg') as string // eslint-disable-line +loadMetadata(gitlab.icon, { + Gitlab: `${icons}#gitlab`, + GitlabRepository: `${icons}#repository` +}) diff --git a/services/gitlab/gitlab-assets/tsconfig.json b/services/gitlab/gitlab-assets/tsconfig.json new file mode 100644 index 0000000000..2857a41ad2 --- /dev/null +++ b/services/gitlab/gitlab-assets/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/assets/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "types": ["node", "jest"], + "tsBuildInfoFile": ".build/build.tsbuildinfo" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "lib", "dist", "types", "bundle"] +} \ No newline at end of file diff --git a/services/gitlab/gitlab-resources/.eslintrc.js b/services/gitlab/gitlab-resources/.eslintrc.js new file mode 100644 index 0000000000..bb8fd7450d --- /dev/null +++ b/services/gitlab/gitlab-resources/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/ui/eslint.config.json'], + parserOptions: { tsconfigRootDir: __dirname } +} diff --git a/services/gitlab/gitlab-resources/.prettierrc b/services/gitlab/gitlab-resources/.prettierrc new file mode 100644 index 0000000000..792942803a --- /dev/null +++ b/services/gitlab/gitlab-resources/.prettierrc @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "trailingComma": "none", + "tabWidth": 2, + "semi": false, + "singleQuote": true, + "printWidth": 120, + "useTabs": false, + "bracketSpacing": true, + "proseWrap": "preserve", + "plugins": [ + "prettier-plugin-svelte" + ], + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ] +} \ No newline at end of file diff --git a/services/gitlab/gitlab-resources/config/rig.json b/services/gitlab/gitlab-resources/config/rig.json new file mode 100644 index 0000000000..bcad6f7c33 --- /dev/null +++ b/services/gitlab/gitlab-resources/config/rig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig", + "rigProfile": "ui" +} diff --git a/services/gitlab/gitlab-resources/jest.config.js b/services/gitlab/gitlab-resources/jest.config.js new file mode 100644 index 0000000000..2cfd408b67 --- /dev/null +++ b/services/gitlab/gitlab-resources/jest.config.js @@ -0,0 +1,7 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + roots: ["./src"], + coverageReporters: ["text-summary", "html"] +} diff --git a/services/gitlab/gitlab-resources/package.json b/services/gitlab/gitlab-resources/package.json new file mode 100644 index 0000000000..5425b6c9f0 --- /dev/null +++ b/services/gitlab/gitlab-resources/package.json @@ -0,0 +1,55 @@ +{ + "name": "@hcengineering/gitlab-resources", + "version": "0.7.0", + "main": "src/index.ts", + "scripts": { + "build": "compile ui", + "build:docs": "api-extractor run --local", + "format": "format src", + "build:watch": "compile ui", + "_phase:build": "compile ui", + "_phase:format": "format src", + "_phase:validate": "compile validate", + "svelte-check": "do-svelte-check", + "_phase:svelte-check": "do-svelte-check", + "test": "jest --passWithNoTests --silent", + "_phase:test": "jest --passWithNoTests --silent" + }, + "devDependencies": { + "@hcengineering/platform-rig": "workspace:^0.7.21", + "svelte-loader": "^3.2.0", + "sass": "^1.80.0", + "svelte-preprocess": "^5.1.4", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-n": "^15.4.0", + "eslint": "^8.54.0", + "@typescript-eslint/parser": "^6.21.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "prettier-plugin-svelte": "^3.4.0", + "prettier": "^3.6.2", + "svelte-check": "^3.6.9", + "typescript": "^5.9.3", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "@types/jest": "^29.5.5", + "eslint-plugin-svelte": "^2.35.1", + "svelte-eslint-parser": "^0.33.1" + }, + "dependencies": { + "@hcengineering/core": "workspace:^0.7.26", + "@hcengineering/platform": "workspace:^0.7.20", + "@hcengineering/ui": "workspace:^0.7.0", + "@hcengineering/presentation": "workspace:^0.7.0", + "@hcengineering/tracker": "workspace:^0.7.0", + "@hcengineering/setting": "workspace:^0.7.0", + "@hcengineering/setting-resources": "workspace:^0.7.0", + "@hcengineering/account-client": "workspace:^0.7.25", + "@hcengineering/analytics": "workspace:^0.7.19", + "@hcengineering/login": "workspace:^0.7.0", + "@hcengineering/view-resources": "workspace:^0.7.0", + "@hcengineering/gitlab": "workspace:^0.7.0", + "svelte": "^4.2.20" + } +} diff --git a/services/gitlab/gitlab-resources/postcss.config.js b/services/gitlab/gitlab-resources/postcss.config.js new file mode 100644 index 0000000000..88752c6cb0 --- /dev/null +++ b/services/gitlab/gitlab-resources/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: [ + require('autoprefixer') + ] +} diff --git a/services/gitlab/gitlab-resources/src/__tests__/errors.test.ts b/services/gitlab/gitlab-resources/src/__tests__/errors.test.ts new file mode 100644 index 0000000000..5fea3b94f8 --- /dev/null +++ b/services/gitlab/gitlab-resources/src/__tests__/errors.test.ts @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: EPL-2.0 +import { PlatformError, Severity, Status, unknownError } from '@hcengineering/platform' +import { APP_IN_USE_PREFIX, errorText, isAppInUseError } from '../errors' + +describe('errorText', () => { + it('returns the message param of a PlatformError (as wrapped by sendGLServiceRequest)', () => { + const err = new PlatformError(unknownError('Disconnect GitLab before changing the application (connected: alice)')) + expect(err.message).toContain('UnknownError') + expect(errorText(err)).toBe('Disconnect GitLab before changing the application (connected: alice)') + }) + + it('falls back to the message of a PlatformError without a message param', () => { + const err = new PlatformError(new Status(Severity.ERROR, 'platform:status:Forbidden' as any, {})) + expect(errorText(err)).toBe(err.message) + }) + + it('ignores a non-string message param', () => { + const err = new PlatformError(new Status(Severity.ERROR, 'platform:status:X' as any, { message: 42 })) + expect(errorText(err)).toBe(err.message) + }) + + it('returns the message of a plain Error', () => { + expect(errorText(new Error('boom'))).toBe('boom') + }) + + it.each([['text', 'text'], [42, '42'], [undefined, 'undefined']])('stringifies %p', (value, expected) => { + expect(errorText(value)).toBe(expected) + }) +}) + +describe('isAppInUseError', () => { + it('detects the pod refusal through the PlatformError wrapper', () => { + expect(isAppInUseError(new PlatformError(unknownError(`${APP_IN_USE_PREFIX} (connected: alice, bob)`)))).toBe(true) + }) + + it('ignores other errors', () => { + expect(isAppInUseError(new Error('Only workspace owners can change the GitLab application'))).toBe(false) + }) +}) diff --git a/services/gitlab/gitlab-resources/src/__tests__/state.test.ts b/services/gitlab/gitlab-resources/src/__tests__/state.test.ts new file mode 100644 index 0000000000..28ed9c14f8 --- /dev/null +++ b/services/gitlab/gitlab-resources/src/__tests__/state.test.ts @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: EPL-2.0 +import { + OAuthCallbackTracker, + applicationLinks, + isLinkableProject, + isValidHostInput, + isOAuthCallbackMessage, + isOAuthErrorMessage, + isOAuthResultMessage, + oauthCallbackMessage, + oauthErrorMessage, + oauthResultMessage, + parseServiceResponse, + serviceUrl, + stateFromAuthorizeUrl +} from '../state' + +const garbage: unknown[] = [ + undefined, + null, + 0, + 'gitlab-oauth-callback', + [], + {}, + { type: 'other', code: 'c', state: 's' }, + { type: 'gitlab-oauth-callback' }, + { type: 'gitlab-oauth-callback', code: 1, state: 's' }, + { type: 'gitlab-oauth-callback', code: 'c', state: null }, + { type: 'gitlab-oauth-result', state: 's' }, + { type: 'gitlab-oauth-result', state: 's', ok: 'yes' }, + { type: 'gitlab-oauth-result', state: 5, ok: true }, + { type: 'gitlab-oauth-result', state: 's', ok: false, error: 42 }, + { type: 'gitlab-oauth-error', error: 1 } +] + +describe('serviceUrl', () => { + it('joins service url and path without double slashes', () => { + expect(serviceUrl('http://pod:3600/', 'auth')).toBe('http://pod:3600/api/v1/auth') + expect(serviceUrl('http://pod:3600', 'repository-enable')).toBe('http://pod:3600/api/v1/repository-enable') + }) +}) + +describe('oauth broadcast messages', () => { + it('builds messages that pass their own guard only', () => { + const callback = oauthCallbackMessage('c', 's') + const result = oauthResultMessage('s', false, 'boom') + const error = oauthErrorMessage('access_denied', 'The user denied access') + expect(callback).toEqual({ type: 'gitlab-oauth-callback', code: 'c', state: 's' }) + expect(result).toEqual({ type: 'gitlab-oauth-result', state: 's', ok: false, error: 'boom' }) + expect(error).toEqual({ type: 'gitlab-oauth-error', error: 'access_denied', description: 'The user denied access' }) + expect(isOAuthCallbackMessage(callback)).toBe(true) + expect(isOAuthResultMessage(callback)).toBe(false) + expect(isOAuthResultMessage(result)).toBe(true) + expect(isOAuthCallbackMessage(result)).toBe(false) + expect(isOAuthErrorMessage(error)).toBe(true) + expect(isOAuthCallbackMessage(error)).toBe(false) + }) + + it('omits an undefined error from a successful result', () => { + expect(oauthResultMessage('s', true)).toEqual({ type: 'gitlab-oauth-result', state: 's', ok: true }) + expect(isOAuthResultMessage(oauthResultMessage('s', true))).toBe(true) + }) + + it('rejects garbage input', () => { + for (const value of garbage) { + const callbackOk = isOAuthCallbackMessage(value) + const resultOk = isOAuthResultMessage(value) + const errorOk = isOAuthErrorMessage(value) + expect({ value, callbackOk, resultOk, errorOk }).toEqual({ value, callbackOk: false, resultOk: false, errorOk: false }) + } + }) +}) + +describe('OAuthCallbackTracker', () => { + it('accepts an expected state exactly once', () => { + const tracker = new OAuthCallbackTracker() + tracker.expect('s1') + expect(tracker.accept(oauthCallbackMessage('c', 's1'))).toBe(true) + expect(tracker.accept(oauthCallbackMessage('c', 's1'))).toBe(false) + }) + + it('rejects a state it did not request', () => { + const tracker = new OAuthCallbackTracker() + expect(tracker.accept(oauthCallbackMessage('c', 'unknown'))).toBe(false) + tracker.expect('s1') + expect(tracker.accept(oauthCallbackMessage('c', 'unknown'))).toBe(false) + expect(tracker.accept(oauthCallbackMessage('c', 's1'))).toBe(true) + }) + + it('keeps several pending states independent', () => { + const tracker = new OAuthCallbackTracker() + tracker.expect('a') + tracker.expect('b') + expect(tracker.accept(oauthCallbackMessage('c', 'b'))).toBe(true) + expect(tracker.accept(oauthCallbackMessage('c', 'a'))).toBe(true) + expect(tracker.accept(oauthCallbackMessage('c', 'a'))).toBe(false) + }) +}) + +describe('stateFromAuthorizeUrl', () => { + it('reads the state query parameter', () => { + expect(stateFromAuthorizeUrl('https://gitlab.com/oauth/authorize?client_id=x&state=abc.def')).toBe('abc.def') + }) + + it('returns undefined without a state or for an invalid url', () => { + expect(stateFromAuthorizeUrl('https://gitlab.com/oauth/authorize?client_id=x')).toBeUndefined() + expect(stateFromAuthorizeUrl('https://gitlab.com/oauth/authorize?state=')).toBeUndefined() + expect(stateFromAuthorizeUrl('not a url')).toBeUndefined() + }) +}) + +describe('parseServiceResponse', () => { + it('returns the parsed body of an ok JSON response', () => { + expect(parseServiceResponse(200, true, '{"url":"https://x"}')).toEqual({ url: 'https://x' }) + }) + + it('throws the error field of an error JSON response', () => { + expect(() => parseServiceResponse(403, false, '{"error":"Forbidden caller"}')).toThrow('Forbidden caller') + }) + + it('throws HTTP for a non-JSON error response', () => { + expect(() => parseServiceResponse(502, false, 'Bad Gateway')).toThrow('HTTP 502') + }) + + it('handles an empty body', () => { + expect(() => parseServiceResponse(500, false, '')).toThrow('HTTP 500') + expect(parseServiceResponse(200, true, '')).toEqual({}) + }) + + it('rejects an ok response that is not a JSON object', () => { + expect(() => parseServiceResponse(200, true, '')).toThrow('HTTP 200') + }) +}) + +describe('applicationLinks', () => { + it('builds user and admin application pages under a sub-path host', () => { + expect(applicationLinks('https://git.corp.local/gitlab/')).toEqual({ + user: 'https://git.corp.local/gitlab/-/user_settings/applications', + admin: 'https://git.corp.local/gitlab/admin/applications', + groupHint: 'https://git.corp.local/gitlab/groups//-/settings/applications' + }) + }) + + it('builds gitlab.com links', () => { + expect(applicationLinks('https://gitlab.com').user).toBe('https://gitlab.com/-/user_settings/applications') + }) +}) + +describe('isValidHostInput', () => { + it.each([ + ['https://gitlab.com', true], + ['http://localhost:8929', true], + ['http://gitlab.com', false], + ['gitlab.com', false], + ['', false], + ['https://git.corp.local/gitlab/', true], + ['http://git.corp.local', true], + ['http://127.0.0.1:8080', true], + ['https://gitlab.com/?x=1', false], + ['https://gitlab.com/#top', false], + ['https://user:pass@gitlab.com', false], + ['ftp://gitlab.com', false] + ])('%s -> %s', (raw, ok) => { + expect(isValidHostInput(raw as string)).toBe(ok) + }) +}) + +describe('isLinkableProject', () => { + const existing = new Set(['current', 'other']) + + it('accepts a project without the GitLab mixin', () => { + expect(isLinkableProject(undefined, 'current', existing)).toBe(true) + }) + + it('accepts a project already owned by the same integration', () => { + expect(isLinkableProject({ integration: 'current', repositories: ['r1'] }, 'current', existing)).toBe(true) + }) + + it('accepts a project pointing at a deleted integration with no repositories (live case)', () => { + expect(isLinkableProject({ integration: 'gone', repositories: [] }, 'current', existing)).toBe(true) + }) + + it('accepts a project pointing at a deleted integration with dangling repositories', () => { + expect(isLinkableProject({ integration: 'gone', repositories: ['r1'] }, 'current', existing)).toBe(true) + }) + + it('accepts a project of another existing integration that links no repositories', () => { + expect(isLinkableProject({ integration: 'other', repositories: [] }, 'current', existing)).toBe(true) + expect(isLinkableProject({ integration: 'other' }, 'current', existing)).toBe(true) + }) + + it('rejects a project actively linked to another existing integration', () => { + expect(isLinkableProject({ integration: 'other', repositories: ['r1'] }, 'current', existing)).toBe(false) + }) +}) diff --git a/services/gitlab/gitlab-resources/src/components/Configure.svelte b/services/gitlab/gitlab-resources/src/components/Configure.svelte new file mode 100644 index 0000000000..4c107ed19b --- /dev/null +++ b/services/gitlab/gitlab-resources/src/components/Configure.svelte @@ -0,0 +1,9 @@ + + + + diff --git a/services/gitlab/gitlab-resources/src/components/Connect.svelte b/services/gitlab/gitlab-resources/src/components/Connect.svelte new file mode 100644 index 0000000000..bf511dc02a --- /dev/null +++ b/services/gitlab/gitlab-resources/src/components/Connect.svelte @@ -0,0 +1,171 @@ + + + + { + dispatch('close') + }} + canSave={true} + okLabel={presentation.string.Ok} + on:close={() => dispatch('close')} + on:changeContent +> +
+ { + appConfigured = ev.detail + }} + /> + {#if auth !== undefined && auth.login !== ''} +
+ + {#if integrations.length > 0} + + {:else if mergeRequest.files > 0} +