Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2370491
Add GitLab integration with two-way issue sync
robertozimek Oct 8, 2026
737abca
Add GitLab merge request, review and image models
robertozimek Oct 9, 2026
b49633f
Sync GitLab merge requests, reviews and images
robertozimek Oct 9, 2026
ff7be88
Add GitLab merge request and review UI
robertozimek Oct 9, 2026
9d9d750
Let ToDo automation helpers veto auto-completion
robertozimek Oct 9, 2026
759419f
Add a LinkMark extension point for custom link rendering
robertozimek Oct 9, 2026
43ae28f
Count deleted lines in the large diff limit
robertozimek Oct 9, 2026
415f050
Fix lint and formatting in the GitLab packages
robertozimek Oct 9, 2026
4f84840
Format the LinkMark helper
robertozimek Oct 9, 2026
8a4ada4
Restore the shared presentation and time code to develop
robertozimek Oct 10, 2026
c1b1c41
Fold the GitLab social id tests into the account test suites
robertozimek Oct 10, 2026
4d010a2
Move the dev GitLab service into an optional compose overlay
robertozimek Oct 10, 2026
08237d3
Tidy the GitLab model, API and server trigger packages
robertozimek Oct 10, 2026
13d5fcb
Harden and tidy the GitLab sync service
robertozimek Oct 10, 2026
f0f26a3
Move GitLab UI logic into tested modules and fix UI bugs
robertozimek Oct 10, 2026
bcc7d88
Merge develop into feature/gitlab-phase1
robertozimek Oct 10, 2026
edb9ce7
Move the GitLab packages to ESLint 9 and Jest 30
robertozimek Oct 10, 2026
cb01f41
Fix ESLint 9 findings in the GitLab packages
robertozimek Oct 10, 2026
d71690e
Load assets strings through their lang modules
robertozimek Oct 10, 2026
e46211a
Address GitLab security review findings
robertozimek Oct 14, 2026
34d4dec
Merge remote-tracking branch 'origin/develop' into feature/gitlab-phase1
robertozimek Oct 14, 2026
0d615a0
Align cors and @types/cors versions in pod-gitlab
robertozimek Oct 14, 2026
2a31673
Apply formatter output to GitLab sources
robertozimek Oct 14, 2026
12b6387
Address second GitLab review round
robertozimek Oct 14, 2026
196b604
Merge remote-tracking branch 'origin/develop' into feature/gitlab-phase1
robertozimek Oct 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
752 changes: 752 additions & 0 deletions common/config/rush/pnpm-lock.yaml

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,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",
Expand Down
6 changes: 6 additions & 0 deletions desktop/src/ui/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ import { ipcMainExposed } from './typesUtils'
import github, { githubId } from '@hcengineering/github'
import '@hcengineering/github-assets'
import * as GithubLang from '@hcengineering/github-assets/lang'
import gitlab, { gitlabId } from '@hcengineering/gitlab'
import '@hcengineering/gitlab-assets'
import * as GitlabLang from '@hcengineering/gitlab-assets/lang'
import { preferenceId } from '@hcengineering/preference'
import { uiId } from '@hcengineering/ui/src/plugin'

