Skip to content

Commit 2dbb708

Browse files
committed
chore: merge main
1 parent 820e909 commit 2dbb708

286 files changed

Lines changed: 14693 additions & 2514 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.test

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
NODE_ENV=test
2+
DB_HOST=localhost
3+
DB_PORT=5434
4+
DB_USER=postgres
5+
DB_PASSWORD=example
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
6+
# shellcheck source=scripts/utils
7+
source "${REPO_ROOT}/scripts/utils"
8+
9+
: "${DB_HOST:?DB_HOST is required}"
10+
: "${DB_PORT:?DB_PORT is required}"
11+
: "${DB_USER:?DB_USER is required}"
12+
: "${DB_PASSWORD:?DB_PASSWORD is required}"
13+
14+
say "Applying Sequin bootstrap SQL..."
15+
docker run --rm --network host \
16+
-v "${REPO_ROOT}/scripts/scaffold/sequin/postgres-docker-entrypoint-initdb.d/create-sequin-database.sql:/bootstrap.sql:ro" \
17+
-e "PGPASSWORD=${DB_PASSWORD}" \
18+
postgres:14-alpine \
19+
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres -v ON_ERROR_STOP=1 -f /bootstrap.sql
20+
21+
say "Recreating test_template..."
22+
docker run --rm --network host \
23+
-e "PGPASSWORD=${DB_PASSWORD}" \
24+
postgres:14-alpine \
25+
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres -v ON_ERROR_STOP=1 \
26+
-c "DROP DATABASE IF EXISTS test_template;" \
27+
-c "CREATE DATABASE test_template;"
28+
29+
if [[ "${SKIP_FLYWAY_BUILD:-0}" == "1" ]]; then
30+
say "Using pre-built crowd_flyway image."
31+
else
32+
say "Building flyway image..."
33+
docker build -t crowd_flyway -f "${REPO_ROOT}/backend/src/database/Dockerfile.flyway" "${REPO_ROOT}/backend/src/database"
34+
fi
35+
36+
say "Migrating test_template..."
37+
docker run --rm --network host \
38+
-e "PGHOST=${DB_HOST}" \
39+
-e "PGPORT=${DB_PORT}" \
40+
-e "PGUSER=${DB_USER}" \
41+
-e "PGPASSWORD=${DB_PASSWORD}" \
42+
-e PGDATABASE=test_template \
43+
crowd_flyway
44+
45+
say "Test template database ready."

.github/workflows/server-tests.yml

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
name: Server Tests
2+
3+
on:
4+
pull_request:
5+
# Only trigger when relevant files change to save CI minutes
6+
paths-ignore:
7+
- 'frontend/**'
8+
- '**.md'
9+
- '.gitignore'
10+
- '.editorconfig'
11+
- '**/.eslintrc*'
12+
- '.prettierrc'
13+
- '.prettierignore'
14+
- 'LICENSE'
15+
16+
# Automatically cancel in-progress runs if you push new code to the same PR
17+
concurrency:
18+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
19+
cancel-in-progress: true
20+
21+
permissions:
22+
contents: read
23+
actions: write # GHA cache for Flyway image layers
24+
25+
jobs:
26+
test:
27+
runs-on: ubuntu-latest
28+
timeout-minutes: 30
29+
30+
services:
31+
postgres:
32+
image: postgres:14-alpine
33+
command: postgres -c wal_level=logical
34+
env:
35+
POSTGRES_USER: postgres
36+
POSTGRES_PASSWORD: example
37+
POSTGRES_DB: postgres
38+
ports:
39+
- 5432:5432
40+
options: >-
41+
--health-cmd "pg_isready -U postgres"
42+
--health-interval 2s
43+
--health-timeout 5s
44+
--health-retries 15
45+
46+
env:
47+
NODE_ENV: test
48+
DB_HOST: localhost
49+
DB_PORT: 5432
50+
DB_USER: postgres
51+
DB_PASSWORD: example
52+
53+
steps:
54+
- name: Check out repository code
55+
uses: actions/checkout@v4
56+
57+
- name: Setup pnpm
58+
uses: pnpm/action-setup@v4
59+
60+
- name: Setup Node
61+
uses: actions/setup-node@v4
62+
with:
63+
node-version: 20
64+
cache: 'pnpm'
65+
66+
- name: Install dependencies
67+
run: pnpm i --frozen-lockfile
68+
69+
- name: Set up Docker Buildx
70+
uses: docker/setup-buildx-action@v3
71+
72+
- name: Build Flyway image
73+
uses: docker/build-push-action@v6
74+
with:
75+
context: backend/src/database
76+
file: backend/src/database/Dockerfile.flyway
77+
load: true
78+
tags: crowd_flyway
79+
cache-from: type=gha,scope=flyway
80+
cache-to: type=gha,mode=max,scope=flyway
81+
82+
- name: Prepare test database
83+
env:
84+
SKIP_FLYWAY_BUILD: 1
85+
run: ./.github/scripts/prepare-test-template-db.sh
86+
87+
- name: Run server tests
88+
run: pnpm test:server

