Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,5 @@ cython_debug/

# Other files and folders
.python-version

.vscode/
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ uv add jetbase

```bash
jetbase init
cd jetbase
```

This creates a `jetbase/` directory with:
Expand Down
1 change: 0 additions & 1 deletion docs/commands/current.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,4 @@ No migrations have been applied yet.

## Notes

- Must be run from inside the `jetbase/` directory
- Shows only the version of the latest **versioned** migration
5 changes: 0 additions & 5 deletions docs/commands/history.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,3 @@ The exact timestamp when the migration was applied. The format is:
```
YYYY-MM-DD HH:MM:SS.microseconds
```

## Notes

- Must be run from inside the `jetbase/` directory

22 changes: 3 additions & 19 deletions docs/commands/init.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,6 @@ sqlalchemy_url = "postgresql://user:password@localhost:5432/mydb"
!!! important
After running `init`, you need to update the `sqlalchemy_url` in `env.py` with your actual database connection string.

> **Next Step:**
> After running `jetbase init`, move into the `jetbase` directory with:
>
> ```bash
> cd jetbase
> ```
>
> **Important:**
> All future `jetbase` commands should be run *inside* the `jetbase` directory.

## Examples

### Basic Usage
Expand All @@ -59,7 +49,6 @@ Output:

```
Initialized Jetbase project in /path/to/your/project/jetbase
Run 'cd jetbase' to get started!
```

### Complete Workflow
Expand All @@ -68,21 +57,16 @@ Run 'cd jetbase' to get started!
# 1. Initialize Jetbase
jetbase init

# 2. Move into the jetbase directory
cd jetbase

# 3. Edit env.py with your database URL
# 2. Edit env.py with your database URL
# (use your favorite editor)

# 4. Create your first migration
# 3. Create your first migration
jetbase new "create users table" -v 1

# 5. Apply migrations
# 4. Apply migrations
jetbase upgrade
```

## Notes

- All other Jetbase commands must be run from inside the `jetbase/` directory.
- The `migrations/` directory will be empty after initialization. Use `jetbase new` to create your first migration, (or create migration files manually. See [Migrations Overview](../migrations/index.md)).

1 change: 0 additions & 1 deletion docs/commands/lock-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ If you are absolutely certain that no migration or fix command is active, you ma
For a detailed explanation of how migration locking works, see [Migration Locking](../advanced/migration-locking.md).


- Must be run from inside the `jetbase/` directory
- The lock is stored in the `jetbase_lock` database table
- Locks are automatically released whether the migration was successful or an error occurred.

1 change: 0 additions & 1 deletion docs/commands/new.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ Include `-- rollback` sections. This allows you to safely undo migrations if nee

## Notes

- Must be run from inside the `jetbase/` directory
- Use `-v` to specify a custom version number (e.g., `1`, `1.5`)
- If `-v` is not provided, a timestamp-based version is automatically generated
- You do not have to use the `jetbase new` CLI command to create a new migration. You can create a new file manually in the `jetbase/migrations` directory and follow the `V<version>__<description>.sql` naming convention. For full details and best practices, see [Migrations Overview](../migrations/index.md).
Expand Down
1 change: 0 additions & 1 deletion docs/commands/rollback.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ jetbase status

## Notes

- Must be run from inside the `jetbase/` directory
- Rollbacks are applied in reverse chronological order
- Cannot use both `--count` and `--to-version` together

1 change: 0 additions & 1 deletion docs/commands/status.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,5 @@ Example:

## Notes

- Must be run from inside the `jetbase/` directory
- Compares files in `migrations/` with database records

1 change: 0 additions & 1 deletion docs/commands/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ Jetbase stops safely at the first sign of trouble, preventing partial or out-of-

## Notes

- Must be run from inside the `jetbase/` directory
- Cannot use both `--count` and `--to-version` together
- The automatic lock prevents concurrent migrations from causing conflicts

1 change: 0 additions & 1 deletion docs/commands/validate-checksums.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ Checksums help catch when:

## Notes

- Must be run from inside the `jetbase/` directory
- Only validates migrations that have been applied
- Only uses upgrade SQL statements in the migration file to calculate checksums

11 changes: 1 addition & 10 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,7 @@ jetbase/
└── env.py # Database configuration
```

### Step 2: Navigate to the Jetbase Directory

```bash
cd jetbase
```

!!! important
All Jetbase commands must be run from inside the `jetbase/` directory.

### Step 3: Configure Your Database Connection
### Step 2: Configure Your Database Connection

Open `env.py` and update the `sqlalchemy_url` with your database connection string:

Expand Down
1 change: 0 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ Jetbase helps you manage database migrations in a simple, version-controlled way

```bash
jetbase init
cd jetbase
```

This creates a `jetbase/` directory with:
Expand Down
6 changes: 2 additions & 4 deletions jetbase/commands/fix_checksums.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import os

