better azure pipelines integration and building docker image

This commit is contained in:
13xforever 2019-08-24 20:46:10 +05:00
parent 8b14e84e5f
commit 62b3c69a49
17 changed files with 158 additions and 29 deletions

3
.dockerignore Normal file
View File

@ -0,0 +1,3 @@
.vs/
.vscode/
_ReSharper.*/

View File

@ -8,6 +8,7 @@
<UserSecretsId>c2e6548b-b215-4a18-a010-958ef294b310</UserSecretsId>
<LangVersion>latest</LangVersion>
<NoWarn>1701;1702;VSTHRD200</NoWarn>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
@ -37,6 +38,7 @@
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.2.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="2.2.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.4.10" />
<PackageReference Include="Nerdbank.Streams" Version="2.3.26" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="NLog" Version="4.6.6" />

View File

@ -5,6 +5,7 @@ using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading;
using CompatBot.Utils;
using DSharpPlus.Entities;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.UserSecrets;
@ -54,13 +55,16 @@ namespace CompatBot
public static int MinimumPiracyTriggerLength => config.GetValue(nameof(MinimumPiracyTriggerLength), 4);
public static string Token => config.GetValue(nameof(Token), "");
public static string LogPath => config.GetValue(nameof(LogPath), "logs/bot.log"); // paths are relative to the assembly, so this will put it in the project's root
public static string LogPath => config.GetValue(nameof(LogPath), "../../../logs/"); // paths are relative to the assembly, so this will put it in the project's root
public static string IrdCachePath => config.GetValue(nameof(IrdCachePath), "./ird/");
public static string GoogleApiConfigPath
{
get
{
if (SandboxDetector.Detect() == "Docker")
return "/bot-config/credentials.json";
if (Assembly.GetEntryAssembly().GetCustomAttribute<UserSecretsIdAttribute>() is UserSecretsIdAttribute attribute)
{
var path = Path.GetDirectoryName(PathHelper.GetSecretsPathFromSecretsId(attribute.UserSecretsId));
@ -68,6 +72,7 @@ namespace CompatBot
if (File.Exists(path))
return path;
}
return "Properties/credentials.json";
}
}
@ -154,7 +159,7 @@ namespace CompatBot
config = new ConfigurationBuilder()
.AddUserSecrets(Assembly.GetExecutingAssembly()) // lower priority
//.AddEnvironmentVariables()
.AddEnvironmentVariables()
.AddInMemoryCollection(inMemorySettings) // higher priority
.Build();
Log = GetLog();
@ -172,7 +177,7 @@ namespace CompatBot
{
var config = new NLog.Config.LoggingConfiguration();
var fileTarget = new FileTarget("logfile") {
FileName = Path.Combine("../../../", LogPath),
FileName = Path.Combine(LogPath, "bot.log"),
ArchiveEvery = FileArchivePeriod.Day,
ArchiveNumbering = ArchiveNumberingMode.DateAndSequence,
KeepFileOpen = true,

View File

@ -3,6 +3,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using CompatBot.Database.Migrations;
using CompatBot.Utils;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations.Internal;
@ -110,6 +111,9 @@ namespace CompatBot.Database
internal static string GetDbPath(string dbName, Environment.SpecialFolder desiredFolder)
{
if (SandboxDetector.Detect() == "Docker")
return Path.Combine("/bot-db/", dbName);
var settingsFolder = Path.Combine(Environment.GetFolderPath(desiredFolder), "compat-bot");
try
{

View File

@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using CompatBot.Commands;
@ -8,10 +9,12 @@ using CompatBot.Database;
using CompatBot.Database.Providers;
using CompatBot.EventHandlers;
using CompatBot.ThumbScrapper;
using CompatBot.Utils;
using DSharpPlus;
using DSharpPlus.CommandsNext;
using DSharpPlus.Entities;
using DSharpPlus.Interactivity;
using Microsoft.Extensions.Configuration.UserSecrets;
using Microsoft.Extensions.DependencyInjection;
namespace CompatBot
@ -24,6 +27,17 @@ namespace CompatBot
internal static async Task Main(string[] args)
{
if (args.Length > 0 && args[0] == "--dry-run")
{
Console.WriteLine("Confinement: " + SandboxDetector.Detect());
Console.WriteLine("Database path: " + Path.GetDirectoryName(Path.GetFullPath(DbImporter.GetDbPath("fake.db", Environment.SpecialFolder.ApplicationData))));
if (Assembly.GetEntryAssembly().GetCustomAttribute<UserSecretsIdAttribute>() is UserSecretsIdAttribute attribute)
{
Console.WriteLine("Bot config path: " + Path.GetDirectoryName(Path.GetFullPath(Config.GoogleApiConfigPath)));
}
return;
}
var singleInstanceCheckThread = new Thread(() =>
{
using (var instanceLock = new Mutex(false, @"Global\RPCS3 Compatibility Bot"))

View File

@ -2,6 +2,9 @@
"profiles": {
"default": {
"commandName": "Project"
},
"Docker": {
"commandName": "Docker"
}
}
}

View File

@ -218,7 +218,7 @@ namespace CompatBot.Utils
return result.ToString(0, result.Length-1);
}
public static string GetMoons(decimal? stars)
public static string GetMoons(decimal? stars, bool haveFun = true)
{
if (!stars.HasValue)
return null;
@ -232,7 +232,7 @@ namespace CompatBot.Utils
if (halfStar == 4)
{
if (new Random().Next(100) == 69)
if (haveFun && new Random().Next(100) == 69)
result += "🌝";
else
result += "🌕";
@ -246,7 +246,7 @@ namespace CompatBot.Utils
for (var i = 0; i < noStars; i++)
{
if (i == 0 && halfStar == 0 && new Random().Next(100) == 69)
if (haveFun && i == 0 && halfStar == 0 && new Random().Next(100) == 69)
result += "🌚";
else
result += "🌑";

View File

@ -12,6 +12,9 @@ namespace CompatBot.Utils
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("FLATPAK_SYSTEM_DIR")))
return "Flatpak";
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("RUNNING_IN_DOCKER")))
return "Docker";
return null;
}
}

View File

@ -82,5 +82,7 @@ namespace CompatBot.Utils
return date.ToUniversalTime();
return date.AsUtc();
}
public static List<string> GetSupportedTimeZoneAbbreviations() => TimeZoneMap.Keys.ToList();
}
}