backend/.env.dist.local

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ CROWD_TEMPORAL_ENCRYPTION_KEY_ID=local
151151
CROWD_TEMPORAL_ENCRYPTION_KEY=FweBMRnGCLshER8FlSvNusQA6G3MRUKt
152152

153153
# Temporal — packages namespace
154+
CROWD_PACKAGES_TEMPORAL_SERVER_URL=localhost:7233
154155
CROWD_PACKAGES_TEMPORAL_NAMESPACE=default
155156

156157
# Seach sync api
@@ -201,7 +202,7 @@ OSSPCKGS_GCP_CREDENTIALS_B64=e30=
201202
# maven/all.zip 404s). The allowlist check and DB storage normalize to lowercase
202203
# internally per ADR-0001 §OSV "Ecosystem normalization", so downstream stays lowercase.
203204
OSV_BULK_BASE_URL=https://osv-vulnerabilities.storage.googleapis.com
204-
OSV_ECOSYSTEMS=npm,Maven,cargo,NuGet
205+
OSV_ECOSYSTEMS=npm,Maven,cargo,NuGet,RubyGems,Go
205206
OSV_TMP_DIR=/tmp/osv
206207
OSV_BATCH_SIZE=500
207208
OSV_DERIVE_BATCH_SIZE=1000

backend/config/custom-environment-variables.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,12 @@
180180
"certificate": "CROWD_TEMPORAL_CERTIFICATE",
181181
"privateKey": "CROWD_TEMPORAL_PRIVATE_KEY"
182182
},
183+
"packagesTemporal": {
184+
"serverUrl": "CROWD_PACKAGES_TEMPORAL_SERVER_URL",
185+
"namespace": "CROWD_PACKAGES_TEMPORAL_NAMESPACE",
186+
"certificate": "CROWD_TEMPORAL_CERTIFICATE",
187+
"privateKey": "CROWD_TEMPORAL_PRIVATE_KEY"
188+
},
183189
"searchSyncApi": {
184190
"baseUrl": "CROWD_SEARCH_SYNC_API_URL"
185191
},

backend/config/default.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"isEnabled": "false"
4444
},
4545
"temporal": {},
46+
"packagesTemporal": {},
4647
"searchSyncApi": {},
4748
"encryption": {},
4849
"openStatusApi": {},

backend/package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@
9494
"dotenv": "8.2.0",
9595
"dotenv-expand": "^8.0.3",
9696
"emoji-dictionary": "^1.0.11",
97-
"erlpack": "^0.1.4",
9897
"express": "4.17.1",
9998
"express-oauth2-jwt-bearer": "^1.7.4",
10099
"express-rate-limit": "6.5.1",
@@ -133,7 +132,6 @@
133132
"uuid": "^9.0.0",
134133
"validator": "^13.7.0",
135134
"verify-github-webhook": "^1.0.1",
136-
"zlib-sync": "^0.1.8",
137135
"zod": "^4.3.6"
138136
},
139137
"private": true,

backend/src/api/member/memberMerge.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { CommonMemberService, invalidateMemberQueryCache } from '@crowd/common_services'
2-
import { optionsQx } from '@crowd/data-access-layer'
2+
3+
import { optionsQx } from '@/database/sequelizeQueryExecutor'
34

