Deploy DSpace #2392
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
| # DSpace Docker deploy on dataquest servers | |
| name: Deploy DSpace | |
| on: | |
| workflow_call: | |
| inputs: | |
| DEV_MACHINE: | |
| required: false | |
| type: string | |
| default: 'dev-6' | |
| INSTANCE: | |
| required: false | |
| type: string | |
| default: '8603' | |
| IMPORT: | |
| required: false | |
| default: false | |
| type: boolean | |
| ERASE_DB: | |
| required: false | |
| default: false | |
| type: boolean | |
| DUMP_PATH: | |
| required: false | |
| type: string | |
| default: '/opt/dspace-envs/8603/dspace_dev5_dump_26.07.16.sql' | |
| workflow_dispatch: | |
| inputs: | |
| INSTANCE: | |
| required: true | |
| default: '8603' | |
| type: choice | |
| options: | |
| - '8603' | |
| IMPORT: | |
| required: true | |
| default: true | |
| type: boolean | |
| ERASE_DB: | |
| required: false | |
| default: false | |
| type: boolean | |
| DEV_MACHINE: | |
| required: false | |
| type: string | |
| default: 'dev-6' | |
| DUMP_PATH: | |
| description: 'Absolute path of the SQL dump ON THE DEV MACHINE (used only when IMPORT is checked)' | |
| required: false | |
| type: string | |
| default: '/opt/dspace-envs/8603/dump/dspace_dev5_dump_26.07.16.sql' | |
| PROBE_ONLY: | |
| description: 'Only report the state of the target machine; deploy and import nothing' | |
| required: false | |
| default: false | |
| type: boolean | |
| jobs: | |
| # Read-only report of the machine we are about to touch. Runs first so that a | |
| # misconfigured target is visible in the log even when the deploy then fails. | |
| probe: | |
| runs-on: dspace-${{inputs.DEV_MACHINE}}-dep-1 | |
| timeout-minutes: 10 | |
| env: | |
| INSTANCE: ${{inputs.INSTANCE}} | |
| CONFIG_PATH: /opt/dspace-envs/${{inputs.INSTANCE}} | |
| ENVFILE: /opt/dspace-envs/${{inputs.INSTANCE}}/.env | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: config dir + dump | |
| run: | | |
| echo "===== $CONFIG_PATH" | |
| ls -la "$CONFIG_PATH/" || echo "MISSING: $CONFIG_PATH" | |
| echo "===== $CONFIG_PATH/dump" | |
| ls -la "$CONFIG_PATH/dump/" || echo "MISSING: $CONFIG_PATH/dump" | |
| echo "===== dump referenced by DUMP_PATH input" | |
| ls -la "${{ inputs.DUMP_PATH }}" || echo "MISSING dump: ${{ inputs.DUMP_PATH }}" | |
| echo "===== port-ish keys from .env (values shown; these are ports, not secrets)" | |
| grep -E '^(PG_PORT|SOLR_PORT|DSPACE_PORT|DSPACE_FE_PORT|DSPACE_REST_PORT|DSPACE_DOCKER_REST_PORT|DSPACE_VER|DSPACE_HOST)=' "$ENVFILE" || echo "no port keys found" | |
| - name: who owns the host ports | |
| run: | | |
| echo "===== all dspace-ish containers and their published ports" | |
| docker ps -a --format '{{.Names}}\t{{.Status}}\t{{.Ports}}' | grep -Ei 'dspace|postgres|solr' || echo "none" | |
| echo "===== anything publishing host port 5432" | |
| docker ps --format '{{.Names}}\t{{.Ports}}' | grep -E '(^|[^0-9])5432->' || echo "nothing publishes 5432" | |
| echo "===== host listeners on 5432" | |
| (ss -lntp 2>/dev/null || netstat -lntp 2>/dev/null) | grep -E ':5432' || echo "no listener on 5432 (or no permission to see it)" | |
| - name: resolved compose ports for this instance | |
| run: | | |
| echo "===== ports WITH the instance-ports reset (this is what deploy actually uses)" | |
| docker compose --env-file "$ENVFILE" -p dspace-$INSTANCE \ | |
| -f docker/docker-compose.yml -f docker/docker-compose-rest.yml \ | |
| -f docker/docker-compose-instance-ports.yml \ | |
| -f "$CONFIG_PATH/docker-compose-rest.yml" -f "$CONFIG_PATH/docker-compose.yml" \ | |
| config 2>&1 | grep -nE '^ [a-z-]+:|container_name:|published:|^\s+ports:' | head -40 \ | |
| || echo "compose config failed" | |
| echo "===== ports WITHOUT the reset (the vanilla fixed ports we are dropping)" | |
| docker compose --env-file "$ENVFILE" -p dspace-$INSTANCE \ | |
| -f docker/docker-compose.yml -f docker/docker-compose-rest.yml \ | |
| -f "$CONFIG_PATH/docker-compose-rest.yml" -f "$CONFIG_PATH/docker-compose.yml" \ | |
| config 2>&1 | grep -nE '^ [a-z-]+:|container_name:|published:|^\s+ports:' | head -40 \ | |
| || echo "compose config failed" | |
| - name: postgres inside the instance db container | |
| run: | | |
| docker exec dspacedb$INSTANCE sh -c 'echo "PGPORT=$PGPORT"; pg_isready -U dspace; psql -U dspace -d postgres -tAc "SHOW port;" 2>/dev/null' || echo "could not probe dspacedb$INSTANCE" | |
| deploy-8603: | |
| if: (inputs.INSTANCE == '*' || inputs.INSTANCE == '8603') && !inputs.PROBE_ONLY | |
| needs: probe | |
| runs-on: dspace-${{inputs.DEV_MACHINE}}-dep-1 | |
| timeout-minutes: 60 | |
| env: | |
| INSTANCE: '8603' | |
| CONFIG_PATH: /opt/dspace-envs/${{inputs.INSTANCE}} | |
| ENVFILE: /opt/dspace-envs/${{inputs.INSTANCE}}/.env | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/erase-db | |
| if: inputs.ERASE_DB | |
| with: | |
| INSTANCE: ${{ env.INSTANCE }} | |
| NAME: dspace-${{ env.INSTANCE }} | |
| - name: Set start.sh as executable | |
| run: chmod +x build-scripts/run/start.sh | |
| - name: deploy to ${{inputs.DEV_MACHINE}} | |
| working-directory: build-scripts/run/ | |
| env: | |
| ADMIN_PASSWORD: ${{ secrets.DSPACE_ADMIN_PASSWORD }} | |
| USER_PASSWORD: ${{ secrets.DSPACE_USER_PASSWORD }} | |
| INSTANCE_OVERLAY: ${{ env.CONFIG_PATH }} | |
| run: | | |
| ./start.sh dspace-$INSTANCE | |
| cd ../.. | |
| docker compose --env-file $ENVFILE -p dspace-$INSTANCE -f docker/docker-compose.yml -f docker/docker-compose-rest.yml -f docker/docker-compose-instance-ports.yml -f $CONFIG_PATH/docker-compose-rest.yml -f $CONFIG_PATH/docker-compose.yml up -d --no-build --remove-orphans | |
| import-8603: | |
| runs-on: dspace-${{inputs.DEV_MACHINE}}-dep-1 | |
| if: inputs.IMPORT | |
| timeout-minutes: 900 | |
| needs: deploy-8603 | |
| env: | |
| INSTANCE: ${{inputs.INSTANCE}} | |
| CONFIG_PATH: /opt/dspace-envs/${{inputs.INSTANCE}} | |
| DUMP_PATH: ${{inputs.DUMP_PATH}} | |
| ADMIN_PASSWORD: ${{ secrets.DSPACE_ADMIN_PASSWORD }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: vanilla import | |
| run: | | |
| export DNAME=dspace${INSTANCE} | |
| export DDBNAME=dspacedb${INSTANCE} | |
| if [ ! -f "$DUMP_PATH" ]; then | |
| echo "::error::Dump not found on ${{inputs.DEV_MACHINE}}: $DUMP_PATH" | |
| exit 1 | |
| fi | |
| echo "Importing dump: $DUMP_PATH ($(du -h "$DUMP_PATH" | cut -f1))" | |
| # Stop the app so it cannot hold connections to the database we are about to drop. | |
| docker stop $DNAME || true | |
| echo "=====" | |
| docker cp "$DUMP_PATH" $DDBNAME:/tmp/dump.sql | |
| # Recreate the database from scratch and load the vanilla dump. | |
| docker exec $DDBNAME /bin/bash -c " | |
| dropdb -U dspace dspace || true && | |
| createdb -U dspace --owner=dspace --encoding=UNICODE dspace && | |
| psql -U dspace -d dspace -f /tmp/dump.sql | |
| " | |
| echo "=====" | |
| docker exec $DDBNAME /bin/bash -c "rm -f /tmp/dump.sql" || true | |
| - name: run database migration | |
| run: | | |
| export DNAME=dspace${INSTANCE} | |
| export ENVFILE=$CONFIG_PATH/.env | |
| export OVERLAY=$CONFIG_PATH | |
| # --------------------------------------------------------------- | |
| # Why a one-off `docker compose run` instead of `docker exec $DNAME migrate`: | |
| # The dspace entrypoint auto-runs `dspace database migrate` (WITHOUT `ignored`) | |
| # and then starts the REST webapp. On a freshly imported dump the | |
| # "Ignored" XMLWorkflow migrations are NOT applied, so the webapp boots against | |
| # an un-migrated DB, crashes, and (with the restart policy) the container | |
| # restarts. That restart SIGKILLs any `docker exec` running inside it -> the | |
| # migration dies with exit 137. So we stop the app and run the migration as a | |
| # one-off compose container (overridden entrypoint = migrate only, no webapp, | |
| # no restart policy), using the same compose files/env as the deploy step. | |
| # --------------------------------------------------------------- | |
| echo "Stopping app container so its restart loop can't kill the migration..." | |
| docker stop $DNAME || true | |
| echo "Running one-off DB migration via docker compose..." | |
| docker compose --env-file $ENVFILE -p dspace-${INSTANCE} \ | |
| -f docker/docker-compose.yml -f docker/docker-compose-rest.yml \ | |
| -f docker/docker-compose-instance-ports.yml \ | |
| -f $OVERLAY/docker-compose-rest.yml -f $OVERLAY/docker-compose.yml \ | |
| run --rm --no-deps --entrypoint /bin/bash dspace \ | |
| -c "while (!</dev/tcp/dspacedb/5432) >/dev/null 2>&1; do sleep 1; done; cd /dspace/bin && ./dspace database migrate ignored" | |
| echo "Migration done. Starting app container (entrypoint migrate is now a no-op)..." | |
| docker start $DNAME | |
| echo "Waiting for DSpace to be ready..." | |
| sleep 10 | |
| - name: restart dspace container | |
| run: | | |
| export DNAME=dspace${INSTANCE} | |
| echo "Restarting DSpace container..." | |
| docker restart $DNAME | |
| sleep 60 | |
| - name: create administrator | |
| run: | | |
| export DNAME=dspace${INSTANCE} | |
| echo "Creating DSpace administrator..." | |
| docker exec $DNAME /bin/bash -c "cd /dspace/bin && ./dspace create-administrator -e dspace.admin.dev@dataquest.sk -f admin -l user -p \"${ADMIN_PASSWORD}\" -c en" | |
| - name: rebuild discovery index and run oai import | |
| run: | | |
| export DNAME=dspace${INSTANCE} | |
| echo "Rebuilding discovery index..." | |
| docker exec $DNAME /bin/bash -c "/dspace/bin/dspace index-discovery -b" | |
| echo "Running OAI import..." | |
| docker exec $DNAME /bin/bash -c "/dspace/bin/dspace oai import -c" |