Expand Down Expand Up @@ -276,6 +279,7 @@ function configureI18n (): void {
addStringsLoader(documentId, DocumentLang.loadLang)
addStringsLoader(timeId, TimeLang.loadLang)
addStringsLoader(githubId, GithubLang.loadLang)
addStringsLoader(gitlabId, GitlabLang.loadLang)
addStringsLoader(documentsId, ControlledDocumentsLang.loadLang)
addStringsLoader(productsId, ProductsLang.loadLang)
addStringsLoader(questionsId, QuestionsLang.loadLang)
Expand Down Expand Up @@ -363,6 +367,7 @@ export async function configurePlatform (onWorkbenchConnect?: () => Promise<void
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(communication.metadata.Enabled, config.COMMUNICATION_API_ENABLED === 'true')

Expand Down Expand Up @@ -469,6 +474,7 @@ export async function configurePlatform (onWorkbenchConnect?: () => Promise<void
addLocation(recorderId, async () => 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 () =>
Expand Down
1 change: 1 addition & 0 deletions desktop/src/ui/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 36 additions & 0 deletions dev/docker-compose.gitlab.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Dev overlay to run the GitLab integration service.
#
# Usage from the dev/ directory:
# docker compose -f docker-compose.yaml -f docker-compose.gitlab.yaml up -d
#
# Build the image first: rush docker:build --to @hcengineering/pod-gitlab
# front already points GITLAB_URL at http://huly.local:3600 in docker-compose.yaml.
# GitLab.com cannot reach huly.local: set GITLAB_WEBHOOK_BASE_URL to a tunnel URL (smee, ngrok) to receive webhooks.
#
services:
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
# Blob storage for merge request diffs (same as the other services); unset = no diffs
- STORAGE_CONFIG=${STORAGE_CONFIG}
# 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}
# Dev only: lets a self-managed GitLab on http://localhost or *.local be used
- GITLAB_ALLOW_INSECURE_HOSTS=true
- GITLAB_READONLY=${GITLAB_READONLY:-false}
# Part B: comma list of trusted GitLab hosts (private addresses allowed); per-request timeout
- GITLAB_ALLOWED_HOSTS=${GITLAB_ALLOWED_HOSTS:-}
- GITLAB_REQUEST_TIMEOUT_MS=${GITLAB_REQUEST_TIMEOUT_MS:-30000}
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
1 change: 1 addition & 0 deletions dev/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions dev/prod/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,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",
Expand Down
1 change: 1 addition & 0 deletions dev/prod/public/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
8 changes: 8 additions & 0 deletions dev/prod/src/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,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'
Expand Down Expand Up @@ -191,6 +192,8 @@ import '@hcengineering/mail-assets'
import * as MailLang from '@hcengineering/mail-assets/lang'
import '@hcengineering/github-assets'
import * as GithubLang from '@hcengineering/github-assets/lang'
import '@hcengineering/gitlab-assets'
import * as GitlabLang from '@hcengineering/gitlab-assets/lang'
import '@hcengineering/achievement-assets'
import * as AchievementLang from '@hcengineering/achievement-assets/lang'
import '@hcengineering/communication-assets'
Expand Down Expand Up @@ -235,6 +238,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
Expand Down Expand Up @@ -384,6 +388,7 @@ function configureI18n(): void {
addStringsLoader(documentId, DocumentLang.loadLang)
addStringsLoader(timeId, TimeLang.loadLang)
addStringsLoader(githubId, GithubLang.loadLang)
addStringsLoader(gitlabId, GitlabLang.loadLang)
addStringsLoader(documentsId, ControlledDocumentsLang.loadLang)
addStringsLoader(productsId, ProductsLang.loadLang)
addStringsLoader(questionsId, QuestionsLang.loadLang)
Expand Down Expand Up @@ -523,6 +528,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)

Expand Down Expand Up @@ -558,6 +564,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],
Expand Down Expand Up @@ -643,6 +650,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')
Expand Down
1 change: 1 addition & 0 deletions foundations/core/packages/core/src/classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,7 @@ export interface WorkspaceMemberInfo {
export enum SocialIdType {
EMAIL = 'email',
GITHUB = 'github',
GITLAB = 'gitlab',
GOOGLE = 'google',
PHONE = 'phone',
OIDC = 'oidc',
Expand Down
2 changes: 2 additions & 0 deletions models/all/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
15 changes: 15 additions & 0 deletions models/all/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -561,6 +575,7 @@ export default function buildModel (): Builder {
[serverActivityModel, serverActivityId],
[serverDocumentModel, serverDocumentId],
[serverGithubModel, serverGithubId],
[serverGitlabModel, serverGitlabId],
[serverLoveModel, serverLoveId],
[serverTimeModel, serverTimeId],
[serverGuestModel, serverGuestId],
Expand Down
1 change: 1 addition & 0 deletions packages/safe-fetch/src/__tests__/ranges.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ describe('isBlockedAddress', () => {
'64:ff9b::8.8.8.8',
'64:ff9b:1::a00:1',
'64:ff9b:1:ffff::808:808',
'100::1',
'::ffff:0:a00:1',
'2002:0a00:0001::',
'2002:0808:0808::',
Expand Down
1 change: 1 addition & 0 deletions packages/safe-fetch/src/ranges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const BLOCKED_IPV6_RANGES: readonly string[] = [
'::1/128', // loopback
'64:ff9b::/96', // NAT64 well-known prefix, blocked as a whole
'64:ff9b:1::/48', // NAT64 local-use prefix (RFC 8215), blocked as a whole
'100::/64', // discard-only (RFC 6666)
'2001::/32', // Teredo, blocked as a whole
'2002::/16', // 6to4, blocked as a whole
'fc00::/7', // unique local
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
let hideDiffLabel = getHideDiffLabel(file)

function getHideDiffLabel(file: DiffFile): IntlString | undefined {
if (diffRenderLimit >= 0 && file.stats.addedLines + file.stats.addedLines > diffRenderLimit) {
if (diffRenderLimit >= 0 && file.stats.addedLines + file.stats.deletedLines > diffRenderLimit) {
return diffview.string.LargeDiffsAreHidden
}
if (file.diffType === 'delete') {
Expand Down
2 changes: 2 additions & 0 deletions plugins/diffview-resources/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import DiffView from './components/DiffView.svelte'
import Highlight from './components/Highlight.svelte'
import InlineDiffView from './components/InlineDiffView.svelte'

export { parseDiff, type ParseDiffOptions } from './parser'

export default async (): Promise<Resources> => ({
component: {
DiffView,
Expand Down
15 changes: 13 additions & 2 deletions plugins/diffview-resources/src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,19 @@ const diff2htmlConfig: Diff2HtmlConfig = {
/**
* @public
*/
export function parseDiff (diff: Diff): DiffFile[] {
const files = parse(diff, diff2htmlConfig)
export interface ParseDiffOptions {
/** Files with more changed lines come back without hunks (isTooBig); null: no limit. Default 10000 */
maxChanges?: number | null
}

/**
* @public
*/
export function parseDiff (diff: Diff, options: ParseDiffOptions = {}): DiffFile[] {
// diff2html reads an undefined diffMaxChanges as no limit
const diffMaxChanges =
options.maxChanges === null ? undefined : (options.maxChanges ?? diff2htmlConfig.diffMaxChanges)
const files = parse(diff, { ...diff2htmlConfig, diffMaxChanges })
return files.map(mapFile)
}

Expand Down
1 change: 1 addition & 0 deletions pods/front/src/__start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 ?? '',
Expand Down
40 changes: 40 additions & 0 deletions rush.json
Original file line number Diff line number Diff line change
Expand Up @@ -2176,6 +2176,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",
Expand Down
30 changes: 29 additions & 1 deletion server/account/src/__tests__/operations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ import {
leaveWorkspace,
checkJoin,
mergeSpecifiedPersons,
canMergeSpecifiedPersons
canMergeSpecifiedPersons,
ensurePerson
} from '../operations'
import { accountPlugin } from '../plugin'

Expand Down Expand Up @@ -3831,3 +3832,30 @@ describe('merge specified persons', () => {
})
})
})

describe('ensurePerson', () => {
const mockCtx = {
error: jest.fn(),
info: jest.fn(),
warn: jest.fn()
} as unknown as MeasureContext

test('should let the gitlab service ensure a person without a workspace role check', async () => {
;(decodeTokenVerbose as jest.Mock).mockReturnValue({ account: 'svc', extra: { service: 'gitlab' } })
const roleSpy = jest.spyOn(utils, 'getWorkspaceRole')
const mockDb = {
socialId: { findOne: jest.fn(async () => ({ _id: 'sid-1' as PersonId, personUuid: 'p-1' as PersonUuid })) }
} as unknown as AccountDB

const result = await ensurePerson(mockCtx, mockDb, null, 'test-token', {
socialType: SocialIdType.GITLAB,
socialValue: '42@gitlab.com',
firstName: 'Alice',
lastName: ''
})

expect(result).toEqual({ uuid: 'p-1', socialId: 'sid-1' })
expect(roleSpy).not.toHaveBeenCalled()
roleSpy.mockRestore()
})
})
Loading
Loading