45
import Permissions from '../../security/permissions'
56
import track from '../../segment/track'

backend/src/api/public/middlewares/staticApiKeyMiddleware.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import crypto from 'crypto'
22
import type { NextFunction, Request, RequestHandler, Response } from 'express'
33

44
import { UnauthorizedError } from '@crowd/common'
5-
import { findApiKeyByHash, optionsQx, touchApiKeyLastUsed } from '@crowd/data-access-layer'
5+
import { findApiKeyByHash, touchApiKeyLastUsed } from '@crowd/data-access-layer'
6+
7+
import { optionsQx } from '@/database/sequelizeQueryExecutor'
68

79
export function staticApiKeyMiddleware(): RequestHandler {
810
return async (req: Request, _res: Response, next: NextFunction): Promise<void> => {

backend/src/api/public/openapi.yaml

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -779,8 +779,11 @@ paths:
779779
/organizations:
780780
get:
781781
operationId: getOrganization
782-
summary: Look up an organization by domain
783-
description: Find a verified organization by its primary domain.
782+
summary: Look up an organization by domain or name
783+
description: >
784+
Provide domain, name, or both. When both are provided, the domain and
785+
name must belong to the same organization. If multiple organizations
786+
match, the most active one is returned.
784787
tags:
785788
- Organizations
786789
security:
@@ -789,12 +792,20 @@ paths:
789792
parameters:
790793
- name: domain
791794
in: query
792-
required: true
795+
required: false
793796
description: Primary domain of the organization.
794797
schema:
795798
type: string
796799
minLength: 1
797800
example: linuxfoundation.org
801+
- name: name
802+
in: query
803+
required: false
804+
description: Exact display name of the organization.
805+
schema:
806+
type: string
807+
minLength: 1
808+
example: Linux Foundation
798809
responses:
799810
'200':
800811
description: Organization found.
@@ -805,13 +816,14 @@ paths:
805816
example:
806817
id: 550e8400-e29b-41d4-a716-446655440000
807818
name: Linux Foundation
819+
domain: linuxfoundation.org
808820
logo: https://example.com/logo.png
809821
'401':
810822
$ref: '#/components/responses/Unauthorized'
811823
'403':
812824
$ref: '#/components/responses/Forbidden'
813825
'404':
814-
description: No verified organization found for the given domain.
826+
description: No organization found for the given domain or name.
815827
content:
816828
application/json:
817829
schema:
@@ -876,15 +888,26 @@ paths:
876888
required:
877889
- id
878890
- name
891+
- domain
879892
properties:
880893
id:
881894
type: string
882895
format: uuid
883896
name:
884897
type: string
898+
domain:
899+
type: string
900+
description: Verified primary domain of the organization.
901+
logo:
902+
type:
903+
- string
904+
- 'null'
905+
description: URL of the organization logo.
885906
example:
886907
id: 550e8400-e29b-41d4-a716-446655440000
887908
name: Acme Corp
909+
domain: acme.com
910+
logo: https://example.com/logo.png
888911
'400':
889912
$ref: '#/components/responses/BadRequest'
890913
'401':
@@ -1218,6 +1241,7 @@ components:
12181241
- id
12191242
- organizationId
12201243
- organizationName
1244+
- organizationDomains
12211245
- jobTitle
12221246
- verified
12231247
- verifiedBy
@@ -1243,6 +1267,11 @@ components:
12431267
- string
12441268
- 'null'
12451269
description: URL of the organization logo.
1270+
organizationDomains:
1271+
type: array
1272+
items:
1273+
type: string
1274+
description: Verified primary domains for the organization, in alphabetical order.
12461275
jobTitle:
12471276
type:
12481277
- string
@@ -1531,13 +1560,17 @@ components:
15311560
required:
15321561
- id
15331562
- name
1563+
- domain
15341564
properties:
15351565
id:
15361566
type: string
15371567
format: uuid
15381568
name:
15391569
type: string
15401570
description: Display name of the organization.
1571+
domain:
1572+
type: string
1573+
description: Verified primary domain.
15411574
logo:
15421575
type: string
15431576
description: URL of the organization logo. Only present if available.

0 commit comments

Comments
 (0)