Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6e2e58aad | ||
|
|
489d4b0584 | ||
|
|
36019156da | ||
|
|
f2a371fc2d | ||
|
|
ad2f038817 | ||
|
|
8f2604509d | ||
|
|
ea0963d75f | ||
|
|
46d5718011 | ||
|
|
f8740e04f3 | ||
|
|
669717d27b | ||
|
|
715dfc556a | ||
|
|
cafb7d3f7b | ||
|
|
5f92f6ec7f | ||
|
|
eea610dfe8 | ||
|
|
e8733a9bd9 | ||
|
|
d292931073 | ||
|
|
f6a0db753a | ||
|
|
8518e179ee | ||
|
|
3f40cdced7 | ||
|
|
4cf17185da | ||
|
|
b8ae92c2d4 | ||
|
|
bad67020cc | ||
|
|
2afcd498f3 | ||
|
|
ce4c3d5360 | ||
|
|
d575e36879 | ||
|
|
98fcabfcb5 | ||
|
|
1e2773345a | ||
|
|
f847633cf3 | ||
|
|
bd2ddd7702 | ||
|
|
26b7b34bd4 | ||
|
|
6f3e446a79 | ||
|
|
f0d7f2e6c7 | ||
|
|
c8f7abfd53 | ||
|
|
2b29ff1820 | ||
|
|
5627e9c919 | ||
|
|
582b713b84 | ||
|
|
5eb8e58b65 | ||
|
|
403688b18c | ||
|
|
f0d13a7acd | ||
|
|
78f1a7f3e5 | ||
|
|
a57ab4d12a | ||
|
|
3b6c6374e3 | ||
|
|
4b508b0c25 | ||
|
|
1cbef867ce | ||
|
|
05444f48d4 | ||
|
|
2ea227ca06 | ||
|
|
35aaad175c | ||
|
|
863fe05907 | ||
|
|
1c9560044f | ||
|
|
343f06c57d | ||
|
|
e2bb647386 | ||
|
|
a3cb9dab57 | ||
|
|
410ab028c7 | ||
|
|
54dd738f61 | ||
|
|
6f16c7e48d | ||
|
|
82ddd95cf0 | ||
|
|
2b658fae7e | ||
|
|
a822151e92 | ||
|
|
0ae49c6fdc | ||
|
|
1d069d2da3 | ||
|
|
274342a5e1 | ||
|
|
21a5d1bfc4 | ||
|
|
e421bc7df9 | ||
|
|
e41ba2bfde | ||
|
|
d2e75b0f20 | ||
|
|
07b272e024 | ||
|
|
a08caa9bdb | ||
|
|
29afddb3a6 | ||
|
|
f85f856734 | ||
|
|
c6bc19c002 | ||
|
|
538555f000 | ||
|
|
8681036b21 | ||
|
|
e55b909ca6 | ||
|
|
4bb36704ee | ||
|
|
9bbdb35d88 | ||
|
|
2f443fa2ef | ||
|
|
6d7d7da287 | ||
|
|
1f6751f95b | ||
|
|
92081a097d | ||
|
|
ba1a33c3ac | ||
|
|
193ff12b6e | ||
|
|
3296ffaa07 | ||
|
|
0456d92462 | ||
|
|
db3b15e537 | ||
|
|
6e723ebced | ||
|
|
a6a231c81e | ||
|
|
7c2bd9b76a | ||
|
|
5dfa8d465d | ||
|
|
fc5301e904 | ||
|
|
88e5a7e54a | ||
|
|
5d24bb4b73 | ||
|
|
03a4056179 | ||
|
|
ec76bc3b0b | ||
|
|
b1070acaca | ||
|
|
7b5beb8f98 | ||
|
|
97410e12b9 | ||
|
|
bdecbfeee3 | ||
|
|
5a970056ae | ||
|
|
8581405352 | ||
|
|
df8c4db2ed | ||
|
|
ea62bdaddf | ||
|
|
e983b02677 | ||
|
|
1e4b78d78d | ||
|
|
ee6131f98e | ||
|
|
a56bdd035b | ||
|
|
04901c296e | ||
|
|
59cadad98c | ||
|
|
14d6454eaf | ||
|
|
64f87d2c93 | ||
|
|
cfd66e5ef7 | ||
|
|
6480389778 | ||
|
|
0e736e7a56 | ||
|
|
d246cd9842 | ||
|
|
4fa1eb683d | ||
|
|
ea6842a276 | ||
|
|
d97270f995 | ||
|
|
0ef76f8b66 | ||
|
|
05b5a10af9 | ||
|
|
e1150acaf6 | ||
|
|
4abdbe0584 | ||
|
|
9223a52af9 | ||
|
|
06f58d18bf | ||
|
|
7c5f9c44b6 | ||
|
|
ef341d1518 | ||
|
|
d956963ef0 | ||
|
|
83b7d9e8b4 | ||
|
|
12e73228d1 | ||
|
|
4002401c66 | ||
|
|
18a8e059b6 | ||
|
|
f3582e514e | ||
|
|
c76bfd4a58 | ||
|
|
7f8cf5f5da | ||
|
|
5ef926477c | ||
|
|
87f5b97983 | ||
|
|
4c7b2b4ae0 | ||
|
|
d1e52dd6a6 | ||
|
|
cb147dfc69 | ||
|
|
a5fdeddfb4 | ||
|
|
e0e5f6000f | ||
|
|
cffb7d6f2f | ||
|
|
360616be13 | ||
|
|
d8fe8929b6 | ||
|
|
6b99a21227 | ||
|
|
16631e1fc4 | ||
|
|
587de94cea | ||
|
|
0ebe8a67d2 | ||
|
|
39a988dac7 | ||
|
|
f95928bcdd | ||
|
|
0f736bd294 | ||
|
|
6cbd9839c6 | ||
|
|
649bc5825a | ||
|
|
8038009afd | ||
|
|
f07f21117d | ||
|
|
77c7463c53 | ||
|
|
449e8f25f6 | ||
|
|
437c01511e | ||
|
|
00fddc1550 | ||
|
|
00b5cccae5 | ||
|
|
e545dd5b28 | ||
|
|
efde6ed20e | ||
|
|
32022f51f2 | ||
|
|
d282f72d75 | ||
|
|
29fceacee9 | ||
|
|
a7a5a359dc | ||
|
|
e362e895d4 | ||
|
|
45a07dfa19 | ||
|
|
7323e63608 | ||
|
|
0baf18dcb2 | ||
|
|
cca3d6bff2 | ||
|
|
f3cecf129b | ||
|
|
fe03fc6f55 | ||
|
|
b59187c74d | ||
|
|
5210c80369 | ||
|
|
f57725ac8b | ||
|
|
138e0fb3bf | ||
|
|
be494a7376 | ||
|
|
ab23feefbb | ||
|
|
4c079044f6 | ||
|
|
81fcfc7370 | ||
|
|
6ec6446787 | ||
|
|
4b5a43641a | ||
|
|
2fb0b01fe2 | ||
|
|
3632e5d495 | ||
|
|
dfe566e2aa | ||
|
|
6c5ad9e98d | ||
|
|
aeb7d5edcc | ||
|
|
d5b89748c9 | ||
|
|
b161016edb | ||
|
|
2fff2d511f | ||
|
|
cc92f91c1f | ||
|
|
d2e777bafc | ||
|
|
c0c3ab307d | ||
|
|
5121913ee6 | ||
|
|
6ef2b19925 | ||
|
|
f2450be255 | ||
|
|
04ca32a93e | ||
|
|
c92de6c647 | ||
|
|
5b302843d4 | ||
|
|
7eb8c4c641 | ||
|
|
619f361510 | ||
|
|
330eeaa216 | ||
|
|
d627dee3b2 | ||
|
|
d8714ea9c4 | ||
|
|
3c05920705 | ||
|
|
c6629bda75 | ||
|
|
588bcc9d17 | ||
|
|
c31f8f9d65 | ||
|
|
c0280e28ca | ||
|
|
7c3ac387ef | ||
|
|
d6f79d9ddf | ||
|
|
9448993cf0 | ||
|
|
1c556b4cc3 | ||
|
|
94a9067fe3 | ||
|
|
cfba4df70e | ||
|
|
11c1d12872 | ||
|
|
91364db882 | ||
|
|
bea31d5fec | ||
|
|
48ecbc261a | ||
|
|
8bc467fb07 | ||
|
|
cffc44f0f4 | ||
|
|
ea0fc07e30 | ||
|
|
b8d241fa33 | ||
|
|
ccdfcd4a85 | ||
|
|
6cf72d1757 | ||
|
|
bb1cdbda3a | ||
|
|
fab122dd8f | ||
|
|
42803b0ae8 | ||
|
|
5a78da8ff6 | ||
|
|
0ef88e88eb | ||
|
|
a7b5f1847a | ||
|
|
5cb6e63b88 | ||
|
|
48eb4a9ec3 | ||
|
|
d9231fd431 | ||
|
|
e5b578bd8e | ||
|
|
f38979037d | ||
|
|
45ca88f5b8 | ||
|
|
a1dee16a3d | ||
|
|
6a37a2a661 | ||
|
|
a8caf4bba4 | ||
|
|
29dcc82e28 | ||
|
|
dbf0501031 | ||
|
|
ef28fa2ac1 | ||
|
|
779a7e0976 | ||
|
|
7f3ca1fc23 | ||
|
|
59ab68b4ed | ||
|
|
248bc0a64a | ||
|
|
5fdace1432 | ||
|
|
71b6b454a3 | ||
|
|
3b02a06f5e | ||
|
|
ebdcde1c4d | ||
|
|
829ac2b410 | ||
|
|
09745a0349 | ||
|
|
cd73dd3027 | ||
|
|
ca511edeff | ||
|
|
bb5f30ce2a | ||
|
|
87d5c68e71 | ||
|
|
0e87ab37c2 | ||
|
|
756b005fe1 | ||
|
|
3d86b5f876 | ||
|
|
2a647fd618 | ||
|
|
f8fc31bcac | ||
|
|
b7ab88b0f1 | ||
|
|
57a30d7c34 | ||
|
|
cd90665b0e | ||
|
|
98c657231e | ||
|
|
14da247318 | ||
|
|
6a77d6c778 | ||
|
|
8f1785446b | ||
|
|
41225eefc0 | ||
|
|
7772fee21a | ||
|
|
d870ac914a | ||
|
|
451f7eab92 | ||
|
|
029f8af4c7 | ||
|
|
b1b4c8c861 | ||
|
|
cdde2af224 | ||
|
|
4a2922dda0 | ||
|
|
b6b5a8414f | ||
|
|
e7cc735c03 | ||
|
|
6121bdcf63 | ||
|
|
da70079675 | ||
|
|
9089875ca7 | ||
|
|
b4627ecfa3 | ||
|
|
4ede8a525a | ||
|
|
2c23818c86 | ||
|
|
fa4b03d62d | ||
|
|
698e900b85 | ||
|
|
b7d47383c5 | ||
|
|
6123741765 | ||
|
|
ab25be3520 | ||
|
|
950a54c704 | ||
|
|
4293c073f0 | ||
|
|
62a2cd5ff3 | ||
|
|
2d827c2a49 | ||
|
|
a8305772d0 | ||
|
|
eab115e70c | ||
|
|
c02b232b0c | ||
|
|
f01640d009 | ||
|
|
63fd65690c |
No files matched your search
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Set directory to location of this script
|
||||
# https://stackoverflow.com/a/3355423/1867984
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
yarn -v
|
||||
node -v
|
||||
|
||||
echo 'Installing Gitbook CLI'
|
||||
|
||||
yarn global bin
|
||||
yarn config get prefix
|
||||
yarn config set prefix ~/.yarn
|
||||
export PATH="$PATH:`yarn global bin`"
|
||||
|
||||
echo 'Running Gitbook installation'
|
||||
|
||||
# Generate all version's GitBook output
|
||||
# For each directory in /docs ...
|
||||
cd ./../docs/
|
||||
for D in *; do
|
||||
if [ -d "${D}" ]; then
|
||||
|
||||
echo "Generating output for: ${D}"
|
||||
cd "${D}"
|
||||
|
||||
# Clear previous output, generate new
|
||||
rm -rf _book
|
||||
gitbook install
|
||||
gitbook build
|
||||
|
||||
cd ..
|
||||
|
||||
fi
|
||||
done
|
||||
|
||||
# Move CNAME File into `latest`
|
||||
cp CNAME ./latest/_book/CNAME
|
||||
|
||||
# Create a history folder in our latest version's output
|
||||
mkdir ./latest/_book/history
|
||||
|
||||
# Move each version's files to latest's history folder
|
||||
for D in *; do
|
||||
if [ -d "${D}" ]; then
|
||||
if [ "${D}" == v* ] ; then
|
||||
|
||||
echo "Moving ${D} to the latest version's history folder"
|
||||
|
||||
mkdir "./latest/_book/history/${D}"
|
||||
cp -v -r "./${D}/_book"/* "./latest/_book/history/${D}"
|
||||
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Back to repo root
|
||||
cd ..
|
||||
|
||||
echo "Done generating documentation output"
|
||||
echo 'STARTING PUBLISH'
|
||||
|
||||
# WILL ALWAYS FAIL IF INITIATED FROM PR BRANCH
|
||||
npx gh-pages \
|
||||
--silent \
|
||||
--repo https://$GITHUB_TOKEN@github.com/OHIF/Viewers.git \
|
||||
--message 'Autogenerated Message: [ci skip]' \
|
||||
--dist docs/latest/_book
|
||||
@@ -1,59 +1,83 @@
|
||||
version: 2.1
|
||||
|
||||
# ci: re-trigger pipeline
|
||||
### ABOUT
|
||||
#
|
||||
# This configuration powers our Circleci.io integration
|
||||
#
|
||||
# Note:
|
||||
# Netlify works independently from this configuration to
|
||||
# create pull request previews and to update `https://docs.ohif.org`
|
||||
###
|
||||
|
||||
## https://github.com/cypress-io/circleci-orb
|
||||
##
|
||||
orbs:
|
||||
codecov: codecov/codecov@1.0.5
|
||||
cypress: cypress-io/cypress@3.4.2
|
||||
cypress: cypress-io/cypress@1.26.0
|
||||
executors:
|
||||
# Custom executor to override Cypress config
|
||||
deploy-to-prod-executor:
|
||||
docker:
|
||||
- image: 'cypress/browsers:node14.15.0-chrome86-ff82'
|
||||
environment:
|
||||
CYPRESS_BASE_URL: https://ohif-staging.netlify.com/
|
||||
chrome-and-pacs:
|
||||
docker:
|
||||
# Primary container image where all steps run.
|
||||
- image: 'cypress/browsers:node14.15.0-chrome86-ff82'
|
||||
- image: 'ohif/viewer-testdata:0.1-test'
|
||||
|
||||
defaults: &defaults
|
||||
docker:
|
||||
- image: cimg/node:24.0.0
|
||||
- image: circleci/node:14.15.0
|
||||
environment:
|
||||
TERM: xterm
|
||||
TERM: xterm # Enable colors in term
|
||||
QUICK_BUILD: true
|
||||
working_directory: ~/repo
|
||||
|
||||
commands:
|
||||
install_pnpm:
|
||||
steps:
|
||||
- run:
|
||||
name: Install pnpm
|
||||
command: |
|
||||
# The cimg/node global modules dir (/usr/local/lib/node_modules) is
|
||||
# root-owned, so a plain `npm install -g` fails with EACCES. Install
|
||||
# with sudo so the global pnpm binary lands in the shared prefix.
|
||||
sudo npm install -g pnpm@11.5.2
|
||||
echo 'export PATH="$(pnpm store path)/../.bin:$PATH"' >> $BASH_ENV
|
||||
source $BASH_ENV
|
||||
|
||||
jobs:
|
||||
###
|
||||
# Workflow: PR_CHECKS
|
||||
###
|
||||
UNIT_TESTS:
|
||||
<<: *defaults
|
||||
resource_class: large
|
||||
steps:
|
||||
- install_pnpm
|
||||
- run: node --version
|
||||
- checkout
|
||||
# Update yarn
|
||||
- run: yarn -v
|
||||
# Checkout code and ALL Git Tags
|
||||
- checkout:
|
||||
post:
|
||||
- git fetch --all
|
||||
# - restore_cache:
|
||||
# name: Restore Yarn and Cypress Package Cache
|
||||
# keys:
|
||||
# # when lock file changes, use increasingly general patterns to restore cache
|
||||
# - yarn-packages-{{ checksum "yarn.lock" }}
|
||||
# - yarn-packages-
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: pnpm install --frozen-lockfile
|
||||
command: yarn install --frozen-lockfile
|
||||
- save_cache:
|
||||
name: Save Yarn Package Cache
|
||||
paths:
|
||||
- ~/.cache ## Cache yarn and Cypress
|
||||
key: yarn-packages-{{ checksum "yarn.lock" }}
|
||||
# RUN TESTS
|
||||
- run:
|
||||
name: 'JavaScript Test Suite'
|
||||
command: pnpm run test:unit:ci
|
||||
# platform/app
|
||||
command: yarn run test:unit:ci
|
||||
# PLATFORM/VIEWER
|
||||
- run:
|
||||
name: 'VIEWER: Combine report output'
|
||||
command: |
|
||||
viewerCov="/home/circleci/repo/platform/app/coverage"
|
||||
viewerCov="/home/circleci/repo/platform/viewer/coverage"
|
||||
touch "${viewerCov}/reports"
|
||||
cat "${viewerCov}/clover.xml" >> "${viewerCov}/reports"
|
||||
echo "\<<\<<\<< EOF" >> "${viewerCov}/reports"
|
||||
cat "${viewerCov}/lcov.info" >>"${viewerCov}/reports"
|
||||
echo "\<<\<<\<< EOF" >> "${viewerCov}/reports"
|
||||
- codecov/upload:
|
||||
file: '/home/circleci/repo/platform/app/coverage/reports'
|
||||
file: '/home/circleci/repo/platform/viewer/coverage/reports'
|
||||
flags: 'viewer'
|
||||
# PLATFORM/CORE
|
||||
- run:
|
||||
@@ -69,135 +93,163 @@ jobs:
|
||||
file: '/home/circleci/repo/platform/core/coverage/reports'
|
||||
flags: 'core'
|
||||
|
||||
BUILD:
|
||||
###
|
||||
# Workflow: PR_OPTIONAL_DOCKER_PUBLISH
|
||||
###
|
||||
DOCKER_PR_PUBLISH:
|
||||
<<: *defaults
|
||||
resource_class: large
|
||||
steps:
|
||||
# Enable yarn workspaces
|
||||
- run: yarn config set workspaces-experimental true
|
||||
|
||||
# Checkout code and ALL Git Tags
|
||||
- checkout
|
||||
- install_pnpm
|
||||
- checkout:
|
||||
post:
|
||||
- git fetch --all
|
||||
|
||||
# - restore_cache:
|
||||
# name: Restore Yarn and Cypress Package Cache
|
||||
# keys:
|
||||
# # when lock file changes, use increasingly general patterns to restore cache
|
||||
# - yarn-packages-{{ checksum "yarn.lock" }}
|
||||
# - yarn-packages-
|
||||
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: pnpm install --frozen-lockfile
|
||||
command: yarn install --frozen-lockfile
|
||||
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: false
|
||||
|
||||
- run:
|
||||
name: Build and push Docker image
|
||||
command: |
|
||||
# Remove npm config
|
||||
rm -f ./.npmrc
|
||||
# Set our version number using vars
|
||||
echo $CIRCLE_BUILD_NUM
|
||||
# Build our image, auth, and push
|
||||
docker build --tag ohif/viewer:PR_BUILD-$CIRCLE_BUILD_NUM .
|
||||
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
|
||||
docker push ohif/viewer:PR_BUILD-$CIRCLE_BUILD_NUM
|
||||
|
||||
###
|
||||
# Workflow: DEPLOY
|
||||
###
|
||||
BUILD:
|
||||
<<: *defaults
|
||||
steps:
|
||||
# Checkout code and ALL Git Tags
|
||||
- checkout:
|
||||
post:
|
||||
- git fetch --all
|
||||
# - restore_cache:
|
||||
# name: Restore Yarn and Cypress Package Cache
|
||||
# keys:
|
||||
# # when lock file changes, use increasingly general patterns to restore cache
|
||||
# - yarn-packages-{{ checksum "yarn.lock" }}
|
||||
# - yarn-packages-
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: yarn install --frozen-lockfile
|
||||
- save_cache:
|
||||
name: Save Yarn Package Cache
|
||||
paths:
|
||||
- ~/.cache ## Cache yarn and Cypress
|
||||
key: yarn-packages-{{ checksum "yarn.lock" }}
|
||||
# Build & Test
|
||||
- run:
|
||||
name: 'Perform the versioning before build'
|
||||
command: node ./version.mjs
|
||||
- run:
|
||||
name: 'Build the OHIF Viewer'
|
||||
command: pnpm run build
|
||||
command: yarn run build
|
||||
no_output_timeout: 45m
|
||||
- run:
|
||||
name: 'Upload SourceMaps, Send Deploy Notification'
|
||||
command: |
|
||||
# export FILE_1=$(find ./build/static/js -type f -name "2.*.js" -exec basename {} \;)
|
||||
# export FILE_MAIN=$(find ./build/static/js -type f -name "main.*.js" -exec basename {} \;)
|
||||
# export FILE_RUNTIME_MAIN=$(find ./build/static/js -type f -name "runtime~main.*.js" -exec basename {} \;)
|
||||
# curl https://api.rollbar.com/api/1/sourcemap -F source_map=@build/static/js/$FILE_1.map -F access_token=$ROLLBAR_TOKEN -F version=$CIRCLE_SHA1 -F minified_url=https://$GOOGLE_STORAGE_BUCKET/static/js/$FILE_1
|
||||
# curl https://api.rollbar.com/api/1/sourcemap -F source_map=@build/static/js/$FILE_MAIN.map -F access_token=$ROLLBAR_TOKEN -F version=$CIRCLE_SHA1 -F minified_url=https://$GOOGLE_STORAGE_BUCKET/static/js/$FILE_MAIN
|
||||
# curl https://api.rollbar.com/api/1/sourcemap -F source_map=@build/static/js/$FILE_RUNTIME_MAIN.map -F access_token=$ROLLBAR_TOKEN -F version=$CIRCLE_SHA1 -F minified_url=https://$GOOGLE_STORAGE_BUCKET/static/js/$FILE_RUNTIME_MAIN
|
||||
curl --request POST https://api.rollbar.com/api/1/deploy/ -F access_token=$ROLLBAR_TOKEN -F environment=$GOOGLE_STORAGE_BUCKET -F revision=$CIRCLE_SHA1 -F local_username=CircleCI
|
||||
# - run:
|
||||
# name: 'Upload SourceMaps, Send Deploy Notification'
|
||||
# command: |
|
||||
# # export FILE_1=$(find ./build/static/js -type f -name "2.*.js" -exec basename {} \;)
|
||||
# # export FILE_MAIN=$(find ./build/static/js -type f -name "main.*.js" -exec basename {} \;)
|
||||
# # export FILE_RUNTIME_MAIN=$(find ./build/static/js -type f -name "runtime~main.*.js" -exec basename {} \;)
|
||||
# # curl https://api.rollbar.com/api/1/sourcemap -F source_map=@build/static/js/$FILE_1.map -F access_token=$ROLLBAR_TOKEN -F version=$CIRCLE_SHA1 -F minified_url=https://$GOOGLE_STORAGE_BUCKET/static/js/$FILE_1
|
||||
# # curl https://api.rollbar.com/api/1/sourcemap -F source_map=@build/static/js/$FILE_MAIN.map -F access_token=$ROLLBAR_TOKEN -F version=$CIRCLE_SHA1 -F minified_url=https://$GOOGLE_STORAGE_BUCKET/static/js/$FILE_MAIN
|
||||
# # curl https://api.rollbar.com/api/1/sourcemap -F source_map=@build/static/js/$FILE_RUNTIME_MAIN.map -F access_token=$ROLLBAR_TOKEN -F version=$CIRCLE_SHA1 -F minified_url=https://$GOOGLE_STORAGE_BUCKET/static/js/$FILE_RUNTIME_MAIN
|
||||
# curl --request POST https://api.rollbar.com/api/1/deploy/ -F access_token=$ROLLBAR_TOKEN -F environment=$GOOGLE_STORAGE_BUCKET -F revision=$CIRCLE_SHA1 -F local_username=CircleCI
|
||||
# Persist :+1:
|
||||
- persist_to_workspace:
|
||||
root: ~/repo
|
||||
paths:
|
||||
- platform/app/dist
|
||||
- Dockerfile
|
||||
- version.txt
|
||||
- commit.txt
|
||||
- version.json
|
||||
- platform/viewer/dist
|
||||
- netlify.toml
|
||||
- .netlify
|
||||
|
||||
BUILD_PACKAGES_QUICK:
|
||||
<<: *defaults
|
||||
resource_class: large
|
||||
DEPLOY_TO_DEV:
|
||||
docker:
|
||||
- image: circleci/node:14.15.0
|
||||
environment:
|
||||
TERM: xterm
|
||||
NETLIFY_SITE_ID: 32708787-c9b0-4634-b50f-7ca41952da77
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- install_pnpm
|
||||
# Checkout code and ALL Git Tags
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
# SECURITY AUDIT - only when pnpm-lock.yaml has changed
|
||||
- run: cd .netlify && npm install
|
||||
- run:
|
||||
name: 'Security Audit - High Risk Vulnerabilities'
|
||||
command: |
|
||||
git fetch origin master 2>/dev/null || true
|
||||
BASE_REF=$(git merge-base HEAD origin/master 2>/dev/null)
|
||||
if [[ -z "$BASE_REF" ]]; then
|
||||
echo "Could not determine base ref (e.g. shallow clone or no origin/master), skipping security audit."
|
||||
exit 0
|
||||
fi
|
||||
CHANGED_FILES=$(git diff --name-only origin/master...HEAD 2>/dev/null || echo "")
|
||||
if ! echo "$CHANGED_FILES" | grep -qx 'pnpm-lock.yaml'; then
|
||||
echo "pnpm-lock.yaml unchanged - skipping security audit."
|
||||
exit 0
|
||||
fi
|
||||
echo "pnpm-lock.yaml changed - running pnpm audit for security vulnerabilities..."
|
||||
echo "Checking for HIGH-RISK vulnerabilities..."
|
||||
cp .netlify/deploy-workflow/_redirects platform/viewer/dist/_redirects
|
||||
- run: cd .netlify && npm run deploy
|
||||
|
||||
if pnpm audit --audit-level high; then
|
||||
echo "No high-risk vulnerabilities found"
|
||||
echo "Security audit passed!"
|
||||
else
|
||||
echo ""
|
||||
echo "HIGH-RISK VULNERABILITIES DETECTED!"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
echo "To fix these issues:"
|
||||
echo " 1. Run: pnpm audit"
|
||||
echo " 2. Review the vulnerability details"
|
||||
echo " 3. Update affected packages to secure versions"
|
||||
echo " 4. Test your changes"
|
||||
echo " 5. Re-run: pnpm audit --audit-level high"
|
||||
echo ""
|
||||
echo "Full audit report:"
|
||||
DEPLOY_TO_STAGING:
|
||||
docker:
|
||||
- image: circleci/node:14.15.0
|
||||
environment:
|
||||
TERM: xterm
|
||||
NETLIFY_SITE_ID: c7502ae3-b150-493c-8422-05701e44a969
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
- run: cd .netlify && npm install
|
||||
- run:
|
||||
cp .netlify/deploy-workflow/_redirects platform/viewer/dist/_redirects
|
||||
- run: cd .netlify && npm run deploy
|
||||
|
||||
pnpm audit || true
|
||||
|
||||
echo ""
|
||||
echo "This build cannot proceed until high-risk vulnerabilities are resolved."
|
||||
exit 1
|
||||
fi
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: pnpm install
|
||||
- run:
|
||||
name: Avoid hosts unknown for github
|
||||
command: |
|
||||
rm -rf ~/.ssh
|
||||
mkdir ~/.ssh/
|
||||
echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config
|
||||
git config --global user.email "danny.ri.brown+ohif-bot@gmail.com"
|
||||
git config --global user.name "ohif-bot"
|
||||
- run:
|
||||
name: Authenticate with NPM registry
|
||||
# Write the auth token to npm's per-user config (~/.npmrc), not the
|
||||
# repo root. publish-package.mjs chdir's into each package dir to run
|
||||
# `npm publish`, and npm only reads the project .npmrc from that dir
|
||||
# plus the user .npmrc from $HOME -- it never sees ~/repo/.npmrc, so a
|
||||
# root-level token yields ENEEDAUTH. Using ~/.npmrc also avoids
|
||||
# clobbering the committed workspace-config .npmrc (node-linker=hoisted).
|
||||
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
|
||||
- run:
|
||||
name: build half of the packages (to avoid out of memory in circleci)
|
||||
command: |
|
||||
pnpm run build:package-all
|
||||
- run:
|
||||
name: build the other half of the packages
|
||||
command: |
|
||||
pnpm run build:package-all-1
|
||||
DEPLOY_TO_PRODUCTION:
|
||||
docker:
|
||||
- image: circleci/node:14.15.0
|
||||
environment:
|
||||
TERM: xterm
|
||||
NETLIFY_SITE_ID: 79c4a5da-5c95-4dc9-84f7-45fd9dfe21b0
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
- run: cd .netlify && npm install
|
||||
- run:
|
||||
cp .netlify/deploy-workflow/_redirects platform/viewer/dist/_redirects
|
||||
- run: cd .netlify && npm run deploy
|
||||
|
||||
###
|
||||
# Workflow: RELEASE
|
||||
###
|
||||
NPM_PUBLISH:
|
||||
<<: *defaults
|
||||
resource_class: large
|
||||
steps:
|
||||
- install_pnpm
|
||||
- run: yarn -v
|
||||
# Checkout code and ALL Git Tags
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
- checkout:
|
||||
post:
|
||||
- git fetch --all
|
||||
# Use increasingly general patterns to restore cache
|
||||
# - restore_cache:
|
||||
# name: Restore Yarn and Cypress Package Cache
|
||||
# keys:
|
||||
# - yarn-packages-{{ checksum "yarn.lock" }}
|
||||
# - yarn-packages-
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: pnpm install --frozen-lockfile
|
||||
command: yarn install --frozen-lockfile
|
||||
- save_cache:
|
||||
name: Save Yarn Package Cache
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
key: yarn-packages-{{ checksum "yarn.lock" }}
|
||||
- run:
|
||||
name: Avoid hosts unknown for github
|
||||
command: |
|
||||
@@ -208,373 +260,249 @@ jobs:
|
||||
git config --global user.name "ohif-bot"
|
||||
- run:
|
||||
name: Authenticate with NPM registry
|
||||
# Write the auth token to npm's per-user config (~/.npmrc), not the
|
||||
# repo root. publish-package.mjs chdir's into each package dir to run
|
||||
# `npm publish`, and npm only reads the project .npmrc from that dir
|
||||
# plus the user .npmrc from $HOME -- it never sees ~/repo/.npmrc, so a
|
||||
# root-level token yields ENEEDAUTH. Using ~/.npmrc also avoids
|
||||
# clobbering the committed workspace-config .npmrc (node-linker=hoisted).
|
||||
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
|
||||
- run:
|
||||
name: build half of the packages (to avoid out of memory in circleci)
|
||||
command: |
|
||||
pnpm run build:package-all
|
||||
- run:
|
||||
name: build the other half of the packages
|
||||
command: |
|
||||
pnpm run build:package-all-1
|
||||
- run:
|
||||
name: increase min time out
|
||||
command: |
|
||||
npm config set fetch-retry-mintimeout 20000
|
||||
- run:
|
||||
name: increase max time out
|
||||
command: |
|
||||
npm config set fetch-retry-maxtimeout 120000
|
||||
- run:
|
||||
name: publish package versions
|
||||
command: |
|
||||
node ./publish-version.mjs
|
||||
- run:
|
||||
name: Re-assert the NPM auth token before publishing
|
||||
# Write the auth token to npm's per-user config (~/.npmrc), not the
|
||||
# repo root. publish-package.mjs chdir's into each package dir to run
|
||||
# `npm publish`, and npm only reads the project .npmrc from that dir
|
||||
# plus the user .npmrc from $HOME -- it never sees ~/repo/.npmrc, so a
|
||||
# root-level token yields ENEEDAUTH. Using ~/.npmrc also avoids
|
||||
# clobbering the committed workspace-config .npmrc (node-linker=hoisted).
|
||||
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
|
||||
- run:
|
||||
name: publish package dist
|
||||
command: |
|
||||
node ./publish-package.mjs
|
||||
command:
|
||||
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc
|
||||
- run: npx lerna version
|
||||
- run: npx lerna publish from-package
|
||||
- persist_to_workspace:
|
||||
root: ~/repo
|
||||
paths:
|
||||
- .
|
||||
paths: .
|
||||
|
||||
DOCKER_RELEASE_PUBLISH:
|
||||
DOCS_PUBLISH:
|
||||
docker:
|
||||
# Gitbook fails due to graceful-fs updates above this node version :-(
|
||||
- image: circleci/node:12.9.1
|
||||
environment:
|
||||
TERM: xterm # Enable colors in term
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Avoid hosts unknown for github
|
||||
command: |
|
||||
rm -rf ~/.ssh
|
||||
mkdir ~/.ssh/
|
||||
echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config
|
||||
git config --global user.email "danny.ri.brown+ohif-bot@gmail.com"
|
||||
git config --global user.name "ohif-bot"
|
||||
- run: yarn global add gitbook-cli gh-pages
|
||||
- run: chmod +x ~/repo/.circleci/build-and-publish-docs.sh
|
||||
- run: ~/repo/.circleci/build-and-publish-docs.sh
|
||||
|
||||
DOCKER_MASTER_PUBLISH:
|
||||
<<: *defaults
|
||||
resource_class: large
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: false
|
||||
- run:
|
||||
name: Build Docker image for amd64
|
||||
name: Build and push Docker image
|
||||
command: |
|
||||
# This file will exist if a new version was published by
|
||||
# our command in the previous job.
|
||||
if [[ ! -e version.txt ]]; then
|
||||
# our command in the previous job. Created in npm postpublish hook
|
||||
# in the `platform/viewer` project.
|
||||
if [[ ! -e platform/viewer/success_version.txt ]]; then
|
||||
exit 0
|
||||
else
|
||||
# Restore the committed .npmrc (pnpm workspace config such as
|
||||
# node-linker=hoisted). NPM_PUBLISH overwrote it with a publish
|
||||
# auth token, but the Dockerfile COPYs .npmrc for the in-image
|
||||
# pnpm install, so it needs the config file present and token-free.
|
||||
git checkout -- .npmrc
|
||||
# Set our version number using vars
|
||||
export IMAGE_VERSION=$(cat version.txt)
|
||||
export IMAGE_VERSION_FULL=v$IMAGE_VERSION
|
||||
echo $IMAGE_VERSION
|
||||
echo $IMAGE_VERSION_FULL
|
||||
# Build our amd64 image, auth, and push
|
||||
docker build --platform linux/amd64 --tag ohif/app:$IMAGE_VERSION_FULL-amd64 --tag ohif/app:latest-amd64 .
|
||||
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
|
||||
docker push ohif/app:$IMAGE_VERSION_FULL-amd64
|
||||
docker push ohif/app:latest-amd64
|
||||
fi
|
||||
- persist_to_workspace:
|
||||
root: ~/repo
|
||||
paths:
|
||||
- .
|
||||
|
||||
DOCKER_RELEASE_PUBLISH_ARM:
|
||||
<<: *defaults
|
||||
resource_class: arm.large
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: false
|
||||
- run:
|
||||
name: Build Docker image for arm64 (Release)
|
||||
command: |
|
||||
# This file will exist if a new version was published by
|
||||
# our command in the previous job.
|
||||
if [[ ! -e version.txt ]]; then
|
||||
exit 0
|
||||
else
|
||||
# Restore the committed .npmrc (pnpm workspace config such as
|
||||
# node-linker=hoisted). NPM_PUBLISH overwrote it with a publish
|
||||
# auth token, but the Dockerfile COPYs .npmrc for the in-image
|
||||
# pnpm install, so it needs the config file present and token-free.
|
||||
git checkout -- .npmrc
|
||||
# Set our version number using vars
|
||||
export IMAGE_VERSION=$(cat version.txt)
|
||||
export IMAGE_VERSION_FULL=v$IMAGE_VERSION
|
||||
echo $IMAGE_VERSION
|
||||
echo $IMAGE_VERSION_FULL
|
||||
# Build our arm64 image, auth, and push
|
||||
docker build --platform linux/arm64 --tag ohif/app:$IMAGE_VERSION_FULL-arm64 --tag ohif/app:latest-arm64 .
|
||||
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
|
||||
docker push ohif/app:$IMAGE_VERSION_FULL-arm64
|
||||
docker push ohif/app:latest-arm64
|
||||
fi
|
||||
- persist_to_workspace:
|
||||
root: ~/repo
|
||||
paths:
|
||||
- .
|
||||
|
||||
DOCKER_BETA_PUBLISH:
|
||||
<<: *defaults
|
||||
resource_class: large
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: false
|
||||
- run:
|
||||
name: Build Docker image for amd64 (Beta)
|
||||
command: |
|
||||
echo $(ls -l)
|
||||
|
||||
# This file will exist if a new version was published by
|
||||
# our command in the previous job.
|
||||
if [[ ! -e version.txt ]]; then
|
||||
echo "don't have version txt"
|
||||
exit 0
|
||||
else
|
||||
echo "Building and pushing Docker image from the master branch (beta releases)"
|
||||
# Restore the committed .npmrc (pnpm workspace config such as
|
||||
# node-linker=hoisted). NPM_PUBLISH overwrote it with a publish
|
||||
# auth token, but the Dockerfile COPYs .npmrc for the in-image
|
||||
# pnpm install, so it needs the config file present and token-free.
|
||||
git checkout -- .npmrc
|
||||
|
||||
# Set our version number using vars
|
||||
export IMAGE_VERSION=$(cat version.txt)
|
||||
export IMAGE_VERSION_FULL=v$IMAGE_VERSION
|
||||
echo $IMAGE_VERSION
|
||||
echo $IMAGE_VERSION_FULL
|
||||
# Build our amd64 image, auth, and push
|
||||
docker build --platform linux/amd64 --tag ohif/app:$IMAGE_VERSION_FULL-amd64 --tag ohif/app:latest-beta-amd64 .
|
||||
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
|
||||
docker push ohif/app:$IMAGE_VERSION_FULL-amd64
|
||||
docker push ohif/app:latest-beta-amd64
|
||||
fi
|
||||
|
||||
DOCKER_BETA_PUBLISH_ARM:
|
||||
<<: *defaults
|
||||
resource_class: arm.large
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: false
|
||||
- run:
|
||||
name: Build Docker image for arm64 (Beta)
|
||||
command: |
|
||||
echo $(ls -l)
|
||||
|
||||
# This file will exist if a new version was published by
|
||||
# our command in the previous job.
|
||||
if [[ ! -e version.txt ]]; then
|
||||
echo "don't have version txt"
|
||||
exit 0
|
||||
else
|
||||
echo "Building and pushing ARM64 Docker image from the master branch (beta releases)"
|
||||
# Restore the committed .npmrc (pnpm workspace config such as
|
||||
# node-linker=hoisted). NPM_PUBLISH overwrote it with a publish
|
||||
# auth token, but the Dockerfile COPYs .npmrc for the in-image
|
||||
# pnpm install, so it needs the config file present and token-free.
|
||||
git checkout -- .npmrc
|
||||
# Set our version number using vars
|
||||
export IMAGE_VERSION=$(cat version.txt)
|
||||
export IMAGE_VERSION_FULL=v$IMAGE_VERSION
|
||||
echo $IMAGE_VERSION
|
||||
echo $IMAGE_VERSION_FULL
|
||||
# Build our arm64 image, auth, and push
|
||||
docker build --platform linux/arm64 --tag ohif/app:$IMAGE_VERSION_FULL-arm64 --tag ohif/app:latest-beta-arm64 .
|
||||
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
|
||||
docker push ohif/app:$IMAGE_VERSION_FULL-arm64
|
||||
docker push ohif/app:latest-beta-arm64
|
||||
fi
|
||||
|
||||
CYPRESS:
|
||||
<<: *defaults
|
||||
resource_class: large
|
||||
parallelism: 8
|
||||
steps:
|
||||
- install_pnpm
|
||||
- run:
|
||||
name: Install System Dependencies
|
||||
command: |
|
||||
# CircleCI's base image registers third-party apt sources (git-lfs via
|
||||
# packagecloud, git-core PPA via launchpad) that periodically hang
|
||||
# `apt-get update`. The Cypress libs below all come from the Ubuntu
|
||||
# archive, so drop those sources and bound the update with timeouts +
|
||||
# retries so a slow mirror can't stall the job indefinitely.
|
||||
sudo rm -f /etc/apt/sources.list.d/*git* || true
|
||||
sudo apt-get update \
|
||||
-o Acquire::Retries=3 \
|
||||
-o Acquire::http::Timeout=20 \
|
||||
-o Acquire::https::Timeout=20
|
||||
sudo apt-get install -y xvfb libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6
|
||||
- run:
|
||||
name: Start Xvfb
|
||||
command: Xvfb :99 -screen 0 1920x1080x24 &
|
||||
background: true
|
||||
- run:
|
||||
name: Export Display Variable
|
||||
command: export DISPLAY=:99
|
||||
- cypress/install:
|
||||
install-command: pnpm install
|
||||
- cypress/run-tests:
|
||||
# CI runs headless under Xvfb with no GPU, so Electron uses software
|
||||
# WebGL. Newer Chromium deprecated that implicit fallback (canvas
|
||||
# rendering degrades and races with element clicks). ELECTRON_EXTRA_LAUNCH_ARGS
|
||||
# is the reliable way to pass the opt-in flag to Cypress's Electron browser.
|
||||
cypress-command: |
|
||||
npx wait-on@latest http://localhost:3000 && cd platform/app && ELECTRON_EXTRA_LAUNCH_ARGS="--enable-unsafe-swiftshader" npx cypress run --record --parallel
|
||||
start-command: pnpm run test:data && pnpm run test:e2e:serve
|
||||
|
||||
DOCKER_MULTIARCH_MANIFEST:
|
||||
<<: *defaults
|
||||
resource_class: large
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: false
|
||||
- run:
|
||||
name: Create and push multi-architecture manifest (Release)
|
||||
command: |
|
||||
# This file will exist if a new version was published by
|
||||
# our command in the previous job.
|
||||
if [[ ! -e version.txt ]]; then
|
||||
exit 0
|
||||
else
|
||||
echo "Building and pushing multi-architecture manifest from the master branch (release releases)"
|
||||
# Remove npm config
|
||||
rm -f ./.npmrc
|
||||
# Set our version number using vars
|
||||
export IMAGE_VERSION=$(cat version.txt)
|
||||
export IMAGE_VERSION_FULL=v$IMAGE_VERSION
|
||||
export IMAGE_VERSION=$(cat platform/viewer/success_version.txt)
|
||||
export IMAGE_VERSION_FULL=v$IMAGE_VERSION.${CIRCLE_BUILD_NUM}
|
||||
echo $IMAGE_VERSION
|
||||
echo $IMAGE_VERSION_FULL
|
||||
# Build our image, auth, and push
|
||||
docker build --tag ohif/$IMAGE_NAME:$IMAGE_VERSION_FULL --tag ohif/$IMAGE_NAME:latest .
|
||||
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
|
||||
|
||||
# Create and push manifest for specific version
|
||||
docker manifest create ohif/app:$IMAGE_VERSION_FULL \
|
||||
--amend ohif/app:$IMAGE_VERSION_FULL-amd64 \
|
||||
--amend ohif/app:$IMAGE_VERSION_FULL-arm64
|
||||
docker manifest push ohif/app:$IMAGE_VERSION_FULL
|
||||
|
||||
# Create and push manifest for "latest" tag
|
||||
docker manifest create ohif/app:latest \
|
||||
--amend ohif/app:latest-amd64 \
|
||||
--amend ohif/app:latest-arm64
|
||||
docker manifest push ohif/app:latest
|
||||
fi
|
||||
|
||||
DOCKER_BETA_MULTIARCH_MANIFEST:
|
||||
<<: *defaults
|
||||
resource_class: large
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: false
|
||||
- run:
|
||||
name: Create and push multi-architecture manifest (Beta)
|
||||
command: |
|
||||
echo $(ls -l)
|
||||
|
||||
# This file will exist if a new version was published by
|
||||
# our command in the previous job.
|
||||
if [[ ! -e version.txt ]]; then
|
||||
exit 0
|
||||
else
|
||||
echo "Building and pushing multi-architecture manifest from the master branch (beta releases)"
|
||||
rm -f ./.npmrc
|
||||
# Set our version number using vars
|
||||
export IMAGE_VERSION=$(cat version.txt)
|
||||
export IMAGE_VERSION_FULL=v$IMAGE_VERSION
|
||||
echo $IMAGE_VERSION
|
||||
echo $IMAGE_VERSION_FULL
|
||||
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
|
||||
|
||||
# Create and push manifest for specific beta version
|
||||
docker manifest create ohif/app:$IMAGE_VERSION_FULL \
|
||||
--amend ohif/app:$IMAGE_VERSION_FULL-amd64 \
|
||||
--amend ohif/app:$IMAGE_VERSION_FULL-arm64
|
||||
docker manifest push ohif/app:$IMAGE_VERSION_FULL
|
||||
|
||||
# Create and push manifest for "latest-beta" tag
|
||||
docker manifest create ohif/app:latest-beta \
|
||||
--amend ohif/app:latest-beta-amd64 \
|
||||
--amend ohif/app:latest-beta-arm64
|
||||
docker manifest push ohif/app:latest-beta
|
||||
docker push ohif/$IMAGE_NAME:$IMAGE_VERSION_FULL
|
||||
docker push ohif/$IMAGE_NAME:latest
|
||||
fi
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
PR_CHECKS:
|
||||
jobs:
|
||||
- BUILD_PACKAGES_QUICK:
|
||||
- UNIT_TESTS:
|
||||
filters:
|
||||
branches:
|
||||
ignore: master
|
||||
- UNIT_TESTS
|
||||
- CYPRESS:
|
||||
name: 'Cypress Tests'
|
||||
context: cypress
|
||||
ignore:
|
||||
- master
|
||||
- feature/*
|
||||
- hotfix/*
|
||||
# E2E: PWA
|
||||
- cypress/run:
|
||||
name: 'E2E: PWA'
|
||||
executor: chrome-and-pacs
|
||||
browser: chrome
|
||||
pre-steps:
|
||||
- run: |
|
||||
# Clear yarn cache; update to latest
|
||||
rm -rf ~/.yarn
|
||||
npm i -g yarn
|
||||
yarn -v
|
||||
yarn: true
|
||||
record: true
|
||||
store_artifacts: true
|
||||
working_directory: platform/viewer
|
||||
build:
|
||||
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
|
||||
yarn run build
|
||||
start: yarn run test:e2e:serve
|
||||
spec: 'cypress/integration/common/**/*,cypress/integration/pwa/**/*'
|
||||
wait-on: 'http://localhost:3000'
|
||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
||||
no-workspace: true # Don't persist workspace
|
||||
post-steps:
|
||||
- store_artifacts:
|
||||
path: platform/viewer/cypress/screenshots
|
||||
- store_artifacts:
|
||||
path: platform/viewer/cypress/videos
|
||||
- store_test_results:
|
||||
path: platform/viewer/cypress/results
|
||||
requires:
|
||||
- UNIT_TESTS
|
||||
# E2E: script-tag
|
||||
- cypress/run:
|
||||
name: 'E2E: Script Tag'
|
||||
executor: chrome-and-pacs
|
||||
browser: chrome
|
||||
pre-steps:
|
||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v' # Use yarn latest
|
||||
yarn: true
|
||||
record: true
|
||||
store_artifacts: true
|
||||
working_directory: platform/viewer
|
||||
build:
|
||||
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
|
||||
yarn run build:package
|
||||
start: yarn run test:e2e:serve
|
||||
spec: 'cypress/integration/common/**/*,cypress/integration/script-tag/**/*'
|
||||
wait-on: 'http://localhost:3000'
|
||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
||||
no-workspace: true # Don't persist workspace
|
||||
post-steps:
|
||||
- store_artifacts:
|
||||
path: platform/viewer/cypress/screenshots
|
||||
- store_artifacts:
|
||||
path: platform/viewer/cypress/videos
|
||||
- store_test_results:
|
||||
path: platform/viewer/cypress/results
|
||||
requires:
|
||||
- UNIT_TESTS
|
||||
|
||||
# viewer-dev.ohif.org
|
||||
DEPLOY_MASTER:
|
||||
PR_OPTIONAL_VISUAL_TESTS:
|
||||
jobs:
|
||||
- AWAIT_APPROVAL:
|
||||
type: approval
|
||||
# Update hub.docker.org
|
||||
- cypress/run:
|
||||
name: 'Generate Percy Snapshots'
|
||||
executor: chrome-and-pacs
|
||||
browser: chrome
|
||||
pre-steps:
|
||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
||||
add wait-on' # Use yarn latest
|
||||
yarn: true
|
||||
store_artifacts: false
|
||||
working_directory: platform/viewer
|
||||
build:
|
||||
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
|
||||
yarn run build
|
||||
# start server --> verify running --> percy + chrome + cypress
|
||||
command: yarn run test:e2e:dist
|
||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
||||
no-workspace: true # Don't persist workspace
|
||||
post-steps:
|
||||
- store_artifacts:
|
||||
path: platform/viewer/cypress/screenshots
|
||||
- store_artifacts:
|
||||
path: platform/viewer/cypress/videos
|
||||
requires:
|
||||
- AWAIT_APPROVAL
|
||||
|
||||
PR_OPTIONAL_DOCKER_PUBLISH:
|
||||
jobs:
|
||||
# https://circleci.com/docs/2.0/workflows/#holding-a-workflow-for-a-manual-approval
|
||||
- AWAIT_APPROVAL:
|
||||
type: approval
|
||||
# Update hub.docker.org
|
||||
- DOCKER_PR_PUBLISH:
|
||||
context: Docker Hub
|
||||
requires:
|
||||
- AWAIT_APPROVAL
|
||||
|
||||
###
|
||||
# Our workflow for building, deploying, and promoting builds across our
|
||||
# development, staging, and production environments.
|
||||
###
|
||||
DEPLOY:
|
||||
jobs:
|
||||
- BUILD:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
# - HOLD_FOR_APPROVAL:
|
||||
# type: approval
|
||||
# requires:
|
||||
# - BUILD
|
||||
- NPM_PUBLISH:
|
||||
- DEPLOY_TO_DEV:
|
||||
requires:
|
||||
# - HOLD_FOR_APPROVAL
|
||||
- BUILD
|
||||
- DOCKER_BETA_PUBLISH:
|
||||
requires:
|
||||
- NPM_PUBLISH
|
||||
- DOCKER_BETA_PUBLISH_ARM:
|
||||
requires:
|
||||
- DOCKER_BETA_PUBLISH
|
||||
- DOCKER_BETA_MULTIARCH_MANIFEST:
|
||||
requires:
|
||||
- DOCKER_BETA_PUBLISH_ARM
|
||||
|
||||
# viewer.ohif.org
|
||||
DEPLOY_RELEASE:
|
||||
jobs:
|
||||
- BUILD:
|
||||
filters:
|
||||
branches:
|
||||
only: /^release\/.*/
|
||||
- HOLD_FOR_APPROVAL:
|
||||
- PROMOTE_TO_STAGING:
|
||||
type: approval
|
||||
requires:
|
||||
- BUILD
|
||||
- NPM_PUBLISH:
|
||||
- DEPLOY_TO_DEV
|
||||
- DEPLOY_TO_STAGING:
|
||||
requires:
|
||||
- HOLD_FOR_APPROVAL
|
||||
- DOCKER_RELEASE_PUBLISH:
|
||||
- PROMOTE_TO_STAGING
|
||||
- PROMOTE_TO_PRODUCTION:
|
||||
type: approval
|
||||
requires:
|
||||
- DEPLOY_TO_STAGING
|
||||
- DEPLOY_TO_PRODUCTION:
|
||||
requires:
|
||||
- PROMOTE_TO_PRODUCTION
|
||||
###
|
||||
# Unit and E2E tests have already run for PR_CHECKS
|
||||
# Re-running should not gain us any confidence here
|
||||
###
|
||||
RELEASE:
|
||||
jobs:
|
||||
- NPM_PUBLISH:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- DOCS_PUBLISH:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
# Update base branch snapshots
|
||||
# and record a Cypress dashboard test run
|
||||
- cypress/run:
|
||||
name: 'Generate Percy Snapshots'
|
||||
executor: chrome-and-pacs
|
||||
browser: chrome
|
||||
pre-steps:
|
||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
||||
add wait-on' # Use yarn latest
|
||||
yarn: true
|
||||
store_artifacts: false
|
||||
working_directory: platform/viewer
|
||||
build:
|
||||
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
|
||||
yarn run build
|
||||
# start server --> verify running --> percy + chrome + cypress
|
||||
command: yarn run test:e2e:dist
|
||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
||||
no-workspace: true # Don't persist workspace
|
||||
post-steps:
|
||||
- store_artifacts:
|
||||
path: platform/viewer/cypress/screenshots
|
||||
- store_artifacts:
|
||||
path: platform/viewer/cypress/videos
|
||||
- store_test_results:
|
||||
path: platform/viewer/cypress/results
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- DOCKER_MASTER_PUBLISH:
|
||||
requires:
|
||||
- NPM_PUBLISH
|
||||
- DOCKER_RELEASE_PUBLISH_ARM:
|
||||
requires:
|
||||
- DOCKER_RELEASE_PUBLISH
|
||||
- DOCKER_MULTIARCH_MANIFEST:
|
||||
requires:
|
||||
- DOCKER_RELEASE_PUBLISH_ARM
|
||||
@@ -0,0 +1,25 @@
|
||||
# ABOUT:
|
||||
# https://docs.codecov.io/docs/codecov-yaml
|
||||
#
|
||||
#
|
||||
# COMMIT STATUS:
|
||||
# https://docs.codecov.io/docs/commit-status
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
threshold: 0.5%
|
||||
core:
|
||||
flags: core
|
||||
threshold: 0.5%
|
||||
viewer:
|
||||
flags: viewer
|
||||
threshold: 0.5%
|
||||
patch: off
|
||||
flags:
|
||||
core:
|
||||
paths:
|
||||
- platform/core
|
||||
viewer:
|
||||
paths:
|
||||
- platform/viewer
|
||||
@@ -1,6 +0,0 @@
|
||||
[codespell]
|
||||
skip = .git,*.pdf,*.svg,yarn.lock,*.min.js,locales
|
||||
# ignore words ending with … and some camelcased variables and names
|
||||
ignore-regex = \b\S+…\S*|\b(doubleClick|afterAll|PostgresSQL)\b|\bWee, L\.|.*te.*Telugu.*
|
||||
# some odd variables
|
||||
ignore-words-list = datea,ser,childrens
|
||||
@@ -0,0 +1,48 @@
|
||||
worker_processes 1;
|
||||
|
||||
events { worker_connections 1024; }
|
||||
|
||||
http {
|
||||
|
||||
upstream orthanc-server {
|
||||
server orthanc:8042;
|
||||
}
|
||||
|
||||
server {
|
||||
listen [::]:80 default_server;
|
||||
listen 80;
|
||||
|
||||
# CORS Magic
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow_Credentials' 'true';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
|
||||
|
||||
location / {
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow_Credentials' 'true';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
|
||||
add_header 'Access-Control-Max-Age' 1728000;
|
||||
add_header 'Content-Type' 'text/plain charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
|
||||
proxy_pass http://orthanc:8042;
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Host $server_name;
|
||||
|
||||
# CORS Magic
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow_Credentials' 'true';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
version: '3.5'
|
||||
|
||||
services:
|
||||
orthanc:
|
||||
image: jodogne/orthanc-plugins:1.5.6
|
||||
hostname: orthanc
|
||||
volumes:
|
||||
# Config
|
||||
- ./config/orthanc.json:/etc/orthanc/orthanc.json:ro
|
||||
# Persist data
|
||||
- ./volumes/orthanc-db/:/var/lib/orthanc/db/
|
||||
ports:
|
||||
- '4242:4242' # DICOM
|
||||
- '8042:8042' # Web
|
||||
restart: unless-stopped
|
||||
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -1,43 +0,0 @@
|
||||
# Docker compose files
|
||||
|
||||
This folder contains docker-compose files used to spin up OHIF-Viewer with
|
||||
different options such as locally or with any PAS you desire to
|
||||
|
||||
## Public Server
|
||||
|
||||
## Local Orthanc
|
||||
|
||||
### Build
|
||||
|
||||
`$ docker-compose -f docker-compose-orthanc.yml build`
|
||||
|
||||
### Run
|
||||
|
||||
Starts containers and leaves them running in the background.
|
||||
|
||||
`$ docker-compose -f docker-compose-orthanc.yml up -d`
|
||||
|
||||
then, access the application at [http://localhost](http://localhost)
|
||||
|
||||
**remember that you have to access orthanc application and include your studies
|
||||
there**
|
||||
|
||||
## Local Dcm4chee
|
||||
|
||||
#### build
|
||||
|
||||
`$ docker-compose -f docker-compose-dcm4chee.yml build`
|
||||
|
||||
#### run
|
||||
|
||||
`$ docker-compose -f docker-compose-dcm4chee.yml up -d`
|
||||
|
||||
then, access the application at [http://localhost](http://localhost)
|
||||
|
||||
**remember that you have to access dcm4chee application and include your studies
|
||||
there** You can use the following command to import your studies into dcm4che
|
||||
|
||||
`$ docker run -v {YOUR_STUDY_FOLDER}:/tmp --rm --network=docker_dcm4che_default dcm4che/dcm4che-tools:5.14.0 storescu -cDCM4CHEE@arc:11112 /tmp`
|
||||
|
||||
**make sure that your Docker network name is docker_dcm4chee_default or change
|
||||
it to the right one**
|
||||
@@ -0,0 +1,12 @@
|
||||
server {
|
||||
listen 80;
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -n "$CLIENT_ID" ] || [ -n "$HEALTHCARE_API_ENDPOINT" ]
|
||||
then
|
||||
# If CLIENT_ID is specified, use the google.js configuration with the modified ID
|
||||
if [ -n "$CLIENT_ID" ]
|
||||
then
|
||||
echo "Google Cloud Healthcare \$CLIENT_ID has been provided: "
|
||||
echo "$CLIENT_ID"
|
||||
echo "Updating config..."
|
||||
|
||||
# - Use SED to replace the CLIENT_ID that is currently in google.js
|
||||
sed -i -e "s/YOURCLIENTID.apps.googleusercontent.com/$CLIENT_ID/g" /usr/share/nginx/html/google.js
|
||||
fi
|
||||
|
||||
# If HEALTHCARE_API_ENDPOINT is specified, use the google.js configuration with the modified endpoint
|
||||
if [ -n "$HEALTHCARE_API_ENDPOINT" ]
|
||||
then
|
||||
echo "Google Cloud Healthcare \$HEALTHCARE_API_ENDPOINT has been provided: "
|
||||
echo "$HEALTHCARE_API_ENDPOINT"
|
||||
echo "Updating config..."
|
||||
|
||||
# - Use SED to replace the HEALTHCARE_API_ENDPOINT that is currently in google.js
|
||||
sed -i -e "s+https://healthcare.googleapis.com/v1beta1+$HEALTHCARE_API_ENDPOINT+g" /usr/share/nginx/html/google.js
|
||||
fi
|
||||
|
||||
# - Copy google.js to overwrite app-config.js
|
||||
cp /usr/share/nginx/html/google.js /usr/share/nginx/html/app-config.js
|
||||
fi
|
||||
|
||||
if [ -n "${PORT}" ]
|
||||
then
|
||||
echo "Changing port to ${PORT}..."
|
||||
sed -i -e "s/listen 80/listen ${PORT}/g" /etc/nginx/conf.d/default.conf
|
||||
fi
|
||||
|
||||
echo "Starting Nginx to serve the OHIF Viewer..."
|
||||
|
||||
exec "$@"
|
||||
@@ -1,21 +0,0 @@
|
||||
server {
|
||||
gzip_static always;
|
||||
gzip_proxied expired no-cache no-store private auth;
|
||||
gunzip on;
|
||||
listen ${PORT} default_server;
|
||||
listen [::]:${PORT} default_server;
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ ${PUBLIC_URL}index.html;
|
||||
add_header Cross-Origin-Resource-Policy same-origin;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
|
||||
}
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
server {
|
||||
listen ${SSL_PORT} ssl http2 default_server;
|
||||
listen [::]:${SSL_PORT} ssl http2 default_server;
|
||||
ssl_certificate /etc/ssl/certs/ssl-certificate.crt;
|
||||
ssl_certificate_key /etc/ssl/private/ssl-private-key.key;
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /index.html;
|
||||
add_header Cross-Origin-Resource-Policy same-origin;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
|
||||
}
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ -n "$SSL_PORT" ]
|
||||
then
|
||||
envsubst '${SSL_PORT}:${PORT}' < /usr/src/default.ssl.conf.template | envsubst '${PUBLIC_URL}' > /etc/nginx/conf.d/default.conf
|
||||
else
|
||||
envsubst '${PORT}:${PUBLIC_URL}' < /usr/src/default.conf.template > /etc/nginx/conf.d/default.conf
|
||||
fi
|
||||
|
||||
if [ -n "$APP_CONFIG" ]; then
|
||||
echo "$APP_CONFIG" > /usr/share/nginx/html${PUBLIC_URL}app-config.js
|
||||
echo "Using custom APP_CONFIG environment variable"
|
||||
else
|
||||
echo "Not using custom APP_CONFIG"
|
||||
fi
|
||||
|
||||
if [ -f /usr/share/nginx/html${PUBLIC_URL}app-config.js ]; then
|
||||
if [ -s /usr/share/nginx/html${PUBLIC_URL}app-config.js ]; then
|
||||
echo "Detected non-empty app-config.js. Ensuring .gz file is updated..."
|
||||
rm -f /usr/share/nginx/html${PUBLIC_URL}app-config.js.gz
|
||||
gzip /usr/share/nginx/html${PUBLIC_URL}app-config.js
|
||||
touch /usr/share/nginx/html${PUBLIC_URL}app-config.js
|
||||
echo "Compressed app-config.js to app-config.js.gz"
|
||||
else
|
||||
echo "app-config.js is empty. Skipping compression."
|
||||
fi
|
||||
else
|
||||
echo "No app-config.js file found. Skipping compression."
|
||||
fi
|
||||
|
||||
|
||||
|
||||
if [ -n "$CLIENT_ID" ] || [ -n "$HEALTHCARE_API_ENDPOINT" ]
|
||||
then
|
||||
# If CLIENT_ID is specified, use the google.js configuration with the modified ID
|
||||
if [ -n "$CLIENT_ID" ]
|
||||
then
|
||||
echo "Google Cloud Healthcare \$CLIENT_ID has been provided: "
|
||||
echo "$CLIENT_ID"
|
||||
echo "Updating config..."
|
||||
|
||||
# - Use SED to replace the CLIENT_ID that is currently in google.js
|
||||
sed -i -e "s/YOURCLIENTID.apps.googleusercontent.com/$CLIENT_ID/g" /usr/share/nginx/html/google.js
|
||||
fi
|
||||
|
||||
# If HEALTHCARE_API_ENDPOINT is specified, use the google.js configuration with the modified endpoint
|
||||
if [ -n "$HEALTHCARE_API_ENDPOINT" ]
|
||||
then
|
||||
echo "Google Cloud Healthcare \$HEALTHCARE_API_ENDPOINT has been provided: "
|
||||
echo "$HEALTHCARE_API_ENDPOINT"
|
||||
echo "Updating config..."
|
||||
|
||||
# - Use SED to replace the HEALTHCARE_API_ENDPOINT that is currently in google.js
|
||||
sed -i -e "s+https://healthcare.googleapis.com/v1+$HEALTHCARE_API_ENDPOINT+g" /usr/share/nginx/html/google.js
|
||||
fi
|
||||
|
||||
# - Copy google.js to overwrite app-config.js
|
||||
cp /usr/share/nginx/html/google.js /usr/share/nginx/html/app-config.js
|
||||
fi
|
||||
|
||||
echo "Starting Nginx to serve the OHIF Viewer on ${PUBLIC_URL}"
|
||||
|
||||
exec "$@"
|
||||
@@ -1,4 +0,0 @@
|
||||
find platform/app/dist -name "*.js" -exec gzip -9 "{}" \; -exec touch "{}" \;
|
||||
find platform/app/dist -name "*.map" -exec gzip -9 "{}" \; -exec touch "{}" \;
|
||||
find platform/app/dist -name "*.css" -exec gzip -9 "{}" \; -exec touch "{}" \;
|
||||
find platform/app/dist -name "*.svg" -exec gzip -9 "{}" \; -exec touch "{}" \;
|
||||
@@ -1,18 +1,12 @@
|
||||
# Reduces size of context and hides
|
||||
# files from Docker (can't COPY or ADD these)
|
||||
|
||||
# Note that typically the Docker context for various OHIF containers is the
|
||||
# directory of this file (i.e. the root of the source). As such, this is
|
||||
# the .dockerignore file for ALL Docker containers that are built. For example,
|
||||
# the Docker containers built from the recipes in ./platform/app/.recipes will
|
||||
# have this file as their .dockerignore.
|
||||
|
||||
# Output
|
||||
**/dist/
|
||||
**/build/
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Dependencies
|
||||
**/node_modules/
|
||||
node_modules/
|
||||
|
||||
# Root
|
||||
README.md
|
||||
@@ -33,5 +27,4 @@ dockerfile
|
||||
.scripts/
|
||||
.vscode/
|
||||
coverage/
|
||||
platform/docs/
|
||||
testdata/
|
||||
docs/
|
||||
@@ -0,0 +1 @@
|
||||
PERCY_TOKEN=<your token here>
|
||||
@@ -1,4 +0,0 @@
|
||||
config/**
|
||||
docs/**
|
||||
img/**
|
||||
node_modules
|
||||
@@ -1,13 +1,11 @@
|
||||
{
|
||||
"plugins": ["@typescript-eslint", "import", "eslint-plugin-tsdoc", "prettier"],
|
||||
"extends": [
|
||||
"react-app",
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parser": "babel-eslint",
|
||||
"env": {
|
||||
"jest": true
|
||||
},
|
||||
@@ -16,11 +14,6 @@
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
// Enforce consistent brace style for all control statements for readability
|
||||
"curly": "error",
|
||||
"import/no-anonymous-default-export": "off"
|
||||
},
|
||||
"globals": {
|
||||
"cy": true,
|
||||
"before": true,
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Set the default behavior,
|
||||
# in case people don't have core.autocrlf set.
|
||||
* text=auto
|
||||
# Declares that files will always have CRLF line ends
|
||||
*.sh text eol=lf
|
||||
@@ -1,13 +0,0 @@
|
||||
version: 2
|
||||
enable-beta-ecosystems: true
|
||||
updates:
|
||||
- package-ecosystem: 'npm'
|
||||
# Disable all pull requests for npm/pnpm version updates.
|
||||
open-pull-requests-limit: 0
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
labels: ['dependencies']
|
||||
commit-message:
|
||||
prefix: 'chore'
|
||||
include: 'scope'
|
||||
@@ -1 +0,0 @@
|
||||
custom: https://giving.massgeneral.org/ohif
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: "\U0001F41B Bug report"
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'Community: Report :bug:, Awaiting Reproduction, Triage :white_flag:'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
> **Before Creating an issue**
|
||||
>
|
||||
> - Are you running the latest version?
|
||||
> - Are you reporting to the correct repository?
|
||||
> - Did you search existing issues?
|
||||
|
||||
## Bug Report
|
||||
|
||||
### Describe the Bug
|
||||
|
||||
_A clear and concise description of what the bug is._
|
||||
|
||||
### What steps can we follow to reproduce the bug?
|
||||
|
||||
1. First step
|
||||
2. Second step
|
||||
3. ...
|
||||
|
||||
```js
|
||||
Please use code blocks to show formatted errors or code snippets
|
||||
```
|
||||
|
||||
> :warning: Reports we cannot reproduce are at risk of being marked stale and
|
||||
> closed. The more information you can provide, the more likely we are to look
|
||||
> into and address your issue.
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
name: "\U0001F680 Feature request"
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: 'Community: Request :hand:, Triage :white_flag:'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
> :hand: Many people requests features. Tell us why yours is important to the
|
||||
> community. How does it add value? Why _this feature_?
|
||||
>
|
||||
> Is your request very specific to your needs? Consider
|
||||
> [contributing it](https://docs.ohif.org/contributing.html) yourself! Or reach
|
||||
> out to a community member that offers
|
||||
> [consulting services](https://docs.ohif.org/help.html#paid--commercial).
|
||||
|
||||
## Request
|
||||
|
||||
**What feature or change would you like to see made?**
|
||||
|
||||
...
|
||||
|
||||
**Why should we prioritize this feature?**
|
||||
|
||||
...
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: "\U0001F917 Support Question"
|
||||
about: "I have a question \U0001F4AC"
|
||||
title: ''
|
||||
labels: 'Community: Question :question:, Triage :white_flag:'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
> :hand: We are a small team with limited resources. Your question is much more
|
||||
> likely to be answered if it is
|
||||
> [a good question](https://stackoverflow.com/help/how-to-ask)
|
||||
|
||||
**Description**
|
||||
|
||||
Questions can often be answered by our documentation. Unable to find an answer
|
||||
in our docs? We'll try to help. In the meantime, if you answer your own
|
||||
question, please respond with the answer here so that others may benefit as
|
||||
well. Better yet, open a PR to expand our docs ^\_^
|
||||
@@ -1,85 +0,0 @@
|
||||
name: 'Bug report'
|
||||
description: Create a report to help us improve
|
||||
title: '[Bug] '
|
||||
labels: ['Community: Report :bug:', 'Awaiting Reproduction']
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
👋 Hello, and thank you for contributing to our project! Your support is greatly appreciated.
|
||||
|
||||
🔍 Before proceeding, please make sure to read our [Rules of Conduct](https://github.com/OHIF/Viewers/blob/master/CODE_OF_CONDUCT.md) and familiarize yourself with our [development process](https:/docs.ohif.org/development/our-process).
|
||||
|
||||
❓ If you're here to seek general support or ask a question, we encourage you to visit our [community discussion board](https://community.ohif.org/)
|
||||
|
||||
🐞 For bug reports, please complete the following template in as much detail as possible. This will help us reproduce and address the issue efficiently.
|
||||
|
||||
🧪 Finally, ensure that you're using the latest version of the software and check if your issue has already been reported to avoid duplicates.
|
||||
|
||||
- type: textarea
|
||||
id: bug_description
|
||||
attributes:
|
||||
label: Describe the Bug
|
||||
description: 'A clear and concise description of what the bug is.'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reproduction_steps
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: 'Please describe the steps to reproduce the issue.'
|
||||
placeholder: "1. First step\n2. Second step\n3. ..."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: current_behavior
|
||||
attributes:
|
||||
label: The current behavior
|
||||
description:
|
||||
'A clear and concise description of what happens instead of the expected behavior.'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected_behavior
|
||||
attributes:
|
||||
label: The expected behavior
|
||||
description: 'A clear and concise description of what you expected to happen.'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: system_info
|
||||
attributes:
|
||||
label: 'System Information'
|
||||
description: 'Please run the following command in your terminal and paste the output:'
|
||||
placeholder: |
|
||||
Run: npx envinfo --system --binaries --browsers
|
||||
|
||||
Then paste the output here. It should look something like:
|
||||
|
||||
System:
|
||||
OS: Windows 10 10.0.19042
|
||||
CPU: (8) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
|
||||
Memory: 15.89 GB / 31.74 GB
|
||||
Shell: 1.0.0 - C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe
|
||||
Binaries:
|
||||
Node: 20.18.1 - C:\Program Files\nodejs\node.EXE
|
||||
Yarn: 1.22.22 - C:\Users\user\AppData\Roaming\npm\yarn.CMD
|
||||
npm: 10.8.2 - C:\Program Files\nodejs\npm.CMD
|
||||
Browsers:
|
||||
Chrome: 83.0.4103.116
|
||||
Edge: Spartan (44.19041.1266.0), Chromium (83.0.478.58)
|
||||
Firefox: 77.0.1
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
> :warning: Reports we cannot reproduce are at risk of being marked stale and > closed. The
|
||||
more information you can provide, the more likely we are to look > into and address your
|
||||
issue.
|
||||
@@ -1,5 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 🤗 Support Question
|
||||
url: https://community.ohif.org/
|
||||
about: Please use our forum if you have questions or need help.
|
||||
@@ -1,34 +0,0 @@
|
||||
name: Feature request
|
||||
description: Create a feature request
|
||||
labels: ['Community: Request :hand:']
|
||||
title: '[Feature Request] '
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
👋 Hello and thank you for your interest in our project!
|
||||
|
||||
🔍 Before you proceed, please read our [Rules of Conduct](https://github.com/OHIF/Viewers/blob/master/CODE_OF_CONDUCT.md).
|
||||
|
||||
🚀 If your request is specific to your needs, consider contributing it yourself! Read our [contributing guides](https://docs.ohif.org/development/contributing) to get started.
|
||||
|
||||
🖊️ Please provide as much detail as possible for your feature request. Mock-up screenshots, workflow or logic flow diagrams are very helpful. Discuss how your requested feature would interact with existing features.
|
||||
|
||||
⏱️ Lastly, tell us why we should prioritize your feature. What impact would it have?
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 'What feature or change would you like to see made?'
|
||||
description:
|
||||
'Please include as much detail as possible including possibly mock up screen shots, workflow
|
||||
or logic flow diagrams etc.'
|
||||
placeholder: '...'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 'Why should we prioritize this feature?'
|
||||
description: 'Discuss if and how the requested feature interacts with existing features.'
|
||||
placeholder: '...'
|
||||
validations:
|
||||
required: true
|
||||
@@ -1,91 +1,14 @@
|
||||
<!-- Do Not Delete This! pr_template -->
|
||||
<!-- Please read our Rules of Conduct: https://github.com/OHIF/Viewers/blob/master/CODE_OF_CONDUCT.md -->
|
||||
<!-- 🕮 Read our guide about our Contributing Guide here https://docs.ohif.org/development/contributing -->
|
||||
<!-- :hand: Thank you for starting this amazing contribution! -->
|
||||
### PR Checklist
|
||||
|
||||
- [ ] Brief description of changes
|
||||
- [ ] Links to any relevant issues
|
||||
- [ ] Required status checks are passing
|
||||
- [ ] User cases if changes impact the user's experience
|
||||
- [ ] `@mention` a maintainer to request a review
|
||||
|
||||
<!--
|
||||
⚠️⚠️ Please make sure the checklist section below is complete before submitting your PR.
|
||||
To complete the checklist, add an 'x' to each item: [] -> [x]
|
||||
(PRs that do not have all the checkboxes marked will not be approved)
|
||||
-->
|
||||
|
||||
### Context
|
||||
|
||||
<!--
|
||||
Provide a clear explanation of the reasoning behind this change, such as:
|
||||
- A link to the issue being addressed, using the format "Fixes #ISSUE_NUMBER"
|
||||
- An image showing the issue or problem being addressed (if not already in the issue)
|
||||
- Error logs or callStacks to help with the understanding of the problem (if not already in the issue)
|
||||
-->
|
||||
|
||||
### Changes & Results
|
||||
|
||||
<!--
|
||||
List all the changes that have been done, such as:
|
||||
- Add new components
|
||||
- Remove old components
|
||||
- Update dependencies
|
||||
|
||||
What are the effects of this change?
|
||||
- Before vs After
|
||||
- Screenshots / GIFs / Videos
|
||||
-->
|
||||
|
||||
### Testing
|
||||
|
||||
<!--
|
||||
Describe how we can test your changes.
|
||||
- open a URL
|
||||
- visit a page
|
||||
- click on a button
|
||||
- etc.
|
||||
-->
|
||||
|
||||
### Checklist
|
||||
|
||||
#### PR
|
||||
|
||||
<!--
|
||||
https://semantic-release.gitbook.io/semantic-release/#how-does-it-work
|
||||
|
||||
Examples:
|
||||
Please note the letter casing in the provided examples (upper or lower).
|
||||
|
||||
- feat(MeasurementService): add ...
|
||||
- fix(Toolbar): fix ...
|
||||
- docs(Readme): update ...
|
||||
- style(Whitespace): fix ...
|
||||
- refactor(ExtensionManager): ...
|
||||
- test(HangingProtocol): Add test ...
|
||||
- chore(git): update ...
|
||||
- perf(VolumeLoader): ...
|
||||
|
||||
You don't need to have each commit within the Pull Request follow the rule,
|
||||
but the PR title must comply with it, as it will be used as the commit message
|
||||
after the commits are squashed.
|
||||
-->
|
||||
|
||||
- [] My Pull Request title is descriptive, accurate and follows the
|
||||
semantic-release format and guidelines.
|
||||
|
||||
#### Code
|
||||
|
||||
- [] My code has been well-documented (function documentation, inline comments,
|
||||
etc.)
|
||||
|
||||
#### Public Documentation Updates
|
||||
|
||||
<!-- https://docs.ohif.org/ -->
|
||||
|
||||
- [] The documentation page has been updated as necessary for any public API
|
||||
additions or removals.
|
||||
|
||||
#### Tested Environment
|
||||
|
||||
- [] OS: <!--[e.g. Windows 10, macOS 10.15.4]-->
|
||||
- [] Node version: <!--[e.g. 18.16.1]-->
|
||||
- [] Browser:
|
||||
<!--[e.g. Chrome 83.0.4103.116, Firefox 77.0.1, Safari 13.1.1]-->
|
||||
Links
|
||||
-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[blog]: https://circleci.com/blog/triggering-trusted-ci-jobs-on-untrusted-forks/
|
||||
|
||||
@@ -4,22 +4,29 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 180
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 60
|
||||
daysUntilClose: 9000
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- 'Story :raised_hands:'
|
||||
- 'Bug: Verified :bug:'
|
||||
- 'Task: CI/Tooling :robot:'
|
||||
- 'Task: Docs 📖'
|
||||
- 'Task: Docs :book:'
|
||||
- 'Task: Refactor :hammer_and_wrench:'
|
||||
- 'Task: Tests :microscope:'
|
||||
- 'PR: Awaiting Review 👀'
|
||||
- 'Triage :white_flag:'
|
||||
- 'Extension: Discussion'
|
||||
- 'Announcement 🎉'
|
||||
- 'IDC:priority'
|
||||
- 'IDC:candidate'
|
||||
- 'IDC:collaboration'
|
||||
- 'Community: Request :hand:'
|
||||
- 'Community: Report :bug:'
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: 'Stale :baguette_bread:'
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had recent activity. It will
|
||||
be closed if no further activity occurs. Thank you for your contributions.
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
@@ -1,120 +0,0 @@
|
||||
name: Build and Deploy Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
env:
|
||||
ACTIONS_STEP_DEBUG: true
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-and-deploy-docs:
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
# Need permissions to read actions and pull requests
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
pull-requests: read
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
|
||||
- name: Configure git for private repos
|
||||
run: git config --global url."https://${GITHUB_TOKEN}:x-oauth-basic@github.com/".insteadOf ssh://git@github.com/
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install dependencies
|
||||
# Internal @ohif/* deps are workspace:* in the committed manifests and the
|
||||
# lockfile records them as links, so pnpm-lock.yaml stays in sync across
|
||||
# version bumps and a frozen install passes. Frozen is the right choice
|
||||
# for a deploy job: it fails fast if a PR ever changed a real dependency
|
||||
# without committing the lockfile update, instead of silently reconciling.
|
||||
# (Exact versions appear only in published tarballs, where pnpm publish
|
||||
# rewrites workspace:* at pack time -- not publish-version.mjs.)
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
# Removed Playwright tests and coverage generation steps
|
||||
|
||||
- name: Find PR and associated workflow run
|
||||
id: find_pr_run
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
MERGE_COMMIT_SHA: ${{ github.sha }}
|
||||
run: |
|
||||
# Find the PR associated with the merge commit SHA
|
||||
# Note: This relies on the merge commit being directly pushed to main
|
||||
PR_DATA=$(gh pr list --state merged --search "$MERGE_COMMIT_SHA" --json number,headRefOid --jq '.[0]')
|
||||
if [ -z "$PR_DATA" ]; then
|
||||
echo "::warning::Could not find merged PR for commit $MERGE_COMMIT_SHA. Skipping coverage embedding."
|
||||
echo "coverage_found=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
PR_HEAD_SHA=$(echo "$PR_DATA" | jq -r '.headRefOid')
|
||||
PR_NUMBER=$(echo "$PR_DATA" | jq -r '.number')
|
||||
echo "Found PR Number: $PR_NUMBER"
|
||||
echo "Found PR Head SHA: $PR_HEAD_SHA"
|
||||
|
||||
# Find the latest *successful* playwright.yml run for the PR head commit so we
|
||||
# never embed coverage from a failed/incomplete run.
|
||||
RUN_ID=$(gh run list --workflow playwright.yml --commit "$PR_HEAD_SHA" --event pull_request --status success --json databaseId --jq '.[0].databaseId')
|
||||
|
||||
if [ -z "$RUN_ID" ]; then
|
||||
echo "::warning::Could not find a successful 'playwright.yml' run for PR $PR_NUMBER (Head SHA: $PR_HEAD_SHA). Skipping coverage embedding."
|
||||
echo "coverage_found=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
echo "Found Run ID: $RUN_ID"
|
||||
echo "run_id=$RUN_ID" >> $GITHUB_OUTPUT
|
||||
echo "coverage_found=true" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Download coverage artifact from PR run
|
||||
if: steps.find_pr_run.outputs.coverage_found == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
mkdir -p ./coverage-artifact
|
||||
gh run download ${{ steps.find_pr_run.outputs.run_id }} -n coverage-report-pr --dir ./coverage-artifact
|
||||
# Verify the artifact contains an HTML coverage report rather than checking a single asset
|
||||
if [ -z "$(ls -A ./coverage-artifact)" ] || ! ls ./coverage-artifact/*.html >/dev/null 2>&1; then
|
||||
echo "Failed to download or find an HTML coverage report in artifact 'coverage-report-pr' from run ${{ steps.find_pr_run.outputs.run_id }}."
|
||||
exit 1
|
||||
fi
|
||||
echo "Artifact downloaded successfully."
|
||||
|
||||
- name: Copy coverage to docs static directory
|
||||
if: steps.find_pr_run.outputs.coverage_found == 'true'
|
||||
run: |
|
||||
# Copy files from the downloaded artifact directory
|
||||
mkdir -p platform/docs/static/coverage
|
||||
cp -r ./coverage-artifact/* platform/docs/static/coverage/
|
||||
# Copy specific asset files from the downloaded artifact root to static root
|
||||
cp ./coverage-artifact/base.css platform/docs/static/
|
||||
cp ./coverage-artifact/block-navigation.js platform/docs/static/
|
||||
cp ./coverage-artifact/prettify.css platform/docs/static/
|
||||
cp ./coverage-artifact/prettify.js platform/docs/static/
|
||||
cp ./coverage-artifact/favicon.png platform/docs/static/
|
||||
cp ./coverage-artifact/sort-arrow-sprite.png platform/docs/static/
|
||||
cp ./coverage-artifact/sorter.js platform/docs/static/
|
||||
|
||||
- name: Build docs
|
||||
run: pnpm --filter ohif-docs run build
|
||||
|
||||
- name: Deploy to Netlify
|
||||
run: |
|
||||
cd platform/docs
|
||||
npx netlify-cli deploy --dir=./build --prod
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
||||
@@ -1,100 +0,0 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL Advanced"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
schedule:
|
||||
- cron: '15 1 * * 5'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||
# - https://gh.io/supported-runners-and-hardware-resources
|
||||
# - https://gh.io/using-larger-runners (GitHub.com only)
|
||||
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# required to fetch internal or private CodeQL packs
|
||||
packages: read
|
||||
|
||||
# only required for workflows in private repositories
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- language: actions
|
||||
build-mode: none
|
||||
- language: javascript-typescript
|
||||
build-mode: none
|
||||
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
|
||||
# Use `c-cpp` to analyze code written in C, C++ or both
|
||||
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
||||
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
||||
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
||||
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
||||
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Add any setup steps before running the `github/codeql-action/init` action.
|
||||
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
||||
# or others). This is typically only required for manual builds.
|
||||
# - name: Setup runtime (example)
|
||||
# uses: actions/setup-example@v1
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@dd903d2e4f5405488e5ef1422510ee31c8b32357 # v3.36.2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
# If the analyze step fails for one of the languages you are analyzing with
|
||||
# "We were unable to automatically build your code", modify the matrix above
|
||||
# to set the build mode to "manual" for that language. Then modify this step
|
||||
# to build your code.
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
- if: matrix.build-mode == 'manual'
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'If you are using a "manual" build mode for one or more of the' \
|
||||
'languages you are analyzing, replace this with the commands to build' \
|
||||
'your code, for example:'
|
||||
echo ' make bootstrap'
|
||||
echo ' make release'
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@dd903d2e4f5405488e5ef1422510ee31c8b32357 # v3.36.2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
@@ -1,220 +0,0 @@
|
||||
name: Playwright Tests
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master, release/*]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
cs3d_ref:
|
||||
description: >-
|
||||
CS3D branch (e.g. main, origin:feat/foo) or version (e.g. 4.18.2, 4.19+, 4.x). Only used
|
||||
when ohif-integration label is present or via workflow_dispatch.
|
||||
required: false
|
||||
default: '4.19+'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
issues: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
playwright-tests:
|
||||
timeout-minutes: 120
|
||||
runs-on: self-hosted
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: [24]
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
# Self-hosted runner hygiene: wipe any stale/corrupted pnpm self-install
|
||||
# before action-setup reinstalls it. A prior `cache: pnpm` restore left a
|
||||
# pnpm install missing dist/worker.js, breaking `pnpm install`. Plain shell
|
||||
# so it runs even when pnpm itself is broken. Cheap: action-setup reinstalls.
|
||||
- name: Reset stale pnpm self-install
|
||||
shell: bash
|
||||
run: rm -rf "$HOME/setup-pnpm"
|
||||
- uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
# No `cache: pnpm`: this is a self-hosted runner with a persistent
|
||||
# pnpm store on local disk. The Actions cache doesn't preserve the
|
||||
# pnpm self-install's links faithfully, dropping pnpm/dist/worker.js
|
||||
# and breaking `pnpm install` with MODULE_NOT_FOUND.
|
||||
|
||||
# ── CS3D integration: detect label and ref type ──────────────────────
|
||||
- name: Check for CS3D integration label
|
||||
id: cs3d-check
|
||||
run: bash .scripts/ci/cs3d-check-integration.sh
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
CS3D_REF_INPUT: ${{ github.event.inputs.cs3d_ref || '4.19+' }}
|
||||
REPO: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
|
||||
- name: Detect CS3D ref type
|
||||
id: cs3d-ref
|
||||
if: steps.cs3d-check.outputs.enabled == 'true'
|
||||
run: |
|
||||
REF="${CS3D_REF}"
|
||||
if [[ "$REF" =~ ^[0-9]+\.[0-9x]+\+?(\.[0-9x]+)?(-[a-zA-Z0-9._]+)?$ ]]; then
|
||||
echo "type=version" >> "$GITHUB_OUTPUT"
|
||||
RESOLVED=$(node .scripts/cs3d-resolve-version.mjs "$REF")
|
||||
echo "version=$RESOLVED" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice::CS3D version: $REF -> $RESOLVED"
|
||||
else
|
||||
echo "type=branch" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice::CS3D branch: $REF"
|
||||
fi
|
||||
env:
|
||||
CS3D_REF: ${{ steps.cs3d-check.outputs.cs3d_ref }}
|
||||
|
||||
# ── CS3D branch path: clone and build before OHIF install ───────────
|
||||
- name: Clone CS3D
|
||||
if: steps.cs3d-check.outputs.enabled == 'true' && steps.cs3d-ref.outputs.type == 'branch'
|
||||
run: |
|
||||
REF="${CS3D_REF}"
|
||||
if [[ "$REF" == *:* ]]; then
|
||||
REPO="https://github.com/${REF%%:*}/cornerstone3D.git"
|
||||
BRANCH="${REF#*:}"
|
||||
else
|
||||
REPO="https://github.com/cornerstonejs/cornerstone3D.git"
|
||||
BRANCH="$REF"
|
||||
fi
|
||||
echo "::notice::Cloning CS3D from $REPO branch $BRANCH"
|
||||
git clone --depth 1 --branch "$BRANCH" "$REPO" libs/@cornerstonejs
|
||||
env:
|
||||
CS3D_REF: ${{ steps.cs3d-check.outputs.cs3d_ref }}
|
||||
|
||||
- name: Install & Build CS3D
|
||||
if: steps.cs3d-check.outputs.enabled == 'true' && steps.cs3d-ref.outputs.type == 'branch'
|
||||
working-directory: libs/@cornerstonejs
|
||||
run: pnpm install --frozen-lockfile && pnpm run build:esm
|
||||
|
||||
# ── Common: install OHIF dependencies ───────────────────────────────
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
# ── CS3D branch path: link packages after OHIF install ──────────────
|
||||
- name: Link CS3D packages
|
||||
if: steps.cs3d-check.outputs.enabled == 'true' && steps.cs3d-ref.outputs.type == 'branch'
|
||||
working-directory: libs/@cornerstonejs
|
||||
run: node scripts/link-ohif-cornerstone-node-modules.mjs "$GITHUB_WORKSPACE"
|
||||
|
||||
# ── CS3D version path: update versions after OHIF install ───────────
|
||||
- name: Set CS3D version
|
||||
if: steps.cs3d-check.outputs.enabled == 'true' && steps.cs3d-ref.outputs.type == 'version'
|
||||
run: |
|
||||
node .scripts/cs3d-set-version.mjs "${CS3D_VERSION}"
|
||||
pnpm install --no-frozen-lockfile
|
||||
env:
|
||||
CS3D_VERSION: ${{ steps.cs3d-ref.outputs.version }}
|
||||
|
||||
# ── Common: run tests ───────────────────────────────────────────────
|
||||
- name: Install Playwright browsers
|
||||
run: npx playwright install
|
||||
- name: Run Playwright tests
|
||||
run: |
|
||||
export NODE_OPTIONS="--max_old_space_size=10192"
|
||||
pnpm run test:e2e:coverage
|
||||
|
||||
# ── Common: collect test results and coverage ───────────────────────
|
||||
- name: Create directory of test results
|
||||
if: ${{ !cancelled() }}
|
||||
run: |
|
||||
mkdir -p packaged-test-results
|
||||
cp -r ./tests/test-results packaged-test-results/ || true
|
||||
cp -r ./tests/playwright-report packaged-test-results/ || true
|
||||
- name: Upload directory of test results artifact
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: playwright-results
|
||||
path: packaged-test-results/
|
||||
retention-days: 5
|
||||
- name: create the coverage report
|
||||
run: |
|
||||
pnpm exec nyc report --reporter=lcov --reporter=text
|
||||
- name: Upload the coverage report to GitHub Actions Artifacts
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: coverage-report-pr
|
||||
path: coverage
|
||||
retention-days: 3
|
||||
|
||||
# ── CS3D: build and deploy preview to Netlify ───────────────────────
|
||||
- name: Log build context (OHIF/CS3D branch and version for build diagnosis)
|
||||
if: steps.cs3d-check.outputs.enabled == 'true'
|
||||
run: |
|
||||
if [[ "$CS3D_REF_TYPE" == "branch" ]]; then
|
||||
echo "::notice::Build type: ohif-downstream | OHIF: ${{ github.repository }}@${{ github.ref }} (${{ github.sha }}) | CS3D: branch ${{ steps.cs3d-check.outputs.cs3d_ref }}"
|
||||
else
|
||||
echo "::notice::Build type: ohif-upstream | OHIF: ${{ github.repository }}@${{ github.ref }} (${{ github.sha }}) | CS3D: version ${{ steps.cs3d-ref.outputs.version }}"
|
||||
fi
|
||||
node .scripts/log-build-context.mjs
|
||||
env:
|
||||
BUILD_TYPE: ${{ steps.cs3d-ref.outputs.type == 'branch' && 'ohif-downstream' || 'ohif-upstream' }}
|
||||
CS3D_REF_TYPE: ${{ steps.cs3d-ref.outputs.type }}
|
||||
|
||||
- name: Build OHIF viewer (CS3D preview)
|
||||
if: steps.cs3d-check.outputs.enabled == 'true'
|
||||
run: pnpm run build:ci
|
||||
|
||||
- name: Deploy CS3D preview to Netlify
|
||||
if: steps.cs3d-check.outputs.enabled == 'true'
|
||||
run: |
|
||||
RESULT=$(npx netlify-cli deploy --dir=platform/app/dist --alias="cs3d-pr-${PR_NUM}" --json --filter=@ohif/app) || {
|
||||
echo "::error::Netlify deploy command failed"
|
||||
exit 1
|
||||
}
|
||||
URL=$(echo "$RESULT" | jq -r '.deploy_url')
|
||||
if [[ -z "$URL" || "$URL" == "null" ]]; then
|
||||
echo "::error::Netlify deploy did not return a valid URL"
|
||||
echo "$RESULT"
|
||||
exit 1
|
||||
fi
|
||||
echo "::notice::CS3D preview deployed: $URL"
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
||||
PR_NUM: ${{ github.event.pull_request.number || 'manual' }}
|
||||
|
||||
# ── CS3D: log results ───────────────────────────────────────────────
|
||||
- name: Log CS3D build used
|
||||
if: steps.cs3d-check.outputs.enabled == 'true'
|
||||
run: |
|
||||
if [[ "$CS3D_REF_TYPE" == "branch" ]]; then
|
||||
echo "::notice::CS3D integration PASSED with branch ${CS3D_REF} (linked from libs/@cornerstonejs)"
|
||||
else
|
||||
echo "::notice::CS3D integration PASSED with @cornerstonejs/*@${CS3D_VERSION}"
|
||||
fi
|
||||
env:
|
||||
CS3D_REF_TYPE: ${{ steps.cs3d-ref.outputs.type }}
|
||||
CS3D_REF: ${{ steps.cs3d-check.outputs.cs3d_ref }}
|
||||
CS3D_VERSION: ${{ steps.cs3d-ref.outputs.version }}
|
||||
|
||||
# ── Separate job: block merge when using a CS3D branch ─────────────
|
||||
cs3d-branch-merge-guard:
|
||||
name: "CS3D Branch Merge Guard"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check for CS3D branch usage
|
||||
run: bash .scripts/ci/cs3d-branch-merge-guard.sh
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
CS3D_REF_INPUT: ${{ github.event.inputs.cs3d_ref || '4.19+' }}
|
||||
REPO: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
@@ -1,8 +1,6 @@
|
||||
# Packages
|
||||
node_modules
|
||||
.cursor/
|
||||
.claude/
|
||||
.nyc_output/
|
||||
|
||||
# Output
|
||||
build
|
||||
dist
|
||||
@@ -11,15 +9,15 @@ src/version.js
|
||||
junit.xml
|
||||
coverage/
|
||||
.docz/
|
||||
.yarn/
|
||||
.nx/
|
||||
addOns/yarn.lock
|
||||
playwright-report/
|
||||
*.js.map
|
||||
.yarn
|
||||
platform/docs/.docusaurus
|
||||
platform/ui/storybook-static
|
||||
|
||||
# YALC (for Erik)
|
||||
.yalc
|
||||
yalc.lock
|
||||
*.dcm
|
||||
|
||||
# Logging, System files, misc.
|
||||
.idea/
|
||||
.npm
|
||||
@@ -28,8 +26,6 @@ package-lock.json
|
||||
yarn-error.log
|
||||
.DS_Store
|
||||
.env
|
||||
*.code-workspace
|
||||
.directory
|
||||
|
||||
# Common Example Data Directories
|
||||
sampledata/
|
||||
@@ -38,39 +34,8 @@ docker/dcm4che/dcm4che-arc
|
||||
|
||||
# Cypress test results
|
||||
videos/
|
||||
|
||||
screenshots/
|
||||
|
||||
# Locize settings
|
||||
.locize
|
||||
|
||||
# autogenerated files
|
||||
platform/app/src/pluginImports.js
|
||||
CLAUDE.md
|
||||
/Viewers.iml
|
||||
platform/app/.recipes/Nginx-Dcm4Chee/logs/*
|
||||
platform/app/.recipes/OpenResty-Orthanc/logs/*
|
||||
.vercel
|
||||
|
||||
.vs
|
||||
|
||||
# PlayWright
|
||||
|
||||
node_modules/
|
||||
tests/test-results/
|
||||
tests/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
|
||||
**/.claude/settings.local.json
|
||||
|
||||
# cornerstone3D local linking
|
||||
libs/
|
||||
|
||||
# Backup files
|
||||
*~
|
||||
|
||||
# cornerstone3D local linking
|
||||
libs/
|
||||
link-cs3d.js
|
||||
unlink-cs3d.js
|
||||
auth.json
|
||||
@@ -1,4 +0,0 @@
|
||||
[submodule "testdata"]
|
||||
path = testdata
|
||||
url = https://github.com/OHIF/viewer-testdata-dicomweb.git
|
||||
branch = main
|
||||
@@ -6,21 +6,36 @@ cd "$(dirname "$0")"
|
||||
cd .. # Up to project root
|
||||
|
||||
# Helpful to verify which versions we're using
|
||||
echo 'My pnpm version is... '
|
||||
|
||||
pnpm -v
|
||||
yarn -v
|
||||
node -v
|
||||
|
||||
# Install build deps and all monorepo package dependencies. Yarn Workspaces
|
||||
# should also symlink all projects appropriately
|
||||
yarn run lerna:restore
|
||||
yarn install --no-ignore-optional --pure-lockfile
|
||||
|
||||
# Build && Move PWA Output
|
||||
pnpm run build:ci
|
||||
yarn run build:ci
|
||||
mkdir -p ./.netlify/www/pwa
|
||||
mv platform/app/dist/* .netlify/www/pwa -v
|
||||
echo 'Web application built and copied'
|
||||
mv platform/viewer/dist/* .netlify/www/pwa -v
|
||||
|
||||
# Build && Move Docusaurus Output (for the docs themselves)
|
||||
pnpm --filter ohif-docs run build
|
||||
mkdir -p ./.netlify/www/docs
|
||||
mv platform/docs/build/* .netlify/www/docs -v
|
||||
echo 'Docs built (docusaurus) and copied'
|
||||
# Build && Move script output
|
||||
# yarn run build:package
|
||||
|
||||
# Build && Move Docz Output
|
||||
# Using local yarn install to prevent Gatsby from needing to access
|
||||
# node_modules above the platform/ui folder
|
||||
cd platform/ui
|
||||
yarn install
|
||||
yarn run build
|
||||
cd ../..
|
||||
mkdir -p ./.netlify/www/ui
|
||||
mv platform/ui/.docz/dist/* .netlify/www/ui -v
|
||||
|
||||
# Cache all of the node_module dependencies in
|
||||
# extensions, modules, and platform packages
|
||||
yarn run lerna:cache
|
||||
echo 'Nothing left to see here. Go home, folks.'
|
||||
|
||||
# Build using react-scripts
|
||||
# npx cross-env PUBLIC_URL=/demo APP_CONFIG=config/netlify.js react-scripts --max_old_space_size=4096 build
|
||||
@@ -2,13 +2,14 @@
|
||||
"name": "root",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"pnpm": ">=11"
|
||||
"node": ">=10",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
"scripts": {
|
||||
"deploy": "netlify deploy --prod --dir ./../platform/app/dist"
|
||||
"deploy": "netlify deploy --prod --dir ./../platform/viewer/dist"
|
||||
},
|
||||
"devDependencies": {
|
||||
"netlify-cli": "2.21.0"
|
||||
"netlify-cli": "^2.21.0"
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,3 @@
|
||||
|
||||
# PWA Demo
|
||||
/pwa/* /pwa/index.html 200
|
||||
# UI Demo
|
||||
/ui/* /ui/index.html 200
|
||||
# UI Demo
|
||||
/docs/* /docs/index.html 200
|
||||
@@ -2,19 +2,12 @@
|
||||
<head>
|
||||
<title>OHIF Viewer: Deploy Preview</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Index of Previews</h1>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/pwa">OHIF Viewer</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs">Documentation</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/ui">UI: Component Library</a>
|
||||
<a href="/pwa">Progressive Web App</a>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
24
|
||||
@@ -1,5 +0,0 @@
|
||||
node-linker=hoisted
|
||||
strict-peer-dependencies=false
|
||||
link-workspace-packages=true
|
||||
prefer-workspace-packages=true
|
||||
manage-package-manager-versions=false
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"extends": "@istanbuljs/nyc-config-typescript",
|
||||
"instrument": true,
|
||||
"sourceMap": true,
|
||||
"cache": false,
|
||||
"all": true,
|
||||
"include": [
|
||||
"platform/*/src/**/*.ts",
|
||||
"platform/*/src/**/*.js",
|
||||
"extensions/*/src/**/*.ts",
|
||||
"extensions/*/src/**/*.js",
|
||||
"modes/*/src/**/*.ts",
|
||||
"modes/*/src/**/*.js"
|
||||
],
|
||||
"exclude": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/test/**",
|
||||
"**/tests/**",
|
||||
"**/examples/**",
|
||||
"**/stories/**",
|
||||
"platform/docs/**"
|
||||
]
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
*.md
|
||||
@@ -1,12 +1,8 @@
|
||||
{
|
||||
"plugins": ["prettier-plugin-tailwindcss"],
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 100,
|
||||
"printWidth": 80,
|
||||
"proseWrap": "always",
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"arrowParens": "avoid",
|
||||
"singleAttributePerLine": true,
|
||||
"endOfLine": "auto"
|
||||
"singleQuote": true
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# CS3D branch merge guard: blocks merge when tests ran against a CS3D branch (not a version).
|
||||
# Exits 0 when merge is allowed or guard is skipped; exits 1 when merge must be blocked.
|
||||
#
|
||||
# Required env: GH_TOKEN, EVENT_NAME, REPO, PR_NUMBER
|
||||
# Optional env: CS3D_REF_INPUT (for workflow_dispatch, default 4.19+)
|
||||
|
||||
set -e
|
||||
|
||||
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
|
||||
echo "::notice::workflow_dispatch — no merge to block, skipping guard."
|
||||
exit 0
|
||||
elif [[ "$EVENT_NAME" == "pull_request" ]]; then
|
||||
LABELS=$(gh api "repos/${REPO}/issues/${PR_NUMBER}/labels" --jq '.[].name')
|
||||
if echo "$LABELS" | grep -q "ohif-integration"; then
|
||||
ENABLED=true
|
||||
CS3D_REF=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}" --jq '.body' \
|
||||
| sed -n 's/^[[:space:]]*CS3D_REF:[[:space:]]*\([^[:space:]]*\).*/\1/p' | head -1)
|
||||
if [[ -z "$CS3D_REF" ]]; then
|
||||
CS3D_REF="4.19+"
|
||||
fi
|
||||
else
|
||||
ENABLED=false
|
||||
fi
|
||||
else
|
||||
ENABLED=false
|
||||
fi
|
||||
|
||||
if [[ "$ENABLED" != "true" ]]; then
|
||||
echo "::notice::No ohif-integration label — skipping merge guard."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if the ref is a branch (not a version)
|
||||
if [[ "$CS3D_REF" =~ ^[0-9]+\.[0-9x]+\+?(\.[0-9x]+)?(-[a-zA-Z0-9._]+)?$ ]]; then
|
||||
echo "::notice::CS3D ref '$CS3D_REF' is a version — merge allowed."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "::error::Tests ran against CS3D branch '${CS3D_REF}' — this build cannot be merged."
|
||||
echo "::error::Re-run with a published CS3D version (e.g. 4.19+) before merging."
|
||||
exit 1
|
||||
@@ -1,29 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# CS3D integration check: detects ohif-integration label and parses CS3D_REF.
|
||||
# Writes to GITHUB_OUTPUT: enabled (true|false), cs3d_ref (when enabled).
|
||||
#
|
||||
# Required env: GH_TOKEN, EVENT_NAME, REPO, PR_NUMBER, GITHUB_OUTPUT
|
||||
# Optional env: CS3D_REF_INPUT (for workflow_dispatch, default 4.19+)
|
||||
|
||||
set -e
|
||||
|
||||
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
|
||||
echo "enabled=true" >> "$GITHUB_OUTPUT"
|
||||
echo "cs3d_ref=${CS3D_REF_INPUT:-4.19+}" >> "$GITHUB_OUTPUT"
|
||||
elif [[ "$EVENT_NAME" == "pull_request" ]]; then
|
||||
LABELS=$(gh api "repos/${REPO}/issues/${PR_NUMBER}/labels" --jq '.[].name')
|
||||
if echo "$LABELS" | grep -q "ohif-integration"; then
|
||||
echo "enabled=true" >> "$GITHUB_OUTPUT"
|
||||
REF=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}" --jq '.body' \
|
||||
| sed -n 's/^[[:space:]]*CS3D_REF:[[:space:]]*\([^[:space:]]*\).*/\1/p' | head -1)
|
||||
if [[ -z "$REF" ]]; then
|
||||
REF="4.19+"
|
||||
fi
|
||||
echo "cs3d_ref=${REF}" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice::CS3D ref from PR body: ${REF}"
|
||||
else
|
||||
echo "enabled=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
else
|
||||
echo "enabled=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
@@ -1,55 +0,0 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { execSync } from 'node:child_process';
|
||||
|
||||
const workflowPath = path.resolve(
|
||||
process.cwd(),
|
||||
'libs',
|
||||
'@cornerstonejs',
|
||||
'.github',
|
||||
'workflows',
|
||||
'ohif-downstream.yml'
|
||||
);
|
||||
|
||||
if (!fs.existsSync(workflowPath)) {
|
||||
console.error(`[cs3d:check] Workflow file not found: ${workflowPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const workflowText = fs.readFileSync(workflowPath, 'utf8');
|
||||
const ohifRefMatch = workflowText.match(/^\s*OHIF_REF:\s*["']?([^"'\r\n]+)["']?\s*$/m);
|
||||
|
||||
if (!ohifRefMatch) {
|
||||
console.error('[cs3d:check] Could not find OHIF_REF in ohif-downstream workflow.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const expectedBranch = ohifRefMatch[1].trim();
|
||||
let currentBranch = '';
|
||||
|
||||
try {
|
||||
currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {
|
||||
cwd: process.cwd(),
|
||||
encoding: 'utf8',
|
||||
}).trim();
|
||||
} catch (error) {
|
||||
console.error('[cs3d:check] Failed to determine current git branch.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (currentBranch !== expectedBranch) {
|
||||
console.error(
|
||||
`[cs3d:check] Branch mismatch: current='${currentBranch}', expected='${expectedBranch}' from ${path.relative(
|
||||
process.cwd(),
|
||||
workflowPath
|
||||
)}`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[cs3d:check] OK: current branch '${currentBranch}' matches OHIF_REF in ${path.relative(
|
||||
process.cwd(),
|
||||
workflowPath
|
||||
)}`
|
||||
);
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Resolves a version pattern to a concrete npm version of @cornerstonejs/core.
|
||||
*
|
||||
* Patterns:
|
||||
* 4.18.2 -> 4.18.2 (exact, returned as-is)
|
||||
* 4.18.2-beta.3 -> 4.18.2-beta.3 (exact prerelease)
|
||||
* 4.x -> latest 4.* from npm
|
||||
* 4.17.x -> latest 4.17.* from npm
|
||||
* 4.19+ -> latest >=4.19.0 <5.0.0-0 from npm (4.19 and later, same major)
|
||||
*
|
||||
* Prints the resolved version to stdout.
|
||||
*/
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
const pattern = process.argv[2];
|
||||
if (!pattern) {
|
||||
console.error('Usage: cs3d-resolve-version.mjs <pattern>');
|
||||
console.error(' e.g. 4.x, 4.17.x, 4.19+, 4.18.2, 4.18.2-beta.3');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Exact version (no wildcard or range) — pass through unchanged
|
||||
if (!pattern.includes('x') && !pattern.endsWith('+')) {
|
||||
console.log(pattern);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Convert "M.m+" to npm semver range ">=M.m.0 <(M+1).0.0-0"
|
||||
let npmRange = pattern;
|
||||
const plusMatch = pattern.match(/^(\d+)\.(\d+)\+$/);
|
||||
if (plusMatch) {
|
||||
const major = Number(plusMatch[1]);
|
||||
const minor = Number(plusMatch[2]);
|
||||
npmRange = `>=${major}.${minor}.0 <${major + 1}.0.0-0`;
|
||||
}
|
||||
|
||||
try {
|
||||
// Let npm resolve the range: @package@<range> → concrete version
|
||||
const raw = execSync(`npm view @cornerstonejs/core@"${npmRange}" version --json`, {
|
||||
encoding: 'utf8',
|
||||
timeout: 30_000,
|
||||
});
|
||||
|
||||
const resolved = JSON.parse(raw);
|
||||
if (!resolved) {
|
||||
console.error(`npm returned no version when resolving @cornerstonejs/core@${npmRange}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// npm may return an array of versions for ranges; pick the latest (last) one
|
||||
const version = Array.isArray(resolved) ? resolved[resolved.length - 1] : resolved;
|
||||
console.log(version);
|
||||
} catch (err) {
|
||||
const message =
|
||||
(err && (err.stderr?.toString() || err.message || String(err))) ||
|
||||
`Unknown error resolving @cornerstonejs/core@${pattern}`;
|
||||
console.error(`Failed to resolve @cornerstonejs/core version for pattern "${pattern}":`);
|
||||
console.error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Updates all @cornerstonejs/* package versions across the OHIF workspace.
|
||||
*
|
||||
* Usage: node .scripts/cs3d-set-version.mjs <version>
|
||||
*
|
||||
* Only updates the 8 main CS3D packages (not codec packages):
|
||||
* adapters, ai, core, dicom-image-loader, labelmap-interpolation,
|
||||
* nifti-volume-loader, polymorphic-segmentation, tools
|
||||
*/
|
||||
|
||||
import { readFileSync, writeFileSync, existsSync, readdirSync } from 'fs';
|
||||
import { resolve, dirname, join } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const rootDir = resolve(__dirname, '..');
|
||||
|
||||
const version = process.argv[2];
|
||||
if (!version) {
|
||||
console.error('Usage: cs3d-set-version.mjs <version>');
|
||||
console.error(' e.g. 4.18.2, 4.19.0-beta.1');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// The 8 CS3D packages that are built from source (not codecs)
|
||||
const CS3D_PACKAGES = [
|
||||
'@cornerstonejs/adapters',
|
||||
'@cornerstonejs/ai',
|
||||
'@cornerstonejs/core',
|
||||
'@cornerstonejs/dicom-image-loader',
|
||||
'@cornerstonejs/labelmap-interpolation',
|
||||
'@cornerstonejs/nifti-volume-loader',
|
||||
'@cornerstonejs/polymorphic-segmentation',
|
||||
'@cornerstonejs/tools',
|
||||
];
|
||||
|
||||
// Read root package.json to get workspace globs
|
||||
const rootPkgPath = resolve(rootDir, 'package.json');
|
||||
const rootPkg = JSON.parse(readFileSync(rootPkgPath, 'utf8'));
|
||||
const workspaceGlobs = rootPkg.workspaces?.packages || rootPkg.workspaces || [];
|
||||
|
||||
// Collect all package.json paths from workspace globs
|
||||
function findWorkspacePackageJsons() {
|
||||
const paths = [rootPkgPath]; // include root
|
||||
|
||||
for (const pattern of workspaceGlobs) {
|
||||
const parts = pattern.split('/');
|
||||
let searchDir = rootDir;
|
||||
let hasWildcard = false;
|
||||
|
||||
for (const part of parts) {
|
||||
if (part === '*') {
|
||||
hasWildcard = true;
|
||||
break;
|
||||
}
|
||||
searchDir = join(searchDir, part);
|
||||
}
|
||||
|
||||
if (hasWildcard) {
|
||||
try {
|
||||
const entries = readdirSync(searchDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
const pkgJson = join(searchDir, entry.name, 'package.json');
|
||||
if (existsSync(pkgJson)) {
|
||||
paths.push(pkgJson);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// directory doesn't exist, skip
|
||||
}
|
||||
} else {
|
||||
const pkgJson = join(rootDir, pattern, 'package.json');
|
||||
if (existsSync(pkgJson)) {
|
||||
paths.push(pkgJson);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
// Update a dependencies object, returning count of changes
|
||||
function updateDeps(deps, targetVersion) {
|
||||
let count = 0;
|
||||
if (!deps) return count;
|
||||
for (const pkg of CS3D_PACKAGES) {
|
||||
if (pkg in deps && deps[pkg] !== targetVersion) {
|
||||
deps[pkg] = targetVersion;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
const pkgPaths = findWorkspacePackageJsons();
|
||||
let totalChanges = 0;
|
||||
|
||||
for (const pkgPath of pkgPaths) {
|
||||
const content = readFileSync(pkgPath, 'utf8');
|
||||
const pkg = JSON.parse(content);
|
||||
let changes = 0;
|
||||
|
||||
changes += updateDeps(pkg.dependencies, version);
|
||||
changes += updateDeps(pkg.devDependencies, version);
|
||||
changes += updateDeps(pkg.peerDependencies, version);
|
||||
changes += updateDeps(pkg.resolutions, version);
|
||||
|
||||
if (changes > 0) {
|
||||
// Preserve original formatting (detect indent — restrict to spaces/tabs so
|
||||
// we don't accidentally capture a CRLF newline as part of the indent string)
|
||||
const indent = content.match(/^([ \t]+)/m)?.[1] || ' ';
|
||||
writeFileSync(pkgPath, JSON.stringify(pkg, null, indent) + '\n');
|
||||
const rel = pkgPath.replace(rootDir + '/', '').replace(rootDir + '\\', '');
|
||||
console.log(` Updated ${rel} (${changes} packages)`);
|
||||
totalChanges += changes;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
`\nDone: ${totalChanges} version(s) updated to ${version} across ${pkgPaths.length} package files.`
|
||||
);
|
||||
console.log(
|
||||
'This step changes package.json; the following install must not use a frozen Bun lockfile ' +
|
||||
'(OHIF+CS3D combined “version” CI does: `bun install --config=./bunfig.update-lockfile.toml`). ' +
|
||||
'Other installs stay frozen. Locally after this script, use that bun command and/or ' +
|
||||
'`bun run install:update-lockfile` when you intend to commit lockfile updates.\n'
|
||||
);
|
||||
@@ -6,9 +6,9 @@ PROJECT=$1
|
||||
if [ -z "$PROJECT" ]
|
||||
then
|
||||
# Default
|
||||
pnpm --filter @ohif/app run dev:viewer
|
||||
npx lerna run dev:viewer
|
||||
else
|
||||
pnpm --filter @ohif/app run "dev:$PROJECT"
|
||||
eval "npx lerna run dev:$PROJECT"
|
||||
fi
|
||||
|
||||
read -p 'Press [Enter] key to continue...'
|
||||
@@ -1,273 +0,0 @@
|
||||
/*
|
||||
* This script uses nodejs to generate a JSON file from a DICOM study folder.
|
||||
* You need to have dcmjs installed in your project.
|
||||
* The JSON file can be used to load the study into the OHIF Viewer. You can get more detail
|
||||
* in the DICOM JSON Data source on docs.ohif.org
|
||||
*
|
||||
* Usage: node dicom-json-generator.js <studyFolder> <urlPrefix> <outputJSONPath> <optional scheme>
|
||||
*
|
||||
* params:
|
||||
* - studyFolder: path to the study folder which contains the DICOM files
|
||||
* - urlPrefix: prefix to the url that will be used to load the study into the viewer. For instance
|
||||
* we use https://ohif-assets.s3.us-east-2.amazonaws.com/dicom-json/data as the urlPrefix for the
|
||||
* example since the data is hosted on S3 and each study is in a folder. So the url in the generated
|
||||
* json file for the first instance of the first series of the first study will be
|
||||
* dicomweb:https://ohif-assets.s3.us-east-2.amazonaws.com/dicom-json/data/Series1/Instance1
|
||||
*
|
||||
* as you see the dicomweb is a prefix that is used to load the data into the viewer, which is suited when
|
||||
* the .dcm file is hosted statically and can be accessed via a URL (like our example above)
|
||||
* However, you can specify a new scheme bellow.
|
||||
*
|
||||
* - outputJSONPath: path to the output JSON file
|
||||
* - scheme: default dicomweb if not provided
|
||||
*/
|
||||
const dcmjs = require('dcmjs');
|
||||
const path = require('path');
|
||||
const fs = require('fs').promises;
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const [studyDirectory, urlPrefix, outputPath, scheme = 'dicomweb'] = args;
|
||||
|
||||
if (args.length < 3 || args.length > 4) {
|
||||
console.error(
|
||||
'Usage: node dicom-json-generator.js <studyFolder> <urlPrefix> <outputJSONPath> [scheme]'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const model = {
|
||||
studies: [],
|
||||
};
|
||||
|
||||
async function convertDICOMToJSON(studyDirectory, urlPrefix, outputPath, scheme) {
|
||||
try {
|
||||
const files = await recursiveReadDir(studyDirectory);
|
||||
console.debug('Processing...');
|
||||
|
||||
for (const file of files) {
|
||||
if (!file.includes('.DS_Store') && !file.includes('.xml')) {
|
||||
const arrayBuffer = await fs.readFile(file);
|
||||
const dicomDict = dcmjs.data.DicomMessage.readFile(arrayBuffer.buffer);
|
||||
const instance = dcmjs.data.DicomMetaDictionary.naturalizeDataset(dicomDict.dict);
|
||||
|
||||
instance.fileLocation = createImageId(file, urlPrefix, studyDirectory, scheme);
|
||||
processInstance(instance);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Successfully loaded data');
|
||||
|
||||
model.studies.forEach(study => {
|
||||
study.NumInstances = findInstancesNumber(study);
|
||||
study.Modalities = findModalities(study).join('/');
|
||||
});
|
||||
|
||||
await fs.writeFile(outputPath, JSON.stringify(model, null, 2));
|
||||
console.log('JSON saved');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function recursiveReadDir(dir) {
|
||||
let results = [];
|
||||
const list = await fs.readdir(dir);
|
||||
for (const file of list) {
|
||||
const filePath = path.resolve(dir, file);
|
||||
const stat = await fs.stat(filePath);
|
||||
if (stat.isDirectory()) {
|
||||
const res = await recursiveReadDir(filePath);
|
||||
results = results.concat(res);
|
||||
} else {
|
||||
results.push(filePath);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function createImageId(fileLocation, urlPrefix, studyDirectory, scheme) {
|
||||
const relativePath = path.relative(studyDirectory, fileLocation);
|
||||
const normalizedPath = path.normalize(relativePath).replace(/\\/g, '/');
|
||||
return `${scheme}:${urlPrefix}${normalizedPath}`;
|
||||
}
|
||||
|
||||
function processInstance(instance) {
|
||||
const { StudyInstanceUID, SeriesInstanceUID } = instance;
|
||||
let study = getStudy(StudyInstanceUID);
|
||||
|
||||
if (!study) {
|
||||
study = createStudyMetadata(StudyInstanceUID, instance);
|
||||
model.studies.push(study);
|
||||
}
|
||||
|
||||
let series = getSeries(StudyInstanceUID, SeriesInstanceUID);
|
||||
|
||||
if (!series) {
|
||||
series = createSeriesMetadata(instance);
|
||||
study.series.push(series);
|
||||
}
|
||||
|
||||
const instanceMetaData =
|
||||
instance.NumberOfFrames > 1
|
||||
? createInstanceMetaDataMultiFrame(instance)
|
||||
: createInstanceMetaData(instance);
|
||||
|
||||
series.instances.push(...[].concat(instanceMetaData));
|
||||
}
|
||||
|
||||
function getStudy(StudyInstanceUID) {
|
||||
return model.studies.find(study => study.StudyInstanceUID === StudyInstanceUID);
|
||||
}
|
||||
|
||||
function getSeries(StudyInstanceUID, SeriesInstanceUID) {
|
||||
const study = getStudy(StudyInstanceUID);
|
||||
return study
|
||||
? study.series.find(series => series.SeriesInstanceUID === SeriesInstanceUID)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
const findInstancesNumber = study => {
|
||||
let numInstances = 0;
|
||||
study.series.forEach(aSeries => {
|
||||
numInstances = numInstances + aSeries.instances.length;
|
||||
});
|
||||
return numInstances;
|
||||
};
|
||||
|
||||
const findModalities = study => {
|
||||
let modalities = new Set();
|
||||
study.series.forEach(aSeries => {
|
||||
modalities.add(aSeries.Modality);
|
||||
});
|
||||
return Array.from(modalities);
|
||||
};
|
||||
|
||||
function createStudyMetadata(StudyInstanceUID, instance) {
|
||||
return {
|
||||
StudyInstanceUID,
|
||||
StudyDescription: instance.StudyDescription,
|
||||
StudyDate: instance.StudyDate,
|
||||
StudyTime: instance.StudyTime,
|
||||
PatientName: instance.PatientName,
|
||||
PatientID: instance.PatientID || '1234', // this is critical to have
|
||||
AccessionNumber: instance.AccessionNumber,
|
||||
PatientAge: instance.PatientAge,
|
||||
PatientSex: instance.PatientSex,
|
||||
PatientWeight: instance.PatientWeight,
|
||||
series: [],
|
||||
};
|
||||
}
|
||||
function createSeriesMetadata(instance) {
|
||||
return {
|
||||
SeriesInstanceUID: instance.SeriesInstanceUID,
|
||||
SeriesDescription: instance.SeriesDescription,
|
||||
SeriesNumber: instance.SeriesNumber,
|
||||
SeriesTime: instance.SeriesTime,
|
||||
Modality: instance.Modality,
|
||||
SliceThickness: instance.SliceThickness,
|
||||
instances: [],
|
||||
};
|
||||
}
|
||||
function commonMetaData(instance) {
|
||||
return {
|
||||
Columns: instance.Columns,
|
||||
Rows: instance.Rows,
|
||||
InstanceNumber: instance.InstanceNumber,
|
||||
SOPClassUID: instance.SOPClassUID,
|
||||
AcquisitionNumber: instance.AcquisitionNumber,
|
||||
PhotometricInterpretation: instance.PhotometricInterpretation,
|
||||
BitsAllocated: instance.BitsAllocated,
|
||||
BitsStored: instance.BitsStored,
|
||||
PixelRepresentation: instance.PixelRepresentation,
|
||||
SamplesPerPixel: instance.SamplesPerPixel,
|
||||
PixelSpacing: instance.PixelSpacing,
|
||||
HighBit: instance.HighBit,
|
||||
ImageOrientationPatient: instance.ImageOrientationPatient,
|
||||
ImagePositionPatient: instance.ImagePositionPatient,
|
||||
FrameOfReferenceUID: instance.FrameOfReferenceUID,
|
||||
ImageType: instance.ImageType,
|
||||
Modality: instance.Modality,
|
||||
SOPInstanceUID: instance.SOPInstanceUID,
|
||||
SeriesInstanceUID: instance.SeriesInstanceUID,
|
||||
StudyInstanceUID: instance.StudyInstanceUID,
|
||||
WindowCenter: instance.WindowCenter,
|
||||
WindowWidth: instance.WindowWidth,
|
||||
RescaleIntercept: instance.RescaleIntercept,
|
||||
RescaleSlope: instance.RescaleSlope,
|
||||
};
|
||||
}
|
||||
|
||||
function conditionalMetaData(instance) {
|
||||
return {
|
||||
...(instance.ConceptNameCodeSequence && {
|
||||
ConceptNameCodeSequence: instance.ConceptNameCodeSequence,
|
||||
}),
|
||||
...(instance.SeriesDate && { SeriesDate: instance.SeriesDate }),
|
||||
...(instance.ReferencedSeriesSequence && {
|
||||
ReferencedSeriesSequence: instance.ReferencedSeriesSequence,
|
||||
}),
|
||||
...(instance.SharedFunctionalGroupsSequence && {
|
||||
SharedFunctionalGroupsSequence: instance.SharedFunctionalGroupsSequence,
|
||||
}),
|
||||
...(instance.PerFrameFunctionalGroupsSequence && {
|
||||
PerFrameFunctionalGroupsSequence: instance.PerFrameFunctionalGroupsSequence,
|
||||
}),
|
||||
...(instance.ContentSequence && { ContentSequence: instance.ContentSequence }),
|
||||
...(instance.ContentTemplateSequence && {
|
||||
ContentTemplateSequence: instance.ContentTemplateSequence,
|
||||
}),
|
||||
...(instance.CurrentRequestedProcedureEvidenceSequence && {
|
||||
CurrentRequestedProcedureEvidenceSequence: instance.CurrentRequestedProcedureEvidenceSequence,
|
||||
}),
|
||||
...(instance.CodingSchemeIdentificationSequence && {
|
||||
CodingSchemeIdentificationSequence: instance.CodingSchemeIdentificationSequence,
|
||||
}),
|
||||
...(instance.RadiopharmaceuticalInformationSequence && {
|
||||
RadiopharmaceuticalInformationSequence: instance.RadiopharmaceuticalInformationSequence,
|
||||
}),
|
||||
...(instance.ROIContourSequence && {
|
||||
ROIContourSequence: instance.ROIContourSequence,
|
||||
}),
|
||||
...(instance.StructureSetROISequence && {
|
||||
StructureSetROISequence: instance.StructureSetROISequence,
|
||||
}),
|
||||
...(instance.ReferencedFrameOfReferenceSequence && {
|
||||
ReferencedFrameOfReferenceSequence: instance.ReferencedFrameOfReferenceSequence,
|
||||
}),
|
||||
...(instance.CorrectedImage && { CorrectedImage: instance.CorrectedImage }),
|
||||
...(instance.Units && { Units: instance.Units }),
|
||||
...(instance.DecayCorrection && { DecayCorrection: instance.DecayCorrection }),
|
||||
...(instance.AcquisitionDate && { AcquisitionDate: instance.AcquisitionDate }),
|
||||
...(instance.AcquisitionTime && { AcquisitionTime: instance.AcquisitionTime }),
|
||||
...(instance.PatientWeight && { PatientWeight: instance.PatientWeight }),
|
||||
...(instance.NumberOfFrames && { NumberOfFrames: instance.NumberOfFrames }),
|
||||
...(instance.FrameTime && { FrameTime: instance.FrameTime }),
|
||||
...(instance.EncapsulatedDocument && { EncapsulatedDocument: instance.EncapsulatedDocument }),
|
||||
...(instance.SequenceOfUltrasoundRegions && {
|
||||
SequenceOfUltrasoundRegions: instance.SequenceOfUltrasoundRegions,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function createInstanceMetaData(instance) {
|
||||
const metadata = {
|
||||
...commonMetaData(instance),
|
||||
...conditionalMetaData(instance),
|
||||
};
|
||||
return { metadata, url: instance.fileLocation };
|
||||
}
|
||||
|
||||
function createInstanceMetaDataMultiFrame(instance) {
|
||||
const instances = [];
|
||||
const commonData = commonMetaData(instance);
|
||||
const conditionalData = conditionalMetaData(instance);
|
||||
|
||||
for (let i = 1; i <= instance.NumberOfFrames; i++) {
|
||||
const metadata = { ...commonData, ...conditionalData };
|
||||
const result = { metadata, url: instance.fileLocation + `?frame=${i}` };
|
||||
instances.push(result);
|
||||
}
|
||||
return instances;
|
||||
}
|
||||
|
||||
convertDICOMToJSON(studyDirectory, urlPrefix, outputPath, scheme);
|
||||
@@ -1,108 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Logs build context (OHIF branch/version, CS3D source) for diagnosing build issues on GitHub.
|
||||
* Run from repo root. Used by version.mjs and can be invoked from CI workflows.
|
||||
*/
|
||||
import { execa } from 'execa';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const REPO_ROOT = path.resolve(__dirname, '..');
|
||||
|
||||
function log(msg) {
|
||||
console.log(`[build-context] ${msg}`);
|
||||
}
|
||||
|
||||
async function detectCs3dSource() {
|
||||
const libsCs3d = path.join(REPO_ROOT, 'libs/@cornerstonejs');
|
||||
const coreInNodeModules = path.join(REPO_ROOT, 'node_modules/@cornerstonejs/core');
|
||||
|
||||
try {
|
||||
const libsExists = await fs.access(libsCs3d).then(() => true).catch(() => false);
|
||||
if (!libsExists) {
|
||||
return { source: 'npm', detail: 'published @cornerstonejs packages (no libs/@cornerstonejs)' };
|
||||
}
|
||||
|
||||
const coreStat = await fs.lstat(coreInNodeModules).catch(() => null);
|
||||
const isSymlink = coreStat?.isSymbolicLink();
|
||||
if (isSymlink) {
|
||||
const target = await fs.readlink(coreInNodeModules);
|
||||
return { source: 'integrated', detail: `linked from libs/@cornerstonejs (→ ${target})` };
|
||||
}
|
||||
|
||||
return { source: 'npm', detail: 'published @cornerstonejs packages (libs exists but not linked)' };
|
||||
} catch {
|
||||
return { source: 'unknown', detail: 'could not detect' };
|
||||
}
|
||||
}
|
||||
|
||||
async function getCs3dVersion() {
|
||||
try {
|
||||
const corePkg = path.join(REPO_ROOT, 'node_modules/@cornerstonejs/core/package.json');
|
||||
const pkg = JSON.parse(await fs.readFile(corePkg, 'utf-8'));
|
||||
return pkg.version || 'unknown';
|
||||
} catch {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
async function getCs3dBranchFromLibs() {
|
||||
try {
|
||||
const libsCs3d = path.join(REPO_ROOT, 'libs/@cornerstonejs');
|
||||
const { stdout } = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
||||
cwd: libsCs3d,
|
||||
});
|
||||
return stdout;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function run() {
|
||||
const inCI = process.env.GITHUB_ACTIONS === 'true';
|
||||
const buildType = inCI ? (process.env.BUILD_TYPE || 'ohif-upstream') : 'local';
|
||||
|
||||
log('═══════════════════════════════════════════════════════════════');
|
||||
log('Build context (for diagnosing GitHub build issues)');
|
||||
log('═══════════════════════════════════════════════════════════════');
|
||||
|
||||
if (inCI) {
|
||||
log(`Build type: ${process.env.BUILD_TYPE || 'ohif-upstream'}`);
|
||||
log(`GitHub repo: ${process.env.GITHUB_REPOSITORY || 'unknown'}`);
|
||||
log(`GitHub ref: ${process.env.GITHUB_REF || 'unknown'}`);
|
||||
log(`GitHub SHA: ${process.env.GITHUB_SHA || 'unknown'}`);
|
||||
log(`Workflow: ${process.env.GITHUB_WORKFLOW || 'unknown'}`);
|
||||
}
|
||||
|
||||
try {
|
||||
const { stdout: branch } = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
||||
cwd: REPO_ROOT,
|
||||
});
|
||||
const { stdout: sha } = await execa('git', ['rev-parse', '--short', 'HEAD'], {
|
||||
cwd: REPO_ROOT,
|
||||
});
|
||||
log(`OHIF branch: ${branch} (${sha})`);
|
||||
} catch {
|
||||
log('OHIF branch: (not a git repo or error)');
|
||||
}
|
||||
|
||||
const cs3d = await detectCs3dSource();
|
||||
log(`CS3D source: ${cs3d.source} — ${cs3d.detail}`);
|
||||
|
||||
if (cs3d.source === 'integrated') {
|
||||
const branch = await getCs3dBranchFromLibs();
|
||||
if (branch) log(`CS3D branch (libs/@cornerstonejs): ${branch}`);
|
||||
} else {
|
||||
const ver = await getCs3dVersion();
|
||||
log(`CS3D version (@cornerstonejs/core): ${ver}`);
|
||||
}
|
||||
|
||||
log('═══════════════════════════════════════════════════════════════');
|
||||
}
|
||||
|
||||
run().catch((err) => {
|
||||
console.error('[build-context] Error:', err.message);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
@@ -1,13 +1,8 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"esbenp.prettier-vscode",
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"sysoev.language-stylus",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"mikestead.dotenv",
|
||||
"bungcip.better-toml",
|
||||
"silvenon.mdx",
|
||||
"gruntfuggly.todo-tree",
|
||||
"wayou.vscode-todo-highlight",
|
||||
"bradlc.vscode-tailwindcss"
|
||||
"mikestead.dotenv"
|
||||
]
|
||||
}
|
||||
@@ -11,18 +11,5 @@
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
// {
|
||||
// "name": "Debug Jest Tests",
|
||||
// "type": "node",
|
||||
// "request": "launch",
|
||||
// "runtimeArgs": [
|
||||
// "--inspect-brk",
|
||||
// "${workspaceRoot}/node_modules/.bin/jest",
|
||||
// "--runInBand"
|
||||
// ],
|
||||
// "console": "integratedTerminal",
|
||||
// "internalConsoleOptions": "neverOpen",
|
||||
// "port": 9229
|
||||
// }
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.rulers": [80, 120],
|
||||
// ===
|
||||
// Spacing
|
||||
@@ -15,103 +14,22 @@
|
||||
// Event Triggers
|
||||
// ===
|
||||
"editor.formatOnSave": true,
|
||||
"eslint.autoFixOnSave": true,
|
||||
"eslint.run": "onSave",
|
||||
"jest.autoRun": "off",
|
||||
"eslint.validate": [
|
||||
{
|
||||
"language": "javascript",
|
||||
"autoFix": true
|
||||
},
|
||||
{
|
||||
"language": "javascriptreact",
|
||||
"autoFix": true
|
||||
}
|
||||
],
|
||||
"prettier.disableLanguages": ["html"],
|
||||
"prettier.endOfLine": "lf",
|
||||
"workbench.colorCustomizations": {},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"cSpell.userWords": [
|
||||
"aabb",
|
||||
"architectured",
|
||||
"attrname",
|
||||
"Barksy",
|
||||
"browserslist",
|
||||
"bulkdata",
|
||||
"Cacheable",
|
||||
"cfun",
|
||||
"clonedeep",
|
||||
"Colormap",
|
||||
"Colormaps",
|
||||
"Comlink",
|
||||
"cornerstonejs",
|
||||
"Crosshairs",
|
||||
"datasource",
|
||||
"dcmjs",
|
||||
"decache",
|
||||
"decached",
|
||||
"decaching",
|
||||
"deepmerge",
|
||||
"Dicom",
|
||||
"dicomweb",
|
||||
"DISPLAYSETS",
|
||||
"glwindow",
|
||||
"grababble",
|
||||
"grabbable",
|
||||
"Hounsfield",
|
||||
"Interactable",
|
||||
"Interactor",
|
||||
"istyle",
|
||||
"kitware",
|
||||
"labelmap",
|
||||
"labelmaps",
|
||||
"livewire",
|
||||
"Mergeable",
|
||||
"multiframe",
|
||||
"nifti",
|
||||
"ofun",
|
||||
"OHIF",
|
||||
"polylines",
|
||||
"POLYSEG",
|
||||
"prapogation",
|
||||
"precisionmetrics",
|
||||
"prefetch",
|
||||
"Prescaled",
|
||||
"pydicom",
|
||||
"Radiopharmaceutical",
|
||||
"rasterizing",
|
||||
"reconstructable",
|
||||
"Rehydratable",
|
||||
"renderable",
|
||||
"resampler",
|
||||
"resemblejs",
|
||||
"reslice",
|
||||
"resliced",
|
||||
"Reslices",
|
||||
"roadmap",
|
||||
"ROADMAPS",
|
||||
"rtstruct",
|
||||
"Segmentations",
|
||||
"semibold",
|
||||
"sitk",
|
||||
"SUBRESOLUTION",
|
||||
"suvbsa",
|
||||
"suvbw",
|
||||
"suvlbm",
|
||||
"textbox",
|
||||
"thresholded",
|
||||
"thresholding",
|
||||
"timepoint",
|
||||
"timepoints",
|
||||
"TMTV",
|
||||
"TOOLGROUP",
|
||||
"tqdm",
|
||||
"transferables",
|
||||
"typedoc",
|
||||
"unsubscriptions",
|
||||
"uuidv",
|
||||
"viewplane",
|
||||
"viewports",
|
||||
"Voxel",
|
||||
"Voxels",
|
||||
"Vtkjs",
|
||||
"wado",
|
||||
"wadors",
|
||||
"wadouri",
|
||||
"workerpool",
|
||||
"Colorbar",
|
||||
"Colorbars"
|
||||
]
|
||||
"source.fixAll.eslint": true
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,11 @@ function excludeNodeModulesExcept(modules) {
|
||||
if (pathSep == '\\')
|
||||
// must be quoted for use in a regexp:
|
||||
pathSep = '\\\\';
|
||||
var moduleRegExps = modules.map(function (modName) {
|
||||
var moduleRegExps = modules.map(function(modName) {
|
||||
return new RegExp('node_modules' + pathSep + modName);
|
||||
});
|
||||
|
||||
return function (modulePath) {
|
||||
return function(modulePath) {
|
||||
if (/node_modules/.test(modulePath)) {
|
||||
for (var i = 0; i < moduleRegExps.length; i++)
|
||||
if (moduleRegExps[i].test(modulePath)) return false;
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
const path = require('path');
|
||||
|
||||
// Shared module-resolution rules (alias + module search paths) used by BOTH
|
||||
// build pipelines so they cannot drift apart:
|
||||
// - the webpack/rspack build: webpack.base.js -> webpack.pwa.js, plus every
|
||||
// per-package webpack.prod.js / webpack.dev.js that merges webpack.base.js
|
||||
// - the rsbuild build: rsbuild.config.ts (dev:fast)
|
||||
//
|
||||
// Plugin aliases (writePluginImportsFile.getPluginResolveAliases) are NOT
|
||||
// included here: they depend on pluginConfig.json and are merged in separately
|
||||
// by each config.
|
||||
//
|
||||
// All paths are anchored to this file's location (the repo-root `.webpack/`
|
||||
// directory), so the values are identical regardless of which package's build
|
||||
// is doing the resolving.
|
||||
|
||||
const alias = {
|
||||
// Some extensions import app-level utilities (e.g. history,
|
||||
// preserveQueryParameters) from '@ohif/app'. pnpm's isolated layout does not
|
||||
// expose the top-level app package to those extensions, and adding it as a
|
||||
// workspace dependency would create an app<->default cycle, so we resolve the
|
||||
// bare specifier to the app source here ($ = exact match).
|
||||
'@ohif/app$': path.resolve(__dirname, '../platform/app/src/index.js'),
|
||||
'@': path.resolve(__dirname, '../platform/app/src'),
|
||||
'@components': path.resolve(__dirname, '../platform/app/src/components'),
|
||||
'@hooks': path.resolve(__dirname, '../platform/app/src/hooks'),
|
||||
'@routes': path.resolve(__dirname, '../platform/app/src/routes'),
|
||||
'@state': path.resolve(__dirname, '../platform/app/src/state'),
|
||||
};
|
||||
|
||||
// Directories to search when resolving modules. The leading bare 'node_modules'
|
||||
// preserves the default importer-relative walk-up, which pnpm's isolated layout
|
||||
// requires so that transitive deps (e.g. react-remove-scroll -> tslib 2.x)
|
||||
// resolve to the sibling copy inside .pnpm/<pkg>/node_modules rather than a
|
||||
// hoisted older version.
|
||||
const moduleSearchPaths = [
|
||||
'node_modules',
|
||||
path.resolve(__dirname, '../node_modules'),
|
||||
path.resolve(__dirname, '../../../node_modules'),
|
||||
path.resolve(__dirname, '../platform/app/node_modules'),
|
||||
path.resolve(__dirname, '../platform/ui/node_modules'),
|
||||
];
|
||||
|
||||
// Build the resolve.modules array for a build. `srcDir` is the building
|
||||
// package's source root; it is appended last (matching the previous inline
|
||||
// behavior in webpack.base.js). Pass nothing to get just the shared paths.
|
||||
function getModules(srcDir) {
|
||||
return srcDir ? [...moduleSearchPaths, srcDir] : [...moduleSearchPaths];
|
||||
}
|
||||
|
||||
module.exports = { alias, moduleSearchPaths, getModules };
|
||||
@@ -1,26 +1,14 @@
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const path = require('path');
|
||||
const tailwindcss = require('tailwindcss');
|
||||
const tailwindConfigPath = path.resolve('../../platform/app/tailwind.config.js');
|
||||
const rspack = require('@rspack/core');
|
||||
const devMode = process.env.NODE_ENV !== 'production';
|
||||
|
||||
const cssToJavaScript = {
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
//'style-loader',
|
||||
devMode ? 'style-loader' : rspack.CssExtractRspackPlugin.loader,
|
||||
'style-loader',
|
||||
{ loader: 'css-loader', options: { importLoaders: 1 } },
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
postcssOptions: {
|
||||
verbose: true,
|
||||
plugins: [
|
||||
[tailwindcss(tailwindConfigPath)],
|
||||
[autoprefixer('last 2 version', 'ie >= 11')],
|
||||
],
|
||||
},
|
||||
plugins: () => [autoprefixer('last 2 version', 'ie >= 11')],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -4,8 +4,7 @@ function transpileJavaScript(mode) {
|
||||
const exclude =
|
||||
mode === 'production'
|
||||
? excludeNodeModulesExcept([
|
||||
// Workspace packages (needed for pnpm shamefully-hoist where they resolve through node_modules)
|
||||
'@ohif',
|
||||
'vtk.js',
|
||||
// 'dicomweb-client',
|
||||
// https://github.com/react-dnd/react-dnd/blob/master/babel.config.js
|
||||
'react-dnd',
|
||||
@@ -22,24 +21,17 @@ function transpileJavaScript(mode) {
|
||||
: excludeNodeModulesExcept([]);
|
||||
|
||||
return {
|
||||
// Include mjs, ts, tsx, js, and jsx files.
|
||||
test: /\.(mjs|ts|js)x?$/,
|
||||
test: /\.jsx?$/,
|
||||
// These are packages that are not transpiled to our lowest supported
|
||||
// JS version (currently ES5). Most of these leverage ES6+ features,
|
||||
// that we need to transpile to a different syntax.
|
||||
exclude: [/(codecs)/, /(dicomicc)/, exclude],
|
||||
exclude,
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
// Find babel.config.js in monorepo root
|
||||
// https://babeljs.io/docs/en/options#rootmode
|
||||
rootMode: 'upward',
|
||||
envName: mode,
|
||||
cacheCompression: false,
|
||||
// Note: This was causing a lot of issues with yarn link of the cornerstone
|
||||
// only set this to true if you don't have a yarn link to external libs
|
||||
// otherwise expect the lib changes not to be reflected in the dev server
|
||||
// as it will be cached
|
||||
cacheDirectory: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,94 +2,39 @@
|
||||
const dotenv = require('dotenv');
|
||||
//
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const webpack = require('@rspack/core');
|
||||
|
||||
// ~~ PLUGINS
|
||||
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
|
||||
// ~~ PackageJSON
|
||||
// const vtkRules = require('vtk.js/Utilities/config/dependency.js').webpack.core
|
||||
// .rules;
|
||||
const webpack = require('webpack');
|
||||
// ~~ RULES
|
||||
// const loadShadersRule = require('./rules/loadShaders.js');
|
||||
const loadShadersRule = require('./rules/loadShaders.js');
|
||||
const loadWebWorkersRule = require('./rules/loadWebWorkers.js');
|
||||
const transpileJavaScriptRule = require('./rules/transpileJavaScript.js');
|
||||
const cssToJavaScript = require('./rules/cssToJavaScript.js');
|
||||
// Module-resolution rules shared with the rsbuild build (see rsbuild.config.ts).
|
||||
const resolveConfig = require('./resolveConfig');
|
||||
// Only uncomment for old v2 stylus
|
||||
// const stylusToJavaScript = require('./rules/stylusToJavaScript.js');
|
||||
let ReactRefreshWebpackPlugin;
|
||||
try {
|
||||
const mod = require('@rspack/plugin-react-refresh');
|
||||
ReactRefreshWebpackPlugin = mod.ReactRefreshRspackPlugin || mod.default || mod;
|
||||
} catch { ReactRefreshWebpackPlugin = null; }
|
||||
|
||||
// ~~ PLUGINS
|
||||
const TerserJSPlugin = require('terser-webpack-plugin');
|
||||
// ~~ ENV VARS
|
||||
const NODE_ENV = process.env.NODE_ENV;
|
||||
const QUICK_BUILD = process.env.QUICK_BUILD;
|
||||
const BUILD_NUM = process.env.CIRCLE_BUILD_NUM || '0';
|
||||
const IS_COVERAGE = process.env.COVERAGE === 'true';
|
||||
|
||||
// read from ../version.txt
|
||||
const VERSION_NUMBER = fs.readFileSync(path.join(__dirname, '../version.txt'), 'utf8') || '';
|
||||
|
||||
const COMMIT_HASH = fs.readFileSync(path.join(__dirname, '../commit.txt'), 'utf8') || '';
|
||||
|
||||
//
|
||||
dotenv.config();
|
||||
|
||||
const defineValues = {
|
||||
/* Application */
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||
'process.env.NODE_DEBUG': JSON.stringify(process.env.NODE_DEBUG),
|
||||
'process.env.DEBUG': JSON.stringify(process.env.DEBUG),
|
||||
'process.env.PUBLIC_URL': JSON.stringify(process.env.PUBLIC_URL || '/'),
|
||||
'process.env.BUILD_NUM': JSON.stringify(BUILD_NUM),
|
||||
'process.env.VERSION_NUMBER': JSON.stringify(VERSION_NUMBER),
|
||||
'process.env.COMMIT_HASH': JSON.stringify(COMMIT_HASH),
|
||||
/* i18n */
|
||||
'process.env.USE_LOCIZE': JSON.stringify(process.env.USE_LOCIZE || ''),
|
||||
'process.env.LOCIZE_PROJECTID': JSON.stringify(process.env.LOCIZE_PROJECTID || ''),
|
||||
'process.env.LOCIZE_API_KEY': JSON.stringify(process.env.LOCIZE_API_KEY || ''),
|
||||
'process.env.REACT_APP_I18N_DEBUG': JSON.stringify(process.env.REACT_APP_I18N_DEBUG || ''),
|
||||
'process.env.TEST_ENV': JSON.stringify(process.env.TEST_ENV || ''),
|
||||
};
|
||||
module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
||||
if (!process.env.NODE_ENV) {
|
||||
throw new Error('process.env.NODE_ENV not set');
|
||||
}
|
||||
|
||||
// Only redefine updated values. This avoids warning messages in the logs
|
||||
if (!process.env.APP_CONFIG) {
|
||||
defineValues['process.env.APP_CONFIG'] = '';
|
||||
}
|
||||
|
||||
module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
|
||||
const mode = NODE_ENV === 'production' ? 'production' : 'development';
|
||||
const isProdBuild = NODE_ENV === 'production';
|
||||
const isQuickBuild = QUICK_BUILD === 'true';
|
||||
|
||||
const config = {
|
||||
mode: isProdBuild ? 'production' : 'development',
|
||||
devtool: isProdBuild ? 'source-map' : 'cheap-module-source-map',
|
||||
// `rspack serve` (@rspack/cli) auto-enables lazyCompilation for web-only
|
||||
// apps unless the config defines it explicitly. The on-demand proxy chunks
|
||||
// it produces fail to load in the headless cypress/electron e2e run
|
||||
// (ChunkLoadError on cornerstone vendor chunks), so disable it here to match
|
||||
// the rsbuild build (see rsbuild.config.ts `dev.lazyCompilation: false`).
|
||||
lazyCompilation: false,
|
||||
entry: ENTRY,
|
||||
optimization: {
|
||||
// splitChunks: {
|
||||
// // include all types of chunks
|
||||
// chunks: 'all',
|
||||
// },
|
||||
//runtimeChunk: 'single',
|
||||
minimize: isProdBuild,
|
||||
sideEffects: false,
|
||||
devtool: isProdBuild ? 'source-map' : 'cheap-module-eval-source-map',
|
||||
entry: {
|
||||
app: `${SRC_DIR}/index.js`,
|
||||
},
|
||||
output: {
|
||||
// clean: true,
|
||||
publicPath: '/',
|
||||
optimization: {
|
||||
minimize: isProdBuild,
|
||||
sideEffects: true,
|
||||
},
|
||||
context: SRC_DIR,
|
||||
stats: {
|
||||
@@ -103,149 +48,62 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
|
||||
children: false,
|
||||
warnings: true,
|
||||
},
|
||||
cache: isProdBuild ? false : { type: 'filesystem' },
|
||||
module: {
|
||||
noParse: [/(dicomicc)/],
|
||||
rules: [
|
||||
...(isProdBuild
|
||||
? []
|
||||
: [
|
||||
...(IS_COVERAGE
|
||||
? [
|
||||
{
|
||||
test: /\.[jt]sx?$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['@babel/preset-typescript', '@babel/preset-react'],
|
||||
plugins: ['istanbul'],
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
test: /\.[jt]sx?$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
plugins: isProdBuild ? [] : ['react-refresh/babel'],
|
||||
},
|
||||
},
|
||||
]),
|
||||
]),
|
||||
{
|
||||
test: /\.svg?$/,
|
||||
oneOf: [
|
||||
{
|
||||
use: [
|
||||
{
|
||||
loader: '@svgr/webpack',
|
||||
options: {
|
||||
svgoConfig: {
|
||||
plugins: [
|
||||
{
|
||||
name: 'preset-default',
|
||||
params: {
|
||||
overrides: {
|
||||
removeViewBox: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
prettier: false,
|
||||
svgo: true,
|
||||
titleProp: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
issuer: {
|
||||
and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
transpileJavaScriptRule(mode),
|
||||
loadWebWorkersRule,
|
||||
// loadShadersRule,
|
||||
{
|
||||
test: /\.m?js/,
|
||||
resolve: {
|
||||
fullySpecified: false,
|
||||
},
|
||||
},
|
||||
cssToJavaScript,
|
||||
{
|
||||
test: /\.wasm/,
|
||||
type: 'asset/resource',
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg)$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: 'assets/images/[name].[ext]',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(woff|woff2|eot|ttf|otf)$/i,
|
||||
type: 'asset/resource',
|
||||
},
|
||||
], //.concat(vtkRules),
|
||||
loadShadersRule,
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
mainFields: ['module', 'browser', 'main'],
|
||||
// alias and modules are shared with the rsbuild build via ./resolveConfig
|
||||
// so the two pipelines resolve identically.
|
||||
alias: {
|
||||
...resolveConfig.alias,
|
||||
},
|
||||
modules: resolveConfig.getModules(SRC_DIR),
|
||||
// Which directories to search when resolving modules
|
||||
modules: [
|
||||
// Modules specific to this package
|
||||
path.resolve(__dirname, '../node_modules'),
|
||||
// Hoisted Yarn Workspace Modules
|
||||
path.resolve(__dirname, '../../../node_modules'),
|
||||
SRC_DIR,
|
||||
],
|
||||
// Attempt to resolve these extensions in order.
|
||||
extensions: ['.js', '.jsx', '.json', '.ts', '.tsx', '*'],
|
||||
// Workspace packages use relative imports between sibling packages.
|
||||
// Resolve symlinks to keep those imports anchored at the real package paths.
|
||||
extensions: ['.js', '.jsx', '.json', '*'],
|
||||
// symlinked resources are resolved to their real path, not their symlinked location
|
||||
symlinks: true,
|
||||
fallback: {
|
||||
fs: false,
|
||||
path: false,
|
||||
zlib: false,
|
||||
buffer: require.resolve('buffer'),
|
||||
},
|
||||
},
|
||||
node: {
|
||||
// Leave __filename / __dirname references alone. The previous 'mock'
|
||||
// value triggers an rspack warning whenever bundled deps reference
|
||||
// __dirname (e.g. Emscripten-compiled cornerstone codecs). Those refs
|
||||
// sit inside `if (ENVIRONMENT_IS_NODE)` branches that never execute in
|
||||
// the browser, so leaving them un-substituted is harmless at runtime.
|
||||
__filename: false,
|
||||
__dirname: false,
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin(defineValues),
|
||||
new webpack.ProvidePlugin({
|
||||
Buffer: ['buffer', 'Buffer'],
|
||||
new webpack.DefinePlugin({
|
||||
/* Application */
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||
'process.env.DEBUG': JSON.stringify(process.env.DEBUG),
|
||||
'process.env.APP_CONFIG': JSON.stringify(process.env.APP_CONFIG || ''),
|
||||
'process.env.PUBLIC_URL': JSON.stringify(process.env.PUBLIC_URL || '/'),
|
||||
'process.env.VERSION_NUMBER': webpack.DefinePlugin.runtimeValue(() => {
|
||||
const package = require('../platform/viewer/package.json');
|
||||
return JSON.stringify(package.version || '');
|
||||
}, ['../platform/viewer/package.json']),
|
||||
'process.env.BUILD_NUM': JSON.stringify(BUILD_NUM),
|
||||
/* i18n */
|
||||
'process.env.USE_LOCIZE': JSON.stringify(process.env.USE_LOCIZE || ''),
|
||||
'process.env.LOCIZE_PROJECTID': JSON.stringify(process.env.LOCIZE_PROJECTID || ''),
|
||||
'process.env.LOCIZE_API_KEY': JSON.stringify(process.env.LOCIZE_API_KEY || ''),
|
||||
}),
|
||||
new webpack.IgnorePlugin({
|
||||
resourceRegExp: /^(fs|path)$/,
|
||||
contextRegExp: /@cornerstonejs[\\/]codec-/,
|
||||
}),
|
||||
...(isProdBuild || IS_COVERAGE || !ReactRefreshWebpackPlugin
|
||||
? []
|
||||
: [new ReactRefreshWebpackPlugin({ overlay: false })]),
|
||||
// Uncomment to generate bundle analyzer
|
||||
// new BundleAnalyzerPlugin(),
|
||||
],
|
||||
// Fix: https://github.com/webpack-contrib/css-loader/issues/447#issuecomment-285598881
|
||||
// For issue in cornerstone-wado-image-loader
|
||||
node: {
|
||||
fs: 'empty',
|
||||
},
|
||||
};
|
||||
|
||||
if (isProdBuild) {
|
||||
config.optimization.minimizer = [new webpack.SwcJsMinimizerRspackPlugin()];
|
||||
config.optimization.minimizer = [
|
||||
new TerserJSPlugin({
|
||||
// Supports:
|
||||
// source-map and inline-source-map
|
||||
sourceMap: isProdBuild && !isQuickBuild,
|
||||
parallel: true,
|
||||
terserOptions: {},
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
if (isQuickBuild) {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
const merge = require('webpack-merge');
|
||||
const webpackBase = require('./webpack.base.js');
|
||||
const cssToJavaScriptRule = require('./rules/cssToJavaScript.js');
|
||||
const stylusToJavaScriptRule = require('./rules/stylusToJavaScript.js');
|
||||
|
||||
/**
|
||||
* WebPack configuration for CommonJS Bundles. Extends rules of BaseConfig by making
|
||||
* sure we're bundling styles and other files that would normally be split in a
|
||||
* PWA.
|
||||
*/
|
||||
module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
||||
const baseConfig = webpackBase(env, argv, { SRC_DIR, DIST_DIR });
|
||||
|
||||
return merge(baseConfig, {
|
||||
module: {
|
||||
rules: [cssToJavaScriptRule, stylusToJavaScriptRule],
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,201 +0,0 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file provides guidance to AI coding agents (Claude, Codex, and other LLM tools) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is **OHIF** v3 (Open Health Imaging Foundation) - a medical imaging viewer. It's an extensible web imaging platform.
|
||||
## Development Commands
|
||||
|
||||
### Main Development
|
||||
```bash
|
||||
# Start development server for all packages
|
||||
yarn dev
|
||||
```
|
||||
|
||||
### Building
|
||||
```bash
|
||||
# Build all packages for production
|
||||
yarn build
|
||||
|
||||
# Build specific packages
|
||||
cd platform/app && yarn build # Main viewer app
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Monorepo Structure
|
||||
- **`platform/`** - Core OHIF infrastructure
|
||||
- `app/` - Main viewer application (`@ohif/viewer`)
|
||||
- `core/` - Core services and utilities
|
||||
- `ui-next/` - Modern UI component library
|
||||
- **`extensions/`** - Modular functionality plugins
|
||||
- **`modes/`** - Application workflow configurations
|
||||
|
||||
### Key Extension Architecture
|
||||
|
||||
**Extension System**: Each extension exports modules (viewports, tools, panels, commands) that the app dynamically loads. Extensions are self-contained with their own webpack builds.
|
||||
|
||||
|
||||
**Core Extensions:**
|
||||
- `cornerstone/` - Medical image rendering engine
|
||||
- `cornerstone-dicom-pmp/` - DICOM PMP support
|
||||
- `cornerstone-dicom-seg/` - DICOM Segmentation support
|
||||
- `cornerstone-dicom-sr/` - DICOM SR support
|
||||
- `dicom-pdf/` - DICOM PDF support
|
||||
- `dicom-video/` - DICOM Video support
|
||||
- `measurement-tracking/` - Measurement tracking support
|
||||
- `default/` - Standard OHIF functionality
|
||||
|
||||
### Service-Oriented Design (PUB-SUB)
|
||||
|
||||
The app uses a Services Manager pattern with these core services:
|
||||
- **Display Set Service**: Manages image series organization
|
||||
- **Measurement Service**: Handles annotations and measurements
|
||||
- **Hanging Protocol Service**: Controls image layout and display rules
|
||||
- **UI Service**: Manages panels, modals, and notifications
|
||||
- **Segmentation Service**: AI/ML powered image segmentation, loading segmentations, etc.
|
||||
- **Viewport Grid Service**: Manages viewport layout and display rules
|
||||
- **Viewport Display Set History Service**: Manages viewport display set history
|
||||
- **Viewport Dialog Service**: Manages viewport dialogs
|
||||
- **Notification Service**: Manages notifications
|
||||
- **Modal Service**: Manages modals
|
||||
- **Dialog Service**: Manages dialogs, more general not just viewport dialogs
|
||||
- **Customization Service**: Manages customization of the app
|
||||
- **Toolbar Service**: Manages the toolbar, viewport action corners, tool states
|
||||
- **User Authentication Service**: Manages user authentication, but used only for injecting tokens in dicomweb requests in our context
|
||||
- **Panel Service**: Manages side panels
|
||||
- **Cornerstone Viewport Service**: Manages the cornerstone viewport, rendering engines, presentation states, more tightly coupled to cornerstone than the other services
|
||||
- **Tool Group Service**: Manages tool groups, creating and managing tool groups, etc.
|
||||
- **Sync Group Service**: Manages sync groups, syncing zooming, panning, scrolling, etc.
|
||||
- **Cornerstone Cache Service**: Manages the cornerstone cache, caching images, etc.
|
||||
|
||||
Most of the services utilize a pub sub architecture and extend the pub sub service interace at `pubSubServiceInterface.ts`
|
||||
|
||||
### Commands Manager
|
||||
|
||||
The Commands Manager tracks named commands (or functions) that are scoped to
|
||||
a context. When we attempt to run a command with a given name, we look for it
|
||||
in our active contexts, in the order specified.
|
||||
If found, we run the command, passing in any application
|
||||
or call specific data specified in the command's definition.
|
||||
|
||||
You can call `commandsManager.runCommand` to run a command.
|
||||
|
||||
### Extension Manager
|
||||
|
||||
Aggregates and exposes extension modules throughout the OHIF application, manages data sources, and provides a centralized registry for accessing extension functionality.
|
||||
|
||||
### Build System
|
||||
|
||||
**Yarn Workspaces**: Optimized monorepo builds with dependency caching
|
||||
**Webpack 5**: Module federation for dynamic extension loading
|
||||
**Plugin Import System**: Extensions auto-register via `writePluginImportsFile.js`
|
||||
|
||||
### Key Technologies
|
||||
|
||||
- **React 18 + TypeScript**: UI framework
|
||||
- **Cornerstone.js**: Medical image rendering
|
||||
- **DICOM**: Medical imaging standard support
|
||||
- **ONNX Runtime**: AI model inference (SAM segmentation models)
|
||||
- **Zustand**: State management
|
||||
- **TailwindCSS**: Styling system
|
||||
|
||||
## Development Patterns
|
||||
|
||||
### Adding New Tools
|
||||
1. Create tool class in `extensions/cornerstone/src/tools/`
|
||||
2. Register in tool module's `toolNames.ts`
|
||||
3. Add to toolbar via `getToolbarModule.tsx`
|
||||
4. Add measurement mapping if needed in `measurementServiceMappings/`
|
||||
|
||||
### Creating Extensions
|
||||
Extensions must export:
|
||||
- `id.js` - Unique extension identifier
|
||||
- `index.tsx` - Extension registration
|
||||
- Module functions (`getToolbarModule`, `getViewportModule`, etc.)
|
||||
|
||||
### Viewport Customization
|
||||
Custom viewports extend base Cornerstone viewport:
|
||||
- Override render methods for custom overlays
|
||||
- Implement measurement tracking
|
||||
- Add viewport-specific tools and interactions
|
||||
|
||||
### Service Integration
|
||||
Register services in extension's `servicesManager.registerService` and access via:
|
||||
```javascript
|
||||
const { MeasurementService } = servicesManager.services;
|
||||
```
|
||||
|
||||
### Creating stores
|
||||
To create a store, you can make one in your extension's `stores/` directory, and you can follow the example of an existing store such as `useLutPresentationStore.ts` or `useSynchronizersStore.ts`.
|
||||
|
||||
### Creating hooks
|
||||
To create a hook, you can make one in your extension's `hooks/` directory, and you can follow the example of an existing hook such as `usePatientInfo.tsx`.
|
||||
|
||||
### Creating providers
|
||||
To create a provider, you can make one in your extension's `providers/` or `contexts/` directory, and you can follow the example of an existing provider such as `ViewportGridProvider.tsx`.
|
||||
|
||||
### Adding new icons
|
||||
To add a new icon, you can add it to the `icons/` directory, then register the icon using `import { addIcon } from '@ohif/extension-default/src/utils'`
|
||||
|
||||
### Creating synchronizers
|
||||
You can create custom synchronizers and place them in the `synchronizers/` directory, you can follow the example of `frameViewSynchronizer.ts`
|
||||
|
||||
### Utilites
|
||||
Any new utilites should be placed in the `utils/` directory, and you can follow the example of `formatPN.ts`
|
||||
|
||||
### Commands
|
||||
Commands are created in the commandsModule of the extension, for example the cornerstone extension has `commandsModule.tsx`, sometimes its also named `getCommandsModule.tsx.`
|
||||
|
||||
### Overriding OHIF Components
|
||||
|
||||
To override an OHIF component, you can create a new component in your extension's `components/` directory, then import it instead of the original ui-next component.
|
||||
|
||||
### Mode layout
|
||||
|
||||
The layoutTemplate is a function that returns a layout object, you can follow the example of `longitudinal/src/index.ts`. This would be helpful when you need to override a component as you can know where to look for the original component.
|
||||
|
||||
### Pub Sub
|
||||
Always prioritrize pub sub, by calling a services subscribe over useEffects as it's more reliable, for example
|
||||
|
||||
```ts
|
||||
useEffect(() => {
|
||||
const subscriptions = [
|
||||
cornerstoneViewportService.subscribe(EVENTS.VIEWPORT_DATA_CHANGED, handleViewportDataChanged),
|
||||
syncGroupService.subscribe(EVENTS.VIEWPORT_REMOVED, onHotKeyRemoval),
|
||||
syncGroupService.subscribe(EVENTS.VIEWPORT_ADDED, onHotKeyAddition),
|
||||
];
|
||||
|
||||
return () => {
|
||||
subscriptions.forEach(({ unsubscribe }) => unsubscribe());
|
||||
};
|
||||
}, []);
|
||||
```
|
||||
|
||||
### Never modify core architecture
|
||||
Do not modify the core and always find a way to implement the solution via the extensions and modes, only modify core as a last resort if all other fail or there's an architectural constraint.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Plugin Configuration
|
||||
Extensions are auto-discovered via `pluginConfig.json` and dynamically imported during build.
|
||||
|
||||
## Medical Imaging Specifics
|
||||
|
||||
### DICOM Support
|
||||
- Multi-format: CT, MRI, X-Ray, Mammography, Ultrasound
|
||||
- SOP Class handlers for specialized DICOM types (RT, SEG, SR)
|
||||
- DICOMweb protocol for web-based image retrieval
|
||||
|
||||
### Hanging Protocols
|
||||
Define how images are arranged and displayed:
|
||||
- Located in `hps/` directories
|
||||
- JSON configuration with viewport rules
|
||||
- Support for priors comparison and multi-monitor layouts
|
||||
|
||||
### Measurement Tools
|
||||
- Cornerstone Tools integration for annotations
|
||||
- Bidirectional measurements, polylines, annotations
|
||||
- Export capabilities (DICOM SR, CSV reports)
|
||||
- AI-assisted measurements via ONNX models
|
||||
@@ -0,0 +1,24 @@
|
||||
cff-version: 1.2.0
|
||||
message: "If you use this software, please cite it as below."
|
||||
authors:
|
||||
- family-names: "Ziegler"
|
||||
given-names: "Erik"
|
||||
orcid: "https://orcid.org/0000-0003-1857-8129"
|
||||
- family-names: "Urban"
|
||||
given-names: "Trinity"
|
||||
- family-names: "Brown"
|
||||
given-names: "Danny"
|
||||
- family-names: "Petts"
|
||||
given-names: "James"
|
||||
- family-names: "Pieper"
|
||||
given-names: "Steve D"
|
||||
- family-names: "Lewis"
|
||||
given-names: "Rob"
|
||||
- family-names: "Hafey"
|
||||
given-names: "Chris"
|
||||
- family-names: "Harris"
|
||||
given-names: "Gordon J"
|
||||
title: "Open Health Imaging Foundation Viewer: An Extensible Open-Source Framework for Building Web-Based Imaging Applications to Support Cancer Research"
|
||||
doi: 10.1200/CCI.19.00131
|
||||
version: 1.0.0
|
||||
url: "https://github.com/OHIF/Viewers"
|
||||
@@ -1,297 +0,0 @@
|
||||
# OHIF public demo data sets
|
||||
|
||||
The OHIF Viewer's public demo page, available at https://viewer.ohif.org/, uses publicly anonymized demo datasets.
|
||||
These datasets were mostly obtained from the [NIH NCI Imaging Data Commons](https://datacommons.cancer.gov/repository/imaging-data-commons)
|
||||
and [NIH NCI TCIA](https://www.cancerimagingarchive.net/). Before listing the datasets,
|
||||
we would like to extend a special thank you to all groups who have made their datasets publicly available.
|
||||
Without them, we would not have been able to create this demo page.
|
||||
|
||||
Please find below the list of datasets used on the demo page, along with their respective citations.
|
||||
|
||||
|
||||
## Platforms
|
||||
|
||||
### NIH NCI IDC
|
||||
|
||||
- Fedorov, A., Longabaugh, W.J., Pot, D., Clunie, D.A., Pieper, S., Aerts, H.J., Homeyer, A., Lewis, R., Akbarzadeh, A., Bontempi, D. and Clifford, W., 2021. NCI imaging data commons. Cancer research, 81(16), p.4188.
|
||||
|
||||
### NIH NCI TCIA
|
||||
|
||||
- Clark, K., Vendt, B., Smith, K., Freymann, J., Kirby, J., Koppel, P., Moore, S., Phillips, S., Maffitt, D., Pringle, M., Tarbox, L., & Prior, F. (2013). The Cancer Imaging Archive (TCIA): Maintaining and Operating a Public Information Repository. Journal of Digital Imaging, 26(6), 1045–1057. https://doi.org/10.1007/s10278-013-9622-7
|
||||
|
||||
|
||||
|
||||
|
||||
## Datasets
|
||||
Below you can find the StudyInstanceUID of the studies that are used in the demo page along with their citations.
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.267424821384663813780850856506829388886
|
||||
|
||||
Segmentation of Vestibular Schwannoma from Magnetic Resonance Imaging: An Open Annotated Dataset and Baseline Algorithm (Vestibular-Schwannoma-SEG)
|
||||
|
||||
- Shapey, J., Kujawa, A., Dorent, R., Wang, G., Bisdas, S., Dimitriadis, A., Grishchuck, D., Paddick, I., Kitchen, N., Bradford, R., Saeed, S., Ourselin, S., & Vercauteren, T. (2021). Segmentation of Vestibular Schwannoma from Magnetic Resonance Imaging: An Open Annotated Dataset and Baseline Algorithm [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/TCIA.9YTJ-5Q73
|
||||
|
||||
- Shapey, J., Kujawa, A., Dorent, R., Wang, G., Dimitriadis, A., Grishchuk, D., Paddick, I., Kitchen, N., Bradford, R., Saeed, S. R., Bisdas, S., Ourselin, S., & Vercauteren, T. (2021). Segmentation of vestibular schwannoma from MRI, an open annotated dataset and baseline algorithm. In Scientific Data (Vol. 8, Issue 1). Springer Science and Business Media LLC. https://doi.org/10.1038/s41597-021-01064-w
|
||||
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463
|
||||
### 1.3.6.1.4.1.14519.5.2.1.7009.2403.871108593056125491804754960339
|
||||
|
||||
|
||||
ACRIN-NSCLC-FDG-PET (ACRIN 6668)
|
||||
|
||||
- Kinahan, P., Muzi, M., Bialecki, B., Herman, B., & Coombs, L. (2019). Data from the ACRIN 6668 Trial NSCLC-FDG-PET (Version 2) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/tcia.2019.30ilqfcl
|
||||
|
||||
- Machtay, M., Duan, F., Siegel, B. A., Snyder, B. S., Gorelick, J. J., Reddin, J. S., Munden, R., Johnson, D. W., Wilf, L. H., DeNittis, A., Sherwin, N., Cho, K. H., Kim, S., Videtic, G., Neumann, D. R., Komaki, R., Macapinlac, H., Bradley, J. D., & Alavi, A. (2013). Prediction of Survival by [18F]Fluorodeoxyglucose Positron Emission Tomography in Patients With Locally Advanced Non–Small-Cell Lung Cancer Undergoing Definitive Chemoradiation Therapy: Results of the ACRIN 6668/RTOG 0235 Trial. In Journal of Clinical Oncology (Vol. 31, Issue 30, pp. 3823–3830). American Society of Clinical Oncology (ASCO). https://doi.org/10.1200/jco.2012.47.5947
|
||||
|
||||
|
||||
### 2.25.103659964951665749659160840573802789777
|
||||
|
||||
The Cancer Genome Atlas Glioblastoma Multiforme Collection (TCGA-GBM)
|
||||
|
||||
- Scarpace, L., Mikkelsen, T., Cha, S., Rao, S., Tekchandani, S., Gutman, D., Saltz, J. H., Erickson, B. J., Pedano, N., Flanders, A. E., Barnholtz-Sloan, J., Ostrom, Q., Barboriak, D., & Pierce, L. J. (2016). The Cancer Genome Atlas Glioblastoma Multiforme Collection (TCGA-GBM) (Version 4) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/K9/TCIA.2016.RNYFUYE9
|
||||
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.256467663913010332776401703474716742458
|
||||
|
||||
Abdominal or pelvic enhanced CT images within 10 days before surgery of 230 patients with stage II colorectal cancer (StageII-Colorectal-CT)
|
||||
|
||||
|
||||
- Tong T., Li M. (2022) Abdominal or pelvic enhanced CT images within 10 days before surgery of 230 patients with stage II colorectal cancer (StageII-Colorectal-CT) [Dataset]. The Cancer Imaging Archive. DOI: https://doi.org/10.7937/p5k5-tg43
|
||||
|
||||
- Li, M., Gong, J., Bao, Y., Huang, D., Peng, J., & Tong, T. (2022). Special issue “The advance of solid tumor research in China”: Prognosis prediction for stage II colorectal cancer by fusing computed tomography radiomics and deep‐learning features of primary lesions and peripheral lymph nodes. In International Journal of Cancer. Wiley. https://doi.org/10.1002/ijc.34053
|
||||
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.3023.4024.215308722288168917637555384485
|
||||
|
||||
The Cancer Genome Atlas Sarcoma Collection (TCGA-SARC)
|
||||
|
||||
- Roche, C., Bonaccio, E., & Filippini, J. (2016). The Cancer Genome Atlas Sarcoma Collection (TCGA-SARC) (Version 3) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/K9/TCIA.2016.CX6YLSUX
|
||||
|
||||
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.4792.2001.105216574054253895819671475627
|
||||
|
||||
BREAST-DIAGNOSIS
|
||||
|
||||
|
||||
- Bloch, B. Nicolas, Jain, Ashali, & Jaffe, C. Carl. (2015). BREAST-DIAGNOSIS [Data set]. The Cancer Imaging Archive. http://doi.org/10.7937/K9/TCIA.2015.SDNRQXXR
|
||||
|
||||
|
||||
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.1706.8374.643249677828306008300337414785
|
||||
|
||||
Multimodality annotated HCC cases with and without advanced imaging segmentation (HCC-TACE-Seg)
|
||||
|
||||
|
||||
- Moawad, A. W., Fuentes, D., Morshid, A., Khalaf, A. M., Elmohr, M. M., Abusaif, A., Hazle, J. D., Kaseb, A. O., Hassan, M., Mahvash, A., Szklaruk, J., Qayyom, A., & Elsayes, K. (2021). Multimodality annotated HCC cases with and without advanced imaging segmentation [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/TCIA.5FNA-0924
|
||||
|
||||
- Morshid, A., Elsayes, K. M., Khalaf, A. M., Elmohr, M. M., Yu, J., Kaseb, A. O., Hassan, M., Mahvash, A., Wang, Z., Hazle, J. D., & Fuentes, D. (2019). A Machine Learning Model to Predict Hepatocellular Carcinoma Response to Transcatheter Arterial Chemoembolization. Radiology: Artificial Intelligence, 1(5), e180021. https://doi.org/10.1148/ryai.2019180021
|
||||
|
||||
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.1188.2803.137585363493444318569098508293
|
||||
|
||||
Ultrasound data of a variety of liver masses (B-mode-and-CEUS-Liver)
|
||||
|
||||
- Eisenbrey, J., Lyshchik, A., & Wessner, C. (2021). Ultrasound data of a variety of liver masses [Data set]. The Cancer Imaging Archive. DOI: https://doi.org/10.7937/TCIA.2021.v4z7-tc39
|
||||
|
||||
|
||||
|
||||
### 1.3.6.1.4.1.32722.99.99.62087908186665265759322018723889952421
|
||||
|
||||
NSCLC-Radiomics
|
||||
|
||||
- Aerts, H. J. W. L., Wee, L., Rios Velazquez, E., Leijenaar, R. T. H., Parmar, C., Grossmann, P., Carvalho, S., Bussink, J., Monshouwer, R., Haibe-Kains, B., Rietveld, D., Hoebers, F., Rietbergen, M. M., Leemans, C. R., Dekker, A., Quackenbush, J., Gillies, R. J., Lambin, P. (2019). Data From NSCLC-Radiomics (version 4) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/K9/TCIA.2015.PF0M9REI
|
||||
|
||||
|
||||
- Aerts, H. J. W. L., Velazquez, E. R., Leijenaar, R. T. H., Parmar, C., Grossmann, P., Carvalho, S., Bussink, J., Monshouwer, R., Haibe-Kains, B., Rietveld, D., Hoebers, F., Rietbergen, M. M., Leemans, C. R., Dekker, A., Quackenbush, J., Gillies, R. J., Lambin, P. (2014, June 3). Decoding tumour phenotype by noninvasive imaging using a quantitative radiomics approach. Nature Communications. Nature Publishing Group. https://doi.org/10.1038/ncomms5006 (link)
|
||||
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.3671.4754.298665348758363466150039312520
|
||||
|
||||
QIN-PROSTATE-Repeatability
|
||||
|
||||
- Fedorov, A; Schwier, M; Clunie, D; Herz, C; Pieper, S; Kikinis, R; Tempany, C; Fennessy, F. (2018). Data From QIN-PROSTATE-Repeatability. The Cancer Imaging Archive. DOI: 10.7937/K9/TCIA.2018.MR1CKGND
|
||||
|
||||
|
||||
- Fedorov A, Vangel MG, Tempany CM, Fennessy FM. Multiparametric Magnetic Resonance Imaging of the Prostate: Repeatability of Volume and Apparent Diffusion Coefficient Quantification. Investigative Radiology. 52, 538–546 (2017). DOI: 10.1097/RLI.0000000000000382
|
||||
|
||||
- Fedorov, A., Schwier, M., Clunie, D., Herz, C., Pieper, S., Kikinis,R., Tempany, C. & Fennessy, F. An annotated test-retest collection of prostate multiparametric MRI. Scientific Data 5, 180281 (2018). DOI:
|
||||
|
||||
### 2.25.141277760791347900862109212450152067508
|
||||
|
||||
The Clinical Proteomic Tumor Analysis Consortium Clear Cell Renal Cell Carcinoma Collection (CPTAC-CCRCC)
|
||||
|
||||
- National Cancer Institute Clinical Proteomic Tumor Analysis Consortium (CPTAC). (2018). The Clinical Proteomic Tumor Analysis Consortium Clear Cell Renal Cell Carcinoma Collection (CPTAC-CCRCC) (Version 10) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/K9/TCIA.2018.OBLAMN27
|
||||
|
||||
- The CPTAC program requests that publications using data from this program include the following statement: “Data used in this publication were generated by the National Cancer Institute Clinical Proteomic Tumor Analysis Consortium (CPTAC).”
|
||||
|
||||
|
||||
### 2.25.275741864483510678566144889372061815320
|
||||
|
||||
National Lung Screening Trial
|
||||
|
||||
- National Lung Screening Trial Research Team. (2013). Data from the National Lung Screening Trial (NLST) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/TCIA.HMQ8-J677
|
||||
|
||||
- National Lung Screening Trial Research Team*; Aberle DR, Adams AM, Berg CD, Black WC, Clapp JD, Fagerstrom RM, Gareen IF, Gatsonis C, Marcus PM, Sicks JD (2011). Reduced Lung-Cancer Mortality with Low-Dose Computed Tomographic Screening. New England Journal of Medicine, 365(5), 395–409. https://doi.org/10.1056/nejmoa1102873
|
||||
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.99.1071.26968527900428638961173806140069
|
||||
|
||||
Stony Brook University COVID-19 Positive Cases (COVID-19-NY-SBU)
|
||||
|
||||
- Saltz, J., Saltz, M., Prasanna, P., Moffitt, R., Hajagos, J., Bremer, E., Balsamo, J., & Kurc, T. (2021). Stony Brook University COVID-19 Positive Cases [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/TCIA.BBAG-2923
|
||||
|
||||
|
||||
### 2.16.840.1.114362.1.11972228.22789312658.616067305.306.2
|
||||
|
||||
https://data.kitware.com/
|
||||
|
||||
|
||||
### 1.2.276.0.7230010.3.1.2.296485376.1.1665793212.499772
|
||||
### 2.25.269859997690759739055099378767846712697
|
||||
### 1.3.6.1.4.1.14519.5.2.1.5099.8010.217836670708542506360829799868
|
||||
### 1.3.6.1.4.1.14519.5.2.1.4792.2001.232252967813565730694525674696
|
||||
### 1.3.6.1.4.1.14519.5.2.1.4792.2001.105216574054253895819671475627
|
||||
### 1.3.6.1.4.1.5962.99.1.1117.5035.1620319789811.1.2.1
|
||||
### 1.3.6.1.4.1.5962.99.1.1123.9231.1620326176300.1.2.1
|
||||
### 1.3.6.1.4.1.5962.99.1.1126.3483.1620329455972.1.2.1
|
||||
|
||||
https://github.com/ImagingInformatics/hackathon-images
|
||||
|
||||
### 2.16.124.113543.6004.101.103.20021117.162333.1
|
||||
### 2.16.124.113543.6004.101.103.20021117.190619.1
|
||||
### 2.16.124.113543.6004.101.103.20021117.123455.1
|
||||
### 2.16.124.113543.6004.101.103.20021117.061159.1
|
||||
|
||||
https://www.aapm.org/
|
||||
|
||||
|
||||
### 1.2.840.113619.2.30.1.1762295590.1623.978668949.886
|
||||
|
||||
|
||||
### 1.2.276.0.7230010.3.1.2.447481088.1.1669202398.851612
|
||||
|
||||
Custom data SPECT, specifically I123-FP-CIT (DaTSCAN) SPECT, evaluates the dopaminergic system to diagnose Parkinson's disease, especially when tremor symptoms are unclear. It helps distinguish Parkinson's disease from treatment-related tremor.
|
||||
|
||||
|
||||
|
||||
### 1.3.6.1.4.1.9328.50.1.54652
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/rider-pilot/
|
||||
|
||||
Lung Image Database Consortium (LIDC). (2023) RIDER Pilot [Data set]. The Cancer Imaging Archive (TCIA). https://doi.org/10.7937/m87f-mz83
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.331759366792756327296606233801322964986
|
||||
|
||||
Mayr, N., Yuh, W. T. C., Bowen, S., Harkenrider, M., Knopp, M. V., Lee, E. Y.-P., Leung, E., Lo, S. S., Small Jr., W., & Wolfson, A. H. (2023). Cervical Cancer – Tumor Heterogeneity: Serial Functional and Molecular Imaging Across the Radiation Therapy Course in Advanced Cervical Cancer (Version 1) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/ERZ5-QZ59
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/cc-tumor-heterogeneity/
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.297577087050970310787702792940607009472
|
||||
|
||||
Eslick, E. M., Kipritidis, J., Gradinscak, D., Stevens, M. J., Bailey, D. L., Harris, B., Booth, J. T., & Keall, P. J. (2022). CT Ventilation as a functional imaging modality for lung cancer radiotherapy (CT-vs-PET-Ventilation-Imaging) (Version 1) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/3ppx-7s22
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/ct-vs-pet-ventilation-imaging/
|
||||
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.2103.7010.634114621738943599785009586807
|
||||
### 1.3.6.1.4.1.14519.5.2.1.2103.7010.135953723682765205394176991681
|
||||
|
||||
Huang, W., Tudorica, A., Chui, S., Kemmer, K., Naik, A., Troxell, M., Oh, K., Roy, N., Afzal, A., & Holtorf, M. (2014). Variations of dynamic contrast-enhanced magnetic resonance imaging in evaluation of breast cancer therapy response: a multicenter data analysis challenge (QIN Breast DCE-MRI) (Version 2) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/k9/tcia.2014.a2n1ixox
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/qin-breast-dce-mri/
|
||||
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.1.24766180081901755714059656629507905556
|
||||
|
||||
|
||||
Cancer Moonshot Biobank. (2023). Cancer Moonshoot Biobank – Acute Myeloid Leukemia (CMB-AML) (Version 4) [Dataset]. The Cancer Imaging Archive. https://doi.org/10.7937/PCTE-6M66
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/cmb-aml/
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.3098.5025.285242291560760827564488897577
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/anti-pd-1_lung/
|
||||
|
||||
Madhavi, P., Patel, S., & Tsao, A. S. (2019). Data from Anti-PD-1 Immunotherapy Lung [Data set]. The Cancer Imaging Archive. DOI: 10.7937/tcia.2019.zjjwb9ip
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.1.84416332615988066829602832830236187384
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/cmb-pca/
|
||||
|
||||
Cancer Moonshot Biobank. (2022). Cancer Moonshot Biobank – Prostate Cancer Collection (CMB-PCA) (Version 7) [Dataset]. The Cancer Imaging Archive. https://doi.org/10.7937/25T7-6Y12
|
||||
|
||||
### 1.3.6.1.4.1.32722.99.99.239341353911714368772597187099978969331
|
||||
|
||||
Aerts, H. J. W. L., Wee, L., Rios Velazquez, E., Leijenaar, R. T. H., Parmar, C., Grossmann, P., Carvalho, S., Bussink, J., Monshouwer, R., Haibe-Kains, B., Rietveld, D., Hoebers, F., Rietbergen, M. M., Leemans, C. R., Dekker, A., Quackenbush, J., Gillies, R. J., Lambin, P. (2014). Data From NSCLC-Radiomics (version 4) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/K9/TCIA.2015.PF0M9REI
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/nsclc-radiomics/
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.7085.2626.494695569589117268722281491772
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/cptac-ucec/
|
||||
|
||||
|
||||
National Cancer Institute Clinical Proteomic Tumor Analysis Consortium (CPTAC). (2019). The Clinical Proteomic Tumor Analysis Consortium Uterine Corpus Endometrial Carcinoma Collection (CPTAC-UCEC) (Version 12) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/K9/TCIA.2018.3R3JUISW
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.207544490797667703011829289839681390478
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/remind/
|
||||
|
||||
Juvekar, P., Dorent, R., Kögl, F., Torio, E., Barr, C., Rigolo, L., Galvin, C., Jowkar, N., Kazi, A., Haouchine, N., Cheema, H., Navab, N., Pieper, S., Wells, W. M., Bi, W. L., Golby, A., Frisken, S., & Kapur, T. (2023). The Brain Resection Multimodal Imaging Database (ReMIND) (Version 1) [dataset]. The Cancer Imaging Archive. https://doi.org/10.7937/3RAG-D070
|
||||
|
||||
### 1.3.12.2.1107.5.1.4.60175.30000008042114404745300000010
|
||||
|
||||
Gavrielides, M. A., Kinnard, L. M., Myers, K. J., Peregoy, J., Pritchard, W. F., Zeng, R., Esparza, J., Karanian, J., & Petrick, N. (2015). Data From Phantom FDA [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/k9/TCIA.2015.orbjkmux
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/phantom-fda/
|
||||
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.6834.5010.992793141464713669479982159310
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/4d-lung/
|
||||
|
||||
|
||||
Hugo, G. D., Weiss, E., Sleeman, W. C., Balik, S., Keall, P. J., Lu, J., & Williamson, J. F. (2016). Data from 4D Lung Imaging of NSCLC Patients (Version 2) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/K9/TCIA.2016.ELN8YGLE
|
||||
|
||||
|
||||
### 1.3.6.1.4.1.9328.50.17.15423521354819720574322014551955370036
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/rider-lung-pet-ct/
|
||||
|
||||
Muzi P, Wanner M, & Kinahan P. (2015). Data From RIDER Lung PET-CT. The Cancer Imaging Archive. https://doi.org/10.7937/k9/tcia.2015.ofip7tvm
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.9823.1001.134394060407147891170882809392
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/prostate-mri/
|
||||
|
||||
Choyke P, Turkbey B, Pinto P, Merino M, Wood B. (2016). Data From PROSTATE-MRI. The Cancer Imaging Archive. http://doi.org/10.7937/K9/TCIA.2016.6046GUDv
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.191696062987463500085282581898315738844
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/upenn-gbm/
|
||||
|
||||
Bakas, S., Sako, C., Akbari, H., Bilello, M., Sotiras, A., Shukla, G., Rudie, J. D., Flores Santamaria, N., Fathi Kazerooni, A., Pati, S., Rathore, S., Mamourian, E., Ha, S. M., Parker, W., Doshi, J., Baid, U., Bergman, M., Binder, Z. A., Verma, R., … Davatzikos, C. (2021). Multi-parametric magnetic resonance imaging (mpMRI) scans for de novo Glioblastoma (GBM) patients from the University of Pennsylvania Health System (UPENN-GBM) (Version 2) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/TCIA.709X-DN49
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.4792.2001.921758700577562664959693695481
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/breast-diagnosis/
|
||||
|
||||
Bloch, B. Nicolas, Jain, Ashali, & Jaffe, C. Carl. (2015). BREAST-DIAGNOSIS [Data set]. The Cancer Imaging Archive. http://doi.org/10.7937/K9/TCIA.2015.SDNRQXXR
|
||||
|
||||
|
||||
### 1.3.6.1.4.1.14519.5.2.1.1620.1225.189514895974227080410265976065
|
||||
|
||||
Comstock, C. E., Gatsonis, C., Newstead, G. M., Snyder, B. S., Gareen, I. F., Bergin, J. T., Rahbar, H., Sung, J. S., Jacobs, C., Harvey, J. A., Nicholson, M. H., Ward, R. C., Holt, J., Prather, A., Miller, K. D., Schnall, M. D., & Kuhl, C. K. (2023). Abbreviated Breast MRI and Digital Tomosynthesis Mammography in Screening Women With Dense Breasts (EA1141) (Version 1) [dataset]. The Cancer Imaging Archive. https://doi.org/10.7937/2BAS-HR33
|
||||
|
||||
https://www.cancerimagingarchive.net/collection/ea1141/
|
||||
|
||||
### 1.2.276.0.7230010.3.1.2.2155604110.4180.1021041295.21
|
||||
|
||||
From OFFIS DICOM-Team
|
||||
|
||||
https://www.offis.de/
|
||||
OFFIS DICOM-Team
|
||||
@@ -1,5 +1,4 @@
|
||||
# syntax=docker/dockerfile:1.7-labs
|
||||
# This dockerfile is used to publish the `ohif/app` image on dockerhub.
|
||||
# This dockerfile is used to publish the `ohif/viewer` image on dockerhub.
|
||||
#
|
||||
# It's a good example of how to build our static application and package it
|
||||
# with a web server capable of hosting it as static content.
|
||||
@@ -22,68 +21,46 @@
|
||||
|
||||
# Stage 1: Build the application
|
||||
# docker build -t ohif/viewer:latest .
|
||||
# Copy Files
|
||||
FROM node:24-slim as builder
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends build-essential python3 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN npm install -g pnpm@11
|
||||
FROM node:15.13.0-slim as builder
|
||||
|
||||
RUN mkdir /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
ENV PATH=/usr/src/app/node_modules/.bin:$PATH
|
||||
|
||||
# Copy package manifests for install caching. preinstall.js is included because
|
||||
# the root package.json's "preinstall" lifecycle script (node preinstall.js)
|
||||
# runs during `pnpm install` below -- before the full source is copied -- so the
|
||||
# script file must already be present or install fails with MODULE_NOT_FOUND.
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc preinstall.js ./
|
||||
COPY --parents ./extensions/*/package.json ./modes/*/package.json ./platform/*/package.json ./
|
||||
# Run the install before copying the rest of the files.
|
||||
# Keep --no-frozen-lockfile here (unlike CI): .dockerignore excludes
|
||||
# platform/docs, so the lockfile's docs importer has no manifest in the build
|
||||
# context and a frozen install would fail. pnpm reconciles (drops docs) instead.
|
||||
RUN pnpm install --no-frozen-lockfile
|
||||
# Copy the local directory
|
||||
COPY --link --exclude=pnpm-lock.yaml --exclude=package.json --exclude=Dockerfile . .
|
||||
# Copy Files
|
||||
COPY .docker /usr/src/app/.docker
|
||||
COPY .webpack /usr/src/app/.webpack
|
||||
COPY extensions /usr/src/app/extensions
|
||||
COPY platform /usr/src/app/platform
|
||||
COPY .browserslistrc /usr/src/app/.browserslistrc
|
||||
COPY aliases.config.js /usr/src/app/aliases.config.js
|
||||
COPY babel.config.js /usr/src/app/babel.config.js
|
||||
COPY lerna.json /usr/src/app/lerna.json
|
||||
COPY package.json /usr/src/app/package.json
|
||||
COPY postcss.config.js /usr/src/app/postcss.config.js
|
||||
COPY yarn.lock /usr/src/app/yarn.lock
|
||||
|
||||
# Build here
|
||||
# After install it should hopefully be stable until the local directory changes
|
||||
RUN apt-get update && apt-get install -y python make g++
|
||||
# Run the install before copying the rest of the files
|
||||
RUN yarn config set workspaces-experimental true
|
||||
RUN yarn install
|
||||
|
||||
ENV PATH /usr/src/app/node_modules/.bin:$PATH
|
||||
ENV QUICK_BUILD true
|
||||
# ENV GENERATE_SOURCEMAP=false
|
||||
ARG APP_CONFIG=config/default.js
|
||||
ARG PUBLIC_URL=/
|
||||
ENV PUBLIC_URL=${PUBLIC_URL}
|
||||
# ENV REACT_APP_CONFIG=config/default.js
|
||||
|
||||
RUN pnpm run show:config
|
||||
RUN pnpm run build
|
||||
|
||||
# Precompress files
|
||||
RUN chmod u+x .docker/compressDist.sh
|
||||
RUN ./.docker/compressDist.sh
|
||||
RUN yarn run build
|
||||
|
||||
# Stage 2: Bundle the built application into a Docker container
|
||||
# which runs Nginx using Alpine Linux
|
||||
FROM nginxinc/nginx-unprivileged:1.27-alpine as final
|
||||
#RUN apk add --no-cache bash
|
||||
ARG PUBLIC_URL=/
|
||||
ENV PUBLIC_URL=${PUBLIC_URL}
|
||||
ARG PORT=80
|
||||
ENV PORT=${PORT}
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
USER nginx
|
||||
COPY --chown=nginx:nginx .docker/Viewer-v3.x /usr/src
|
||||
FROM nginx:1.21.1-alpine
|
||||
RUN apk add --no-cache bash
|
||||
RUN rm -rf /etc/nginx/conf.d
|
||||
COPY .docker/Viewer-v2.x /etc/nginx/conf.d
|
||||
COPY .docker/Viewer-v2.x/entrypoint.sh /usr/src/
|
||||
RUN chmod 777 /usr/src/entrypoint.sh
|
||||
COPY --from=builder /usr/src/app/platform/app/dist /usr/share/nginx/html${PUBLIC_URL}
|
||||
# Copy paths that are renamed/redirected generally
|
||||
# Microscopy libraries depend on root level include, so must be copied
|
||||
COPY --from=builder /usr/src/app/platform/app/dist/dicom-microscopy-viewer /usr/share/nginx/html/dicom-microscopy-viewer
|
||||
|
||||
# In entrypoint.sh, app-config.js might be overwritten, so chmod it to be writeable.
|
||||
# The nginx user cannot chmod it, so change to root.
|
||||
USER root
|
||||
RUN chown -R nginx:nginx /usr/share/nginx/html && chmod -R 777 /usr/share/nginx/html
|
||||
USER nginx
|
||||
COPY --from=builder /usr/src/app/platform/viewer/dist /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
EXPOSE 443
|
||||
ENTRYPOINT ["/usr/src/entrypoint.sh"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -1,60 +1,40 @@
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<div align="center">
|
||||
<h1>OHIF Medical Imaging Viewer</h1>
|
||||
<p><strong>The OHIF Viewer</strong> is a zero-footprint medical image viewer
|
||||
provided by the <a href="https://ohif.org/">Open Health Imaging Foundation (OHIF)</a>. It is a configurable and extensible progressive web application with out-of-the-box support for image archives which support <a href="https://www.dicomstandard.org/using/dicomweb/">DICOMweb</a>.</p>
|
||||
<p><strong>The OHIF Viewer</strong> is a zero-footprint medical image viewer provided by the <a href="http://ohif.org/">Open Health Imaging Foundation (OHIF)</a>. It is a configurable and extensible progressive web application with out-of-the-box support for image archives which support <a href="https://www.dicomstandard.org/dicomweb/">DICOMweb</a>.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div align="center">
|
||||
<a href="https://docs.ohif.org/"><strong>Read The Docs</strong></a>
|
||||
<a href="https://docs.ohif.org/"><strong>Read The Docs</strong></a> |
|
||||
<a href="https://github.com/OHIF/Viewers/tree/master/docs/latest">Edit the docs</a>
|
||||
</div>
|
||||
<div align="center">
|
||||
<a href="https://viewer.ohif.org/">Live Demo</a> |
|
||||
<a href="https://ui.ohif.org/">Component Library</a>
|
||||
<a href="https://react.ohif.org/">Component Library</a>
|
||||
</div>
|
||||
<div align="center">
|
||||
📰 <a href="https://ohif.org/news/"><strong>Join OHIF Newsletter</strong></a> 📰
|
||||
</div>
|
||||
<div align="center">
|
||||
📰 <a href="https://ohif.org/news/"><strong>Join OHIF Newsletter</strong></a> 📰
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
[![NPM version][npm-version-image]][npm-url]
|
||||
[![NPM downloads][npm-downloads-image]][npm-url]
|
||||
[![Pulls][docker-pulls-img]][docker-image-url]
|
||||
[![MIT License][license-image]][license-url]
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2FOHIF%2FViewers?ref=badge_shield)
|
||||
|
||||
[![Netlify Status][netlify-image]][netlify-url]
|
||||
[![CircleCI][circleci-image]][circleci-url]
|
||||
[![codecov][codecov-image]][codecov-url]
|
||||
[![This project is using Percy.io for visual regression testing.][percy-image]](percy-url)
|
||||
<!-- [![NPM downloads][npm-downloads-image]][npm-url] -->
|
||||
<!-- [![Pulls][docker-pulls-img]][docker-image-url] -->
|
||||
<!-- [](https://app.fossa.io/projects/git%2Bgithub.com%2FOHIF%2FViewers?ref=badge_shield) -->
|
||||
|
||||
<!-- [![Netlify Status][netlify-image]][netlify-url] -->
|
||||
<!-- [![CircleCI][circleci-image]][circleci-url] -->
|
||||
<!-- [![codecov][codecov-image]][codecov-url] -->
|
||||
<!-- [](#contributors) -->
|
||||
[](#contributors)
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
|
||||
| | | |
|
||||
| :-: | :--- | :--- |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-measurements.webp?raw=true" alt="Measurement tracking" width="350"/> | Measurement Tracking | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-segmentation.webp?raw=true" alt="Segmentations" width="350"/> | Labelmap Segmentations | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.12.2.1107.5.2.32.35162.30000015050317233592200000046) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-ptct.webp?raw=true" alt="Hanging Protocols" width="350"/> | Fusion and Custom Hanging protocols | [Demo](https://viewer.ohif.org/tmtv?StudyInstanceUIDs=1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-volume-rendering.webp?raw=true" alt="Volume Rendering" width="350"/> | Volume Rendering | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5&hangingprotocolId=mprAnd3DVolumeViewport) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-pdf.webp?raw=true" alt="PDF" width="350"/> | PDF | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=2.25.317377619501274872606137091638706705333) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-rtstruct.webp?raw=true" alt="RTSTRUCT" width="350"/> | RT STRUCT | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.5962.99.1.2968617883.1314880426.1493322302363.3.0) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-4d.webp?raw=true" alt="4D" width="350"/> | 4D | [Demo](https://viewer.ohif.org/dynamic-volume?StudyInstanceUIDs=2.25.232704420736447710317909004159492840763) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-video.webp?raw=true" alt="VIDEO" width="350"/> | Video | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=2.25.96975534054447904995905761963464388233) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/microscopy.webp?raw=true" alt="microscopy" width="350"/> | Slide Microscopy | [Demo](https://viewer.ohif.org/microscopy?StudyInstanceUIDs=2.25.141277760791347900862109212450152067508) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-ecg.webp?raw=true" alt="ECG" width="350"/> | ECG Waveform | [Demo](https://viewer-dev.ohif.org/viewer?StudyInstanceUIDs=2.25.209974489360710696739324151261716440238) |
|
||||
|
||||
## About
|
||||
|
||||
The OHIF Viewer can retrieve
|
||||
and load images from most sources and formats, render sets in 2D, 3D, and
|
||||
The OHIF Medical Imaging Viewer is for viewing medical images. It can retrieve
|
||||
and load images from most sources and formats; render sets in 2D, 3D, and
|
||||
reconstructed representations; allows for the manipulation, annotation, and
|
||||
serialization of observations; supports internationalization, OpenID Connect,
|
||||
offline use, hotkeys, and many more features.
|
||||
@@ -74,10 +54,10 @@ contributions of individuals, research groups, and commercial organizations.
|
||||
|
||||
### Built to Adapt
|
||||
|
||||
After more than 8-years of integrating with many companies and organizations,
|
||||
After more than 5-years of integrating with many companies and organizations,
|
||||
The OHIF Viewer has been rebuilt from the ground up to better address the
|
||||
varying workflow and configuration needs of its many users. All of the Viewer's
|
||||
core features are built using its own extension system. The same extensibility
|
||||
core features are built using it's own extension system. The same extensibility
|
||||
that allows us to offer:
|
||||
|
||||
- 2D and 3D medical image viewing
|
||||
@@ -85,7 +65,6 @@ that allows us to offer:
|
||||
- Maximum Intensity Project (MIP)
|
||||
- Whole slide microscopy viewing
|
||||
- PDF and Dicom Structured Report rendering
|
||||
- Segmentation rendering as labelmaps and contours
|
||||
- User Access Control (UAC)
|
||||
- Context specific toolbar and side panel content
|
||||
- and many others
|
||||
@@ -96,46 +75,65 @@ forking).
|
||||
|
||||
### Support
|
||||
|
||||
- [Report a Bug 🐛](https://github.com/OHIF/Viewers/issues/new?assignees=&labels=Community%3A+Report+%3Abug%3A%2CAwaiting+Reproduction&projects=&template=bug-report.yml&title=%5BBug%5D+)
|
||||
- [Request a Feature 🚀](https://github.com/OHIF/Viewers/issues/new?assignees=&labels=Community%3A+Request+%3Ahand%3A&projects=&template=feature-request.yml&title=%5BFeature+Request%5D+)
|
||||
- [Ask a Question 🤗](community.ohif.org)
|
||||
- [Slack Channel](https://join.slack.com/t/cornerstonejs/shared_invite/zt-1r8xb2zau-dOxlD6jit3TN0Uwf928w9Q)
|
||||
We offer support through
|
||||
[GitHub Issues](https://github.com/OHIF/Viewers/issues/new/choose). You can:
|
||||
|
||||
For commercial support, academic collaborations, and answers to common
|
||||
questions; please use [Get Support](https://ohif.org/get-support/) to contact
|
||||
us.
|
||||
- [Report a Bug 🐛](https://github.com/OHIF/Viewers/issues/new?assignees=&labels=Community%3A+Report+%3Abug%3A&template=---bug-report.md)
|
||||
- [Request a Feature 🚀](https://github.com/OHIF/Viewers/issues/new?assignees=&labels=Community%3A+Request+%3Ahand%3A&template=---feature-request.md)
|
||||
- [Ask a Question 🤗](https://github.com/OHIF/Viewers/issues/new?assignees=&labels=Community%3A+Question+%3Aquestion%3A&template=---support-question.md)
|
||||
|
||||
For commercial support, academic collaberations, and answers to common
|
||||
questions; please read our
|
||||
[documented FAQ](https://docs.ohif.org/faq/index.html#does-ohif-offer-commercial-support).
|
||||
|
||||
## Quick Start Deployment
|
||||
|
||||
> This is only one of many ways to configure and deploy the OHIF Viewer. To
|
||||
> learn more about your options, and how to choose the best one for your
|
||||
> requirements, check out
|
||||
> [our deployment recipes and documentation](https://docs.ohif.org/deployment/).
|
||||
|
||||
The fastest and easiest way to get started is to include the OHIF Viewer with a
|
||||
script tag. In practice, this is as simple as:
|
||||
|
||||
- Including the following dependencies with script tags:
|
||||
- [React](https://unpkg.com/react@16/umd/react.production.min.js)
|
||||
- [React Dom](https://unpkg.com/react-dom@16/umd/react-dom.production.min.js)
|
||||
- The [OHIF Viewer](https://unpkg.com/@ohif/viewer)
|
||||
- Have an element with an ID of `root` on the page
|
||||
- Configure the OHIF Viewer at `window.config`:
|
||||
|
||||
```js
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
{
|
||||
name: 'DCM4CHEE',
|
||||
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||
wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||
qidoSupportsIncludeField: true,
|
||||
imageRendering: 'wadors',
|
||||
thumbnailRendering: 'wadors',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
- Install the viewer: `window.OHIFViewer.installViewer(window.config);`
|
||||
|
||||
This exact setup is demonstrated in this
|
||||
[CodeSandbox](https://codesandbox.io/s/viewer-script-tag-tprch) and in our
|
||||
[Embedding The Viewer](https://docs.ohif.org/deployment/recipes/embedded-viewer.html)
|
||||
deployment recipe.
|
||||
|
||||
## Developing
|
||||
|
||||
### Branches
|
||||
|
||||
#### `master` branch - The latest dev (beta) release
|
||||
|
||||
- `master` - The latest dev release
|
||||
|
||||
This is typically where the latest development happens. Code that is in the master branch has passed code reviews and automated tests, but it may not be deemed ready for production. This branch usually contains the most recent changes and features being worked on by the development team. It's often the starting point for creating feature branches (where new features are developed) and hotfix branches (for urgent fixes).
|
||||
|
||||
Each package is tagged with beta version numbers, and published to npm such as `@ohif/ui@3.6.0-beta.1`
|
||||
|
||||
### `release/*` branches - The latest stable releases
|
||||
Once the `master` branch code reaches a stable, release-ready state, we conduct a comprehensive code review and QA testing. Upon approval, we create a new release branch from `master`. These branches represent the latest stable version considered ready for production.
|
||||
|
||||
For example, `release/3.5` is the branch for version 3.5.0, and `release/3.6` is for version 3.6.0. After each release, we wait a few days to ensure no critical bugs. If any are found, we fix them in the release branch and create a new release with a minor version bump, e.g., 3.5.1 in the `release/3.5` branch.
|
||||
|
||||
Each package is tagged with version numbers and published to npm, such as `@ohif/ui@3.5.0`. Note that `master` is always ahead of the `release` branch. We publish docker builds for both beta and stable releases.
|
||||
|
||||
Here is a schematic representation of our development workflow:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### Requirements
|
||||
|
||||
- [Yarn 1.20.0+](https://yarnpkg.com/en/docs/install)
|
||||
- [Node 18+](https://nodejs.org/en/)
|
||||
- [Yarn 1.17.3+](https://yarnpkg.com/en/docs/install)
|
||||
- [Node 10+](https://nodejs.org/en/)
|
||||
- Yarn Workspaces should be enabled on your machine:
|
||||
- `yarn config set workspaces-experimental true`
|
||||
|
||||
@@ -147,14 +145,7 @@ Here is a schematic representation of our development workflow:
|
||||
3. Navigate to the cloned project's directory
|
||||
4. Add this repo as a `remote` named `upstream`
|
||||
- `git remote add upstream https://github.com/OHIF/Viewers.git`
|
||||
5. `yarn install --frozen-lockfile` to restore dependencies and link projects
|
||||
|
||||
:::danger
|
||||
In general run `yarn install` with the `--frozen-lockfile` flag to help avoid
|
||||
supply chain attacks by enforcing reproducible dependencies. That is, if the
|
||||
`yarn.lock` file is clean and does NOT reference compromised packages, then
|
||||
no compromised packages should land on your machine by using this flag.
|
||||
:::
|
||||
5. `yarn install` to restore dependencies and link projects
|
||||
|
||||
#### To Develop
|
||||
|
||||
@@ -165,86 +156,30 @@ _From this repository's root directory:_
|
||||
yarn config set workspaces-experimental true
|
||||
|
||||
# Restore dependencies
|
||||
yarn install --frozen-lockfile
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Cornerstone3D Integration Testing
|
||||
|
||||
OHIF's Playwright end-to-end tests can run against a **CS3D branch** or a
|
||||
**published CS3D version**, allowing changes that span both repositories to be
|
||||
validated together before merging.
|
||||
|
||||
#### Setting up an integration build
|
||||
|
||||
1. Add the **`ohif-integration`** label to your OHIF pull request.
|
||||
2. In the PR body, add a line specifying the CS3D ref:
|
||||
```
|
||||
CS3D_REF: feat/my-feature
|
||||
```
|
||||
- **Version ref** (e.g. `4.19+`, `4.18.2`) — the workflow resolves it to an
|
||||
exact published version and swaps the CS3D dependency via npm.
|
||||
- **Branch ref** (e.g. `main`, `cornerstonejs:feat/foo`) — the workflow
|
||||
clones the branch, builds CS3D from source with `bun run build:esm`, and
|
||||
symlinks the built packages into OHIF's `node_modules`.
|
||||
- For forks, use the `<owner>:<branch>` format
|
||||
(e.g. `myGithubUser:feat/foo`).
|
||||
- If no `CS3D_REF` is specified, the default is `4.19+`.
|
||||
3. The workflow can also be triggered manually via **workflow_dispatch** with a
|
||||
`cs3d_ref` input.
|
||||
|
||||
#### What happens in CI
|
||||
|
||||
The [Playwright workflow](.github/workflows/playwright.yml) runs two jobs:
|
||||
|
||||
| Job | Purpose |
|
||||
|-----|---------|
|
||||
| **Playwright Tests** | Builds OHIF (with CS3D linked or version-swapped), runs the full Playwright suite, uploads test results and coverage, and deploys a Netlify preview when `ohif-integration` is active. |
|
||||
| **CS3D Branch Merge Guard** | A lightweight check that **fails** when the `ohif-integration` label is present and `CS3D_REF` points to a branch (not a version). This prevents merging while still letting the Playwright tests show green so you can see whether the code actually works. |
|
||||
|
||||
#### Testing changes that span both repos
|
||||
|
||||
If a feature requires changes in both Cornerstone3D and OHIF:
|
||||
|
||||
1. Create your feature branch in CS3D and push it.
|
||||
2. Create a matching branch in OHIF.
|
||||
3. Add the `ohif-integration` label to the OHIF pull request.
|
||||
4. In the PR body, add: `CS3D_REF: <your-cs3d-branch>`.
|
||||
5. Playwright tests will build CS3D from source, link it, and run the full
|
||||
suite. The merge guard will block merge until you switch to a published
|
||||
version — but you can see the test results and the preview deploy while
|
||||
iterating.
|
||||
6. Once the CS3D side is merged and published, update the PR body to reference
|
||||
the published version (e.g. `CS3D_REF: 4.19+`). The tests will run against
|
||||
the registry version and the merge guard will pass.
|
||||
|
||||
#### Preview deploys
|
||||
|
||||
When `ohif-integration` is active, the Playwright workflow also builds the OHIF
|
||||
viewer and deploys it to Netlify as a preview. This gives you a live URL to
|
||||
manually test the combined CS3D + OHIF changes without running anything locally.
|
||||
|
||||
For details on linking CS3D locally for development, see the
|
||||
[Cornerstone3D README](libs/@cornerstonejs/README.md#local-development-linking--unlinking).
|
||||
|
||||
## Commands
|
||||
|
||||
These commands are available from the root directory. Each project directory
|
||||
also supports a number of commands that can be found in their respective
|
||||
`README.md` and `package.json` files.
|
||||
`README.md` and `project.json` files.
|
||||
|
||||
| Yarn Commands | Description |
|
||||
| ---------------------------- | ------------------------------------------------------------- |
|
||||
| **Develop** | |
|
||||
| `dev` | Default development experience for Viewer |
|
||||
| `dev:fast` | Our experimental fast dev mode that uses rsbuild instead of webpack |
|
||||
| `dev` or `start` | Default development experience for Viewer |
|
||||
| `dev:project <package-name>` | Replace with `core`, `ui`, `i18n`, `cornerstone`, `vtk`, etc. |
|
||||
| `test:unit` | Jest multi-project test runner; overall coverage |
|
||||
| **Deploy** | |
|
||||
| `build`\* | Builds production output for our PWA Viewer | |
|
||||
| `build`\* | Builds production output for our PWA Viewer |
|
||||
| `build:package`\* | Builds production `commonjs` output for our Viewer |
|
||||
| `build:package-all`\* | Builds commonjs bundles for all projects |
|
||||
|
||||
\* - For more information on different builds, check out our [Deploy
|
||||
\* - For more information on our different builds, check out our [Deploy
|
||||
Docs][deployment-docs]
|
||||
|
||||
## Project
|
||||
## Projects
|
||||
|
||||
The OHIF Medical Image Viewing Platform is maintained as a
|
||||
[`monorepo`][monorepo]. This means that this repository, instead of containing a
|
||||
@@ -253,60 +188,58 @@ you'll see the following:
|
||||
|
||||
```bash
|
||||
.
|
||||
├── extensions #
|
||||
│ ├── _example # Skeleton of example extension
|
||||
│ ├── default # basic set of useful functionalities (datasources, panels, etc)
|
||||
│ ├── cornerstone # image rendering and tools w/ Cornerstone3D
|
||||
│ ├── cornerstone-dicom-sr # DICOM Structured Report rendering and export
|
||||
│ ├── cornerstone-dicom-sr # DICOM Structured Report rendering and export
|
||||
│ ├── cornerstone-dicom-seg # DICOM Segmentation rendering and export
|
||||
│ ├── cornerstone-dicom-rt # DICOM RTSTRUCT rendering
|
||||
│ ├── cornerstone-microscopy # Whole Slide Microscopy rendering
|
||||
│ ├── dicom-pdf # PDF rendering
|
||||
│ ├── dicom-video # DICOM RESTful Services
|
||||
│ ├── measurement-tracking # Longitudinal measurement tracking
|
||||
│ ├── tmtv # Total Metabolic Tumor Volume (TMTV) calculation
|
||||
|
|
||||
|
||||
├── extensions #
|
||||
│ ├── _example # Skeleton of example extension
|
||||
│ ├── cornerstone # 2D images w/ Cornerstone.js
|
||||
│ ├── dicom-html # Structured Reports as HTML in viewport
|
||||
│ ├── dicom-microscopy # Whole slide microscopy viewing
|
||||
│ ├── dicom-pdf # View DICOM wrapped PDFs in viewport
|
||||
│ └── vtk # MPR and Volume support w/ VTK.js
|
||||
│
|
||||
├── modes #
|
||||
│ ├── _example # Skeleton of example mode
|
||||
│ ├── basic-dev-mode # Basic development mode
|
||||
│ ├── longitudinal # Longitudinal mode (measurement tracking)
|
||||
│ ├── tmtv # Total Metabolic Tumor Volume (TMTV) calculation mode
|
||||
│ └── microscopy # Whole Slide Microscopy mode
|
||||
├── platform #
|
||||
│ ├── core # Business Logic
|
||||
│ ├── i18n # Internationalization Support
|
||||
│ ├── ui # React component library
|
||||
│ └── viewer # Connects platform and extension projects
|
||||
│
|
||||
├── platform #
|
||||
│ ├── core # Business Logic
|
||||
│ ├── i18n # Internationalization Support
|
||||
│ ├── ui # React component library
|
||||
│ ├── docs # Documentation
|
||||
│ └── viewer # Connects platform and extension projects
|
||||
│
|
||||
├── ... # misc. shared configuration
|
||||
├── lerna.json # MonoRepo (Lerna) settings
|
||||
├── package.json # Shared devDependencies and commands
|
||||
└── README.md # This file
|
||||
├── ... # misc. shared configuration
|
||||
├── lerna.json # MonoRepo (Lerna) settings
|
||||
├── package.json # Shared devDependencies and commands
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
Want to better understand why and how we've structured this repository? Read
|
||||
more about it in our [Architecture Documentation][ohif-architecture].
|
||||
|
||||
### Platform
|
||||
|
||||
These projects comprise the
|
||||
|
||||
| Name | Description | Links |
|
||||
| ------------------------------- | ---------------------------------------------------------------------------------------------------- | ----------------- |
|
||||
| [@ohif/core][platform-core] | Business logic and classes that model the data, services, and extensions that are framework agnostic | [NPM][core-npm] |
|
||||
| [@ohif/i18n][platform-i18n] | Language files and small API for wrapping component/ui text for translations | [NPM][i18n-npm] |
|
||||
| [@ohif/viewer][platform-viewer] | The OHIF Viewer. Where we consume and configure all platform library's and extensions | [NPM][viewer-npm] |
|
||||
| [@ohif/ui][platform-ui] | Reusable React components we consume and compose to build our Viewer's UI | [NPM][ui-npm] |
|
||||
|
||||
### Extensions
|
||||
|
||||
This is a list of Extensions maintained by the OHIF Core team. It's possible to
|
||||
customize and configure these extensions, and you can even create your own. You
|
||||
can [read more about extensions here][ohif-extensions].
|
||||
|
||||
| Name | Description | Links |
|
||||
| -------------------------------------------------------------- | ------------------------------------------------------- | ---------------------- |
|
||||
| [@ohif/extension-cornestone][extension-cornerstone] | 2D image viewing, annotation, and segementation tools | [NPM][cornerstone-npm] |
|
||||
| [@ohif/extension-dicom-html][extension-dicom-html] | Support for viewing DICOM SR as rendered HTML | [NPM][html-npm] |
|
||||
| [@ohif/extension-dicom-microscopy][extension-dicom-microscopy] | Whole slide microscopy viewing | [NPM][microscopy-npm] |
|
||||
| [@ohif/extension-dicom-pdf][extension-dicom-pdf] | View DICOM wrapped PDFs in a viewport | [NPM][pdf-npm] |
|
||||
| [@ohif/extension-vtk][extension-vtk] | Volume rendering, reconstruction, and 3D visualizations | [NPM][vtk-npm] |
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
To acknowledge the OHIF Viewer in an academic publication, please cite
|
||||
|
||||
> _Open Health Imaging Foundation Viewer: An Extensible Open-Source Framework
|
||||
> for Building Web-Based Imaging Applications to Support Cancer Research_
|
||||
>
|
||||
> Erik Ziegler, Trinity Urban, Danny Brown, James Petts, Steve D. Pieper, Rob
|
||||
> Lewis, Chris Hafey, and Gordon J. Harris
|
||||
>
|
||||
> _JCO Clinical Cancer Informatics_, no. 4 (2020), 336-345, DOI:
|
||||
> [10.1200/CCI.19.00131](https://www.doi.org/10.1200/CCI.19.00131)
|
||||
>
|
||||
> Open-Access on Pubmed Central:
|
||||
> https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7259879/
|
||||
|
||||
or, for v1, please cite:
|
||||
|
||||
> _LesionTracker: Extensible Open-Source Zero-Footprint Web Viewer for Cancer
|
||||
> Imaging Research and Clinical Trials_
|
||||
>
|
||||
@@ -317,7 +250,7 @@ or, for v1, please cite:
|
||||
> [10.1158/0008-5472.CAN-17-0334](https://www.doi.org/10.1158/0008-5472.CAN-17-0334)
|
||||
|
||||
**Note:** If you use or find this repository helpful, please take the time to
|
||||
star this repository on GitHub. This is an easy way for us to assess adoption
|
||||
star this repository on Github. This is an easy way for us to assess adoption
|
||||
and it can help us obtain future funding for the project.
|
||||
|
||||
This work is supported primarily by the National Institutes of Health, National
|
||||
@@ -325,11 +258,21 @@ Cancer Institute, Informatics Technology for Cancer Research (ITCR) program,
|
||||
under a
|
||||
[grant to Dr. Gordon Harris at Massachusetts General Hospital (U24 CA199460)](https://projectreporter.nih.gov/project_info_description.cfm?aid=8971104).
|
||||
|
||||
[NCI Imaging Data Commons (IDC) project](https://imaging.datacommons.cancer.gov/) supported the development of new features and bug fixes marked with ["IDC:priority"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Apriority),
|
||||
["IDC:candidate"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Acandidate) or ["IDC:collaboration"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Acollaboration). NCI Imaging Data Commons is supported by contract number 19X037Q from
|
||||
Leidos Biomedical Research under Task Order HHSN26100071 from NCI. [IDC Viewer](https://learn.canceridc.dev/portal/visualization) is a customized version of the OHIF Viewer.
|
||||
## Projects that contributed to OHIF Viewer
|
||||
|
||||
This project is tested with BrowserStack. Thank you for supporting open-source!
|
||||
The following is a (partial) list of projects that contributed resources towards
|
||||
development of OHIF Viewer:
|
||||
|
||||
- [NCI Imaging Data Commons (IDC) project](https://imaging.datacommons.cancer.gov/)
|
||||
supported development of new features and bug fixes marked with
|
||||
["IDC:priority"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Apriority),
|
||||
["IDC:candidate"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Acandidate)
|
||||
or
|
||||
["IDC:collaboration"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Acollaboration).
|
||||
NCI Imaging Data Commons is supported by the contract number 19X037Q from
|
||||
Leidos Biomedical Research under Task Order HHSN26100071 from NCI.
|
||||
[IDC Viewer](https://learn.canceridc.dev/portal/visualization) is a customized
|
||||
version of the OHIF Viewer.
|
||||
|
||||
## License
|
||||
|
||||
@@ -355,11 +298,11 @@ MIT © [OHIF](https://github.com/OHIF)
|
||||
[semantic-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
|
||||
[semantic-url]: https://github.com/semantic-release/semantic-release
|
||||
<!-- ROW -->
|
||||
[npm-url]: https://npmjs.org/package/@ohif/app
|
||||
[npm-downloads-image]: https://img.shields.io/npm/dm/@ohif/app.svg?style=flat-square
|
||||
[npm-version-image]: https://img.shields.io/npm/v/@ohif/app.svg?style=flat-square
|
||||
[npm-url]: https://npmjs.org/package/@ohif/viewer
|
||||
[npm-downloads-image]: https://img.shields.io/npm/dm/@ohif/viewer.svg?style=flat-square
|
||||
[npm-version-image]: https://img.shields.io/npm/v/@ohif/viewer.svg?style=flat-square
|
||||
[docker-pulls-img]: https://img.shields.io/docker/pulls/ohif/viewer.svg?style=flat-square
|
||||
[docker-image-url]: https://hub.docker.com/r/ohif/app
|
||||
[docker-image-url]: https://hub.docker.com/r/ohif/viewer
|
||||
[license-image]: https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square
|
||||
[license-url]: LICENSE
|
||||
[percy-image]: https://percy.io/static/images/percy-badge.svg
|
||||
@@ -373,7 +316,7 @@ MIT © [OHIF](https://github.com/OHIF)
|
||||
[deployment-docs]: https://docs.ohif.org/deployment/
|
||||
[react-url]: https://reactjs.org/
|
||||
[pwa-url]: https://developers.google.com/web/progressive-web-apps/
|
||||
[ohif-viewer-url]: https://www.npmjs.com/package/@ohif/app
|
||||
[ohif-viewer-url]: https://www.npmjs.com/package/@ohif/viewer
|
||||
[configuration-url]: https://docs.ohif.org/configuring/
|
||||
[extensions-url]: https://docs.ohif.org/extensions/
|
||||
<!-- Platform -->
|
||||
@@ -383,8 +326,8 @@ MIT © [OHIF](https://github.com/OHIF)
|
||||
[i18n-npm]: https://www.npmjs.com/package/@ohif/i18n
|
||||
[platform-ui]: platform/ui/README.md
|
||||
[ui-npm]: https://www.npmjs.com/package/@ohif/ui
|
||||
[platform-viewer]: platform/app/README.md
|
||||
[viewer-npm]: https://www.npmjs.com/package/@ohif/app
|
||||
[platform-viewer]: platform/viewer/README.md
|
||||
[viewer-npm]: https://www.npmjs.com/package/@ohif/viewer
|
||||
<!-- Extensions -->
|
||||
[extension-cornerstone]: extensions/cornerstone/README.md
|
||||
[cornerstone-npm]: https://www.npmjs.com/package/@ohif/extension-cornerstone
|
||||
@@ -398,4 +341,4 @@ MIT © [OHIF](https://github.com/OHIF)
|
||||
[vtk-npm]: https://www.npmjs.com/package/@ohif/extension-vtk
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2FOHIF%2FViewers?ref=badge_large&issueType=license)
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2FOHIF%2FViewers?ref=badge_large)
|
||||
@@ -1,14 +1,10 @@
|
||||
const aliases = require('./aliases.config');
|
||||
const path = require('path');
|
||||
|
||||
// https://babeljs.io/docs/en/options#babelrcroots
|
||||
module.exports = {
|
||||
babelrcRoots: ['./platform/*', './extensions/*', './modes/*'],
|
||||
presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript'],
|
||||
plugins: [
|
||||
['@babel/plugin-transform-class-properties', { loose: true }],
|
||||
'@babel/plugin-transform-typescript',
|
||||
['@babel/plugin-transform-private-property-in-object', { loose: true }],
|
||||
['@babel/plugin-transform-private-methods', { loose: true }],
|
||||
'@babel/plugin-transform-class-static-block',
|
||||
],
|
||||
babelrcRoots: ['./platform/*', './extensions/*'],
|
||||
plugins: ['inline-react-svg', '@babel/plugin-proposal-class-properties'],
|
||||
env: {
|
||||
test: {
|
||||
presets: [
|
||||
@@ -18,26 +14,15 @@ module.exports = {
|
||||
{
|
||||
modules: 'commonjs',
|
||||
debug: false,
|
||||
targets: { node: 'current' },
|
||||
bugfixes: true,
|
||||
},
|
||||
],
|
||||
'@babel/preset-react',
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
plugins: [
|
||||
// jest's babel coverage provider injects babel-plugin-istanbul when
|
||||
// --collectCoverage is set; adding it here too makes babel 7 (pulled in
|
||||
// by jest 30) throw "Duplicate plugin/preset detected".
|
||||
'@babel/plugin-transform-object-rest-spread',
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'@babel/plugin-transform-regenerator',
|
||||
'@babel/transform-destructuring',
|
||||
'@babel/plugin-transform-runtime',
|
||||
'@babel/plugin-transform-typescript',
|
||||
'@babel/plugin-transform-class-static-block',
|
||||
'@babel/plugin-transform-for-of',
|
||||
['babel-plugin-transform-import-meta', { module: 'ES6' }],
|
||||
],
|
||||
},
|
||||
production: {
|
||||
@@ -45,7 +30,6 @@ module.exports = {
|
||||
// WebPack handles ES6 --> Target Syntax
|
||||
['@babel/preset-env', { modules: false }],
|
||||
'@babel/preset-react',
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
|
||||
},
|
||||
@@ -54,9 +38,26 @@ module.exports = {
|
||||
// WebPack handles ES6 --> Target Syntax
|
||||
['@babel/preset-env', { modules: false }],
|
||||
'@babel/preset-react',
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
plugins: ['react-hot-loader/babel'],
|
||||
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// TODO: Plugins; Aliases
|
||||
// We don't currently use aliases, but this is a nice snippet that would help
|
||||
// [
|
||||
// 'module-resolver',
|
||||
// {
|
||||
// // https://github.com/tleunen/babel-plugin-module-resolver/issues/338
|
||||
// // There seem to be a bug with module-resolver with a mono-repo setup:
|
||||
// // It doesn't resolve paths correctly when using root/alias combo, so we
|
||||
// // use this function instead.
|
||||
// resolvePath(sourcePath, currentFile, opts) {
|
||||
// // This will return undefined if aliases has no key for the sourcePath,
|
||||
// // in which case module-resolver will fallback on its default behaviour.
|
||||
// return aliases[sourcePath];
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
@@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Set directory to location of this script
|
||||
# https://stackoverflow.com/a/3355423/1867984
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
yarn -v
|
||||
node -v
|
||||
|
||||
echo 'Installing Gitbook CLI'
|
||||
yarn global add gitbook-cli
|
||||
|
||||
echo 'Running Gitbook installation'
|
||||
|
||||
# Generate all version's GitBook output
|
||||
# For each directory in /docs ...
|
||||
cd ./../docs/
|
||||
for D in *; do
|
||||
if [ -d "${D}" ]; then
|
||||
|
||||
echo "Generating output for: ${D}"
|
||||
cd "${D}"
|
||||
|
||||
# Clear previous output, generate new
|
||||
rm -rf _book
|
||||
gitbook install
|
||||
gitbook build
|
||||
|
||||
cd ..
|
||||
|
||||
fi
|
||||
done
|
||||
|
||||
# Move CNAME File into `latest`
|
||||
cp CNAME ./latest/_book/CNAME
|
||||
|
||||
# Create a history folder in our latest version's output
|
||||
mkdir ./latest/_book/history
|
||||
|
||||
# Move each version's files to latest's history folder
|
||||
for D in *; do
|
||||
if [ -d "${D}" ]; then
|
||||
if [ "${D}" == v* ] ; then
|
||||
|
||||
echo "Moving ${D} to the latest version's history folder"
|
||||
|
||||
mkdir "./latest/_book/history/${D}"
|
||||
cp -v -r "./${D}/_book"/* "./latest/_book/history/${D}"
|
||||
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Back to repo root
|
||||
cd ..
|
||||
|
||||
echo "Done generating documentation output"
|
||||
echo 'PUBLISHING'
|
||||
|
||||
./node_modules/.bin/gh-pages \
|
||||
--silent \
|
||||
--repo https://$GITHUB_TOKEN@github.com/OHIF/Viewers.git \
|
||||
--message 'Autogenerated Message: [ci skip]' \
|
||||
--dist docs/latest/_book
|
||||
@@ -1 +0,0 @@
|
||||
6dd150d401ad73d60632a23378b7dfd4b5142690
|
||||
@@ -0,0 +1 @@
|
||||
_book/
|
||||
@@ -0,0 +1 @@
|
||||
docs.ohif.org
|
||||
@@ -0,0 +1,46 @@
|
||||
# How To: Documentation Step-by-Step
|
||||
|
||||
We use [GitBook](https://www.gitbook.com/) to create our documentation. It primarily uses markdown, html, css, js, misc. plugins, and configuration to generate high quality, easy to read, and easy to maintain documentation.
|
||||
|
||||
## Getting Started
|
||||
|
||||
_Requirements:_
|
||||
|
||||
Make sure you have the [`gitbook-cli`](https://www.npmjs.com/package/gitbook-cli) installed globally:
|
||||
|
||||
> `npm install -g gitbook-cli`
|
||||
|
||||
### Editing and Previewing Changes
|
||||
|
||||
Currently, you can only edit and preview a single "book" at a time. We maintain one "book" per API major version. You can find each version's book at:
|
||||
|
||||
_Past Versions:_
|
||||
|
||||
- Template:
|
||||
- `<project-root>/docs/v<versionNumber>`
|
||||
- Examples:
|
||||
- `/docs/v1`
|
||||
- `/docs/v2`
|
||||
|
||||
_Latest Version:_
|
||||
|
||||
The latest version will always be located in `/docs/latest`
|
||||
|
||||
_Live Preview:_
|
||||
|
||||
In your terminal / command prompt:
|
||||
|
||||
```bash
|
||||
cd /docs/latest
|
||||
gitbook install
|
||||
gitbook serve
|
||||
```
|
||||
|
||||
Which should generate output like:
|
||||
|
||||
> starting server...
|
||||
> serving book on http://localhost:4000
|
||||
|
||||
Navigating to the the provided URL will show a preview of what the generated book should look like. Any edits you make to the book's markdown files should automatically update in your browser.
|
||||
|
||||
### Publishing
|
||||
@@ -0,0 +1,3 @@
|
||||
# Netlify redirects
|
||||
# SPA rules for our docs
|
||||
/* /index.html 200
|
||||
@@ -0,0 +1,54 @@
|
||||
<div class='row'>
|
||||
<div class='column' style='text-align: right; padding: 0 20px'>
|
||||
<strong>Looking for a Live Demo?</strong>
|
||||
<a href="http://viewer.ohif.org/">Preview The OHIF Viewer</a>
|
||||
</div>
|
||||
<div class='column' style='text-align: left; padding: 0 20px'>
|
||||
<a href="https://www.netlify.com">
|
||||
<img src="https://www.netlify.com/img/global/badges/netlify-color-bg.svg"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
> ATTENTION! You are looking at the docs for the `React` version of the OHIF
|
||||
> Viewer. If you're looking for the `Meteor` version's documentation (now
|
||||
> deprecated), select it's version from the dropdown box in the top left corner
|
||||
> of this page.
|
||||
|
||||
# Introduction
|
||||
|
||||
The [Open Health Imaging Foundation][ohif-org] (OHIF) Viewer is an open source,
|
||||
web-based, medical imaging viewer. It can be configured to connect to Image
|
||||
Archives that support [DicomWeb][dicom-web], and offers support for mapping to
|
||||
proprietary API formats. OHIF maintained extensions add support for viewing,
|
||||
annotating, and reporting on DICOM images in 2D (slices) and 3D (volumes).
|
||||
|
||||

|
||||
|
||||
<center><i>The <strong>OHIF Viewer</strong>: A general purpose DICOM Viewer (<a href="http://viewer.ohif.org/">Live Demo</a>)</center>
|
||||
|
||||
The Open Health Imaging Foundation intends to provide a simple general purpose
|
||||
DICOM Viewer which can be easily extended for specific uses. If you find
|
||||
yourself unable to extend the viewer for your purposes, please reach out via our
|
||||
[GitHub issues][gh-issues]. We are actively seeking feedback on ways to improve
|
||||
our integration and extension points.
|
||||
|
||||
## Where to next?
|
||||
|
||||
Check out these helpful links:
|
||||
|
||||
- Ready to dive into some code? Check out our
|
||||
[Getting Started Guide](./development/getting-started.md).
|
||||
- We're an active, vibrant community.
|
||||
[Learn how you can be more involved.](./development/contributing.md)
|
||||
- Feeling lost? Read our [help page](./help.md).
|
||||
|
||||
<!--
|
||||
Links
|
||||
-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[ohif-org]: http://www.ohif.org
|
||||
[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb
|
||||
[gh-issues]: https://github.com/OHIF/Viewers/issues
|
||||
<!-- prettier-ignore-end -->
|
||||
@@ -0,0 +1,61 @@
|
||||
# OHIF Viewers
|
||||
|
||||
- [Our Process](our-process.md)
|
||||
- Development
|
||||
- [Getting Started](development/getting-started.md)
|
||||
- [Contributing](development/contributing.md)
|
||||
- [Continuous Integration](development/continous-integration.md)
|
||||
- [Testing](development/testing.md)
|
||||
- [Configuring](configuring/index.md)
|
||||
- [Data Source](configuring/data-source.md)
|
||||
|
||||
---
|
||||
|
||||
- [Architecture](architecture/index.md)
|
||||
- [Viewer](viewer/index.md)
|
||||
- [Configuration](viewer/configuration.md)
|
||||
- [Themeing](viewer/themeing.md)
|
||||
- [Internationalization](viewer/internationalization.md)
|
||||
- [Extensions](extensions/index.md)
|
||||
- [Registering](extensions/index.md#registering-an-extension)
|
||||
- [Lifecycle Hooks](extensions/index.md#lifecycle-hooks)
|
||||
- [preRegistration](extensions/lifecycle/pre-registration.md)
|
||||
- [Modules](extensions/index.md#modules)
|
||||
- [Commands](extensions/modules/commands.md)
|
||||
- [Panel](extensions/modules/panel.md)
|
||||
- [SOP Class Handler](extensions/modules/sop-class-handler.md)
|
||||
- [Toolbar](extensions/modules/toolbar.md)
|
||||
- [Viewport](extensions/modules/viewport.md)
|
||||
- [Contexts](extensions/index.md#contexts)
|
||||
- [ExtensionManager](extensions/index.md#extensionmanager)
|
||||
- [OHIF Maintained](extensions/index.md#maintained-extensions)
|
||||
- [Services](services/index.md)
|
||||
- [Default](services/default/index.md)
|
||||
- [UI](services/ui/index.md)
|
||||
- [Dialog Service](services/ui/ui-dialog-service.md)
|
||||
- [Modal Service](services/ui/ui-modal-service.md)
|
||||
- [Notification Service](services/ui/ui-notification-service.md)
|
||||
|
||||
---
|
||||
|
||||
- [Deployment](deployment/index.md)
|
||||
- [Embedded](deployment/index.md#embedded-viewer)
|
||||
- [Stand-alone](deployment/index.md#stand-alone-viewer)
|
||||
- [Data](deployment/index.md#data)
|
||||
- Recipes
|
||||
- Script Include
|
||||
- [Embedding the Viewer](deployment/recipes/embedded-viewer.md)
|
||||
- Stand-Alone
|
||||
- [Build for Production](deployment/recipes/build-for-production.md)
|
||||
- [Static](deployment/recipes/static-assets.md)
|
||||
- [Nginx + Image Archive](deployment/recipes/nginx--image-archive.md)
|
||||
- [User Account Control](deployment/recipes/user-account-control.md)
|
||||
- [Google Cloud Healthcare](connecting-to-image-archives/google-cloud-healthcare.md)
|
||||
|
||||
---
|
||||
|
||||
- [FAQ](faq/index.md)
|
||||
- [Scope of Project](faq/scope-of-project.md)
|
||||
- [Browser Support](faq/browser-support.md)
|
||||
- [PWA vs Packaged](faq/pwa-vs-packaged.md)
|
||||
- [Help](help.md)
|
||||
@@ -0,0 +1,153 @@
|
||||
# Architecture
|
||||
|
||||
Looking to extend your instance of the OHIF Viewer? Want learn how to reuse _a
|
||||
portion_ of the Viewer in your own application? Or maybe you want to get
|
||||
involved and draft or suggest a new feature? Regardless, you're in the right
|
||||
place!
|
||||
|
||||
The OHIF Viewer aims to be decoupled, configurable, and extensible; while this
|
||||
allows our code to be used in more ways, it also increases complexity. Below, we
|
||||
aim to demistify that complexity by providing insight into how our Viewer is
|
||||
architected, and the role each of it's dependent libraries plays.
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Business Logic](#business-logic)
|
||||
- [Component Library](#react-component-library)
|
||||
- [Extensions & Configuration](#extensions--configuration)
|
||||
- [Common Questions](#common-questions)
|
||||
|
||||
## Overview
|
||||
|
||||
The [OHIF Medical Image Viewing Platform][viewers-project] is maintained as a
|
||||
[`monorepo`][monorepo]. This means that this repository, instead of containing a
|
||||
single project, contains many projects. If you explore our project structure,
|
||||
you'll see the following:
|
||||
|
||||
```bash
|
||||
.
|
||||
├── extensions
|
||||
│ ├── _example # Skeleton of example extension
|
||||
│ ├── cornerstone # 2D images w/ Cornerstone.js
|
||||
│ ├── dicom-html # Structured Reports as HTML in viewport
|
||||
│ ├── dicom-microscopy # Whole slide microscopy viewing
|
||||
│ ├── dicom-pdf # View DICOM wrapped PDFs in viewport
|
||||
│ └── vtk # MPR and Volume support w/ VTK.js
|
||||
│
|
||||
├── platform
|
||||
│ ├── core # Business Logic
|
||||
│ ├── i18n # Internationalization Support
|
||||
│ ├── ui # React component library
|
||||
│ └── viewer # Connects platform and extension projects
|
||||
│
|
||||
├── ... # misc. shared configuration
|
||||
├── lerna.json # MonoRepo (Lerna) settings
|
||||
├── package.json # Shared devDependencies and commands
|
||||
└── README.md
|
||||
```
|
||||
|
||||
The `platform` directory contains the business logic library, component library,
|
||||
and the application library that combines them to create a powerful medical
|
||||
imaging viewer.
|
||||
|
||||
The `extensions` directory contains many packages that can be registered with
|
||||
`@ohif/core`'s `ExtensionManager` to expand an application's supported features
|
||||
and functionality.
|
||||
|
||||

|
||||
|
||||
<center><i>architecture diagram</i></center>
|
||||
|
||||
This diagram is a conceptual illustration of how the Viewer is architected.
|
||||
|
||||
1. (optional) `extensions` can be registered with `@ohif/core`'s
|
||||
`ExtensionManager`
|
||||
2. `@ohif/core` provides bussiness logic and a way for `@ohif/viewer` to access
|
||||
registered extensions
|
||||
3. The `@ohif/viewer` composes and provides data to components from our
|
||||
component library (`@ohif/ui`)
|
||||
4. The `@ohif/viewer` can be built and served as a stand-alone PWA, or as an
|
||||
embeddable package ([`@ohif/viewer`][viewer-npm])
|
||||
|
||||
## Business Logic
|
||||
|
||||
The [`@ohif/core`][core-github] project offers pre-packaged solutions for
|
||||
features common to Web-based medical imaging viewers. For example:
|
||||
|
||||
- Hotkeys
|
||||
- DICOM Web requests
|
||||
- Hanging Protocols
|
||||
- Managing a study's measurements
|
||||
- Managing a study's DICOM metadata
|
||||
- [A flexible pattern for extensions](../extensions/index.md)
|
||||
- And many others
|
||||
|
||||
It does this while remaining decoupled from any particular view library or
|
||||
rendering logic. While we use it to power our React Viewer, it can be used with
|
||||
Vue, React, Vanilla JS, or any number of other frameworks.
|
||||
|
||||
## React Component Library
|
||||
|
||||
[`@ohif/ui`][ui-github] is a React Component library that contains the reusable
|
||||
components that power the OHIF Viewer. It allows us to build, compose, and test
|
||||
components in isolation; easing the development process by reducing the need to
|
||||
stand-up a local PACS with test case data.
|
||||
|
||||
Extension authors can also use these same components when building their
|
||||
extension's UI; allowing for a consistent look and feel with the rest of the
|
||||
application.
|
||||
|
||||
[Check out our component library!](https://react.ohif.org/)
|
||||
|
||||
## Extensions & Configuration
|
||||
|
||||
While OHIF maintains several high value and commonly requested features in its
|
||||
own extensions, there are many instances where one may wish to further extend
|
||||
the viewer. Some common use cases include:
|
||||
|
||||
- Adding AI/ML tools and insights
|
||||
- Custom workflows for guided diagnosis
|
||||
- Collecting specific annotations for training data or reports
|
||||
- Authentication and granular permissions
|
||||
- Teleconsultation workflow, image comments, and tracking
|
||||
- Adding surgical templating tools and reports
|
||||
- and many others
|
||||
|
||||
We expose common integration points via [extensions](../extensions/index.md) to
|
||||
make this possible. The viewer and many of our own extensions also offer
|
||||
[configuration][configuration]. For a list of extensions maintained by OHIF,
|
||||
[check out this helpful table](../extensions/index.md#maintained-extensions).
|
||||
|
||||
If you find yourself thinking "I wish the Viewer could do X", and you can't
|
||||
accomplish it with an extension today, create a GitHub issue! We're actively
|
||||
looking for ways to improve our extensibility ^\_^
|
||||
|
||||
[Click here to read more about extensions!](../extensions/index.md)
|
||||
|
||||
## Common Questions
|
||||
|
||||
> When should I use the packaged source `@ohif/viewer` versus building a PWA
|
||||
> from the source?
|
||||
|
||||
...
|
||||
|
||||
> Can I create my own Viewer using Vue.js or Angular.js?
|
||||
|
||||
You can, but you will not be able to leverage as much of the existing code and
|
||||
components. `@ohif/core` could still be used for business logic, and to provide
|
||||
a model for extensions. `@ohif/ui` would then become a guide for the components
|
||||
you would need to recreate.
|
||||
|
||||
<!--
|
||||
Links
|
||||
-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[monorepo]: https://github.com/OHIF/Viewers/issues/768
|
||||
[viewers-project]: https://github.com/OHIF/Viewers
|
||||
[viewer-npm]: https://www.npmjs.com/package/@ohif/viewer
|
||||
[pwa]: https://developers.google.com/web/progressive-web-apps/
|
||||
[configuration]: ../configuring/index.md
|
||||
[extensions]: ../extensions/index.md
|
||||
[core-github]: https://github.com/OHIF/viewers/platform/core
|
||||
[ui-github]: https://github.com/OHIF/Viewers/tree/master/platform/ui
|
||||
<!-- prettier-ignore-end -->
|
||||
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 355 KiB |