From eb1075a21bee4f06c2165934bb433b9b9c7b0dda Mon Sep 17 00:00:00 2001 From: MattMckenzy Date: Thu, 15 Feb 2024 22:02:53 -0500 Subject: [PATCH] Added configuration options to support new certificate types.; bumped to version 1.1.0 --- .dockerignore | 3 + .gitignore | 5 +- .vscode/launch.json | 30 ++++++ .vscode/tasks.json | 120 +++++++++++++++++++++ CHANGELOG.md | 11 ++ Dockerfile | 21 ++-- Exceptions/BadRequestException.cs | 7 -- Exceptions/CommunicationException.cs | 9 -- Exceptions/ConflictException.cs | 7 -- Exceptions/ForbiddenException.cs | 9 -- Exceptions/NotFoundException.cs | 7 -- Exceptions/UnauthorizedException.cs | 9 -- Exceptions/UnprocessableEntityException.cs | 9 -- Mail2Gotify.csproj | 39 +++---- Program.cs | 6 ++ README.md | 28 +++-- Services/FileSystemCaching.cs | 8 +- Services/Mail2GotifyService.cs | 66 ++++++++---- appsettings.json | 9 +- publish.sh | 47 ++++++++ 20 files changed, 323 insertions(+), 127 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 CHANGELOG.md create mode 100644 publish.sh diff --git a/.dockerignore b/.dockerignore index 3729ff0..1682a85 100644 --- a/.dockerignore +++ b/.dockerignore @@ -21,5 +21,8 @@ **/obj **/secrets.dev.yaml **/values.dev.yaml +**/*.dockerfile +**/publish.ps1 +**/launchSettings.json LICENSE README.md \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8f5fe03..2bd2593 100644 --- a/.gitignore +++ b/.gitignore @@ -260,5 +260,6 @@ paket-files/ __pycache__/ *.pyc /Build -appsettings.Development.json -appsettings.Production.json + +# Debug secrets +**/.env \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5b33a2b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,30 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/bin/Debug/net7.0/Mail2Gotify.dll", + "args": [], + "cwd": "${workspaceFolder}", + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + }, + { + "name": "Docker .NET Launch", + "type": "docker", + "request": "launch", + "preLaunchTask": "docker-run: debug", + "netCore": { + "appProject": "${workspaceFolder}/Mail2Gotify.csproj" + } + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..0f04b40 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,120 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/Mail2Gotify.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/Mail2Gotify.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/Mail2Gotify.sln" + ], + "problemMatcher": "$msCompile" + }, + { + "type": "docker-build", + "label": "docker-build: debug", + "dependsOn": [ + "build" + ], + "dockerBuild": { + "tag": "mail2gotify:dev", + "target": "base", + "dockerfile": "${workspaceFolder}/Dockerfile", + "context": "${workspaceFolder}", + "pull": true + }, + "netCore": { + "appProject": "${workspaceFolder}/Mail2Gotify.csproj" + } + }, + { + "type": "docker-build", + "label": "docker-build: release", + "dependsOn": [ + "build" + ], + "dockerBuild": { + "tag": "mail2gotify:latest", + "dockerfile": "${workspaceFolder}/Dockerfile", + "context": "${workspaceFolder}", + "platform": { + "os": "linux", + "architecture": "amd64" + }, + "pull": true + }, + "netCore": { + "appProject": "${workspaceFolder}/Mail2Gotify.csproj" + } + }, + { + "type": "docker-run", + "label": "docker-run: debug", + "dependsOn": [ + "docker-build: debug" + ], + "dockerRun": { + "env": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://+:8122", + "TZ": "America/Toronto", + }, + "volumes": [ + { + "containerPath": "/cache", + "localPath": "${userHome}/Working/Mail2Gotify/Cache" + }, + { + "containerPath": "/cert", + "localPath": "${userHome}/Working/Mail2Gotify/Cert" + } + ], + "envFiles": [ + "${workspaceFolder}/.env" + ] + }, + "netCore": { + "appProject": "${workspaceFolder}/Mail2Gotify.csproj", + "enableDebugging": true + } + }, + { + "type": "docker-run", + "label": "docker-run: release", + "dependsOn": [ + "docker-build: release" + ], + "dockerRun": {}, + "netCore": { + "appProject": "${workspaceFolder}/Mail2Gotify.csproj" + } + } + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..28b345d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Change Log + +All notable changes to "Mail2Gotify" will be documented in this file. + +## 1.1.0 + +- Added configuration options to support new certificate types. + +## 1.0.0 + +- Initial release \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 2ca0a3a..a09a8c5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,14 @@ -#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. - -FROM mcr.microsoft.com/dotnet/runtime AS base +FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base WORKDIR /app EXPOSE 587 -FROM mcr.microsoft.com/dotnet/sdk AS build -WORKDIR /src -COPY ["Mail2Gotify.csproj", "."] -RUN dotnet restore "./Mail2Gotify.csproj" -COPY . . -WORKDIR "/src/." -RUN dotnet build "Mail2Gotify.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "Mail2Gotify.csproj" -c Release -o /app/publish +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS publish +ARG TARGETARCH +ARG CONFIG +COPY . ./app +WORKDIR /app +RUN dotnet restore "Mail2Gotify.csproj" -a $TARGETARCH +RUN dotnet publish "Mail2Gotify.csproj" --self-contained -a $TARGETARCH -c $CONFIG -o publish FROM base AS final WORKDIR /app diff --git a/Exceptions/BadRequestException.cs b/Exceptions/BadRequestException.cs index 782369f..117e1a8 100644 --- a/Exceptions/BadRequestException.cs +++ b/Exceptions/BadRequestException.cs @@ -29,12 +29,5 @@ public BadRequestException(string message) : base(message) public BadRequestException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Constructor with serialization info and streaming context. - /// - protected BadRequestException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } \ No newline at end of file diff --git a/Exceptions/CommunicationException.cs b/Exceptions/CommunicationException.cs index 50afcdf..94e9516 100644 --- a/Exceptions/CommunicationException.cs +++ b/Exceptions/CommunicationException.cs @@ -48,14 +48,5 @@ public CommunicationException(string message) : base(message) public CommunicationException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Default constructor with which to serialize. - /// - /// The serialization info to use. - /// The streaming context to use. - protected CommunicationException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/Exceptions/ConflictException.cs b/Exceptions/ConflictException.cs index 8f5bb98..cd39be2 100644 --- a/Exceptions/ConflictException.cs +++ b/Exceptions/ConflictException.cs @@ -29,12 +29,5 @@ public ConflictException(string message) : base(message) public ConflictException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Constructor with serialization info and streaming context. - /// - protected ConflictException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } \ No newline at end of file diff --git a/Exceptions/ForbiddenException.cs b/Exceptions/ForbiddenException.cs index fbbae7d..9470128 100644 --- a/Exceptions/ForbiddenException.cs +++ b/Exceptions/ForbiddenException.cs @@ -32,14 +32,5 @@ public ForbiddenException(string message) : base(message) public ForbiddenException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Default constructor with which to serialize. - /// - /// The serialization info to use. - /// The streaming context to use. - protected ForbiddenException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/Exceptions/NotFoundException.cs b/Exceptions/NotFoundException.cs index 799005a..b2834ee 100644 --- a/Exceptions/NotFoundException.cs +++ b/Exceptions/NotFoundException.cs @@ -29,12 +29,5 @@ public NotFoundException(string message) : base(message) public NotFoundException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Constructor with serialization info and streaming context. - /// - protected NotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } \ No newline at end of file diff --git a/Exceptions/UnauthorizedException.cs b/Exceptions/UnauthorizedException.cs index 6f0a675..461b119 100644 --- a/Exceptions/UnauthorizedException.cs +++ b/Exceptions/UnauthorizedException.cs @@ -32,14 +32,5 @@ public UnauthorizedException(string message) : base(message) public UnauthorizedException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Default constructor with which to serialize. - /// - /// The serialization info to use. - /// The streaming context to use. - protected UnauthorizedException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/Exceptions/UnprocessableEntityException.cs b/Exceptions/UnprocessableEntityException.cs index 23b86a2..110643a 100644 --- a/Exceptions/UnprocessableEntityException.cs +++ b/Exceptions/UnprocessableEntityException.cs @@ -32,14 +32,5 @@ public UnprocessableEntityException(string message) : base(message) public UnprocessableEntityException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Default constructor with which to serialize. - /// - /// The serialization info to use. - /// The streaming context to use. - protected UnprocessableEntityException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/Mail2Gotify.csproj b/Mail2Gotify.csproj index eb8cb6f..dcd0454 100644 --- a/Mail2Gotify.csproj +++ b/Mail2Gotify.csproj @@ -1,35 +1,30 @@  Exe - net7.0 - Linux - . + net8.0 - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - - PreserveNewest - PreserveNewest diff --git a/Program.cs b/Program.cs index 6be38eb..756a856 100644 --- a/Program.cs +++ b/Program.cs @@ -1,4 +1,5 @@ using Mail2Gotify.Services; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Polly; @@ -18,6 +19,11 @@ static async Task Main(string[] args) { #pragma warning disable CA1416 // Validate platform compatibility await Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration(builder => { + builder.SetBasePath(Directory.GetCurrentDirectory()); + builder.AddJsonFile($"appsettings.json", optional: false); + builder.AddEnvironmentVariables(); + }) .UseContentRoot(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)) .ConfigureServices((hostContext, services) => { diff --git a/README.md b/README.md index d449503..b0d46b7 100644 --- a/README.md +++ b/README.md @@ -31,16 +31,19 @@ Services:Gotify:Header | X-Gotify-Key | Gotify's authentication header to use (t Services:Mail2Gotify:HostAddress | | The address on which this service resides. Services:Mail2Gotify:HostPort | 587 | The port that this service will use to receive mail through SMTP. Services:Mail2Gotify:CacheDirectory | | The directory to use for persistent caaching (bind with docker for permanency). -Services:Mail2Gotify:CertLocation | | The location for the X502 certificate used for TLS encryption. If left empty, Mail2Gotify will create a self-signed certificate with the variables below. -Services:Certificate:Name | Mail2Gotify | The name used for the creation of the X502 certificate used for the SMTP server's TLS encryption. -Services:Certificate:Password | | The password used for the creation of the X502 certificate. +Services:Mail2Gotify:CertLocation | | The location for the certificate used for TLS encryption. If left empty, Mail2Gotify will create a self-signed certificate using the "Services:Certificate:Name" and "Services:Certificate:Password" variables shown below. +Services:Mail2Gotify:KeyLocation | | The location of the PEM key file certificate used for TLS encryption. Only used when CertType is "PEM". +Services:Mail2Gotify:CertPassword | | The password of the PEM file if it is encrypted. Only used when CertType is "PEM". +Services:Mail2Gotify:CertType | PEM | The type pf certificate used for TLS encryption. Either "PEM" or "PKCS7". +Services:SelfSignedCertificate:Name | Mail2Gotify | The name used for the creation of the self-signed certificate used for the SMTP server's TLS encryption. +Services:SelfSignedCertificate:Password | | The password used for the creation of the self-signed certificate. -# Docker +## Docker Here's how I configure my installation : -## docker-compose.yaml +### docker-compose.yaml ```yaml version: "3" @@ -56,10 +59,10 @@ services: environment: - Services:Mail2Gotify:HostAddress=localhost - Services:Mail2Gotify:CacheDirectory=/app/cache - - Services:Mail2Gotify:CertLocation=/app/certs/privkey.pfx + - Services:Mail2Gotify:CertLocation=/app/certs/privkey.pfx - Services:Gotify:ServiceUri=https://gotify.exampledomain.ca - TZ=America/Toronto - image: mattmckenzy/mail2gotify:latest + image: mattmckenzy/mail2gotify:latest volumes: - cache:/app/cache - /etc/localtime:/etc/localtime:ro @@ -71,6 +74,15 @@ services: network_mode: "host" ``` -# Donate +## Release Notes + +### 1.0.0 + +- Initial release + +## Donate If you appreciate my work and feel like helping me realize other projects, you can donate at https://paypal.me/MattMckenzy! +### 1.1.0 + +- Added configuration options to support new certificate types. diff --git a/Services/FileSystemCaching.cs b/Services/FileSystemCaching.cs index 85c6041..55b9deb 100644 --- a/Services/FileSystemCaching.cs +++ b/Services/FileSystemCaching.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.Configuration; using Newtonsoft.Json; using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; @@ -16,7 +17,12 @@ public class FileSystemCaching public FileSystemCaching(IConfiguration configuration) { - CacheDirectory = new DirectoryInfo(configuration["Services:Mail2Gotify:CacheDirectory"]); + string cacheDirectory = configuration["Services:Mail2Gotify:CacheDirectory"]; + + if (string.IsNullOrWhiteSpace(cacheDirectory)) + throw new KeyNotFoundException("Could not find the cache directory configuration at \"Services:Mail2Gotify:CacheDirectory\""); + + CacheDirectory = new DirectoryInfo(cacheDirectory); } public CacheItem Get(string key) diff --git a/Services/Mail2GotifyService.cs b/Services/Mail2GotifyService.cs index c614570..5236273 100644 --- a/Services/Mail2GotifyService.cs +++ b/Services/Mail2GotifyService.cs @@ -4,6 +4,8 @@ using SmtpServer; using SmtpServer.ComponentModel; using System; +using System.Collections.Generic; +using System.IO; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading; @@ -11,31 +13,53 @@ namespace Mail2Gotify.Services { - public class Mail2GotifyService : IHostedService + public class Mail2GotifyService(CacheItemProcessingService cacheItemProcessingService, GotifyMessageStore gotifyMessageStore, GotifyUserAuthenticator gotifyUserAuthenticator, IConfiguration configuration, ILogger logger) : IHostedService { - private readonly CacheItemProcessingService _cacheItemProcessingService; - private readonly GotifyMessageStore _gotifyMessageStore; - private readonly GotifyUserAuthenticator _gotifyUserAuthenticator; - private readonly IConfiguration _configuration; - private readonly ILogger _logger; + private readonly CacheItemProcessingService _cacheItemProcessingService = cacheItemProcessingService; + private readonly GotifyMessageStore _gotifyMessageStore = gotifyMessageStore; + private readonly GotifyUserAuthenticator _gotifyUserAuthenticator = gotifyUserAuthenticator; + private readonly IConfiguration _configuration = configuration; + private readonly ILogger _logger = logger; private SmtpServer.SmtpServer _smtpServer; - public Mail2GotifyService(CacheItemProcessingService cacheItemProcessingService, GotifyMessageStore gotifyMessageStore, GotifyUserAuthenticator gotifyUserAuthenticator, IConfiguration configuration, ILogger logger) - { - _cacheItemProcessingService = cacheItemProcessingService; - _gotifyMessageStore = gotifyMessageStore; - _gotifyUserAuthenticator = gotifyUserAuthenticator; - _configuration = configuration; - _logger = logger; - } - public async Task StartAsync(CancellationToken cancellationToken) { await _cacheItemProcessingService.ProcessCacheItems(); - X509Certificate x509Certificate = string.IsNullOrWhiteSpace(_configuration["Services:Mail2Gotify:CertLocation"]) ? CreateX509Certificate2() : - new X509Certificate(_configuration["Services:Mail2Gotify:CertLocation"]); + string certLocation = _configuration["Services:Mail2Gotify:CertLocation"]; + + FileInfo certFileInfo = null; + if (!string.IsNullOrWhiteSpace(certLocation)) + certFileInfo = new(certLocation); + + if (certFileInfo != null && !certFileInfo.Exists) + throw new ArgumentException("The cert file configuration and set but the file doesn't exist!"); + + string keyLocation = _configuration["Services:Mail2Gotify:KeyLocation"]; + + FileInfo keyFileInfo = null; + if (!string.IsNullOrWhiteSpace(keyLocation)) + keyFileInfo = new(keyLocation); + + if (keyFileInfo != null && !keyFileInfo.Exists) + throw new ArgumentException("The key file configuration and set but the file doesn't exist!"); + + string certPassword = _configuration["Services:Mail2Gotify:CertPassword"]; + + string certType = _configuration["Services:Mail2Gotify:CertType"] ?? "PEM"; + if (certType != "PEM" && certType != "PKCS7") + throw new ArgumentException("The configured cert type must be either PEM or PKCS7!"); + + X509Certificate x509Certificate = null; + if (certFileInfo == null) + x509Certificate = CreateX509Certificate2(); + if (certType == "PEM" && !string.IsNullOrWhiteSpace(certPassword)) + x509Certificate = X509Certificate2.CreateFromEncryptedPemFile(certFileInfo.FullName, certPassword, keyFileInfo?.FullName); + else if (certType == "PEM") + x509Certificate = X509Certificate2.CreateFromPemFile(certFileInfo.FullName, keyFileInfo?.FullName); + else if (certType == "PKCS7") + x509Certificate = X509Certificate.CreateFromCertFile(certFileInfo.FullName); ISmtpServerOptions options = new SmtpServerOptionsBuilder() .ServerName(_configuration["Services:Mail2Gotify:HostAddress"]) @@ -44,7 +68,7 @@ public async Task StartAsync(CancellationToken cancellationToken) .Port(_configuration.GetValue("Services:Mail2Gotify:HostPort")) .IsSecure(true) .AuthenticationRequired() - .SupportedSslProtocols(System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls12) + .SupportedSslProtocols( System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls13) .Certificate(x509Certificate)) .Build(); @@ -69,13 +93,13 @@ public Task StopAsync(CancellationToken cancellationToken) return Task.CompletedTask; } - public X509Certificate CreateX509Certificate2() + public X509Certificate2 CreateX509Certificate2() { RSA rsa = RSA.Create(); - CertificateRequest certificateRequest = new($"cn={_configuration["Certificate:Name"]}", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + CertificateRequest certificateRequest = new($"cn={_configuration["SelfSignedCertificate:Name"]}", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); X509Certificate certificate = certificateRequest.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(1)); - return new X509Certificate2(certificate.Export(X509ContentType.Pfx, _configuration["Certificate:Password"]), _configuration["Certificate:Password"]); + return new X509Certificate2(certificate.Export(X509ContentType.Pfx, _configuration["SelfSignedCertificate:Password"]), _configuration["SelfSignedCertificate:Password"]); } } } diff --git a/appsettings.json b/appsettings.json index 4659c40..0cd7309 100644 --- a/appsettings.json +++ b/appsettings.json @@ -1,9 +1,9 @@ { "Logging": { "LogLevel": { - "Default": "Debug", + "Default": "Warning", "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Microsoft.Hosting.Lifetime": "Warning" } }, "Services": { @@ -11,7 +11,10 @@ "HostAddress": "", "HostPort": 587, "CacheDirectory": "", - "CertLocation": "" + "CertLocation": "", + "KeyLocation": "", + "CertPassword": "", + "CertType": "PEM" }, "Gotify": { "ServiceUri": "", diff --git a/publish.sh b/publish.sh new file mode 100644 index 0000000..c10ffec --- /dev/null +++ b/publish.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +CURRENT_VERS=$(sed '5!d' CHANGELOG.md | cut -d " " -f 2) +echo "The Current version is $CURRENT_VERS, enter new version." +read -p "Version: " -i $CURRENT_VERS -e CHOSEN_VERS +CHOSEN_VERS=${CHOSEN_VERS:-$CURRENT_VERS} +echo "Will publish as version: $CHOSEN_VERS" + +echo "Please enter a changelog and commit message. An empty message will only change version number and not add a changelog entry." +read -p "Message: " MESSAGE + +if [[ -z "$MESSAGE" ]]; then + echo "Empty message, will update in place." + sed -i 's|## '$CURRENT_VERS'|## '$CHOSEN_VERS'|' CHANGELOG.md + sed -i 's|### '$CURRENT_VERS'|### '$CHOSEN_VERS'|' README.md + COMMIT_MESSAGE="Bumped to version ${CHOSEN_VERS}" +else + echo "Message, will add changelog entry." + set -o noglob + set +o histexpand + sed -i ''$(grep -Fn '## '$CURRENT_VERS'' CHANGELOG.md | cut -d ":" -f 1)'i## '$CHOSEN_VERS'\n\n- '"$MESSAGE"'\n' CHANGELOG.md + replace '(## Release Notes(?:(?:.|\n)*?))(###(?:.|\n)*)' '$1' README.md + echo -e "### $CHOSEN_VERS\n\n- $MESSAGE" >> README.md + set +o noglob + set -o histexpand + COMMIT_MESSAGE="$MESSAGE; bumped to version ${CHOSEN_VERS}" +fi + +git add . +git commit -m "$COMMIT_MESSAGE" +git push +git tag v$CHOSEN_VERS HEAD +git push --tags + +set -o noglob +set +o histexpand +gh release create -t "Release v$CHOSEN_VERS" -n "$MESSAGE" v$CHOSEN_VERS +set +o noglob +set -o histexpand + +docker buildx build --build-arg CONFIG="Release" --platform=linux/amd64,linux/arm64 --push -t "mattmckenzy/mail2gotify:latest" -t "mattmckenzy/mail2gotify:$CHOSEN_VERS" -f "Dockerfile" . +docker run -v .:/workspace \ + -e DOCKERHUB_USERNAME='mattmckenzy' \ + -e DOCKERHUB_PASSWORD="$(cat ~/.tokens/dh)" \ + -e DOCKERHUB_REPOSITORY='mattmckenzy/mail2gotify' \ + -e README_FILEPATH='/workspace/README.md' \ + peterevans/dockerhub-description:latest \ No newline at end of file