Skip to content
Merged
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
22 changes: 22 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,18 +90,40 @@ jobs:
version=$(basename "$nupkg_file" | sed 's/^atlas-ef\.//' | sed 's/\.nupkg$//')
dotnet tool install --add-source ./nupkg --version "$version" -g atlas-ef
echo "Installed atlas-ef version: $version"

- name: Test tool without context flag (all contexts)
shell: bash
working-directory: ./src/Atlas.Provider.Demo
run: |
atlas-ef -- sqlite

- name: Test tool with context flag
shell: bash
working-directory: ./src/Atlas.Provider.Demo
run: |
atlas-ef --context BloggingContext -- sqlite

- name: Install Atlas CLI
uses: ariga/setup-atlas@v0

- name: Verify Atlas CLI version
shell: pwsh
run: atlas version

- name: Check migrations for sqlite
working-directory: ./src/Atlas.Provider.Demo
run: |
atlas migrate diff --env ef --var dialect=sqlite
env:
ATLAS_TOKEN: ${{ secrets.ATLAS_TOKEN }}

- name: Check migrations for sqlite (with context)
working-directory: ./src/Atlas.Provider.Demo
run: |
atlas migrate diff --env ef --var dialect=sqlite --var context=BloggingContext
env:
ATLAS_TOKEN: ${{ secrets.ATLAS_TOKEN }}

- name: Verify migrations generated
shell: bash
run: |
Expand Down
4 changes: 4 additions & 0 deletions src/Atlas.Provider.Core/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class Options
public string Framework { get; set; } = string.Empty;
public string WorkingDir { get; set; } = string.Empty;
public bool Nullable { get; set; } = true;
public string? Context { get; set; }
public List<string>? PositionalArgs { get; set; }

