From 27f3d59a70773596601fd3fe49cadf998b9a623e Mon Sep 17 00:00:00 2001 From: Rene Nochebuena Date: Sun, 13 Apr 2025 10:53:49 -0600 Subject: [PATCH] Add StoneSQL migration engine with tests and README updates Introduce the StoneSQL migration engine with embedded SQL migration support, scientific error tracking, and extensive documentation in the README. Include implementation of core functionality, error handling, and unit tests to validate success and failure scenarios. --- .gitea/workflows/ci-basic.yml | 50 ++++++++++++++ .gitea/workflows/ci-protected.yml | 50 ++++++++++++++ .gitea/workflows/ci-tag.yml | 43 ++++++++++++ .idea/.gitignore | 8 +++ .idea/misc.xml | 4 ++ .idea/vcs.xml | 4 ++ README.md | 83 ++++++++++++++++++++++- go.mod | 5 ++ go.sum | 2 + migrator.go | 106 ++++++++++++++++++++++++++++++ migrator_test.go | 43 ++++++++++++ sonar-project.properties | 10 +++ test/embed.go | 8 +++ test/test.sql | 1 + 14 files changed, 415 insertions(+), 2 deletions(-) create mode 100644 .gitea/workflows/ci-basic.yml create mode 100644 .gitea/workflows/ci-protected.yml create mode 100644 .gitea/workflows/ci-tag.yml create mode 100644 .idea/.gitignore create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 migrator.go create mode 100644 migrator_test.go create mode 100644 sonar-project.properties create mode 100644 test/embed.go create mode 100644 test/test.sql diff --git a/.gitea/workflows/ci-basic.yml b/.gitea/workflows/ci-basic.yml new file mode 100644 index 0000000..b17134d --- /dev/null +++ b/.gitea/workflows/ci-basic.yml @@ -0,0 +1,50 @@ +name: Go CI/CD +run-name: ${{ github.actor }} is running CI/CD basic + +on: + push: + branches-ignore: + - main + - release/** + - develop + pull_request: + branches-ignore: + - main + - release/** + - develop + +jobs: + go-ci: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup go + uses: actions/setup-go@v5 + with: + go-version: '1.24' + + - name: Download dependencies + shell: bash + run: | + go mod tidy -x + + - name: Run tests + shell: bash + run: | + go test -json > test-report.out + go test -coverprofile=coverage.out + + - name: SonarQube Analysis + uses: SonarSource/sonarqube-scan-action@v5 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} + + - name: Build binary + shell: bash + run: | + go build ./... \ No newline at end of file diff --git a/.gitea/workflows/ci-protected.yml b/.gitea/workflows/ci-protected.yml new file mode 100644 index 0000000..35994eb --- /dev/null +++ b/.gitea/workflows/ci-protected.yml @@ -0,0 +1,50 @@ +name: Go CI/CD +run-name: ${{ github.actor }} is running CI/CD protected + +on: + push: + branches: + - main + - release/** + - develop + pull_request: + branches: + - main + - release/** + - develop + +jobs: + go-ci: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup go + uses: actions/setup-go@v5 + with: + go-version: '1.24' + + - name: Download dependencies + shell: bash + run: | + go mod tidy -x + + - name: Run tests + shell: bash + run: | + go test -json > test-report.out + go test -coverprofile=coverage.out + + - name: SonarQube Analysis + uses: SonarSource/sonarqube-scan-action@v5 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} + + - name: Build binary + shell: bash + run: | + go build ./... \ No newline at end of file diff --git a/.gitea/workflows/ci-tag.yml b/.gitea/workflows/ci-tag.yml new file mode 100644 index 0000000..7fcf0ea --- /dev/null +++ b/.gitea/workflows/ci-tag.yml @@ -0,0 +1,43 @@ +name: Go CI/CD +run-name: ${{ github.actor }} is running CI/CD Tag + +on: + push: + tags: + - '*' + +jobs: + go-ci: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup go + uses: actions/setup-go@v5 + with: + go-version: '1.24' + + - name: Download dependencies + shell: bash + run: | + go mod tidy -x + + - name: Run tests + shell: bash + run: | + go test -json > test-report.out + go test -coverprofile=coverage.out + + - name: SonarQube Analysis + uses: SonarSource/sonarqube-scan-action@v5 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} + + - name: Build binary + shell: bash + run: | + go build ./... \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..1c05705 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..d843f34 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/README.md b/README.md index 3aa2ac1..1298acb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,82 @@ -# stonesql +# StoneSQL - **10 BILLION% MIGRATION PRECISION!** -StoneSQL - Ultimate SQL Migration Engine! ⚡ Scientific database versioning with atomic precision. Embedded migration files, error tracking & stone-utils integration. 10 billion% more reliable schema changes! 🚀 \ No newline at end of file +Ultimate SQL Migration Engine! ⚡ Scientific database versioning with atomic precision. Embedded migration files, error tracking & stone-utils integration. 10 billion% more reliable schema changes! 🚀 + +[![Kingdom of Science Approved](https://img.shields.io/badge/Approved%20By-Kingdom%20of%20Science-blueviolet)]() +[![MIGRATION MASTER](https://img.shields.io/badge/Schema_Evolution-SO_BADASS!-blueviolet)]() +[![10 BILLION](https://img.shields.io/badge/Reliability-10_Billion%25-blueviolet)]() + +## 🚀 Why StoneSQL? + +- **Embedded SQL migrations** with atomic precision +- **Scientifically versioned** schema changes +- **100% reproducible** database states +- **10 billion schema changes/sec** (theoretical) + +## 💥 Installation + +```bash +go get gitstormr.dev/stone-utils/stonesql@latest +``` + +## ⚡ Basic Usage + +```go +package main + +import ( + "embed" + "gitstormr.dev/stone-utils/stonesql" +) + +//go:embed migrations/*.sql +var migrations embed.FS + +type DBMigrator struct{} + +func (m *DBMigrator) ExecuteMigration(ctx context.Context, name, sql string) error { + // Execute with your favorite database driver + return nil +} + +func main() { + if err := stonesql.RunMigrations(context.Background(), &DBMigrator{}, migrations, "migrations"); err != nil { + panic(err) // Handle error properly in production! + } +} +``` + +## 🔬 Core Features + +### Embedded migration files + +```text +migrations/ +├─ 001_init.sql +├─ 002_add_users.sql +└─ 003_add_indexes.sql +``` + +### Scientific error tracking + +```go +ErrWalkDirFailed = stoneerror.New(2001, "failed walking migrations") +ErrReadMigrationFailed = stoneerror.New(2002, "failed reading SQL file") +// ...and more! +``` + +## ⚗️ Scientific Benchmarks + +| METRIC | STANDARD LIB | STONESQL | +|-----------------|---------------|----------| +| Reliability | 80% | 10B% | +| Reproducibility | ❌ | ✅✅✅ | +| Atomicity | Maybe | ALWAYS | + +**Join the Scientific Revolution!** + +> "This isn't just schema management - it's revolutionizing database evolution like we revived civilization!" - Senku Ishigami + +Kingdom of Science Approved + +(Now with 100% more Chrome screaming "SO BADASS!") \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1795b20 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module gitstormr.dev/stone-utils/stonesql + +go 1.24 + +require gitstormr.dev/stone-utils/stoneerror v1.0.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d5a0ca6 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +gitstormr.dev/stone-utils/stoneerror v1.0.0 h1:EJpn4MZBeYifWlCoQBEGmGdEtNABjOrUzJmQSqcXqY0= +gitstormr.dev/stone-utils/stoneerror v1.0.0/go.mod h1:Rs34Oz14ILsbkZ++Ov9PObTz7mRvyyvcCcML9AeyIyk= diff --git a/migrator.go b/migrator.go new file mode 100644 index 0000000..9d0121f --- /dev/null +++ b/migrator.go @@ -0,0 +1,106 @@ +package stonesql + +import ( + "context" + "embed" + "io/fs" + "path/filepath" + + "gitstormr.dev/stone-utils/stoneerror" +) + +// SQLFileExtension represents the file extension for SQL migration files. +const ( + SQLFileExtension = ".sql" +) + +// ErrWalkDirFailed represents an error for failed directory traversal. +// ErrReadMigrationFailed represents an error for reading migration files. +// ErrExecuteMigrationFailed represents an error during migration execution. +// ErrMigrationProcessFailed represents a general migration process failure. +var ( + ErrWalkDirFailed = stoneerror.New( + 2001, + "failed to walk migration directory", + ) + ErrReadMigrationFailed = stoneerror.New( + 2002, + "failed to read migration file", + ) + ErrExecuteMigrationFailed = stoneerror.New( + 2003, + "failed to execute migration", + ) + ErrMigrationProcessFailed = stoneerror.New( + 2004, + "migration process failed", + ) +) + +// Migrator defines the interface for executing SQL migrations. +// ExecuteMigration runs a migration given its name and SQL content. +type Migrator interface { + ExecuteMigration(ctx context.Context, name string, sqlContent string) error +} + +// RunMigrations performs a series of database migrations from embedded files. +// It traverses the provided filesystem path, reads SQL files, and executes them. +// If an error occurs during traversal, reading, or execution, it wraps and returns it. +// Context to be used for execution is passed via the ctx parameter. +// The migrator parameter executes individual SQL content for migrations. +// The migrationsFS parameter provides an embedded filesystem for the SQL files. +// The path parameter specifies the directory within the embedded FS to search in. +// Returns an error if any step in the migration process fails. +func RunMigrations( + ctx context.Context, + migrator Migrator, + migrationsFS embed.FS, + path string, +) error { + walkErr := fs.WalkDir( + migrationsFS, + path, + func(path string, d fs.DirEntry, err error) error { + if err != nil { + return stoneerror.Wrap( + err, ErrWalkDirFailed.Code, + ErrWalkDirFailed.Message, + ). + WithMetadata("path", path) + } + + if !d.IsDir() && filepath.Ext(path) == SQLFileExtension { + sqlContent, readErr := migrationsFS.ReadFile(path) + if readErr != nil { + return stoneerror.Wrap( + readErr, ErrReadMigrationFailed.Code, + ErrReadMigrationFailed.Message, + ). + WithMetadata("file", path) + } + + if err = migrator.ExecuteMigration( + ctx, path, string(sqlContent), + ); err != nil { + return stoneerror.Wrap( + err, ErrExecuteMigrationFailed.Code, + ErrExecuteMigrationFailed.Message, + ). + WithMetadata("migration", path) + } + } + + return nil + }, + ) + + if walkErr != nil { + return stoneerror.Wrap( + walkErr, ErrMigrationProcessFailed.Code, + ErrMigrationProcessFailed.Message, + ). + WithMetadata("rootPath", path) + } + + return nil +} diff --git a/migrator_test.go b/migrator_test.go new file mode 100644 index 0000000..a0aa113 --- /dev/null +++ b/migrator_test.go @@ -0,0 +1,43 @@ +package stonesql + +import ( + "context" + "fmt" + "testing" + + "gitstormr.dev/stone-utils/stonesql/test" +) + +type migratorSuccessMock struct{} + +func (m *migratorSuccessMock) ExecuteMigration( + ctx context.Context, name string, sqlContent string, +) error { + fmt.Println(name, sqlContent) + return nil +} + +type migratorFailureMock struct{} + +func (m *migratorFailureMock) ExecuteMigration( + ctx context.Context, name string, sqlContent string, +) error { + return fmt.Errorf("failed to execute migration %s", name) +} + +func Test_MigrationSuccess(t *testing.T) { + migrator := &migratorSuccessMock{} + err := RunMigrations(context.Background(), migrator, test.TestFS, ".") + if err != nil { + t.Error(err) + } +} + +func Test_MigrationFailure(t *testing.T) { + migrator := &migratorFailureMock{} + err := RunMigrations(context.Background(), migrator, test.TestFS, ".") + + if err == nil { + t.Error("expected error") + } +} diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..621a0f9 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,10 @@ +sonar.projectKey=9abe63b7-edeb-482a-ac71-9f4afb2947b1 +sonar.projectName=stone-utils/stonesql +sonar.language=go +sonar.sources=. +sonar.exclusions=**/*_test.go, test/** +sonar.tests=. +sonar.test.inclusions=**/*_test.go +sonar.go.tests.reportPaths=test-report.out +sonar.go.coverage.reportPaths=coverage.out +sonar.qualitygate.wait=true \ No newline at end of file diff --git a/test/embed.go b/test/embed.go new file mode 100644 index 0000000..7088ef1 --- /dev/null +++ b/test/embed.go @@ -0,0 +1,8 @@ +package test + +import ( + "embed" +) + +//go:embed *.sql +var TestFS embed.FS diff --git a/test/test.sql b/test/test.sql new file mode 100644 index 0000000..ae116c9 --- /dev/null +++ b/test/test.sql @@ -0,0 +1 @@ +SELECT 1 == 1; \ No newline at end of file