Skip to content

Commit 2b464bb

Browse files
authored
Merge branch 'main' into bot-team-strip-fix
2 parents c85a520 + ee24b18 commit 2b464bb

422 files changed

Lines changed: 35373 additions & 11400 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.

.github/dependabot.yml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
version: 2
2+
3+
# NOTE: This file controls Dependabot version-update PRs only.
4+
# It does NOT suppress Dependabot security alerts on the Security tab.
5+
# To auto-dismiss transitive (indirect) alerts, configure auto-triage rules at
6+
# Settings -> Code security -> Dependabot -> "Manage rules".
7+
8+
updates:
9+
- package-ecosystem: "pip"
10+
directory: "/ingestion"
11+
schedule:
12+
interval: "weekly"
13+
day: "monday"
14+
open-pull-requests-limit: 5
15+
labels:
16+
- "dependencies"
17+
- "python"
18+
groups:
19+
python-minor-patch:
20+
update-types:
21+
- "minor"
22+
- "patch"
23+
ignore:
24+
# urllib3 is pinned <2.0 transitively via tableauserverclient==0.25.
25+
# See ingestion/setup.py comment on the tableau pin.
26+
- dependency-name: "urllib3"
27+
versions: [">=2.0.0"]
28+
29+
- package-ecosystem: "maven"
30+
directory: "/"
31+
schedule:
32+
interval: "weekly"
33+
day: "monday"
34+
open-pull-requests-limit: 5
35+
labels:
36+
- "dependencies"
37+
- "java"
38+
groups:
39+
maven-minor-patch:
40+
update-types:
41+
- "minor"
42+
- "patch"
43+
44+
- package-ecosystem: "npm"
45+
directory: "/openmetadata-ui/src/main/resources/ui"
46+
schedule:
47+
interval: "weekly"
48+
day: "monday"
49+
open-pull-requests-limit: 5
50+
labels:
51+
- "dependencies"
52+
- "javascript"
53+
groups:
54+
npm-minor-patch:
55+
update-types:
56+
- "minor"
57+
- "patch"
58+
59+
- package-ecosystem: "github-actions"
60+
directory: "/"
61+
schedule:
62+
interval: "weekly"
63+
day: "monday"
64+
open-pull-requests-limit: 3
65+
labels:
66+
- "dependencies"
67+
- "github-actions"

.github/workflows/security-scan.yml

Lines changed: 110 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
name: security-scan
1313
on:
1414
schedule:
15-
- cron: '0 0 */2 * *'
15+
- cron: "0 0 */2 * *"
1616
workflow_dispatch:
1717

1818
jobs:
@@ -27,7 +27,7 @@ jobs:
2727
- name: Setup Node.js
2828
uses: actions/setup-node@v4
2929
with:
30-
node-version-file: 'openmetadata-ui/src/main/resources/ui/.nvmrc'
30+
node-version-file: "openmetadata-ui/src/main/resources/ui/.nvmrc"
3131

3232
- name: Enable yarn
3333
run: corepack enable
@@ -124,30 +124,6 @@ jobs:
124124
print()
125125
EOF
126126
127-
- name: Slack on Failure
128-
if: steps.retire-scan.outcome == 'failure'
129-
uses: slackapi/slack-github-action@v1.23.0
130-
with:
131-
channel-id: ${{ secrets.SLACK_CHANNEL_IDS }}
132-
payload: |
133-
{
134-
"text": "🚨 Vulnerability scan failed on branch `${{ github.ref_name }}`, please check it <https://github.com/open-metadata/OpenMetadata/actions/runs/${{ github.run_id }}|here>. 🚨"
135-
}
136-
env:
137-
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
138-
139-
- name: Slack on Success
140-
if: steps.retire-scan.outcome == 'success'
141-
uses: slackapi/slack-github-action@v1.23.0
142-
with:
143-
channel-id: ${{ secrets.SLACK_CHANNEL_IDS }}
144-
payload: |
145-
{
146-
"text": "🟢 Vulnerability scan passed for OpenMetadata Repo on branch `${{ github.ref_name }}`, please check it <https://github.com/open-metadata/OpenMetadata/actions/runs/${{ github.run_id }}|here>."
147-
}
148-
env:
149-
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
150-
151127
- name: Force failure on vulnerabilities found
152128
if: steps.retire-scan.outcome == 'failure'
153129
run: exit 1
@@ -163,25 +139,25 @@ jobs:
163139
- name: Free Disk Space (Ubuntu)
164140
uses: jlumbroso/free-disk-space@main
165141
with:
166-
tool-cache: false
167-
android: true
168-
dotnet: true
169-
haskell: true
170-
large-packages: false
171-
docker-images: true
172-
swap-storage: true
142+
tool-cache: false
143+
android: true
144+
dotnet: true
145+
haskell: true
146+
large-packages: false
147+
docker-images: true
148+
swap-storage: true
173149
- uses: actions/checkout@v4
174150

