Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ec9cab2
Add WEKO CI scaffolding
yacchin1205 Nov 28, 2025
1b8765e
Fix timeout for metadata tab
yacchin1205 Nov 29, 2025
bc02d59
Add console.log capture to playwright
yacchin1205 Nov 29, 2025
a245fb2
Add JupyterHub (TLJH) to E2E test matrix
yacchin1205 Nov 29, 2025
679fb7a
Fix dz- class for https://github.com/RCOSDP/RDM-osf.io/pull/636
yacchin1205 Dec 2, 2025
310cafc
Add binderhub notebooks
yacchin1205 Dec 2, 2025
ae46241
Fix params/metadata variables/notebooks
yacchin1205 Dec 2, 2025
b5cbd60
Add WEKO notebooks and test support
yacchin1205 Dec 5, 2025
4388d35
Add GRDM file sync check to BinderHub addon tests
yacchin1205 Dec 8, 2025
bc84f5d
Fix XPath locators to use [1] index
yacchin1205 Dec 8, 2025
2317d14
Fix WEKO metadata form and add WEKO settings
yacchin1205 Dec 8, 2025
c260096
Fix WaterButler env var: DOMAIN -> SERVER_CONFIG_DOMAIN
yacchin1205 Dec 9, 2025
29ef61e
Add patch to include item:create scope in WEKO default_scopes
yacchin1205 Dec 9, 2025
a2707ce
Add asyncio.sleep(10) after package add operations to reduce flakiness
yacchin1205 Dec 9, 2025
3a72420
Add SWORD Client registration for WEKO addon
yacchin1205 Dec 9, 2025
0477c37
Fix SWORD mapping 30002 and validation exit code
yacchin1205 Dec 9, 2025
c248689
Update metadata field label: データの名称 → データの名称または論文表題
yacchin1205 Dec 10, 2025
ca13fac
Remove obsolete WEKO default_scopes patch
yacchin1205 Dec 10, 2025
fba451e
Fix repository URL replacement condition for 192.168.168.167
yacchin1205 Dec 10, 2025
ee24e1c
Add WEKO chardet fix patch for ZIP filename handling
yacchin1205 Dec 10, 2025
9e0c147
Update delete icon selector from fa-times to fa-trash
yacchin1205 Dec 10, 2025
39ffee7
Add WEKO patch to delay file content task to avoid ES version conflict
yacchin1205 Dec 11, 2025
2ddce5d
Remove localhost URL replacement workarounds from Metadata addon tests
yacchin1205 Dec 16, 2025
b72fef9
Add workflow parameters for TLJH configuration
yacchin1205 Dec 16, 2025
f8833a8
Fix notebook header level and rename e-Rad to eRad for Excel export
yacchin1205 Dec 18, 2025
6b32f68
TEST: Remove the patch about chardet
yacchin1205 Dec 19, 2025
4f8db6e
Refactor WEKO notebooks
yacchin1205 Dec 22, 2025
5d7bf87
Improve mapping checkings
yacchin1205 Dec 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
322 changes: 322 additions & 0 deletions .github/patches/sword_mapping_30002.json

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions .github/patches/weko-oauth2-insecure-transport.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--- a/modules/invenio-oauth2server/invenio_oauth2server/validators.py
+++ b/modules/invenio-oauth2server/invenio_oauth2server/validators.py
@@ -10,6 +10,7 @@

from __future__ import absolute_import, print_function

+import os
from flask import current_app
from oauthlib.oauth2.rfc6749.errors import InsecureTransportError, \
InvalidRedirectURIError
@@ -32,6 +33,8 @@ def validate_redirect_uri(value):
if sch != 'https':
if ':' in netloc:
netloc, port = netloc.split(':', 1)
+ if os.environ.get('OAUTHLIB_INSECURE_TRANSPORT'):
+ return
if not (netloc in ('localhost', '127.0.0.1') and sch == 'http'):
raise InsecureTransportError()

58 changes: 57 additions & 1 deletion .github/scripts/generate_ci_config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set -xeuo pipefail

