SPACEPORT DOCS

Migrations(migrations)

Migrations let you prepare or modify a Spaceport instance without fully booting the server. They run in a lightweight runtime with access to your configuration manifest and the connected database, so you can:

Migrations are typically run while the server is spun down, but they can also be executed while it’s running if that suits your workflow.

When to use migrations

Use a migration when you need repeatable, scriptable changes to your instance that should be tracked in source control and re-run safely across environments (local, staging, production). Favor idempotent scripts (safe to run more than once).

How migrations work

Running migrations

spaceport --migrate /path/to/config.spaceport

What happens next:

Notes:

Writing a migration

A migration is a Groovy script that uses the command environment. The minimal structure looks like this:

import spaceport.bridge.Command

Command.with {
    printBox("""
    Friendly title shown before we start.
    """)

    // Perform checks
    // Prompt for input if needed
    // Make database updates

    success('Done!')
}

Inside the block you can call helpers like promptInput, printBox, success, and error, and interact with the database through the Spaceport APIs you normally use.

Runtime helpers and APIs

These are the same classes you’d use in normal Spaceport code; migrations just run them in a small, CLI-friendly context.

Generating a report

Migrations can serve as their own report by printing context and results as they run. Use:

All output is shown in the CLI. If you need a persistent record, capture the CLI output to a file with your shell or add explicit file writing to your script.

Examples

Create or promote a Spaceport administrator

This script ensures the users database exists, then creates a new administrator user or promotes an existing user to spaceport-administrator.

import spaceport.Spaceport
import spaceport.bridge.Command
import spaceport.computer.memory.physical.Document
import spaceport.personnel.ClientDocument

/
 * This migration will create a Spaceport administrator
 * and a 'users' database if they do not already exist,
 * or promote an existing user to 'spaceport-administrator'.
 */

Command.with {

    printBox("""
    This migration will create a default Spaceport administrator
    and a 'users' database if they do not already exist.
    """)

    // Check for database, and create it if it does not exist.

    if (!Spaceport.main_memory_core.containsDatabase('users')) {
        Spaceport.main_memory_core.createDatabase('users')

        success("Created 'users' database.")
    }

    // Get a username and password from the user.

    def username
    def password = ''

    username = promptInput('Enter a new username (default: administrator)')
    if (!username) username = 'administrator'

    // Check if the user already exists in the database, and promote if necessary.

    def user

    if (Document.exists(username, 'users')) {

        user = ClientDocument.getClientDocument(username)

        if (user.hasPermission('spaceport-administrator')) {
            success("User ${username} already exists and is already an administrator.")
            return
        }

        error("User already exists. Promote instead?")
        def promote = promptInput('Promote user to administrator? (y/(n))')

        if (promote == 'y' || promote == 'Y') {
            user.addPermission('spaceport-administrator')
            success("User ${username} promoted to administrator.")
            return
        } else {
            error("User already exists. Exiting.")
            return
        }

    }

    while (!password) {
        password = promptInput('Enter a new password')
        if (password != promptInput('Re-enter password to confirm')) {
            error('Passwords do not match. Please try again.')
            password = ''
        }
    }

    // Create a new ClientDocument for the administrator, and add the 'spaceport-administrator' permission.

    user = ClientDocument.createNewClientDocument(username, password)

    if (user) {
        user.addPermission('spaceport-administrator')
        success("Created new administrator: ${username}")
        return
    } else {
        error("Failed to create new administrator.")
        return
    }

}

Change an existing user’s password

This script prompts for a username in the users database, confirms the user exists, and then changes the password.

import spaceport.Spaceport
import spaceport.bridge.Command
import spaceport.computer.memory.physical.Document
import spaceport.personnel.ClientDocument

/
* This migration will change the password for an existing user
* in the 'users' database.
  */

Command.with {
printBox("""
This migration will change the password for an existing user
in the 'users' database.
""")

    // Check for database existence
    if (!Spaceport.main_memory_core.containsDatabase('users')) {
        error("'users' database does not exist. Exiting.")
        return
    }

    // Prompt for username
    def username = promptInput('Enter the username whose password you want to change')
    if (!username) {
        error('No username entered. Exiting.')
        return
    }

    // Check if user exists
    if (!Document.exists(username, 'users')) {
        error("User ${username} does not exist in 'users' database. Exiting.")
        return
    }

    def password = ''
    while (!password) {
        password = promptInput('Enter a new password')
        if (password != promptInput('Re-enter password to confirm')) {
            error('Passwords do not match. Please try again.')
            password = ''
        }
    }

    // Update the user's password
    def user = ClientDocument.getClientDocument(username)
    if (user) {
        user.changePassword(password)
        success("Password for user ${username} has been changed.")
        return
    } else {
        error("Failed to update password for user ${username}.")
        return
    }
}

Best practices

Troubleshooting

See also