175151
- name: Set up Python 3.10
176152
uses: actions/setup-python@v5
177153
with:
178-
python-version: '3.10'
154+
python-version: "3.10"
179155

180156
- name: Set up JDK 21
181157
uses: actions/setup-java@v4
182158
with:
183-
java-version: '21'
184-
distribution: 'temurin'
159+
java-version: "21"
160+
distribution: "temurin"
185161

186162
- name: Install Ubuntu dependencies
187163
run: |
@@ -215,40 +191,111 @@ jobs:
215191
continue-on-error: true
216192
run: |
217193
source env/bin/activate
218-
make snyk-report
219-
220-
- name: Slack on Failure
221-
if: steps.security-report.outcome != 'success'
222-
uses: slackapi/slack-github-action@v1.23.0
223-
with:
224-
channel-id: ${{ secrets.SLACK_CHANNEL_IDS }}
225-
payload: |
226-
{
227-
"text": "🚨 Security report failed on branch `${{ github.ref_name }}`, please check it <https://github.com/open-metadata/OpenMetadata/actions/runs/${{ github.run_id }}|here>. 🚨"
228-
}
229-
env:
230-
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
231-
232-
- name: Slack on Success
233-
if: steps.security-report.outcome == 'success'
234-
uses: slackapi/slack-github-action@v1.23.0
235-
with:
236-
channel-id: ${{ secrets.SLACK_CHANNEL_IDS }}
237-
payload: |
238-
{
239-
"text": "🟢 Security report generated for OpenMetadata Repo on branch `${{ github.ref_name }}`, please check it <https://github.com/open-metadata/OpenMetadata/actions/runs/${{ github.run_id }}|here>."
240-
}
241-
env:
242-
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
194+
rm -rf security-report
195+
mkdir -p security-report
196+
# Run snyk subtargets directly; skip `export-snyk-pdf-report` which deletes JSONs after PDF conversion.
197+
make snyk-ingestion-report || true
198+
make snyk-ingestion-base-slim-report || true
199+
make snyk-airflow-apis-report || true
200+
make snyk-server-report || true
201+
make snyk-ui-report || true
202+
203+
- name: Publish Snyk Summary
204+
id: snyk-summary
205+
if: always() && steps.maven-build.outcome == 'success'
206+
run: |
207+
python3 scripts/snyk_summary.py security-report \
208+
--counts-file security-report/_counts.json \
209+
--slack-file security-report/_slack.txt \
210+
>> $GITHUB_STEP_SUMMARY
211+
# Expose counts as step output for downstream gating.
212+
counts=$(cat security-report/_counts.json)
213+
echo "counts=$counts" >> $GITHUB_OUTPUT
214+
high=$(jq '.high + .critical' security-report/_counts.json)
215+
echo "high_critical=$high" >> $GITHUB_OUTPUT
216+
217+
- name: Fail on high/critical Snyk findings
218+
if: always() && steps.snyk-summary.outputs.high_critical != '' && steps.snyk-summary.outputs.high_critical != '0'
219+
run: |
220+
echo "::error::Snyk found ${{ steps.snyk-summary.outputs.high_critical }} high/critical vulnerabilities (see Job Summary)"
221+
exit 1
243222
244-
- name: Upload Snyk Report HTML files
245-
if: steps.security-report.outcome == 'success'
223+
- name: Generate Snyk HTML/PDF
224+
if: always() && steps.maven-build.outcome == 'success'
225+
run: |
226+
# Back up JSONs because html_to_pdf.py deletes them after PDF conversion.
227+
mkdir -p /tmp/snyk-json-backup
228+
cp security-report/*.json /tmp/snyk-json-backup/ 2>/dev/null || true
229+
make export-snyk-pdf-report || true
230+
# Restore JSONs alongside generated PDFs/HTMLs.
231+
cp /tmp/snyk-json-backup/*.json security-report/ 2>/dev/null || true
232+
233+
- name: Upload Snyk Reports
234+
if: always() && steps.maven-build.outcome == 'success'
246235
uses: actions/upload-artifact@v4
247236
with:
248237
name: security-report
249238
path: security-report
239+
retention-days: 30
250240

251241
- name: Force failure
252242
if: steps.maven-build.outcome != 'success' || steps.security-report.outcome != 'success'
253243
run: |
254244
exit 1
245+
246+
notify:
247+
runs-on: ubuntu-latest
248+
environment: security-scan
249+
needs: [vulnerability-scan, security-scan]
250+
if: always()
251+
steps:
252+
- name: Download Snyk artifact
253+
if: needs.security-scan.result != 'skipped'
254+
uses: actions/download-artifact@v4
255+
with:
256+
name: security-report
257+
path: security-report
258+
continue-on-error: true
259+
260+
- name: Build Slack payload
261+
id: build
262+
run: |
263+
retire="${{ needs.vulnerability-scan.result }}"
264+
snyk="${{ needs.security-scan.result }}"
265+
status_icon() {
266+
case "$1" in
267+
success) echo "✅" ;;
268+
cancelled) echo "⚠️ (cancelled)" ;;
269+
skipped) echo "⚠️ (skipped)" ;;
270+
*) echo "❌" ;;
271+
esac
272+
}
273+
retire_icon=$(status_icon "$retire")
274+
snyk_icon=$(status_icon "$snyk")
275+
if [ "$retire" = "success" ] && [ "$snyk" = "success" ]; then
276+
icon="🟢"
277+
elif [ "$retire" = "failure" ] || [ "$snyk" = "failure" ]; then
278+
icon="🚨"
279+
else
280+
icon="⚠️"
281+
fi
282+
run_url="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
283+
{
284+
echo "$icon *Security scan* — *OpenMetadata Repo* on branch \`${{ github.ref_name }}\`"
285+
echo "• Vulnerability scan (Retire.js): $retire_icon"
286+
echo "• Security scan (Snyk): $snyk_icon"
287+
echo "<$run_url|Open run details>"
288+
if [ -f security-report/_slack.txt ]; then
289+
echo
290+
cat security-report/_slack.txt
291+
fi
292+
} > slack_body.txt
293+
jq -Rs '{text: ., mrkdwn: true}' slack_body.txt > payload.json
294+
295+
- name: Send Slack Notification
296+
uses: slackapi/slack-github-action@v1.27.1
297+
with:
298+
channel-id: ${{ secrets.SLACK_CHANNEL_IDS }}
299+
payload-file-path: payload.json
300+
env:
301+
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,5 @@ ingestion/.claude/agents
206206
.serena/
207207

208208
test-results/
209+
210+
docs/superpowers/*

bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,3 +363,38 @@ ALTER TABLE search_index_server_stats
363363
-- avoid INSERT failures on /mcp/authorize redirects.
364364
ALTER TABLE mcp_pending_auth_requests
365365
MODIFY COLUMN mcp_state TEXT;
366+
367+
-- Allow multiple typed relations between the same pair of glossary terms.
368+
-- The previous PRIMARY KEY (fromId, toId, relation) caused INSERT ... ON DUPLICATE
369+
-- KEY UPDATE to overwrite the json discriminator when a second relationType
370+
-- ("synonym" + "seeAlso", etc.) was added between the same two terms, silently
371+
-- dropping the first relationship. Adding relationType to the PK lets the same
372+
-- (fromId, toId, RELATED_TO) pair carry one row per relation type.
373+
-- `IF NOT EXISTS` on `ADD COLUMN` only landed in MySQL 8.0.29; supported 8.0.x
374+
-- deployments may be older, so use plain ADD COLUMN. SERVER_CHANGE_LOG gates
375+
-- re-execution at the framework level — same reasoning as the PK swap below.
376+
ALTER TABLE entity_relationship
377+
ADD COLUMN `relationType` varchar(64) NOT NULL DEFAULT '' AFTER `relation`;
378+
379+
-- Backfill relationType for every glossary-term ↔ glossary-term RELATED_TO row.
380+
-- Pre-1.13 data has json = NULL (no discriminator existed yet) — those rows MUST
381+
-- collapse onto 'relatedTo' so that a subsequent insert of the same logical
382+
-- relation matches the existing row instead of creating a duplicate under a
383+
-- different PK. relation=15 is the ordinal of Relationship.RELATED_TO (see
384+
-- openmetadata-spec entityRelationship.json). 'relatedTo' is the default
385+
-- relation type that the application code uses when none is specified.
386+
UPDATE entity_relationship
387+
SET relationType =
388+
COALESCE(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(json, '$.relationType')), ''), 'relatedTo')
389+
WHERE fromEntity = 'glossaryTerm'
390+
AND toEntity = 'glossaryTerm'
391+
AND relation = 15;
392+
393+
-- Swap the PK to include relationType. The native migration framework tracks
394+
-- completion in SERVER_CHANGE_LOG so this runs once per upgrade; we intentionally
395+
-- avoid information_schema gating because least-privilege migration users may
396+
-- not have SELECT on it. A manual replay of this step on an already-migrated
397+
-- table will rebuild the PK with the same columns — wasteful but not broken.
398+
ALTER TABLE entity_relationship
399+
DROP PRIMARY KEY,
400+
ADD PRIMARY KEY (`fromId`, `toId`, `relation`, `relationType`);

bootstrap/sql/migrations/native/1.13.0/postgres/schemaChanges.sql

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,3 +511,34 @@ CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_worksheet_entity_fqnhash_pattern
511511
-- avoid INSERT failures on /mcp/authorize redirects.
512512
ALTER TABLE mcp_pending_auth_requests
513513
ALTER COLUMN mcp_state TYPE TEXT;
514+
515+
-- Allow multiple typed relations between the same pair of glossary terms.
516+
-- The previous PRIMARY KEY (fromId, toId, relation) caused INSERT ... ON CONFLICT
517+
-- DO UPDATE to overwrite the json discriminator when a second relationType
518+
-- ("synonym" + "seeAlso", etc.) was added between the same two terms, silently
519+
-- dropping the first relationship. Adding relationType to the PK lets the same
520+
-- (fromId, toId, RELATED_TO) pair carry one row per relation type.
521+
ALTER TABLE entity_relationship
522+
ADD COLUMN IF NOT EXISTS relationType character varying(64) DEFAULT ''::character varying NOT NULL;
523+
524+
-- Backfill relationType for every glossary-term ↔ glossary-term RELATED_TO row.
525+
-- Pre-1.13 data has json = NULL (no discriminator existed yet) — those rows MUST
526+
-- collapse onto 'relatedTo' so that a subsequent insert of the same logical
527+
-- relation matches the existing row instead of creating a duplicate under a
528+
-- different PK. relation=15 is the ordinal of Relationship.RELATED_TO (see
529+
-- openmetadata-spec entityRelationship.json). 'relatedTo' is the default
530+
-- relation type that the application code uses when none is specified.
531+
UPDATE entity_relationship
532+
SET relationType = COALESCE(NULLIF(json->>'relationType', ''), 'relatedTo')
533+
WHERE fromEntity = 'glossaryTerm'
534+
AND toEntity = 'glossaryTerm'
535+
AND relation = 15;
536+
537+
-- Swap the PK to include relationType. The native migration framework tracks
538+
-- completion in SERVER_CHANGE_LOG so this runs once per upgrade; we intentionally
539+
-- avoid information_schema gating because least-privilege migration users may
540+
-- not have SELECT on it. DROP CONSTRAINT IF EXISTS keeps the statement safe to
541+
-- replay against a table that's already been migrated.
542+
ALTER TABLE entity_relationship DROP CONSTRAINT IF EXISTS entity_relationship_pkey;
543+
ALTER TABLE entity_relationship
544+
ADD CONSTRAINT entity_relationship_pkey PRIMARY KEY (fromId, toId, relation, relationType);

0 commit comments

Comments
 (0)