Added seed command
This commit is contained in:
		
							parent
							
								
									470a0e4932
								
							
						
					
					
						commit
						cdfd558730
					
				| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
src/config/config.go
 | 
					src/config/config.go
 | 
				
			||||||
.vscode
 | 
					.vscode
 | 
				
			||||||
vendor/
 | 
					vendor/
 | 
				
			||||||
 | 
					dbclones/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,11 +5,13 @@ import (
 | 
				
			||||||
	_ "embed"
 | 
						_ "embed"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"git.handmade.network/hmn/hmn/src/config"
 | 
				
			||||||
	"git.handmade.network/hmn/hmn/src/db"
 | 
						"git.handmade.network/hmn/hmn/src/db"
 | 
				
			||||||
	"git.handmade.network/hmn/hmn/src/migration/migrations"
 | 
						"git.handmade.network/hmn/hmn/src/migration/migrations"
 | 
				
			||||||
	"git.handmade.network/hmn/hmn/src/migration/types"
 | 
						"git.handmade.network/hmn/hmn/src/migration/types"
 | 
				
			||||||
| 
						 | 
					@ -61,8 +63,31 @@ func init() {
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						seedFromFileCommand := &cobra.Command{
 | 
				
			||||||
 | 
							Use:   "seedfile <filename> <after migration id>",
 | 
				
			||||||
 | 
							Short: "Resets the db, runs migrations up to and including <after migration id>, and runs the seed file.",
 | 
				
			||||||
 | 
							Run: func(cmd *cobra.Command, args []string) {
 | 
				
			||||||
 | 
								if len(args) < 2 {
 | 
				
			||||||
 | 
									fmt.Printf("You must provide a seed file and migration id.\n\n")
 | 
				
			||||||
 | 
									cmd.Usage()
 | 
				
			||||||
 | 
									os.Exit(1)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								seedFile := args[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								afterMigration, err := time.Parse(time.RFC3339, args[1])
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									fmt.Printf("ERROR: bad version string: %v", err)
 | 
				
			||||||
 | 
									os.Exit(1)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								SeedFromFile(seedFile, types.MigrationVersion(afterMigration))
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	website.WebsiteCommand.AddCommand(migrateCommand)
 | 
						website.WebsiteCommand.AddCommand(migrateCommand)
 | 
				
			||||||
	website.WebsiteCommand.AddCommand(makeMigrationCommand)
 | 
						website.WebsiteCommand.AddCommand(makeMigrationCommand)
 | 
				
			||||||
 | 
						website.WebsiteCommand.AddCommand(seedFromFileCommand)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getSortedMigrationVersions() []types.MigrationVersion {
 | 
					func getSortedMigrationVersions() []types.MigrationVersion {
 | 
				
			||||||
| 
						 | 
					@ -257,3 +282,100 @@ func MakeMigration(name, description string) {
 | 
				
			||||||
	fmt.Println("Successfully created migration file:")
 | 
						fmt.Println("Successfully created migration file:")
 | 
				
			||||||
	fmt.Println(path)
 | 
						fmt.Println(path)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Drops all tables
 | 
				
			||||||
 | 
					func ClearDatabase() {
 | 
				
			||||||
 | 
						conn := db.NewConn()
 | 
				
			||||||
 | 
						defer conn.Close(context.Background())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbName := config.Config.Postgres.DbName
 | 
				
			||||||
 | 
						rows, err := conn.Query(context.Background(), "SELECT tablename FROM pg_tables WHERE tableowner = $1", dbName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(fmt.Errorf("couldn't fetch the list of tables owned by %s: %w", dbName, err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tablesToDrop := []string{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for rows.Next() {
 | 
				
			||||||
 | 
							var tableName string
 | 
				
			||||||
 | 
							err = rows.Scan(&tableName)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(fmt.Errorf("failed to fetch row from pg_tables: %w", err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							tablesToDrop = append(tablesToDrop, tableName)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rows.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tableName := range tablesToDrop {
 | 
				
			||||||
 | 
							conn.Exec(context.Background(), "DROP TABLE $1", tableName)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Applies a cloned db to the local db.
 | 
				
			||||||
 | 
					// Drops the db and runs migrations from scratch.
 | 
				
			||||||
 | 
					// Applies the seed after the migration specified in `afterMigration`, then runs the rest of the migrations.
 | 
				
			||||||
 | 
					func SeedFromFile(seedFile string, afterMigration types.MigrationVersion) {
 | 
				
			||||||
 | 
						file, err := os.Open(seedFile)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(fmt.Errorf("couldn't open seed file %s: %w", seedFile, err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						file.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						migration := migrations.All[afterMigration]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if migration == nil {
 | 
				
			||||||
 | 
							panic(fmt.Errorf("could not find migration: %s", afterMigration))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println("Clearing database...")
 | 
				
			||||||
 | 
						ClearDatabase()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println("Running migrations...")
 | 
				
			||||||
 | 
						Migrate(afterMigration)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println("Executing seed...")
 | 
				
			||||||
 | 
						cmd := exec.Command("psql",
 | 
				
			||||||
 | 
							"--single-transaction",
 | 
				
			||||||
 | 
							"--dbname",
 | 
				
			||||||
 | 
							config.Config.Postgres.DbName,
 | 
				
			||||||
 | 
							"--host",
 | 
				
			||||||
 | 
							config.Config.Postgres.Hostname,
 | 
				
			||||||
 | 
							"--username",
 | 
				
			||||||
 | 
							config.Config.Postgres.User,
 | 
				
			||||||
 | 
							"--password",
 | 
				
			||||||
 | 
							"-f",
 | 
				
			||||||
 | 
							seedFile,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						fmt.Println("Running command:", cmd)
 | 
				
			||||||
 | 
						if err = cmd.Run(); err != nil {
 | 
				
			||||||
 | 
							exitError, isExit := err.(*exec.ExitError)
 | 
				
			||||||
 | 
							if isExit {
 | 
				
			||||||
 | 
								panic(fmt.Errorf("failed to execute seed: %w\n%s", err, string(exitError.Stderr)))
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								panic(fmt.Errorf("failed to execute seed: %w", err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println("Done! You may want to migrate forward from here.")
 | 
				
			||||||
 | 
						ListMigrations()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NOTE(asaf): This will be useful for open-sourcing the website, but is not yet necessary.
 | 
				
			||||||
 | 
					// Creates only what's necessary for a fresh deployment with no data
 | 
				
			||||||
 | 
					func BareMinimumSeed() {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NOTE(asaf): This will be useful for open-sourcing the website, but is not yet necessary.
 | 
				
			||||||
 | 
					// Creates enough data for development
 | 
				
			||||||
 | 
					func SampleSeed() {
 | 
				
			||||||
 | 
						// admin := CreateAdminUser("admin", "12345678")
 | 
				
			||||||
 | 
						// user := CreateUser("regular_user", "12345678")
 | 
				
			||||||
 | 
						// hmnProject := CreateProject("hmn", "Handmade Network")
 | 
				
			||||||
 | 
						// Create category
 | 
				
			||||||
 | 
						// Create thread
 | 
				
			||||||
 | 
						// Create accepted user project
 | 
				
			||||||
 | 
						// Create pending user project
 | 
				
			||||||
 | 
						// Create showcase items
 | 
				
			||||||
 | 
						// Create codelanguages
 | 
				
			||||||
 | 
						// Create library and library resources
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					Clean this up once we get the website working
 | 
				
			||||||
 | 
					---------------------------------------------
 | 
				
			||||||
 | 
					TODO: Questionable db tables that we inherited from Django:
 | 
				
			||||||
 | 
					* auth_group
 | 
				
			||||||
 | 
					* auth_group_permissions
 | 
				
			||||||
 | 
					* auth_permission
 | 
				
			||||||
 | 
					* auth_user_groups
 | 
				
			||||||
 | 
					* auth_user_user_permissions
 | 
				
			||||||
 | 
					* django_admin_log
 | 
				
			||||||
 | 
					* django_content_type
 | 
				
			||||||
 | 
					* django_migrations
 | 
				
			||||||
 | 
					* django_session
 | 
				
			||||||
 | 
					* django_site
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TODO: Questionable db tables that we inherited from the old website:
 | 
				
			||||||
 | 
					* handmade_blacklistemail
 | 
				
			||||||
 | 
					* handmade_blacklisthostname
 | 
				
			||||||
 | 
					* handmade_communicationchoice
 | 
				
			||||||
 | 
					* handmade_communicationchoicelist
 | 
				
			||||||
 | 
					* handmade_communicationsubcategory
 | 
				
			||||||
 | 
					* handmade_communicationsubthread
 | 
				
			||||||
 | 
					* handmade_kunenapost
 | 
				
			||||||
 | 
					* handmade_license
 | 
				
			||||||
 | 
					* handmade_license_texts
 | 
				
			||||||
 | 
					* handmade_memberextended.joomlaid
 | 
				
			||||||
 | 
					* handmade_onetimetoken // Is this for password resets??
 | 
				
			||||||
 | 
					* handmade_otherfile
 | 
				
			||||||
 | 
					* handmade_project_licenses
 | 
				
			||||||
		Loading…
	
		Reference in New Issue