diff --git a/.editorconfig b/.editorconfig
index 2225fd9..e2139a6 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -6,3 +6,6 @@ indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
+
+[*.yml]
+indent_size = 2
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..2f675b3
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,55 @@
+name: Main
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+ schedule:
+ - cron: '0 0 * * 6'
+
+ workflow_dispatch:
+
+jobs:
+ main:
+ strategy:
+ fail-fast: false
+ matrix:
+ config:
+ - name: 'macos'
+ image: 'macos-12'
+ - name: 'linux'
+ image: 'ubuntu-22.04'
+ - name: 'windows'
+ image: 'windows-2022'
+
+ name: main.${{ matrix.config.name }}
+ runs-on: ${{ matrix.config.image }}
+
+ # noinspection SpellCheckingInspection
+ env:
+ DOTNET_NOLOGO: 1
+ DOTNET_CLI_TELEMETRY_OPTOUT: 1
+ NUGET_PACKAGES: ${{ github.workspace }}/.github/nuget-packages
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: NuGet cache
+ uses: actions/cache@v3
+ with:
+ path: ${{ env.NUGET_PACKAGES }}
+ key: ${{ runner.os }}.nuget.${{ hashFiles('**/*.fsproj') }}
+
+ - name: Set up .NET SDK
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Build
+ run: dotnet build
+
+ - name: Test
+ run: dotnet run --project ctor -- verify
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..90e413f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+/.idea/
+
+bin/
+obj/
+
+*.private.json
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..83afccf
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "Fabricator"]
+ path = Fabricator
+ url = git@github.com:ForNeVeR/Fabricator.git
diff --git a/Codingteam.Devops.sln b/Codingteam.Devops.sln
new file mode 100644
index 0000000..e1a92c3
--- /dev/null
+++ b/Codingteam.Devops.sln
@@ -0,0 +1,68 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Codingteam.Ctor", "ctor\Codingteam.Ctor.fsproj", "{E08EC4D8-5FD7-45CB-B48A-689005546B10}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A37A2B6B-F09B-4105-8AA5-47BA5D74B68A}"
+ ProjectSection(SolutionItems) = preProject
+ .gitignore = .gitignore
+ .editorconfig = .editorconfig
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{C4672A21-FB21-4BBF-A0A1-512AC1B66C9A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{67164D20-A669-4BC3-A40E-751D3045BA89}"
+ ProjectSection(SolutionItems) = preProject
+ .github\workflows\main.yml = .github\workflows\main.yml
+ EndProjectSection
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Fabricator.Console", "Fabricator\Fabricator.Console\Fabricator.Console.fsproj", "{0157906C-006C-45F8-A79A-0D55F16B16B2}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Fabricator", "Fabricator", "{853E359A-18C5-4472-A712-D5C80EE99D6B}"
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Fabricator.Core", "Fabricator\Fabricator.Core\Fabricator.Core.fsproj", "{C57439E9-20AF-4EEB-8C91-5D74D346374B}"
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Fabricator.Resources", "Fabricator\Fabricator.Resources\Fabricator.Resources.fsproj", "{414C5063-3201-43AC-9DA1-B18A18802C0D}"
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Fabricator.Templates", "Fabricator\Fabricator.Templates\Fabricator.Templates.fsproj", "{E4F7858D-B0F7-4B42-928F-02688E843566}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E08EC4D8-5FD7-45CB-B48A-689005546B10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E08EC4D8-5FD7-45CB-B48A-689005546B10}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E08EC4D8-5FD7-45CB-B48A-689005546B10}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E08EC4D8-5FD7-45CB-B48A-689005546B10}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0157906C-006C-45F8-A79A-0D55F16B16B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0157906C-006C-45F8-A79A-0D55F16B16B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0157906C-006C-45F8-A79A-0D55F16B16B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0157906C-006C-45F8-A79A-0D55F16B16B2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C57439E9-20AF-4EEB-8C91-5D74D346374B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C57439E9-20AF-4EEB-8C91-5D74D346374B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C57439E9-20AF-4EEB-8C91-5D74D346374B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C57439E9-20AF-4EEB-8C91-5D74D346374B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {414C5063-3201-43AC-9DA1-B18A18802C0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {414C5063-3201-43AC-9DA1-B18A18802C0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {414C5063-3201-43AC-9DA1-B18A18802C0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {414C5063-3201-43AC-9DA1-B18A18802C0D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E4F7858D-B0F7-4B42-928F-02688E843566}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E4F7858D-B0F7-4B42-928F-02688E843566}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E4F7858D-B0F7-4B42-928F-02688E843566}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E4F7858D-B0F7-4B42-928F-02688E843566}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {67164D20-A669-4BC3-A40E-751D3045BA89} = {C4672A21-FB21-4BBF-A0A1-512AC1B66C9A}
+ {0157906C-006C-45F8-A79A-0D55F16B16B2} = {853E359A-18C5-4472-A712-D5C80EE99D6B}
+ {C57439E9-20AF-4EEB-8C91-5D74D346374B} = {853E359A-18C5-4472-A712-D5C80EE99D6B}
+ {414C5063-3201-43AC-9DA1-B18A18802C0D} = {853E359A-18C5-4472-A712-D5C80EE99D6B}
+ {E4F7858D-B0F7-4B42-928F-02688E843566} = {853E359A-18C5-4472-A712-D5C80EE99D6B}
+ EndGlobalSection
+EndGlobal
diff --git a/Codingteam.Devops.sln.DotSettings b/Codingteam.Devops.sln.DotSettings
new file mode 100644
index 0000000..2f3a410
--- /dev/null
+++ b/Codingteam.Devops.sln.DotSettings
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file
diff --git a/Fabricator b/Fabricator
new file mode 160000
index 0000000..b5b64d1
--- /dev/null
+++ b/Fabricator
@@ -0,0 +1 @@
+Subproject commit b5b64d10005b1fd45f4cbe2172e17c1f91e21f9d
diff --git a/ctor/Codingteam.Ctor.fsproj b/ctor/Codingteam.Ctor.fsproj
new file mode 100644
index 0000000..58c3f3a
--- /dev/null
+++ b/ctor/Codingteam.Ctor.fsproj
@@ -0,0 +1,20 @@
+
+
+
+ Exe
+ net8.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ctor/GreenCaptchaBot.fs b/ctor/GreenCaptchaBot.fs
new file mode 100644
index 0000000..a706483
--- /dev/null
+++ b/ctor/GreenCaptchaBot.fs
@@ -0,0 +1,29 @@
+module internal Codingteam.Ctor.GreenCaptchaBot
+
+open Fabricator.Core
+open Fabricator.Resources.Files
+open Fabricator.Resources.Docker
+open Fabricator.Templates.FileTemplates
+
+let private hostConfigDirectory = "/opt/green-captcha-bot/"
+let private docker =
+ let version = "v1.15.1"
+ dockerContainer {
+ Sources = {
+ GitRepository = "https://github.com/ImoutoChan/GreenCaptchaBot.git"
+ GitReference = $"tags/{version}"
+ }
+ DockerfilePath = "CaptchaBot/Dockerfile"
+ Tag = version
+ Name = "green-captcha-bot"
+ Options = [|
+ Volume(hostPath = hostConfigDirectory, containerPath = "/app/Configuration")
+ |]
+ }
+
+let private configFile parameters = FileResource(
+ templatedFile "GreenCaptchaBot.template.json" parameters,
+ $"{hostConfigDirectory}/appsettings.json"
+)
+
+let resources(parameters: Map): IResource[] = [| configFile parameters; docker |]
diff --git a/ctor/GreenCaptchaBot.template.json b/ctor/GreenCaptchaBot.template.json
new file mode 100644
index 0000000..b42143b
--- /dev/null
+++ b/ctor/GreenCaptchaBot.template.json
@@ -0,0 +1,6 @@
+{
+ "Configuration": {
+ "BotToken": "$BOT_TOKEN",
+ "DeleteJoinMessages": "Unsuccessful"
+ }
+}
diff --git a/ctor/Program.fs b/ctor/Program.fs
new file mode 100644
index 0000000..78dee2a
--- /dev/null
+++ b/ctor/Program.fs
@@ -0,0 +1,21 @@
+open Fabricator.Console
+open Fabricator.Core
+open Fabricator.Templates.FileTemplates
+
+open Codingteam.Ctor
+
+let private cluster mode = task {
+ let connectionsFileName = if mode = EntryPoint.RunMode.Verify then "connections.stub.json" else "connections.private.json"
+ let! parameters = readParameterFile "parameters.json"
+ return [|
+ {
+ Name = "ctor"
+ Designator = Designators.fromConnectionsFile connectionsFileName "ctor"
+ Resources = GreenCaptchaBot.resources parameters
+ Type = MachineType.Linux
+ }
+ |]
+}
+
+let main (args: string[]): int =
+ EntryPoint.main args (fun m -> (cluster m).GetAwaiter().GetResult())