if [[ $# -lt 2 ]]; then
cat >&2 <<'USAGE'
Usage: generate_ci_config.sh <output_path> <base_config_yaml> [--minio]
Usage: generate_ci_config.sh <output_path> <base_config_yaml> [--minio] [--jupyterhub]
USAGE
exit 1
fi
Expand All @@ -12,12 +12,20 @@ OUTPUT=$1
BASE_CONFIG=$2
shift 2
MINIO=false
JUPYTERHUB=false
WEKO=false

for arg in "$@"; do
case "$arg" in
--minio)
MINIO=true
;;
--jupyterhub)
JUPYTERHUB=true
;;
--weko)
WEKO=true
;;
*)
echo "Unknown argument: ${arg}" >&2
exit 1
Expand Down Expand Up @@ -61,4 +69,52 @@ storages_s3: []
EOF
fi

if [[ "${JUPYTERHUB}" == "true" ]]; then
if [[ -z "${TLJH_URL:-}" || -z "${TLJH_USERNAME:-}" || -z "${TLJH_PASSWORD:-}" ]]; then
echo "TLJH connection information is not set" >&2
exit 1
fi

cat >> "${OUTPUT}" <<EOF

jupyterhub_enabled: true
tljh_url: '${TLJH_URL}'
tljh_username: '${TLJH_USERNAME}'
tljh_password: '${TLJH_PASSWORD}'
EOF
else
cat >> "${OUTPUT}" <<'EOF'

jupyterhub_enabled: false
EOF
fi