11
Dockerfile Normal file
View File

@ -0,0 +1,11 @@
FROM mcr.microsoft.com/dotnet/core/sdk:latest AS base
COPY packages /root/.nuget/packages/
WORKDIR /src
COPY . .
RUN rm -rf ./packages
RUN git status
RUN dotnet build "CompatBot/CompatBot.csproj" -c Release
ENV RUNNING_IN_DOCKER true
WORKDIR /src/CompatBot
RUN dotnet run -c Release --dry-run
ENTRYPOINT ["dotnet", "run", "-c Release", "CompatBot.csproj"]

View File

@ -47,6 +47,8 @@ How to Build
How to Run in Production
------------------------
### Running from source
* Change configuration if needed (probably just token):
* use `$ dotnet user-secrets set Token <your_token_here>`
* for available configuration variables, see [Config.cs](CompatBot/Config.cs#L31)
@ -54,6 +56,11 @@ How to Run in Production
* `$ cd CompatBot`
* `$ dotnet run -c Release`
### Running with Docker
* Official image is hosted on [Docker Hub](https://hub.docker.com/r/13xforever/rpcs3-discord-bot).
* You should pull images tagged with `release-latest` (same thing as `latest`)
* Please take a look at the [docker-compose.yml](docker-compose.yml) for required configuration (bot token and mounting points for persistent data).
External resources that need manual updates
-------------------------------------------
* [Unicode confusables](http://www.unicode.org/Public/security/latest/confusables.txt) gzipped, for Homoglyph checks

View File

@ -4,7 +4,7 @@ using NUnit.Framework;
namespace Tests
{
[TestFixture]
[TestFixture, Explicit("Requires files to run")]
public class IrdTests
{
[Test]

View File

@ -23,7 +23,7 @@ namespace Tests
[TestCase(1.0, "🌕🌑🌑🌑🌑")]
public void FormatTest(decimal score, string expectedValue)
{
Assert.That(StringUtils.GetMoons(score), Is.EqualTo(expectedValue), "Failed for " + score);
Assert.That(StringUtils.GetMoons(score, false), Is.EqualTo(expectedValue), "Failed for " + score);
}
}
}

View File

@ -8,12 +8,12 @@ namespace Tests
public class TimeParserTests
{
[TestCase("2019-8-19 6:00 PT", "2019-08-19T13:00Z")]
[TestCase("2019-8-19 17:00 bst", "2019-08-19T16:00Z")]
[TestCase("2019-8-19 17:00 cest", "2019-08-19T15:00Z")]
[TestCase("2019-9-1 22:00 jst", "2019-09-01T13:00Z")]
public void TimeZoneConverterTest(string input, string utcInput)
{
var utc = DateTime.Parse(utcInput).Normalize();
Assert.That(TimeParser.TryParse(input, out var result), Is.True);
Assert.That(TimeParser.TryParse(input, out var result), Is.True, $"{input} failed to parse\nSupported time zones: {string.Join(", ", TimeParser.GetSupportedTimeZoneAbbreviations())}");
Assert.That(result, Is.EqualTo(utc));
Assert.That(result.Kind, Is.EqualTo(DateTimeKind.Utc));
}

View File

@ -1,20 +1,75 @@
# ASP.NET Core
# Build and test ASP.NET Core projects targeting .NET Core.
# Add steps that run tests, create a NuGet package, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core
jobs:
- job: BuildAndTest
displayName: 'Run tests'
pool:
vmImage: 'ubuntu-latest'
steps:
- script: dotnet restore --ignore-failed-sources
displayName: 'dotnet restore (first try)'
pool:
vmImage: 'Ubuntu 16.04'
- script: dotnet restore --ignore-failed-sources
displayName: 'dotnet restore (second try)'
variables:
buildConfiguration: 'Release'
- script: dotnet restore
displayName: 'dotnet restore (last try)'
steps:
- script: dotnet restore
displayName: 'dotnet restore'
- script: dotnet build --configuration Debug
- script: dotnet build --configuration Debug
displayName: 'dotnet build Debug'
- script: dotnet build --configuration Release
- task: DotNetCoreCLI@2
displayName: 'dotnet test'
inputs:
command: 'test'
projects: Tests/Tests.csproj
- job: DockerImage
displayName: 'Build Docker image'
condition: and(and(succeeded(), eq(variables['ReleaseBranch'], variables['Build.SourceBranch'])), ne(format('{0}', variables['DockerConnection']), ''))
dependsOn: BuildAndTest
pool:
vmImage: 'ubuntu-latest'
steps:
- script: git checkout -f $(Build.SourceBranchName)
displayName: 'create local tracking branch'
- script: git clean -dfx
displayName: 'clean build artifacts'
- script: dotnet restore --ignore-failed-sources
displayName: 'dotnet restore (first try)'
- script: dotnet restore --ignore-failed-sources
displayName: 'dotnet restore (second try)'
- script: dotnet restore
displayName: 'dotnet restore (last try)'
- script: dotnet build --configuration Release
displayName: 'dotnet build Release'
- script: mkdir packages && cp -a /home/vsts/.nuget/packages ./packages/
displayName: 'copy nuget package cache for docker'
- task: Docker@2
displayName: 'building release docker image'
condition: and(succeeded(), eq(variables['ReleaseKind'], 'release'))
inputs:
containerRegistry: $(DockerConnection)
repository: $(DockerRegistry)
command: 'buildAndPush'
Dockerfile: 'Dockerfile'
tags: |
$(Build.BuildId)
release-latest
latest
- task: Docker@2
displayName: 'building test docker image'
condition: and(succeeded(), not(eq(variables['ReleaseKind'], 'release')))
inputs:
containerRegistry: $(DockerConnection)
repository: $(DockerRegistry)
command: 'buildAndPush'
Dockerfile: 'Dockerfile'
tags: test-latest

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27703.2035
# Visual Studio Version 16
VisualStudioVersion = 16.0.28917.181
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CompatBot", "CompatBot\CompatBot.csproj", "{6D9CA448-60C1-4D66-91D6-EC6C586508E6}"
EndProject
@ -17,9 +17,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Clients", "Clients", "{E7FE0ADD-CBA6-4321-8A1C-0A3B5C3F54C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GithubClient", "Clients\GithubClient\GithubClient.csproj", "{AF8FDA29-864E-4A1C-9568-99DECB7E4B36}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GithubClient", "Clients\GithubClient\GithubClient.csproj", "{AF8FDA29-864E-4A1C-9568-99DECB7E4B36}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppveyorClient", "Clients\AppveyorClient\AppveyorClient.csproj", "{595ED201-1456-49F9-AD60-54B08499A5C1}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppveyorClient", "Clients\AppveyorClient\AppveyorClient.csproj", "{595ED201-1456-49F9-AD60-54B08499A5C1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AD87F38F-BFCE-4EA6-A430-20C497552FD7}"
ProjectSection(SolutionItems) = preProject
azure-pipelines.yml = azure-pipelines.yml
docker-compose.example.yml = docker-compose.example.yml
Dockerfile = Dockerfile
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@ -0,0 +1,13 @@
version: "3"
services:
bot:
image: rpcs3/discord-bot:latest
volumes:
- /home/MY_USER_NAME/.local/share/compat-bot:/bot-db
- /home/MY_USER_NAME/.microsoft/usersecrets/c2e6548b-b215-4a18-a010-958ef294b310:/bot-config
- /var/logs/compat-bot:/src/CompatBot/logs
- /ver/ird:/var/ird
environment:
Token: MY_BOT_TOKEN
LogPath: /var/logs/compat-bot
IrdCachePath: /var/ird