public Options(string[] args)
Expand Down Expand Up @@ -50,6 +51,9 @@ public Options(string[] args)
case "--working-dir":
if (i + 1 < args.Length) WorkingDir = args[++i];
break;
case "--context":
if (i + 1 < args.Length) Context = args[++i];
break;
default:
if (PositionalArgs == null)
{
Expand Down
13 changes: 9 additions & 4 deletions src/Atlas.Provider.Core/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,19 @@ static int Main(string[] args)
var types = executor.GetContextTypes();
foreach (var type in types)
{
if (!type.Contains("Name") || type["Name"] == null)
if (type["Name"] is not string name || string.IsNullOrEmpty(name))
{
continue;
}
var name = type["Name"]!.ToString();
if (string.IsNullOrEmpty(name))
if (!string.IsNullOrEmpty(options.Context))
{
continue;
var fullName = type["FullName"] as string;
var matches = name.Equals(options.Context, StringComparison.OrdinalIgnoreCase) ||
(fullName?.Equals(options.Context, StringComparison.OrdinalIgnoreCase) ?? false);
if (!matches)
{
continue;
}
}
var ctxInfo = executor.GetContextInfo(name);
if (ctxInfo == null || !ctxInfo.Contains("ProviderName") || ctxInfo["ProviderName"] == null)
Expand Down
92 changes: 92 additions & 0 deletions src/Atlas.Provider.Demo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,96 @@ public class AuditEntry
{
public string? Name { get; set; }
}

// Second DbContext for testing --context flag
public class ShopContextFactory : IDesignTimeDbContextFactory<ShopContext>
{
public ShopContext CreateDbContext(string[] args)
{
var provider = args.FirstOrDefault();
return new ShopContext(provider!);
}
}

public class ShopContext : DbContext
{
public DbSet<Product>? Products { get; set; }
public DbSet<Category>? Categories { get; set; }

private readonly string _provider;
public ShopContext(string provider = "SqlServer")
{
_provider = provider;
}

protected override void OnConfiguring(DbContextOptionsBuilder options)
{
switch (_provider.ToLower())
{
case "sqlserver":
options.UseSqlServer("Server=localhost;Database=ShopDb;User Id=your_username;Password=your_password;");
break;
case "sqlite":
options.UseSqlite("Data Source=shop.db;");
break;
case "mysql":
options.UseMySql(
"Server=localhost;Database=ShopDb;User=root;Password=your_password;",
ServerVersion.Create(8, 0, 0, ServerType.MySql),
optionsBuilder => optionsBuilder
.DisableLineBreakToCharSubstition()
.SchemaBehavior(MySqlSchemaBehavior.Ignore)
);
break;
case "mariadb":
options.UseMySql("Server=localhost;Database=ShopDb;User=root;Password=your_password;", ServerVersion.Create(8, 7, 0, ServerType.MariaDb));
break;
case "postgres":
options.UseNpgsql("Host=localhost;Database=ShopDb;Username=your_username;Password=your_password;");
break;
}
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.ToTable("Products", schema: "Shop");

modelBuilder.Entity<Category>()
.ToTable("Categories", schema: "Shop");

modelBuilder.Entity<Product>()
.HasOne(p => p.Category)
.WithMany(c => c.Products)
.HasForeignKey(p => p.CategoryId);

modelBuilder.Entity<Product>()
.Property(p => p.Name)
.HasMaxLength(100)
.IsRequired();

modelBuilder.Entity<Category>()
.Property(c => c.Name)
.HasMaxLength(50)
.IsRequired();
}
}

public class Product
{
[Key]
public int ProductId { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public int CategoryId { get; set; }
public Category? Category { get; set; }
}

public class Category
{
[Key]
public int CategoryId { get; set; }
public string Name { get; set; } = string.Empty;
public List<Product>? Products { get; set; }
}
}
21 changes: 18 additions & 3 deletions src/Atlas.Provider.Demo/atlas.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,43 @@ variable "dialect" {
type = string
}

variable "context" {
type = string
default = ""
}

locals {
dev_url = {
mysql = "docker://mysql/8/dev"
postgres = "docker://postgres/15"
sqlserver = "docker://sqlserver/2022-latest"
sqlite = "sqlite://file::memory:?cache=shared"
}[var.dialect]

schema_url = var.context == "" ? data.external_schema.efcore.url : data.external_schema.efcore_context.url
}

data "external_schema" "efcore" {
program = [
"atlas-ef", # this is the global tool installed with `dotnet tool install -g atlas-ef`
"atlas-ef",
"--", var.dialect,
]
}

data "external_schema" "efcore_context" {
program = [
"atlas-ef",
"--context", var.context,
"--", var.dialect,
]
}

env {
name = atlas.env
src = data.external_schema.efcore.url
src = local.schema_url
dev = local.dev_url
migration {
dir = "file://migrations/${var.dialect}"
dir = var.context == "" ? "file://migrations/${var.dialect}" : "file://migrations/${var.dialect}/${var.context}"
}
format {
migrate {
Expand Down
15 changes: 15 additions & 0 deletions src/Atlas.Provider.Demo/migrations/sqlite/20251020170346.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- Create "Categories" table
CREATE TABLE `Categories` (
`CategoryId` integer NOT NULL PRIMARY KEY AUTOINCREMENT,
`Name` text NOT NULL
);
-- Create "Products" table
CREATE TABLE `Products` (
`ProductId` integer NOT NULL PRIMARY KEY AUTOINCREMENT,
`Name` text NOT NULL,
`Price` text NOT NULL,
`CategoryId` integer NOT NULL,
CONSTRAINT `FK_Products_Categories_CategoryId` FOREIGN KEY (`CategoryId`) REFERENCES `Categories` (`CategoryId`) ON UPDATE NO ACTION ON DELETE CASCADE
);
-- Create index "IX_Products_CategoryId" to table: "Products"
CREATE INDEX `IX_Products_CategoryId` ON `Products` (`CategoryId`);
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-- Create "AuditEntry" table
CREATE TABLE `AuditEntry` (
`Name` text NULL
);
-- Create "Blogs" table
CREATE TABLE `Blogs` (
`BlogId` integer NOT NULL PRIMARY KEY AUTOINCREMENT,
`Url` varchar NOT NULL,
`Rating` decimal NOT NULL,
`Title` text NOT NULL,
`Content` text NOT NULL,
`Author` text NOT NULL DEFAULT 'Anonymous'
);
-- Create index "Blogs_Url" to table: "Blogs"
CREATE UNIQUE INDEX `Blogs_Url` ON `Blogs` (`Url`);
-- Create "Posts" table
CREATE TABLE `Posts` (
`PostId` integer NOT NULL PRIMARY KEY AUTOINCREMENT,
`Title` text NOT NULL,
`Content` text NOT NULL,
`BlogUrl` varchar NULL,
CONSTRAINT `FK_Posts_Blogs_BlogUrl` FOREIGN KEY (`BlogUrl`) REFERENCES `Blogs` (`Url`) ON UPDATE NO ACTION ON DELETE NO ACTION
);
-- Create index "IX_Posts_BlogUrl" to table: "Posts"
CREATE INDEX `IX_Posts_BlogUrl` ON `Posts` (`BlogUrl`);
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
h1:E3wbsnjAwijo4Pr+g9s7DMxRjAANdowsKWueiqjKpBA=
20251020170414.sql h1:z6C10/qQ2Ww6etuF6ze91skUJ92VJrJPBkJhD//h88w=
3 changes: 2 additions & 1 deletion src/Atlas.Provider.Demo/migrations/sqlite/atlas.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
h1:riBDPl8T28Skduf/aAlfc9tHI2xUjO3z6YXGSfgbmyA=
h1:ScFSKLRSo2fXRtSczrJja7Vyk797oT3/rlZKxPj8EiU=
20250603180536.sql h1:BF13Vo+fIA/JsBAx+hmym/3pf3wUPXepEZI5HfvyCv0=
20251020170346.sql h1:uJAb6aBsAxW23xMgH9faLQQ2JjurbUNnes4CHnsy0kA=
5 changes: 5 additions & 0 deletions src/Atlas.Provider.Loader/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ static int Main(
"--startup-assembly", Path.Combine(targetDir, _startupProject.TargetFileName!),
"--startup-project", startupProjectFile,
]);
if (!string.IsNullOrEmpty(context))
{
arguments.Add("--context");
arguments.Add(context);
}
if (string.Equals(_project.Nullable, "enable", StringComparison.OrdinalIgnoreCase)
|| string.Equals(_project.Nullable, "annotations", StringComparison.OrdinalIgnoreCase))
{
Expand Down
2 changes: 1 addition & 1 deletion test/Atlas.Provider.Test/GenerateSchemaTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void Can_generate_script(string providerName, string expectedFile)
{
WorkingDirectory = demoProjectPath,
FileName = "dotnet",
Arguments = $"exec {dllFileName} -- {providerName}",
Arguments = $"exec {dllFileName} --context BloggingContext -- {providerName}",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
Expand Down
Loading