from jetbase.constants import MIGRATIONS_DIR
from jetbase.engine.checksum import calculate_checksum
from jetbase.engine.file_parser import parse_upgrade_statements
from jetbase.engine.lock import migration_lock
Expand All @@ -10,6 +7,7 @@
MigrationVersionMismatchError,
)
from jetbase.logging import logger
from jetbase.paths import get_migrations_directory
from jetbase.repositories.migrations_repo import (
get_checksums_by_version,
update_migration_checksums,
Expand Down Expand Up @@ -151,7 +149,7 @@ def _find_checksum_mismatches(
[("1.0", "def456")] # If file changed
"""
migration_filepaths_by_version: dict[str, str] = get_migration_filepaths_by_version(
directory=os.path.join(os.getcwd(), MIGRATIONS_DIR),
directory=str(get_migrations_directory()),
end_version=latest_migrated_version,
)

Expand Down
5 changes: 2 additions & 3 deletions jetbase/commands/fix_files.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import os

from jetbase.engine.lock import migration_lock
from jetbase.engine.repeatable import get_repeatable_filenames
from jetbase.engine.version import (
get_migration_filepaths_by_version,
)
from jetbase.logging import logger
from jetbase.models import MigrationRecord
from jetbase.paths import get_migrations_directory
from jetbase.repositories.lock_repo import create_lock_table_if_not_exists
from jetbase.repositories.migrations_repo import (
create_migrations_table_if_not_exists,
Expand Down Expand Up @@ -40,7 +39,7 @@ def fix_files_cmd(audit_only: bool = False) -> None:
migrated_versions: list[str] = get_migrated_versions()
current_migration_filepaths_by_version: dict[str, str] = (
get_migration_filepaths_by_version(
directory=os.path.join(os.getcwd(), "migrations")
directory=str(get_migrations_directory()),
)
)
repeatable_migrations: list[MigrationRecord] = fetch_repeatable_migrations()
Expand Down
2 changes: 1 addition & 1 deletion jetbase/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ def initialize_cmd() -> None:
f.write(ENV_FILE_CONTENT)

logger.info(
"Initialized Jetbase project in %s\nRun 'cd jetbase' to get started!",
"Initialized Jetbase project in %s",
Path(BASE_DIR).absolute(),
)
12 changes: 3 additions & 9 deletions jetbase/commands/new.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import datetime as dt
import os

from jetbase.constants import MIGRATIONS_DIR, NEW_MIGRATION_FILE_CONTENT
from jetbase.constants import NEW_MIGRATION_FILE_CONTENT
from jetbase.engine.file_parser import is_filename_length_valid, is_valid_version
from jetbase.exceptions import (
DirectoryNotFoundError,
InvalidVersionError,
MigrationFilenameTooLongError,
)
from jetbase.logging import logger
from jetbase.paths import get_migrations_directory


def generate_new_migration_file_cmd(
Expand Down Expand Up @@ -40,13 +40,7 @@ def generate_new_migration_file_cmd(
Created migration file: V20251201.120000__create_users_table.sql
"""

migrations_dir_path: str = os.path.join(os.getcwd(), MIGRATIONS_DIR)

if not os.path.exists(migrations_dir_path):
raise DirectoryNotFoundError(
"Migrations directory not found. Run 'jetbase initialize' to set up jetbase.\n"
"If you have already done so, run this command from the jetbase directory."
)
migrations_dir_path = str(get_migrations_directory())

filename: str = _generate_new_filename(description=description, version=version)
filepath: str = os.path.join(migrations_dir_path, filename)
Expand Down
3 changes: 2 additions & 1 deletion jetbase/commands/rollback.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from jetbase.enums import MigrationDirectionType
from jetbase.exceptions import VersionNotFoundError
from jetbase.logging import logger
from jetbase.paths import get_migrations_directory
from jetbase.repositories.lock_repo import create_lock_table_if_not_exists
from jetbase.repositories.migrations_repo import (
create_migrations_table_if_not_exists,
Expand Down Expand Up @@ -138,7 +139,7 @@ def _get_versions_to_rollback(latest_migration_versions: list[str]) -> dict[str,
newest versions are rolled back first.
"""
versions_to_rollback: dict[str, str] = get_migration_filepaths_by_version(
directory=os.path.join(os.getcwd(), "migrations"),
directory=str(get_migrations_directory()),
version_to_start_from=latest_migration_versions[-1],
end_version=latest_migration_versions[0],
)
Expand Down
11 changes: 6 additions & 5 deletions jetbase/commands/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from jetbase.engine.version import get_migration_filepaths_by_version
from jetbase.enums import MigrationType
from jetbase.models import MigrationRecord
from jetbase.paths import get_migrations_directory
from jetbase.repositories.migrations_repo import (
create_migrations_table_if_not_exists,
get_existing_on_change_filenames_to_checksums,
Expand Down Expand Up @@ -47,8 +48,10 @@ def status_cmd() -> None:
versioned_migration_records[-1].version if versioned_migration_records else None
)

migrations_directory = str(get_migrations_directory())

pending_versioned_filepaths: dict[str, str] = get_migration_filepaths_by_version(
directory=os.path.join(os.getcwd(), "migrations"),
directory=migrations_directory,
version_to_start_from=latest_migrated_version,
)

Expand All @@ -62,7 +65,7 @@ def status_cmd() -> None:
roc_filenames_changed_only: list[str] = [
os.path.basename(filepath)
for filepath in get_runs_on_change_filepaths(
directory=os.path.join(os.getcwd(), "migrations"), changed_only=True
directory=migrations_directory, changed_only=True
)
]

Expand All @@ -72,9 +75,7 @@ def status_cmd() -> None:

all_roc_filenames: list[str] = [
os.path.basename(filepath)
for filepath in get_runs_on_change_filepaths(
directory=os.path.join(os.getcwd(), "migrations")
)
for filepath in get_runs_on_change_filepaths(directory=migrations_directory)
]

console: Console = Console()
Expand Down
10 changes: 6 additions & 4 deletions jetbase/commands/upgrade.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import os

from jetbase.constants import MIGRATIONS_DIR
from jetbase.engine.dry_run import process_dry_run
from jetbase.engine.file_parser import parse_upgrade_statements
from jetbase.engine.lock import migration_lock
Expand All @@ -15,6 +14,7 @@
from jetbase.enums import MigrationDirectionType, MigrationType
from jetbase.logging import logger
from jetbase.models import MigrationRecord
from jetbase.paths import get_migrations_directory
from jetbase.repositories.lock_repo import create_lock_table_if_not_exists
from jetbase.repositories.migrations_repo import (
create_migrations_table_if_not_exists,
Expand Down Expand Up @@ -78,12 +78,14 @@ def upgrade_cmd(
to_version=to_version,
)

migrations_directory = str(get_migrations_directory())

repeatable_always_filepaths: list[str] = get_repeatable_always_filepaths(
directory=os.path.join(os.getcwd(), MIGRATIONS_DIR)
directory=migrations_directory
)

runs_on_change_filepaths: list[str] = get_runs_on_change_filepaths(
directory=os.path.join(os.getcwd(), MIGRATIONS_DIR),
directory=migrations_directory,
changed_only=True,
)

Expand Down Expand Up @@ -141,7 +143,7 @@ def _get_filepaths_by_version(
FileNotFoundError: If to_version is not found in pending migrations.
"""
filepaths_by_version: dict[str, str] = get_migration_filepaths_by_version(
directory=os.path.join(os.getcwd(), MIGRATIONS_DIR),
directory=str(get_migrations_directory()),
version_to_start_from=latest_migration.version if latest_migration else None,
)

Expand Down
33 changes: 7 additions & 26 deletions jetbase/commands/validators.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,18 @@
from pathlib import Path

from jetbase.exceptions import DirectoryNotFoundError
from jetbase.paths import get_migrations_directory


def validate_jetbase_directory() -> None:
"""
Ensure command is run from jetbase directory with migrations folder.
Ensure a jetbase directory exists in the current directory or parent directories.

Validates that the current working directory is named 'jetbase' and
contains a 'migrations' subdirectory. This validation is required
before running most Jetbase CLI commands.
Validates that a 'jetbase' directory exists in the current working directory
or any of its parent directories, and that it contains a 'migrations' subdirectory.
This validation is required before running most Jetbase CLI commands.

Returns:
None: Returns silently if validation passes.

Raises:
DirectoryNotFoundError: If the current directory is not named
'jetbase' or if the 'migrations' subdirectory does not exist.
DirectoryNotFoundError: If the jetbase directory or migrations subdirectory does not exist.
"""
current_dir = Path.cwd()

# Check if current directory is named 'jetbase'
if current_dir.name != "jetbase":
raise DirectoryNotFoundError(
"Command must be run from the 'jetbase' directory.\n"
"You can run 'jetbase init' to create a Jetbase project."
)

# Check if migrations directory exists
migrations_dir = current_dir / "migrations"
if not migrations_dir.exists() or not migrations_dir.is_dir():
raise DirectoryNotFoundError(
f"'migrations' directory not found in {current_dir}.\n"
"Add a migrations directory inside the 'jetbase' directory to proceed.\n"
"You can also run 'jetbase init' to create a Jetbase project."
)
get_migrations_directory()
3 changes: 2 additions & 1 deletion jetbase/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import tomli

from jetbase.constants import ENV_FILE
from jetbase.paths import get_jetbase_directory


@dataclass
Expand Down Expand Up @@ -193,7 +194,7 @@ def _get_config_from_env_py(key: str, filepath: str = ENV_FILE) -> Any | None:
Any | None: The configuration value if the attribute exists,
otherwise None.
"""
config_path: str = os.path.join(os.getcwd(), filepath)
config_path: str = str(get_jetbase_directory() / filepath)

if not os.path.exists(config_path):
return None
Expand Down
Loading