Atlas CLI is a database schema management tool
written in Go, supporting a handful of popular databases. Atlas works on a similar premise as Prisma, a TypeScript-based ORM, with describing your schema in DDL (.hcl
in the case of Atlas, .prisma
in
the case of Prisma). Often times our schema files, written in our ORM's choice of DDL, are required to be singular
files at a specified location relative to our project root. This snippet simply merges individual schema files
programmatically in a
subdirectory and aggregates the atlas.hcl
file that Atlas CLI expects at the project, allowing for splitting our
schema up into as many individual partial definition files as we'd like.
package main
import (
"fmt"
"io"
"log"
"os"
"os/exec"
)
const schemaDefinitionFile = "atlas.hcl"
const atlasCommand = "atlas schema"
var formatSchemaCommand = fmt.Sprintf("%s fmt .", atlasCommand)
// mergeFileIntoSchemaDefinition inspects the definition file partial and appends
// to the atlas definition file defined in the root of the project.
func mergeFileIntoSchemaDefinition(filePath string, schemaDefinitionFile *os.File) {
log.Printf("Merging file: %s", filePath)
definitionFilePartial, err := os.Open(filePath)
if err != nil {
log.Fatalf("Error opening definition file partial %s - %v", filePath, err)
}
defer func(definitionFilePartial *os.File) {
if err := definitionFilePartial.Close(); err != nil {
log.Fatal(err)
}
}(definitionFilePartial)
if _, err = io.Copy(schemaDefinitionFile, definitionFilePartial); err != nil {
log.Fatalf("Could not copy contents of file %s - %v", filePath, err)
}
if _, err = schemaDefinitionFile.Write([]byte("\n")); err != nil {
log.Fatalf("Error writing new line character: %v", err)
}
log.Printf("%s merged successfully!", filePath)
}
// main batches the task to merge all atlas HCL DDL files together.
func main() {
cwd, err := os.Getwd()
if err != nil {
log.Fatalf("Error reading current directory - %v", err)
}
atlasDirectory := fmt.Sprintf("%s%catlas", cwd, os.PathSeparator)
atlasFiles, err := os.ReadDir(atlasDirectory)
if err != nil {
log.Fatalf("Error reading atlas directory - %v", err)
}
var filesToMerge []string
for _, file := range atlasFiles {
filePath := fmt.Sprintf("%s%c%s", atlasDirectory, os.PathSeparator, file.Name())
filesToMerge = append(filesToMerge, filePath)
}
log.Printf("Found %d schema files to merge, removing existing definition file...", len(atlasFiles))
schemaDefinitionFilePath := fmt.Sprintf("%s%c%s", cwd, os.PathSeparator, schemaDefinitionFile)
if err = os.Remove(schemaDefinitionFilePath); err != nil && !os.IsNotExist(err) {
log.Fatalf("Could not remove the definition file - %v", err)
}
log.Printf("Schema file removed, recreating defintion file at root %s...", schemaDefinitionFilePath)
rehydratedFile, err := os.Create(schemaDefinitionFilePath)
defer func(rehydratedFile *os.File) {
if err := rehydratedFile.Close(); err != nil {
log.Fatal(err)
}
}(rehydratedFile)
if err != nil {
log.Fatalf("Could not recreate the definition file - %v", err)
}
log.Printf("Merging definition partials into %s...", schemaDefinitionFile)
for _, filePath := range filesToMerge {
mergeFileIntoSchemaDefinition(filePath, rehydratedFile)
}
log.Print("Success! Formatting schema file...")
exec.Command(formatSchemaCommand)
log.Print("Atlas definition file created!")
}