From 8b22c4ae0c509a8213b8c7fc050c6bf6ae80967f Mon Sep 17 00:00:00 2001 From: maxime Date: Sat, 17 Feb 2024 21:27:39 +0100 Subject: [PATCH] fix CI --- .gitignore | 2 + API/Program.cs | 11 +- API/appsettings.Development.json | 6 +- AppContext/AppContext.cs | 37 ++- AppContext/AppContext.csproj | 1 + .../Migrations/20240218173232_m1.Designer.cs | 221 ++++++++++++++++++ AppContext/Migrations/20240218173232_m1.cs | 157 +++++++++++++ .../Migrations/AppContextModelSnapshot.cs | 218 +++++++++++++++++ ci/.drone.yml | 74 ++++++ ci/API.dockerfile | 16 ++ ci/DB-init.dockerfile | 15 ++ ci/db-init.sh | 13 ++ ci/deploy_nginx_proxy.sh | 12 + ci/deploy_staging_server.sh | 38 +++ ci/pushimage.sh | 8 + 15 files changed, 817 insertions(+), 12 deletions(-) create mode 100644 AppContext/Migrations/20240218173232_m1.Designer.cs create mode 100644 AppContext/Migrations/20240218173232_m1.cs create mode 100644 AppContext/Migrations/AppContextModelSnapshot.cs create mode 100644 ci/.drone.yml create mode 100644 ci/API.dockerfile create mode 100644 ci/DB-init.dockerfile create mode 100644 ci/db-init.sh create mode 100644 ci/deploy_nginx_proxy.sh create mode 100644 ci/deploy_staging_server.sh create mode 100755 ci/pushimage.sh diff --git a/.gitignore b/.gitignore index c65e4ab..357f024 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ # Build results +efbundle + [Dd]ebug/ [Rr]elease/ x64/ diff --git a/API/Program.cs b/API/Program.cs index 87b3558..94e8f1a 100644 --- a/API/Program.cs +++ b/API/Program.cs @@ -8,7 +8,6 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; using Services; -using StubContext; var builder = WebApplication.CreateBuilder(args); var config = builder.Configuration; @@ -47,8 +46,7 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) builder.Services.AddAuthorization(options => options.AddPolicy(IdentityData.AdminUserPolicyName, p => p.RequireClaim(IdentityData.AdminUserClaimName))); - -builder.Services.AddDbContext(ServiceLifetime.Scoped); +builder.Services.AddDbContext(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -84,9 +82,12 @@ app.Use((context, next) => SymmetricSecurityKey key = new(Encoding.UTF8.GetBytes(config["JWT:Key"]!)); var (jwt, expirationDate) = Authentication.GenerateJwt(key, context.User.Claims); context.Response.Headers["Next-Authorization"] = jwt; - context.Response.Headers["Next-Authorization-Expiration-Date"] = expirationDate.ToString(CultureInfo.InvariantCulture); - context.Response.Headers["Access-Control-Expose-Headers"] = "*"; + context.Response.Headers["Next-Authorization-Expiration-Date"] = + expirationDate.ToString(CultureInfo.InvariantCulture); + context.Response.Headers.AccessControlExposeHeaders = "Next-Authorization, Next-Authorization-Expiration-Date"; return next.Invoke(); }); + + app.Run(); \ No newline at end of file diff --git a/API/appsettings.Development.json b/API/appsettings.Development.json index 0c208ae..fee9626 100644 --- a/API/appsettings.Development.json +++ b/API/appsettings.Development.json @@ -2,7 +2,11 @@ "Logging": { "LogLevel": { "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Microsoft.AspNetCore": "Warning", + "Microsoft.AspNetCore.Authentication": "Trace" } + }, + "Database": { + "Datasource": "Datasource=database.db" } } diff --git a/AppContext/AppContext.cs b/AppContext/AppContext.cs index 41b08be..d789825 100644 --- a/AppContext/AppContext.cs +++ b/AppContext/AppContext.cs @@ -2,10 +2,11 @@ using AppContext.Entities; using Microsoft.AspNetCore.Cryptography.KeyDerivation; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; namespace AppContext; -public class AppContext(DbContextOptions options) : DbContext(options) +public class AppContext : DbContext { public DbSet Users { get; init; } public DbSet Tactics { get; init; } @@ -13,12 +14,36 @@ public class AppContext(DbContextOptions options) : DbContext(option public DbSet Members { get; init; } public DbSet TacticSteps { get; set; } - public AppContext() : this( - new DbContextOptionsBuilder() - .UseSqlite("DataSource=database.db") - .Options - ) + public AppContext() : base() { + + } + + public AppContext(DbContextOptions options) : base(options) + { + + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + base.OnConfiguring(optionsBuilder); + if (optionsBuilder.IsConfigured) + { + return; + } + + var pgsqliteDsn = Environment.GetEnvironmentVariable("PGSQL_DSN"); + Console.WriteLine(pgsqliteDsn); + + if (pgsqliteDsn != null) + { + optionsBuilder.UseNpgsql(pgsqliteDsn); + } + else + { + optionsBuilder.UseSqlite("Data Source=database.db"); + } + } protected override void OnModelCreating(ModelBuilder builder) diff --git a/AppContext/AppContext.csproj b/AppContext/AppContext.csproj index 3ed2224..daec449 100644 --- a/AppContext/AppContext.csproj +++ b/AppContext/AppContext.csproj @@ -16,6 +16,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/AppContext/Migrations/20240218173232_m1.Designer.cs b/AppContext/Migrations/20240218173232_m1.Designer.cs new file mode 100644 index 0000000..ce709aa --- /dev/null +++ b/AppContext/Migrations/20240218173232_m1.Designer.cs @@ -0,0 +1,221 @@ +// +using System; +using AppContext; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AppContext.Migrations +{ + [DbContext(typeof(AppContext))] + [Migration("20240218173232_m1")] + partial class m1 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("AppContext.Entities.MemberEntity", b => + { + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.Property("TeamId") + .HasColumnType("INTEGER"); + + b.Property("Role") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("UserId", "TeamId"); + + b.HasIndex("TeamId"); + + b.ToTable("Members"); + }); + + modelBuilder.Entity("AppContext.Entities.TacticEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("OwnerId") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.ToTable("Tactics"); + }); + + modelBuilder.Entity("AppContext.Entities.TacticStepEntity", b => + { + b.Property("TacticId") + .HasColumnType("INTEGER"); + + b.Property("StepId") + .HasColumnType("INTEGER"); + + b.Property("JsonContent") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("INTEGER"); + + b.Property("ParentStepId") + .HasColumnType("INTEGER"); + + b.Property("ParentTacticId") + .HasColumnType("INTEGER"); + + b.HasKey("TacticId", "StepId"); + + b.HasIndex("ParentTacticId", "ParentStepId"); + + b.ToTable("TacticSteps"); + }); + + modelBuilder.Entity("AppContext.Entities.TeamEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MainColor") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Picture") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SecondColor") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Teams"); + }); + + modelBuilder.Entity("AppContext.Entities.UserEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Email") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsAdmin") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ProfilePicture") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Salt") + .IsRequired() + .HasColumnType("BLOB"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("AppContext.Entities.MemberEntity", b => + { + b.HasOne("AppContext.Entities.TeamEntity", "Team") + .WithMany("Members") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AppContext.Entities.UserEntity", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AppContext.Entities.TacticEntity", b => + { + b.HasOne("AppContext.Entities.UserEntity", "Owner") + .WithMany("Tactics") + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("AppContext.Entities.TacticStepEntity", b => + { + b.HasOne("AppContext.Entities.TacticEntity", "Tactic") + .WithMany() + .HasForeignKey("TacticId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AppContext.Entities.TacticStepEntity", "Parent") + .WithMany("Children") + .HasForeignKey("ParentTacticId", "ParentStepId"); + + b.Navigation("Parent"); + + b.Navigation("Tactic"); + }); + + modelBuilder.Entity("AppContext.Entities.TacticStepEntity", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("AppContext.Entities.TeamEntity", b => + { + b.Navigation("Members"); + }); + + modelBuilder.Entity("AppContext.Entities.UserEntity", b => + { + b.Navigation("Tactics"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AppContext/Migrations/20240218173232_m1.cs b/AppContext/Migrations/20240218173232_m1.cs new file mode 100644 index 0000000..2990d90 --- /dev/null +++ b/AppContext/Migrations/20240218173232_m1.cs @@ -0,0 +1,157 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AppContext.Migrations +{ + /// + public partial class m1 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Teams", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", nullable: false), + Picture = table.Column(type: "TEXT", nullable: false), + MainColor = table.Column(type: "TEXT", nullable: false), + SecondColor = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Teams", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Password = table.Column(type: "TEXT", nullable: false), + Salt = table.Column(type: "BLOB", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + Email = table.Column(type: "TEXT", nullable: false), + ProfilePicture = table.Column(type: "TEXT", nullable: false), + IsAdmin = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Members", + columns: table => new + { + TeamId = table.Column(type: "INTEGER", nullable: false), + UserId = table.Column(type: "INTEGER", nullable: false), + Role = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Members", x => new { x.UserId, x.TeamId }); + table.ForeignKey( + name: "FK_Members_Teams_TeamId", + column: x => x.TeamId, + principalTable: "Teams", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Members_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Tactics", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", nullable: false), + CreationDate = table.Column(type: "TEXT", nullable: false), + OwnerId = table.Column(type: "INTEGER", nullable: false), + Type = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Tactics", x => x.Id); + table.ForeignKey( + name: "FK_Tactics_Users_OwnerId", + column: x => x.OwnerId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "TacticSteps", + columns: table => new + { + TacticId = table.Column(type: "INTEGER", nullable: false), + StepId = table.Column(type: "INTEGER", nullable: false), + ParentId = table.Column(type: "INTEGER", nullable: true), + ParentTacticId = table.Column(type: "INTEGER", nullable: true), + ParentStepId = table.Column(type: "INTEGER", nullable: true), + JsonContent = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TacticSteps", x => new { x.TacticId, x.StepId }); + table.ForeignKey( + name: "FK_TacticSteps_TacticSteps_ParentTacticId_ParentStepId", + columns: x => new { x.ParentTacticId, x.ParentStepId }, + principalTable: "TacticSteps", + principalColumns: new[] { "TacticId", "StepId" }); + table.ForeignKey( + name: "FK_TacticSteps_Tactics_TacticId", + column: x => x.TacticId, + principalTable: "Tactics", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Members_TeamId", + table: "Members", + column: "TeamId"); + + migrationBuilder.CreateIndex( + name: "IX_Tactics_OwnerId", + table: "Tactics", + column: "OwnerId"); + + migrationBuilder.CreateIndex( + name: "IX_TacticSteps_ParentTacticId_ParentStepId", + table: "TacticSteps", + columns: new[] { "ParentTacticId", "ParentStepId" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Members"); + + migrationBuilder.DropTable( + name: "TacticSteps"); + + migrationBuilder.DropTable( + name: "Teams"); + + migrationBuilder.DropTable( + name: "Tactics"); + + migrationBuilder.DropTable( + name: "Users"); + } + } +} diff --git a/AppContext/Migrations/AppContextModelSnapshot.cs b/AppContext/Migrations/AppContextModelSnapshot.cs new file mode 100644 index 0000000..c4249f8 --- /dev/null +++ b/AppContext/Migrations/AppContextModelSnapshot.cs @@ -0,0 +1,218 @@ +// +using System; +using AppContext; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AppContext.Migrations +{ + [DbContext(typeof(AppContext))] + partial class AppContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("AppContext.Entities.MemberEntity", b => + { + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.Property("TeamId") + .HasColumnType("INTEGER"); + + b.Property("Role") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("UserId", "TeamId"); + + b.HasIndex("TeamId"); + + b.ToTable("Members"); + }); + + modelBuilder.Entity("AppContext.Entities.TacticEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("OwnerId") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.ToTable("Tactics"); + }); + + modelBuilder.Entity("AppContext.Entities.TacticStepEntity", b => + { + b.Property("TacticId") + .HasColumnType("INTEGER"); + + b.Property("StepId") + .HasColumnType("INTEGER"); + + b.Property("JsonContent") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("INTEGER"); + + b.Property("ParentStepId") + .HasColumnType("INTEGER"); + + b.Property("ParentTacticId") + .HasColumnType("INTEGER"); + + b.HasKey("TacticId", "StepId"); + + b.HasIndex("ParentTacticId", "ParentStepId"); + + b.ToTable("TacticSteps"); + }); + + modelBuilder.Entity("AppContext.Entities.TeamEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MainColor") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Picture") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SecondColor") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Teams"); + }); + + modelBuilder.Entity("AppContext.Entities.UserEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Email") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsAdmin") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ProfilePicture") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Salt") + .IsRequired() + .HasColumnType("BLOB"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("AppContext.Entities.MemberEntity", b => + { + b.HasOne("AppContext.Entities.TeamEntity", "Team") + .WithMany("Members") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AppContext.Entities.UserEntity", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AppContext.Entities.TacticEntity", b => + { + b.HasOne("AppContext.Entities.UserEntity", "Owner") + .WithMany("Tactics") + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("AppContext.Entities.TacticStepEntity", b => + { + b.HasOne("AppContext.Entities.TacticEntity", "Tactic") + .WithMany() + .HasForeignKey("TacticId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("AppContext.Entities.TacticStepEntity", "Parent") + .WithMany("Children") + .HasForeignKey("ParentTacticId", "ParentStepId"); + + b.Navigation("Parent"); + + b.Navigation("Tactic"); + }); + + modelBuilder.Entity("AppContext.Entities.TacticStepEntity", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("AppContext.Entities.TeamEntity", b => + { + b.Navigation("Members"); + }); + + modelBuilder.Entity("AppContext.Entities.UserEntity", b => + { + b.Navigation("Tactics"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ci/.drone.yml b/ci/.drone.yml new file mode 100644 index 0000000..dd1f311 --- /dev/null +++ b/ci/.drone.yml @@ -0,0 +1,74 @@ +kind: pipeline +type: docker +name: "CI and deploy on iqball.maxou.dev" + + +steps: + - image: mcr.microsoft.com/dotnet/sdk:8.0 + name: "CI" + commands: + - dotnet test + + - image: plugins/docker + name: "build and push docker image" + depends_on: + - "CI" + settings: + dockerfile: ci/API.dockerfile + context: . + registry: hub.codefirst.iut.uca.fr + repo: hub.codefirst.iut.uca.fr/maxime.batista/iqball-api-dotnet + tags: + - ${DRONE_BRANCH} + username: + from_secret: SECRET_REGISTRY_USERNAME + password: + from_secret: SECRET_REGISTRY_PASSWORD + + # deploy staging database and server on codefirst + - image: eeacms/rsync:latest + name: "Instantiate docker images on staging server" + depends_on: + - "build and push docker image" + environment: + PRIVATE_KEY: + from_secret: PRIVATE_KEY + commands: + - mkdir -p ~/.ssh + - echo "$PRIVATE_KEY" > ~/.ssh/id_rsa + - chmod 0600 ~/.ssh + - chmod 0500 ~/.ssh/id_rsa* + - rsync -avz -e "ssh -p 80 -o 'StrictHostKeyChecking=no'" ci/deploy_staging_server.sh iqball@maxou.dev:/srv/www/iqball/$DRONE_BRANCH + - ssh -p 80 -o 'StrictHostKeyChecking=no' iqball@maxou.dev "chmod +x /srv/www/iqball/$DRONE_BRANCH/deploy_staging_server.sh && /srv/www/iqball/$DRONE_BRANCH/deploy_staging_server.sh $DRONE_BRANCH" + + # Deploy the production database and server on codefirst + - image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest + name: "Instantiate dotnet api docker image on codefirst" + depends_on: + - "build and push docker image" + environment: + IMAGENAME: hub.codefirst.iut.uca.fr/maxime.batista/iqball-api-dotnet:master + CONTAINERNAME: iqball_production_dotnet_webserver + OVERWRITE: true + COMMAND: create + + CODEFIRST_CLIENTDRONE_ENV_PGSQL_DSN: + from_secret: POSTGRES_DSN + ADMINS: maximebatista + + - image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest + name: "Instantiate pgsql database docker image on codefirst" + depends_on: + - "build and push docker image" + environment: + IMAGENAME: postgres + CONTAINERNAME: iqball_production_database + COMMAND: create + + CODEFIRST_CLIENTDRONE_ENV_POSTGRES_PASSWORD: + from_secret: POSTGRES_PASSWORD + CODEFIRST_CLIENTDRONE_ENV_POSTGRES_USER: + from_secret: POSTGRES_USER + CODEFIRST_CLIENTDRONE_ENV_POSTGRES_DB: + from_secret: POSTGRES_DB + ADMINS: maximebatista diff --git a/ci/API.dockerfile b/ci/API.dockerfile new file mode 100644 index 0000000..662129c --- /dev/null +++ b/ci/API.dockerfile @@ -0,0 +1,16 @@ +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS builder + +WORKDIR /home/ + +ADD . . + +RUN cd ./API && dotnet publish -c Release + +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime + +WORKDIR /home/ + +COPY --from=builder /home/API/bin/Release/net8.0 /home/ + +#$PGSQL_DSN is an environment variable +ENTRYPOINT /home/API \ No newline at end of file diff --git a/ci/DB-init.dockerfile b/ci/DB-init.dockerfile new file mode 100644 index 0000000..ba8c648 --- /dev/null +++ b/ci/DB-init.dockerfile @@ -0,0 +1,15 @@ +FROM mcr.microsoft.com/dotnet/sdk:8.0 + + +WORKDIR /home/ + +RUN apt update && apt install git -y + +RUN dotnet tool install --global dotnet-ef +ENV PATH="$PATH:/root/.dotnet/tools" + +ADD --chmod=755 db-init.sh ./ + + +ENTRYPOINT ./db-init.sh $BRANCH + diff --git a/ci/db-init.sh b/ci/db-init.sh new file mode 100644 index 0000000..0f7ef2c --- /dev/null +++ b/ci/db-init.sh @@ -0,0 +1,13 @@ +set -xeu + +git clone https://codefirst.iut.uca.fr/git/IQBall/Dotnet-WebAPI + +cd Dotnet-WebAPI + +git switch $1 + + +(cd AppContext && rm -rf Migrations && dotnet ef migrations add iqball-postgres-db-mig) +(cd API && dotnet ef database update --project ../AppContext) + + diff --git a/ci/deploy_nginx_proxy.sh b/ci/deploy_nginx_proxy.sh new file mode 100644 index 0000000..cab7dd0 --- /dev/null +++ b/ci/deploy_nginx_proxy.sh @@ -0,0 +1,12 @@ + +set -xeu + +docker network create iqball_net + +docker run -d \ + --name nginx-iqball-proxy \ + -v /srv/nginx/nginx-iqball-proxy.conf:/etc/nginx/conf.d/default.conf \ + -p 8081:8080 \ + --restart=always \ + --network iqball_net \ + nginx \ No newline at end of file diff --git a/ci/deploy_staging_server.sh b/ci/deploy_staging_server.sh new file mode 100644 index 0000000..2197c6f --- /dev/null +++ b/ci/deploy_staging_server.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +set -exu + +API_CONTAINER_NAME="iqball-api-dotnet-$1" +DB_CONTAINER_NAME="iqball-db-$1" + +(docker stop "$API_CONTAINER_NAME" && docker rm "$API_CONTAINER_NAME") || true + +docker run -d \ + --name "$DB_CONTAINER_NAME" \ + --restart=always \ + --network iqball_net \ + --env POSTGRES_PASSWORD=1234 \ + --env POSTGRES_USER=iqball \ + --env POSTGRES_DATABASE=iqball \ + postgres || true #run the database container if it does not already exists + +# apply migrations on database +docker run --rm -t \ + --env PGSQL_DSN="Server=$DB_CONTAINER_NAME;Username=iqball;Password=1234;Database=iqball" \ + --env BRANCH="$1" \ + --network iqball_net \ + iqball-db-init:latest + + +docker pull "hub.codefirst.iut.uca.fr/maxime.batista/iqball-api-dotnet:$1" + +# run the API +docker run -d \ + --name "$API_CONTAINER_NAME" \ + --restart=always \ + --network iqball_net \ + --env PGSQL_DSN="Server=$DB_CONTAINER_NAME;Username=iqball;Password=1234;Database=iqball" \ + "hub.codefirst.iut.uca.fr/maxime.batista/iqball-api-dotnet:$1" + + + diff --git a/ci/pushimage.sh b/ci/pushimage.sh new file mode 100755 index 0000000..594e7bc --- /dev/null +++ b/ci/pushimage.sh @@ -0,0 +1,8 @@ +set -xeu + +BRANCH=$1 + +sudo docker build -t iqball-api-dotnet:$BRANCH -f ci/Dockerfile . +sudo docker tag iqball-api-dotnet:$BRANCH hub.codefirst.iut.uca.fr/maxime.batista/iqball-api-dotnet:$BRANCH +sudo docker login hub.codefirst.iut.uca.fr +sudo docker push hub.codefirst.iut.uca.fr/maxime.batista/iqball-api-dotnet:$BRANCH \ No newline at end of file