feat(archives): RiC-CM full roadmap (Phases 1-6) — RiC-O Linked Data, agents/activities/places, admin UI, OAI-PMH ric-o (v0.7.12, #122) #68
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Upgrade Smoke Test | |
| # Simulates a real-world upgrade: installs the latest stable release, then | |
| # applies the current branch's migrations on top. Catches migration regressions | |
| # that would break existing installations. | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - 'installer/database/**' | |
| - 'version.json' | |
| - '.github/workflows/ci-upgrade-smoke.yml' | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - 'installer/database/**' | |
| - 'version.json' | |
| - '.github/workflows/ci-upgrade-smoke.yml' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| upgrade-smoke: | |
| name: Upgrade from latest release → verify schema | |
| runs-on: ubuntu-latest | |
| services: | |
| mysql: | |
| image: mysql:8.0 | |
| env: | |
| MYSQL_ROOT_PASSWORD: root | |
| MYSQL_DATABASE: pinakes_upgrade | |
| ports: | |
| - 3306:3306 | |
| options: >- | |
| --health-cmd="mysqladmin ping" | |
| --health-interval=10s | |
| --health-timeout=5s | |
| --health-retries=5 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup PHP | |
| uses: shivammathur/setup-php@v2 | |
| with: | |
| php-version: '8.2' | |
| coverage: none | |
| github-token: '' | |
| - name: Wait for MySQL | |
| run: until mysqladmin ping -h"127.0.0.1" --silent; do sleep 1; done | |
| # ── Step 1: Resolve latest stable release ───────────────────────────── | |
| - name: Resolve latest release | |
| id: release | |
| env: | |
| GH_REPO: ${{ github.repository }} | |
| run: | | |
| HTTP_STATUS=$(curl -s -o /tmp/release.json -w "%{http_code}" \ | |
| "https://api.github.com/repos/${GH_REPO}/releases/latest") | |
| if [ "$HTTP_STATUS" = "404" ]; then | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| echo "No releases found (HTTP 404) — skipping upgrade path (fresh install test only)" | |
| exit 0 | |
| elif [ "$HTTP_STATUS" != "200" ]; then | |
| echo "GitHub API returned HTTP $HTTP_STATUS — aborting" | |
| exit 1 | |
| fi | |
| TAG=$(python3 -c "import json; d=json.load(open('/tmp/release.json')); print(d.get('tag_name',''))") | |
| ASSET=$(python3 -c "import json; d=json.load(open('/tmp/release.json')); assets=[a['browser_download_url'] for a in d.get('assets',[]) if a['name'].endswith('.zip')]; print(assets[0] if assets else '')") | |
| if [ -z "$TAG" ] || [ -z "$ASSET" ]; then | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| echo "No ZIP asset found in latest release — skipping upgrade path" | |
| exit 0 | |
| fi | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "asset=$ASSET" >> "$GITHUB_OUTPUT" | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| echo "Latest release: $TAG ($ASSET)" | |
| # ── Step 2: Install released schema ─────────────────────────────────── | |
| - name: Install released schema (baseline) | |
| if: steps.release.outputs.skip == 'false' | |
| env: | |
| ASSET_URL: ${{ steps.release.outputs.asset }} | |
| RELEASE_TAG: ${{ steps.release.outputs.tag }} | |
| run: | | |
| curl -sfL -o /tmp/release.zip "$ASSET_URL" | |
| mkdir -p /tmp/pinakes-release | |
| unzip -q /tmp/release.zip -d /tmp/pinakes-release | |
| SCHEMA=$(find /tmp/pinakes-release -name "schema.sql" -path "*/database/*" | head -1) | |
| if [ -z "$SCHEMA" ]; then | |
| echo "schema.sql not found in release ZIP — aborting" | |
| exit 1 | |
| fi | |
| echo "Using schema: $SCHEMA (from $RELEASE_TAG)" | |
| mysql -h 127.0.0.1 -u root -proot pinakes_upgrade < "$SCHEMA" | |
| echo "✓ Base schema installed from $RELEASE_TAG" | |
| # ── Step 3: Apply released migrations (idempotent baseline) ─────────── | |
| # Released migrations run on top of the released schema.sql which is | |
| # already fully migrated up to that release. Every error here is by | |
| # definition idempotent: duplicate columns/keys, FKs that already exist, | |
| # RENAME/CHANGE on columns that were already renamed in schema.sql, etc. | |
| # The extended idempotent filter covers cases that older migration files | |
| # (pre-guard era) produce when re-run on an already-migrated schema. | |
| - name: Apply released migrations | |
| if: steps.release.outputs.skip == 'false' | |
| run: | | |
| MIGRATIONS=$(find /tmp/pinakes-release -type d -name "migrations" | head -1) | |
| if [ -z "$MIGRATIONS" ]; then | |
| echo "No migrations dir in release ZIP — skipping" | |
| exit 0 | |
| fi | |
| FAILED=0 | |
| for sql in $(ls "$MIGRATIONS"/migrate_*.sql 2>/dev/null | sort -V); do | |
| echo "→ $(basename $sql)" | |
| OUTPUT=$(mysql --force -h 127.0.0.1 -u root -proot pinakes_upgrade < "$sql" 2>&1) || true | |
| while IFS= read -r line; do | |
| if echo "$line" | grep -qiE '^ERROR'; then | |
| if echo "$line" | grep -qiE \ | |
| 'duplicate (column name|key|index)|table .* already exists|check constraint .* already exists|can.t drop|duplicate foreign key constraint name|unknown column'; then | |
| echo " ↳ idempotent: $line" | |
| else | |
| echo " ✗ $line" | |
| FAILED=1 | |
| fi | |
| fi | |
| done <<< "$OUTPUT" | |
| done | |
| [ "$FAILED" -eq 0 ] || exit 1 | |
| echo "✓ Released migrations applied" | |
| # ── Step 4: Fresh-install fallback (no prior release) ───────────────── | |
| - name: Install base schema (fresh-install fallback) | |
| if: steps.release.outputs.skip == 'true' | |
| run: | | |
| mysql -h 127.0.0.1 -u root -proot pinakes_upgrade < installer/database/schema.sql | |
| echo "✓ Base schema installed from current branch (no prior release)" | |
| # ── Step 5: Apply current branch migrations (upgrade simulation) ─────── | |
| - name: Apply current branch migrations | |
| run: | | |
| FAILED=0 | |
| for sql in $(ls installer/database/migrations/migrate_*.sql | sort -V); do | |
| echo "→ $(basename $sql)" | |
| OUTPUT=$(mysql --force -h 127.0.0.1 -u root -proot pinakes_upgrade < "$sql" 2>&1) || true | |
| while IFS= read -r line; do | |
| if echo "$line" | grep -qiE '^ERROR'; then | |
| if echo "$line" | grep -qiE 'duplicate (column name|key|index)|table .* already exists|check constraint .* already exists|can.t drop|duplicate foreign key constraint name'; then | |
| echo " ↳ idempotent: $line" | |
| else | |
| echo " ✗ $line" | |
| FAILED=1 | |
| fi | |
| fi | |
| done <<< "$OUTPUT" | |
| done | |
| [ "$FAILED" -eq 0 ] || exit 1 | |
| echo "✓ Current branch migrations applied" | |
| # ── Step 6: Verify schema after upgrade ─────────────────────────────── | |
| - name: Verify core tables present | |
| run: | | |
| CORE_TABLES="admin_notifications api_keys autori author_authority_alternates cms_pages collane consent_log contact_messages copie digital_assets donazioni editori email_templates events feedback gdpr_requests generi home_content import_logs languages libri libri_autori libri_collane libri_donati libri_tag log_modifiche mag_project_config mensole migrations ncip_partners ncip_transactions oai_deleted_records oai_resumption_tokens plugin_data plugin_hooks plugin_logs plugin_settings plugins posizioni preferenze_notifica_utenti prenotazioni prestiti recensioni scaffali sedi staff system_settings tag themes update_logs user_sessions utenti volumi wishlist z39_access_logs z39_rate_limits" | |
| FAILED=0 | |
| MISSING="" | |
| for t in $CORE_TABLES; do | |
| EXISTS=$(mysql -h 127.0.0.1 -u root -proot pinakes_upgrade -sN -e \ | |
| "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='pinakes_upgrade' AND table_name='$t'") | |
| if [ "$EXISTS" -eq 0 ]; then | |
| MISSING="$MISSING $t" | |
| FAILED=1 | |
| fi | |
| done | |
| if [ "$FAILED" -eq 0 ]; then | |
| echo "✓ All $(echo $CORE_TABLES | wc -w | tr -d ' ') core tables present" | |
| else | |
| echo "✗ Missing tables:$MISSING" | |
| exit 1 | |
| fi | |
| - name: Verify ENUM values | |
| run: | | |
| FAILED=0 | |
| V=$(mysql -h 127.0.0.1 -u root -proot pinakes_upgrade -sN -e \ | |
| "SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS \ | |
| WHERE TABLE_SCHEMA='pinakes_upgrade' \ | |
| AND TABLE_NAME='oai_deleted_records' \ | |
| AND COLUMN_NAME='entity_type'" 2>/dev/null || echo "") | |
| if [ -n "$V" ]; then | |
| echo "oai_deleted_records.entity_type: $V" | |
| echo "$V" | grep -q "archival_unit" && echo " ✓ 'archival_unit' present" || { echo " ✗ 'archival_unit' missing"; FAILED=1; } | |
| echo "$V" | grep -q "'archive_unit'" && { echo " ✗ old typo 'archive_unit' still present"; FAILED=1; } || true | |
| fi | |
| V=$(mysql -h 127.0.0.1 -u root -proot pinakes_upgrade -sN -e \ | |
| "SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS \ | |
| WHERE TABLE_SCHEMA='pinakes_upgrade' \ | |
| AND TABLE_NAME='prestiti' \ | |
| AND COLUMN_NAME='origine'" 2>/dev/null || echo "") | |
| if [ -n "$V" ]; then | |
| echo "prestiti.origine: $V" | |
| echo "$V" | grep -q "'ncip'" && echo " ✓ 'ncip' present" || { echo " ✗ 'ncip' missing from prestiti.origine"; FAILED=1; } | |
| fi | |
| [ "$FAILED" -eq 0 ] || exit 1 | |
| - name: Verify key columns from recent migrations | |
| run: | | |
| FAILED=0 | |
| check_col() { | |
| local TABLE="$1" COL="$2" | |
| EXISTS=$(mysql -h 127.0.0.1 -u root -proot pinakes_upgrade -sN -e \ | |
| "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='pinakes_upgrade' AND table_name='$TABLE' AND column_name='$COL'") | |
| if [ "$EXISTS" -eq 0 ]; then | |
| echo " ✗ Missing: $TABLE.$COL" | |
| FAILED=1 | |
| else | |
| echo " ✓ $TABLE.$COL" | |
| fi | |
| } | |
| # v0.7.0 — VIAF/ISNI | |
| check_col autori viaf_id | |
| check_col autori isni_id | |
| # v0.7.3 — NCIP | |
| check_col prestiti origine | |
| # v0.7.4 — NCIP partner attrs | |
| check_col ncip_partners isil | |
| check_col ncip_partners notes | |
| # v0.7.4 — digital_assets | |
| check_col digital_assets libro_id | |
| check_col digital_assets url | |
| check_col digital_assets md5_hash | |
| check_col digital_assets filesize | |
| check_col digital_assets filetype | |
| [ "$FAILED" -eq 0 ] || exit 1 | |
| - name: Verify plugin registrations | |
| run: | | |
| EXPECTED="api-book-scraper archives bibframe-linked-data deezer dewey-editor digital-library discogs goodlib musicbrainz ncip-server oai-pmh-server open-library openurl-resolver resource-sync viaf-authority z39-server" | |
| FAILED=0 | |
| for plugin in $EXPECTED; do | |
| EXISTS=$(mysql -h 127.0.0.1 -u root -proot pinakes_upgrade -sN -e \ | |
| "SELECT COUNT(*) FROM plugins WHERE name='$plugin'" 2>/dev/null || echo "0") | |
| if [ "$EXISTS" -eq 0 ]; then | |
| echo " ✗ Plugin not registered: $plugin" | |
| FAILED=1 | |
| else | |
| echo " ✓ $plugin" | |
| fi | |
| done | |
| [ "$FAILED" -eq 0 ] || exit 1 |