if [[ "${WEKO}" == "true" ]]; then
WEKO_URL_VALUE=${WEKO_URL:-http://localhost}
WEKO_ADMIN_EMAIL_VALUE=${WEKO_ADMIN_EMAIL:-}
WEKO_ADMIN_PASSWORD_VALUE=${WEKO_ADMIN_PASSWORD:-}
WEKO_USER_EMAIL_VALUE=${WEKO_USER_EMAIL:-}
WEKO_USER_PASSWORD_VALUE=${WEKO_USER_PASSWORD:-}
WEKO_INSTITUTION_NAME_VALUE=${WEKO_INSTITUTION_NAME:-}
WEKO_INDEX_NAME_VALUE=${WEKO_INDEX_NAME:-'Sample Index'}
WEKO_DOCKER_COMPOSE_PATH_VALUE=${WEKO_DOCKER_COMPOSE_PATH:-}
SWORD_MAPPING_ID_VALUE=${SWORD_MAPPING_ID:-30002}
IGNORE_HTTPS_ERRORS_VALUE=${IGNORE_HTTPS_ERRORS:-false}

cat >> "${OUTPUT}" <<EOF

# WEKO / JAIRO Cloud settings
weko_url: '${WEKO_URL_VALUE}'
weko_admin_email: '${WEKO_ADMIN_EMAIL_VALUE}'
weko_admin_password: '${WEKO_ADMIN_PASSWORD_VALUE}'
weko_user_email: '${WEKO_USER_EMAIL_VALUE}'
weko_user_password: '${WEKO_USER_PASSWORD_VALUE}'
weko_institution_name: '${WEKO_INSTITUTION_NAME_VALUE}'
weko_index_name: '${WEKO_INDEX_NAME_VALUE}'
weko_docker_compose_path: '${WEKO_DOCKER_COMPOSE_PATH_VALUE}'
sword_mapping_id: ${SWORD_MAPPING_ID_VALUE}
ignore_https_errors: ${IGNORE_HTTPS_ERRORS_VALUE}
EOF
fi

cat "${OUTPUT}"
122 changes: 114 additions & 8 deletions .github/scripts/setup_rdm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,15 @@ start_rdm_services() {
# Function to test RDM endpoints
test_rdm_endpoints() {
local include_admin="${INCLUDE_ADMIN:-false}"

# Test main endpoints
ENDPOINT_NAME="OSF Web (port 5000)" ENDPOINT_URL="http://localhost:5000/" test_endpoint
ENDPOINT_NAME="OSF API (port 8000)" ENDPOINT_URL="http://localhost:8000/v2/" test_endpoint
ENDPOINT_NAME="OSF Web (port 5000)" ENDPOINT_URL="http://192.168.168.167:5000/" test_endpoint
ENDPOINT_NAME="OSF API (port 8000)" ENDPOINT_URL="http://192.168.168.167:8000/v2/" test_endpoint
ENDPOINT_NAME="Ember OSF Web (port 4200)" ENDPOINT_URL="http://localhost:4200/" test_endpoint
ENDPOINT_NAME="WaterButler (port 7777)" ENDPOINT_URL="http://localhost:7777/status" test_endpoint
ENDPOINT_NAME="WaterButler (port 7777)" ENDPOINT_URL="http://192.168.168.167:7777/status" test_endpoint
ENDPOINT_NAME="MFR (port 7778)" ENDPOINT_URL="http://localhost:7778/status" test_endpoint
ENDPOINT_NAME="FakeCAS (port 8080)" ENDPOINT_URL="http://localhost:8080/login" test_endpoint

# Test admin endpoint if admin is enabled
if [ "$include_admin" = "true" ]; then
ENDPOINT_NAME="Admin Web (port 8001)" ENDPOINT_URL="http://localhost:8001/" test_endpoint
Expand All @@ -135,18 +135,91 @@ setup_config_files() {
echo "ENABLE_MULTILINGUAL_SEARCH = True" >> ./website/settings/local.py
echo "SEARCH_ANALYZER = defaults.SEARCH_ANALYZER_JAPANESE" >> ./website/settings/local.py
echo "LOG_LEVEL = logging.DEBUG" >> ./website/settings/local.py
# Configure external access via 192.168.168.167
echo "" >> ./website/settings/local.py
echo "# External access configuration for CI environment" >> ./website/settings/local.py
echo "DOMAIN = 'http://192.168.168.167:5000/'" >> ./website/settings/local.py
echo "INTERNAL_DOMAIN = DOMAIN" >> ./website/settings/local.py
echo "API_DOMAIN = 'http://192.168.168.167:8000/'" >> ./website/settings/local.py
echo "WATERBUTLER_URL = 'http://192.168.168.167:7777'" >> ./website/settings/local.py
echo "WATERBUTLER_INTERNAL_URL = WATERBUTLER_URL" >> ./website/settings/local.py
echo "OSF_COOKIE_DOMAIN = '192.168.168.167'" >> ./website/settings/local.py

cp ./api/base/settings/local-dist.py ./api/base/settings/local.py
echo "" >> ./api/base/settings/local.py
echo "# External access configuration for CI environment" >> ./api/base/settings/local.py
echo "ALLOWED_HOSTS.append('192.168.168.167')" >> ./api/base/settings/local.py

cp ./docker-compose-dist.override.yml ./docker-compose.override.yml
cp ./tasks/local-dist.py ./tasks/local.py

# Create admin settings if requested
if [ "$include_admin" = "true" ]; then
cp ./admin/base/settings/local-dist.py ./admin/base/settings/local.py
echo "ALLOWED_HOSTS = ['localhost']" >> ./admin/base/settings/local.py
echo "" >> ./admin/base/settings/local.py
echo "# External access configuration for CI environment" >> ./admin/base/settings/local.py
echo "ALLOWED_HOSTS = ['192.168.168.167', 'localhost']" >> ./admin/base/settings/local.py
echo "SESSION_COOKIE_DOMAIN = '192.168.168.167'" >> ./admin/base/settings/local.py
echo "Admin configuration files created"
fi


# Create binderhub settings
cat > ./addons/binderhub/settings/local.py << 'BINDERHUB_EOF'
BINDERHUB_DEPLOYMENT_IMAGES = [
{
'url': 'yacchin1205/cs-binder-dockerfiles:python-test-lab-4.2.6',
'name': 'Data Science Notebook (JupyterLab 4.2, TEST)',
'description_ja': 'データサイエンス向けのパッケージを含むPython, R, Juliaの実行環境です(テスト)。 https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html#jupyter-datascience-notebook',
'description_en': 'A Python, R, and Julia environment with popular packages for data analysis. https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html#jupyter-datascience-notebook',
'packages': ['conda', 'pip', 'rcran', 'rgithub'],
},
{
'url': '#repo2docker#python=3.12.*,r-base=4.4.*',
'name': 'Python 3.12 + R 4.4 (JupyterLab 4.x)',
'description_ja': 'Jupyter Notebook, JupyterLab, RStudio, Shinyが使えます。',
'description_en': 'Jupyter Notebook, JupyterLab, RStudio, and Shiny are available.',
'packages': ['conda', 'pip', 'rmran', 'mpm'],
'recommended': True,
},
{
'url': 'gcr.io/nii-ap-ops/base/datascience-notebook:main-lab4.x',
'name': 'Data Science Notebook (JupyterLab 4.x)',
'description_ja': 'データサイエンス向けのパッケージを含むPython, R, Juliaの実行環境です。 https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html#jupyter-datascience-notebook',
'description_en': 'A Python, R, and Julia environment with popular packages for data analysis. https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html#jupyter-datascience-notebook',
'packages': ['conda', 'pip', 'rcran', 'rgithub'],
},
{
'url': 'gcr.io/nii-ap-ops/base/matlab-notebook:r2023b',
'name': 'MATLAB (R2023b)',
'description_ja': 'MATLABがインストールされたJupyterLab実行環境です。 https://github.com/mathworks-ref-arch/matlab-integration-for-jupyter/tree/main/matlab',
'description_en': 'JupyterLab runtime environment with MATLAB installed. https://github.com/mathworks-ref-arch/matlab-integration-for-jupyter/tree/main/matlab',
'packages': ['conda', 'pip'],
},
{
'url': '#repo2docker#python=3.12.*,r-base=4.3.*',
'name': 'Python 3.12 + R 4.3 (JupyterLab 4.x, deprecated)',
'description_ja': 'Jupyter Notebook, JupyterLab, RStudio, Shinyが使えます。',
'description_en': 'Jupyter Notebook, JupyterLab, RStudio, and Shiny are available.',
'packages': ['conda', 'pip', 'rmran', 'mpm'],
},
{
'url': '#repo2docker#python=3.9.*,r-base=4.1.3',
'name': 'Python 3.9 + R 4.1.3 (JupyterLab 3.x, deprecated)',
'description_ja': 'Jupyter Notebook, JupyterLab, RStudio, Shinyが使えます。',
'description_en': 'Jupyter Notebook, JupyterLab, RStudio, and Shiny are available.',
'packages': ['conda', 'pip', 'rmran'],
},
{
'url': 'gcr.io/nii-ap-ops/base/datascience-notebook:main',
'name': 'Data Science Notebook (JupyterLab 3.6, deprecated)',
'description_ja': 'データサイエンス向けのパッケージを含むPython, R, Juliaの実行環境です。 https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html#jupyter-datascience-notebook',
'description_en': 'A Python, R, and Julia environment with popular packages for data analysis. https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html#jupyter-datascience-notebook',
'packages': ['conda', 'pip', 'rcran', 'rgithub'],
},
]
BINDERHUB_EOF
echo "BinderHub configuration files created"

echo "Configuration files setup completed"
}

Expand All @@ -173,6 +246,7 @@ create_docker_override() {
services:
fakecas:
image: niicloudoperation/rdm-fakecas:latest
command: fakecas -host=0.0.0.0:8080 -osfhost=192.168.168.167:5000 -dbaddress=postgres://postgres@postgres:5432/osf?sslmode=disable
admin:
image: ${osf_image}
environment:
Expand Down Expand Up @@ -207,6 +281,10 @@ services:
KAKEN_ELASTIC_URI: http://kaken_elasticsearch:9200
ember_osf_web:
image: ${ember_image}
environment:
OSF_URL: http://192.168.168.167:5000/
OSF_API_URL: http://192.168.168.167:8000
OSF_FILE_URL: http://192.168.168.167:7777/
cas:
image: ${cas_image}
mfr:
Expand All @@ -233,11 +311,39 @@ EOL

echo "Docker compose override created"

# Replace localhost with 192.168.168.167 in all .docker-compose*.env files for external access
for envfile in .docker-compose*.env; do
if [ -f "$envfile" ]; then
sed -i.bak \
-e 's|localhost:5000|192.168.168.167:5000|g' \
-e 's|localhost:8000|192.168.168.167:8000|g' \
-e 's|localhost:7777|192.168.168.167:7777|g' \
-e 's|localhost:7778|192.168.168.167:7778|g' \
-e 's|localhost:8001|192.168.168.167:8001|g' \
"$envfile"
rm -f "${envfile}.bak"
echo "Updated $envfile for external access:"
cat "$envfile"
fi
done

# Add WaterButler DOMAIN for external access
echo "" >> .docker-compose.wb.env
echo "SERVER_CONFIG_DOMAIN=http://192.168.168.167:7777" >> .docker-compose.wb.env

if [ "${MINIO_ENABLED:-false}" = "true" ]; then
local script_dir
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
bash "${script_dir}/setup_minio.sh" apply "${PWD}"
fi

if [ "${WEKO_ENABLED:-false}" = "true" ]; then
local script_dir
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
python3 "${script_dir}/weko_setup_cert.py" \
"${PWD}/docker-compose.override.yml" \
"${script_dir}/../../../weko/nginx/keys/server.crt"
fi
}

# Function to run Django migrations
Expand Down
56 changes: 52 additions & 4 deletions .github/scripts/setup_test_data.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
from django.utils import timezone
from osf.models import OSFUser, Node
from osf.models import OSFUser, Node, Institution

INSTITUTION_NAME = 'Virginia Tech [Test]'

test_users = [
{'username': 'testuser1@example.com', 'fullname': 'Test User 1', 'password': 'testpass123', 'is_superuser': True},
{'username': 'testuser2@example.com', 'fullname': 'Test User 2', 'password': 'testpass456'},
{
'username': 'testuser1@example.com',
'fullname': 'Test User 1',
'given_name': 'Test',
'family_name': 'User 1',
'given_name_ja': 'テスト',
'family_name_ja': 'ユーザー1',
'password': 'testpass123',
'is_superuser': True,
},
{
'username': 'testuser2@example.com',
'fullname': 'Test User 2',
'given_name': 'Test',
'family_name': 'User 2',
'given_name_ja': 'テスト',
'family_name_ja': 'ユーザー2',
'password': 'testpass456',
},
]

for user_data in test_users:
Expand All @@ -13,6 +32,10 @@
user = OSFUser(
username=username,
fullname=user_data['fullname'],
given_name=user_data['given_name'],
family_name=user_data['family_name'],
given_name_ja=user_data['given_name_ja'],
family_name_ja=user_data['family_name_ja'],
is_active=True,
date_registered=timezone.now()
)
Expand All @@ -22,6 +45,20 @@
user.is_registered = True
user.date_confirmed = timezone.now()
user.have_email = True
# Set jobs for profile completion (required for is_full_account_required_info)
# Structure must match unserialize_job in website/profile/views.py
user.jobs = [{
'institution': INSTITUTION_NAME,
'department': None,
'institution_ja': INSTITUTION_NAME,
'department_ja': None,
'title': None,
'startMonth': None,
'startYear': None,
'endMonth': None,
'endYear': None,
'ongoing': None,
}]
# Set superuser if specified
if user_data.get('is_superuser', False):
user.is_superuser = True
Expand Down Expand Up @@ -61,4 +98,15 @@
project = user.nodes.filter(category='project').first()
# Output for CI config
print(f"PROJECT_ID_{username}: {project._id}")
print(f"PROJECT_NAME_{username}: {project.title}")
print(f"PROJECT_NAME_{username}: {project.title}")

# Affiliate users with institution
inst = Institution.objects.get(name=INSTITUTION_NAME)
for user_data in test_users:
user = OSFUser.objects.get(username=user_data['username'])
if inst not in user.affiliated_institutions.all():
user.affiliated_institutions.add(inst)
user.save()
print(f"Affiliated {user.username} with {inst.name}")
else:
print(f"User {user.username} already affiliated with {inst.name}")
Loading
Loading