diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index deb94a80..1f7550ad 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,17 +20,6 @@ jobs: timeout-minutes: 15 steps: - uses: actions/checkout@v3 - - name: Build Custom Actions (Tools.GitHubActions project) - run: | - cd src/Tools.GitHubActions/VariableSubstitution - npm ci --cache .npm --prefer-offline - npm run build - - name: Variable Substitution - uses: ./src/Tools.GitHubActions/VariableSubstitution - with: - files: '**/appsettings.json,**/appsettings.Azure.json,**/appsettings.AWS.json,**/appsettings.Deploy.json' - variables: ${{ toJSON(vars)}} - secrets: ${{ toJSON(secrets)}} - name: Setup .NET uses: actions/setup-dotnet@v3 with: @@ -45,12 +34,17 @@ jobs: run: dotnet build --configuration ${{env.DEPLOY_BUILD_CONFIGURATION}} "${{env.SOLUTION_PATH}}" /p:HostingPlatform=HOSTEDONAZURE - name: Build (Backend) for AWS Deploy run: dotnet build --configuration ${{env.DEPLOY_BUILD_CONFIGURATION}} "${{env.SOLUTION_PATH}}" /p:HostingPlatform=HOSTEDONAWS - - name: Build WebsiteHost (FrontEnd) + - name: Build WebsiteHost (FrontEnd) for Deploy run: | cd src/WebsiteHost/ClientApp cp .env.example .env npm ci --cache .npm --prefer-offline npm run build:releasefordeploy + - name: Build Custom GitHub Actions + run: | + cd src/Tools.GitHubActions/VariableSubstitution + npm ci --cache .npm --prefer-offline + npm run build test: runs-on: windows-latest timeout-minutes: 15 @@ -90,7 +84,7 @@ jobs: npm ci --cache .npm --prefer-offline npm run build:releasefordeploy npm run test:ci - - name: Test VariableSubstitution (GitHubAction) + - name: Test Custom GitHub Actions continue-on-error: false run: | cd src/Tools.GitHubActions/VariableSubstitution diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..b3f20c68 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,52 @@ +name: Build and Deploy + +on: [ push ] + +permissions: + contents: read + actions: read + checks: write + +env: + IS_CI_BUILD: 'true' + SOLUTION_PATH: 'src/SaaStack.sln' + TESTINGONLY_BUILD_CONFIGURATION: 'Release' + DEPLOY_BUILD_CONFIGURATION: 'ReleaseForDeploy' + DOTNET_VERSION: 8.0.302 + +jobs: + deploy: + runs-on: windows-latest + timeout-minutes: 15 + environment: 'Demo' + steps: + - uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{env.DOTNET_VERSION}} + - name: Restore dependencies + run: dotnet restore "${{env.SOLUTION_PATH}}" + - name: Build Custom GitHub Actions + run: | + cd src/Tools.GitHubActions/VariableSubstitution + npm ci --cache .npm --prefer-offline + npm run build + - name: Build (Backend) for Azure Deploy + run: dotnet build --configuration ${{env.DEPLOY_BUILD_CONFIGURATION}} "${{env.SOLUTION_PATH}}" /p:HostingPlatform=HOSTEDONAZURE + - name: Build (Backend) for AWS Deploy + run: dotnet build --configuration ${{env.DEPLOY_BUILD_CONFIGURATION}} "${{env.SOLUTION_PATH}}" /p:HostingPlatform=HOSTEDONAWS + - name: Build WebsiteHost (FrontEnd) for Deploy + run: | + cd src/WebsiteHost/ClientApp + cp .env.example .env + npm ci --cache .npm --prefer-offline + npm run build:releasefordeploy + - name: AppSettings Variable Substitution + uses: ./src/Tools.GitHubActions/VariableSubstitution + with: + files: '**/appsettings.json,**/appsettings.Azure.json,**/appsettings.AWS.json,**/appsettings.Deploy.json' + variables: ${{ toJSON(vars)}} + secrets: ${{ toJSON(secrets)}} + - name: Deploy to Azure + run: echo "Deploy to Azure" #TODO: Deploy steps to Azure \ No newline at end of file diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md new file mode 100644 index 00000000..dd05d45a --- /dev/null +++ b/docs/DEPLOYMENT.md @@ -0,0 +1,71 @@ +# Deployment of SaaStack + +This document details the basic steps required to deploy your software into a production environment. + +A production environment might be in the cloud or on premise. The deployment process is similar, except for the tools used to perform the deployment. + +By default, this deployment is assumed to take place from a GitHub repository using GitHub Actions. However, you can use any CI/CD tool you prefer. + +## Automated deployment + +The deployment process is automated using GitHub Actions. The deployment process is defined in the `.github/workflows/deploy.yml` file. + +## Variables + +Most of the required variables should be self-explanatory. + +Here are ones that might need a little more explanation: + +### Operator Whitelist + +Setting name: `HOSTS_ENDUSERSAPI_AUTHORIZATION_OPERATORWHITELIST` + +This is a semicolon `;` delimited list of email addresses of user accounts that are authorized to act as operators in the system. +This list is populated before the user accounts are registered and when they are registered later, they are promoted to `operator` status. + +**IMPORTANT**: You should always have at least one operator account email address defined in this list before your software goes to production for the first time, otherwise you will have no other way to promote other operators in the system. + +> For user accounts that already exist, they can be promoted by existing operator accounts. + + + +## Secrets + +You MUST generate new secrets for your deployed services. + +**IMPORTANT**: You MUST never re-use the secrets in this repository in a production environment. hey are well known to anyone who has access to this repository. + +### HMAC Signing Key + +For secrets such as `HOSTS_APIHOST1_HMACAUTHNSECRET` and `HOSTS_ANCILLARYAPI_HMACAUTHNSECRET`: +* Generate a random value using the [HMACSigner.GenerateKey()](https://github.com/jezzsantos/saastack/blob/main/src/Infrastructure.Web.Api.Common/HMACSigner.cs) method. + +> Note: You can run the unit tests for this class and copy the value of the generated key in the first test. + +**IMPORTANT**: It is vital that all the HMAC signing keys on each deployed host are identical. You can have different values on different hosts, as long as all their client hosts are also updated. + +### CSRF secrets + +For the CSRF `HOSTS_WEBSITEHOST_CSRFHMACSECRET` secret +* Generate a new random value using the [CSRFToken.GenerateKey()](https://github.com/jezzsantos/saastack/blob/main/src/Infrastructure.Web.Api.Common/HMACSigner.cs) method as above for HMAC secrets. + +> Note: You will want to use a different value than the HMAC signing keys. + +For the CSRF `HOSTS_WEBSITEHOST_CSRFAESSECRET` secret: +* Generate a new random value using the [AesEncryptionService.GenerateAesSecret()](https://github.com/jezzsantos/saastack/blob/main/src/Infrastructure.Common/DomainServices/AesEncryptionService.cs) method. + +> Note: You can run the unit tests for this class and copy the value of the generated key in the first test. + +### SSO + +For the `APPLICATIONSERVICES_SSOPROVIDERSSERVICE_SSOUSERTOKENS_AESSECRET` secret: +* Generate a new random value using the [AesEncryptionService.GenerateAesSecret()](https://github.com/jezzsantos/saastack/blob/main/src/Infrastructure.Common/DomainServices/AesEncryptionService.cs) method. + +> Note: You can run the unit tests for this class and copy the value of the generated key in the first test. + +### JWT Signing Key + +For the `HOSTS_IDENTITYAPI_JWT_SIGNINGSECRET` secret: +* Generate a new random value using the [JwtTokenService.GenerateSigningKey()](https://github.com/jezzsantos/saastack/blob/main/src/IdentityInfrastructure/ApplicationServices/JWTTokensService.cs) method. + +> Note: You can run the unit tests for this class and copy the value of the generated key in the first test. diff --git a/docs/README.md b/docs/README.md index 83e52b4d..85a0b1c9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -30,7 +30,7 @@ As well as a code template, there is custom tooling (tailored to this codebase) We make extensive use Roslyn Analyzers, Code Fixes and Source Generators and Architecture tests to help you and your team be highly productive in following the established patterns this codebase. And more importantly detect and fix when those principles are violated. -For more details see the [Developer Tooling](design-principles/0140-developer-tooling.md) documentation. +For more details, see the [Developer Tooling](design-principles/0140-developer-tooling.md) documentation. For example, we make it trivial to define robust REST APIs, and under the covers, the tooling converts those API definitions into MediatR-brokered minimal APIs for you. But you never have to write all that minimal API boilerplate stuff. @@ -42,4 +42,11 @@ Lastly, if you are using JetBrains Rider, we have baked in a set of common codin We also provide you with a number of project templates for adding the various projects for new subdomains. We also give you several macros in the text editor (a.k.a. Live Templates) for creating certain kinds of classes, like DDD ValueObjects and DDD AggregateRoots, and xUnit test classes. -You can see all of these things in the Platform/Tools projects. \ No newline at end of file +You can see all of these things in the Platform/Tools projects. + +## Deployment + +The codebase is ready for deployment immediately, from your GitHub repository. +Deployment can be performed by any tool set to any environment, any way you like, we just made it easy for GitHub actions. + +For more details, see the [Deployment](DEPLOYMENT.md) documentation. \ No newline at end of file diff --git a/src/IdentityInfrastructure.UnitTests/ApplicationServices/JWTTokensServiceSpec.cs b/src/IdentityInfrastructure.UnitTests/ApplicationServices/JWTTokensServiceSpec.cs index c0f9cc0f..a72756f6 100644 --- a/src/IdentityInfrastructure.UnitTests/ApplicationServices/JWTTokensServiceSpec.cs +++ b/src/IdentityInfrastructure.UnitTests/ApplicationServices/JWTTokensServiceSpec.cs @@ -24,7 +24,7 @@ public JWTTokensServiceSpec() var settings = new Mock(); settings.Setup(s => s.Platform.GetString(JWTTokensService.BaseUrlSettingName, null)) .Returns("https://localhost"); - settings.Setup(s => s.Platform.GetString(JWTTokensService.SecretSettingName, null)) + settings.Setup(s => s.Platform.GetString(JWTTokensService.SigningSecretSettingName, null)) .Returns("asecretsigningkeyasecretsigningkeyasecretsigningkeyasecretsigningkey"); settings.Setup(s => s.Platform.GetNumber(It.IsAny(), It.IsAny())) .Returns((string _, double defaultValue) => defaultValue); @@ -35,6 +35,16 @@ public JWTTokensServiceSpec() _service = new JWTTokensService(settings.Object, _tokensService.Object); } + [Fact] + public void WhenGenerateSigningKey_ThenReturnsKey() + { +#if TESTINGONLY + var result = JWTTokensService.GenerateSigningKey(); + + result.Should().NotBeNullOrEmpty(); +#endif + } + [Fact] public async Task WhenIssueTokensAsync_ThenReturnsTokens() { diff --git a/src/IdentityInfrastructure/ApplicationServices/JWTTokensService.cs b/src/IdentityInfrastructure/ApplicationServices/JWTTokensService.cs index fb198b10..c2f5f68a 100644 --- a/src/IdentityInfrastructure/ApplicationServices/JWTTokensService.cs +++ b/src/IdentityInfrastructure/ApplicationServices/JWTTokensService.cs @@ -1,4 +1,5 @@ using System.IdentityModel.Tokens.Jwt; +using System.Security.Cryptography; using System.Text; using Application.Resources.Shared; using Common; @@ -14,8 +15,8 @@ namespace IdentityInfrastructure.ApplicationServices; public class JWTTokensService : IJWTTokensService { public const string BaseUrlSettingName = "Hosts:IdentityApi:BaseUrl"; - public const string DefaultExpirySettingName = "Hosts:IdentityApi:JWT:DefaultExpiryInMinutes"; - public const string SecretSettingName = "Hosts:IdentityApi:JWT:SigningSecret"; + public const string SigningSecretSettingName = "Hosts:IdentityApi:JWT:SigningSecret"; + private const string DefaultExpirySettingName = "Hosts:IdentityApi:JWT:DefaultExpiryInMinutes"; private readonly TimeSpan _accessTokenExpiresAfter; private readonly string _baseUrl; private readonly string _signingSecret; @@ -24,7 +25,7 @@ public class JWTTokensService : IJWTTokensService public JWTTokensService(IConfigurationSettings settings, ITokensService tokensService) { _tokensService = tokensService; - _signingSecret = settings.Platform.GetString(SecretSettingName); + _signingSecret = settings.Platform.GetString(SigningSecretSettingName); _baseUrl = settings.Platform.GetString(BaseUrlSettingName); _accessTokenExpiresAfter = TimeSpan.FromMinutes(settings.Platform.GetNumber(DefaultExpirySettingName, @@ -38,6 +39,13 @@ public Task> IssueTokensAsync(EndUserWithMemberships return Task.FromResult(tokens); } +#if TESTINGONLY + public static string GenerateSigningKey() + { + return RandomNumberGenerator.GetHexString(64); + } +#endif + private Result IssueTokens(EndUserWithMemberships user) { var accessTokenExpiresOn = DateTime.UtcNow.Add(_accessTokenExpiresAfter); diff --git a/src/Infrastructure.Common.UnitTests/DomainServices/AesEncryptionServiceSpec.cs b/src/Infrastructure.Common.UnitTests/DomainServices/AesEncryptionServiceSpec.cs index 68b9a162..5f185b4c 100644 --- a/src/Infrastructure.Common.UnitTests/DomainServices/AesEncryptionServiceSpec.cs +++ b/src/Infrastructure.Common.UnitTests/DomainServices/AesEncryptionServiceSpec.cs @@ -13,7 +13,7 @@ public class AesEncryptionServiceSpec public AesEncryptionServiceSpec() { #if TESTINGONLY - _secret = AesEncryptionService.CreateAesSecret(); + _secret = AesEncryptionService.GenerateAesSecret(); #else _secret = string.Empty; #endif diff --git a/src/Infrastructure.Common/DomainServices/AesEncryptionService.cs b/src/Infrastructure.Common/DomainServices/AesEncryptionService.cs index 05fa4e39..763b4aad 100644 --- a/src/Infrastructure.Common/DomainServices/AesEncryptionService.cs +++ b/src/Infrastructure.Common/DomainServices/AesEncryptionService.cs @@ -73,13 +73,13 @@ private static SymmetricAlgorithm CreateAes() } #if TESTINGONLY - public static string CreateAesSecret() + public static string GenerateAesSecret() { - CreateKeyAndIv(out var key, out var iv); + GenerateKeyAndIv(out var key, out var iv); return $"{Convert.ToBase64String(key)}{SecretKeyDelimiter}{Convert.ToBase64String(iv)}"; } - private static void CreateKeyAndIv(out byte[] key, out byte[] iv) + private static void GenerateKeyAndIv(out byte[] key, out byte[] iv) { using var aes = CreateAes(); key = aes.Key; diff --git a/src/Infrastructure.Web.Api.Common.UnitTests/HMACSignerSpec.cs b/src/Infrastructure.Web.Api.Common.UnitTests/HMACSignerSpec.cs index 867a4b36..b5cd133e 100644 --- a/src/Infrastructure.Web.Api.Common.UnitTests/HMACSignerSpec.cs +++ b/src/Infrastructure.Web.Api.Common.UnitTests/HMACSignerSpec.cs @@ -11,6 +11,16 @@ public class HMACSignerSpec [Trait("Category", "Unit")] public class GivenARequest { + [Fact] + public void WhenGenerateKey_ThenReturnsRandomKey() + { +#if TESTINGONLY + var result = HMACSigner.GenerateKey(); + + result.Should().NotBeNullOrEmpty(); +#endif + } + [Fact] public void WhenConstructedWithEmptySecret_ThenThrows() { diff --git a/src/Infrastructure.Web.Api.Common/HMACSigner.cs b/src/Infrastructure.Web.Api.Common/HMACSigner.cs index cfb2e708..025b8bc5 100644 --- a/src/Infrastructure.Web.Api.Common/HMACSigner.cs +++ b/src/Infrastructure.Web.Api.Common/HMACSigner.cs @@ -35,6 +35,14 @@ public HMACSigner(byte[] data, string secret) _secret = secret; } +#if TESTINGONLY + public static string GenerateKey() + { + var algorithm = new HMACSHA256(); + return Convert.ToBase64String(algorithm.Key); + } +#endif + public string Sign() { var key = SignatureEncoding.GetBytes(_secret); diff --git a/src/Infrastructure.Web.Hosting.Common.UnitTests/Pipeline/CSRFServiceSpec.cs b/src/Infrastructure.Web.Hosting.Common.UnitTests/Pipeline/CSRFServiceSpec.cs index e9cb20f4..15a4b761 100644 --- a/src/Infrastructure.Web.Hosting.Common.UnitTests/Pipeline/CSRFServiceSpec.cs +++ b/src/Infrastructure.Web.Hosting.Common.UnitTests/Pipeline/CSRFServiceSpec.cs @@ -21,7 +21,7 @@ public CSRFServiceSpec() settings.Setup(s => s.GetWebsiteHostCSRFSigningSecret()) .Returns("asecret"); #if TESTINGONLY - _encryptionService = new AesEncryptionService(AesEncryptionService.CreateAesSecret()); + _encryptionService = new AesEncryptionService(AesEncryptionService.GenerateAesSecret()); #endif _service = new CSRFService(settings.Object, _encryptionService); diff --git a/src/Infrastructure.Web.Hosting.Common.UnitTests/Pipeline/CSRFTokenPairSpec.cs b/src/Infrastructure.Web.Hosting.Common.UnitTests/Pipeline/CSRFTokenPairSpec.cs index d53a56d4..f029f5e1 100644 --- a/src/Infrastructure.Web.Hosting.Common.UnitTests/Pipeline/CSRFTokenPairSpec.cs +++ b/src/Infrastructure.Web.Hosting.Common.UnitTests/Pipeline/CSRFTokenPairSpec.cs @@ -17,7 +17,7 @@ public CSRFTokenPairSpec() { #if TESTINGONLY _encryptionService = - new AesEncryptionService(AesEncryptionService.CreateAesSecret()); + new AesEncryptionService(AesEncryptionService.GenerateAesSecret()); #endif } diff --git a/src/SaaStack.sln b/src/SaaStack.sln index 3653ef31..001f3dcb 100644 --- a/src/SaaStack.sln +++ b/src/SaaStack.sln @@ -10,6 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\README_DERIVATIVE.md = ..\README_DERIVATIVE.md ..\README.md = ..\README.md ..\CONTRIBUTING.md = ..\CONTRIBUTING.md + ..\docs\DEPLOYMENT.md = ..\docs\DEPLOYMENT.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{508E7DA4-4DF2-4201-955D-CCF70C41AD05}" diff --git a/src/Tools.GitHubActions/VariableSubstitution/README.md b/src/Tools.GitHubActions/VariableSubstitution/README.md index 2adee731..34061fb2 100644 --- a/src/Tools.GitHubActions/VariableSubstitution/README.md +++ b/src/Tools.GitHubActions/VariableSubstitution/README.md @@ -1,25 +1,28 @@ # AppSettings Variable Substitution -This action performs variable substitution of .NET projects (that use `appsettings.json` files) where you also store their variables and secrets in a GitHub repository. +This action performs variable substitution of .NET projects (that use `appsettings.json` files) where you also store their variables and secrets in a GitHub repository. It also provides a mechanism to verify that all the required settings (defined in `appsettings.json` files) are defined in the GitHub repository before deployment. -> Unlike the well-known GitHub action [`microsoft/variable-substitution`](https://github.com/microsoft/variable-substitution), this action verifies the settings before substitution, and does not require the developer to change the `build.yml` file when they add or remove settings in their code. -> This action will fail the build when there is a mismatch between what the code says, and what the GitHub repository says. -> This dramatically decreases the chances of deploying mis-configured software, as the code changes. +> Unlike the well-known GitHub action [`microsoft/variable-substitution`](https://github.com/microsoft/variable-substitution), this action verifies the settings before substitution, and does not require the developer to modify the `build.yml` file when they add or remove settings in their code. +> This action will fail the build when there is a mismatch between what the `appsettings.json` files say, and what the GitHub repository has defined in its secrets and environment variables. +> This capability dramatically decreases the chances of deploying mis-configured software, as the code changes between deployments. In this way, this action is a safeguard for deployment. ## How It Works -This action scans for a set of `appsettings.json` files in the current repository (using a glob filter), and for each found file: -1. Verifies that all variables that are referenced in the `Required` sections, have corresponding variables in the GitHub Secrets or in GitHub variables of the repository. This is to ensure that new settings that are designated as "required", have been correctly set up in the GitHub repository first, before deployment. -2. Substitutes the values of the variables and secrets stored in the GitHub project for the specified environment, into all the `appsettings.json` files. +This action scans for a set of `appsettings.json` files across the current repository (using a glob pattern filter). + +For each settings file found, it will: +1. Verify that all settings that are referenced in the `Deploy -> Required -> Keys` sections, have corresponding variables defined in the GitHub secrets or in GitHub environment variables of the GitHub repository. This ensures that new settings that are designated as "required", have been correctly set up in the GitHub repository before deployment of the software. It is up to the developer to define what is "required" and what is not "required". +2. Substitutes the values of all settings of all `appsettings.json` files, with the values of all secrets and environment variables stored in the GitHub project (for the specified environment of the build). These files are then rewritten back to the build artifacts, for deployment. ## Set up -This action relies on the source code declaring what variables must be present in the GitHub repository, before deployment. -This action also relies on the variables/secrets defined in the GitHub project, using a naming convention that can automatically match the variables in the `appsettings.json` files. +This action relies on the source code (i.e., `appsettings.json` files) declaring what variables/secrets must be present in the GitHub repository, before deployment. This is achieved with the definition of a `Deploy -> Required -> Keys` section of the `appsettings.json` file for each deployable host. + +This action also relies on the variables/secrets being defined in the GitHub project for a specific environment, using a naming convention that can automatically match the variables in the `appsettings.json` files. ### Source Code -This action depends on the definition of a set of "Required" settings defined in one of your `appsettings.json` files. +This action depends on the definition of a set of `Deploy -> Required -> Keys` settings defined in one of your `appsettings.json` files. For example, @@ -59,17 +62,17 @@ For example, } ``` -> Note: These definitions are assumed exist within your main `appsettings.json` file. Or they can also exist in separate files, just used for deployment use. e.g., `appsettings.Deploy.json`. -> Note: Keys where `Disabled` is set to `true` will be ignored. -> Note: without any of these "Keys" definitions, this action will just perform variable substitution, without any verification. +> Note: These definitions are assumed exist within your main `appsettings.json` file. Or they can also exist in separate files that are just used for deployment use. e.g., `appsettings.Deploy.json`. +> Note: Keys where `Disabled` is set to `true` will be ignored. By default, a section without the `Disabled` value will be enabled. +> Note: Without any of these "Keys" definitions, this action will just perform variable substitution, without any verification. ### GitHub Project -You define your variables and secrets in the GitHub project, either in the `Settings` -> `Secrets and variables` section, or in the `Settings` -> `Environments` -> `Secrets` section. +You define your variables and secrets in the GitHub project, either in the `Settings` -> `Secrets and variables` section, or in the `Settings` -> `Environments` -> `Secrets` section of a specific environment. -By default, GitHub suggests you define the name of your variable or secret in uppercase, using underscores to separate words. +By default, GitHub suggests you define the name of your variable or secret in uppercase, using underscores to separate words. However, settings in `appsettings.json` have a very different naming convention. A mapping between the two is assumed by this action. -To make the variable substitution work correctly, you must define all the variables and secrets in the GitHub project using the same names as the keys in the `appsettings.json` files, and replacing any `:`, `-` characters with an underscore `_` character. Where all the characters of the variable/secret name are in uppercase or lowercase (no mixed-casing). +To make the variable substitution work correctly, you must define all the variables and secrets in the GitHub project using the same names as the keys in the `appsettings.json` files, and replacing any `:`, `-` and `.` characters with an underscore `_` character. Where all the characters of the variable/secret name are in uppercase (no mixed-casing). For example, in the `appsettings.json` file we might have the following settings: ```json @@ -95,8 +98,6 @@ In the GitHub project, you would define the following variables or secrets: - `APPLICATIONSERVICES_PERSISTENCE_KURRENT_2_CONNECTIONSTRING` - `APPLICATIONSERVICES_PERSISTENCE_KURRENT_2_1_CONNECTIONSTRING` -> or the lowercase versions of these same names. - ## Inputs ### `files` diff --git a/src/Tools.GitHubActions/VariableSubstitution/action.yml b/src/Tools.GitHubActions/VariableSubstitution/action.yml index 27c0b71c..f0f639bb 100644 --- a/src/Tools.GitHubActions/VariableSubstitution/action.yml +++ b/src/Tools.GitHubActions/VariableSubstitution/action.yml @@ -5,10 +5,12 @@ inputs: description: 'A comma-delimited list of glob patterns that identify all the `appsettings.json` files to be processed, relative to the root of the repository' required: true default: '**/appsettings.json' - environment: - description: 'The name of the environment (in the GitHub repository) from which the variables and secrets should be substituted' - required: false - default: '' + secrets: + description: 'The secrets of the GitHub repository. Must be the value: `${{ toJSON(secrets)}}`' + required: true + variables: + description: 'The variables of the GitHub repository (and environment). Must be the value: `${{ toJSON(vars)}}`' + required: true runs: using: 'node20' main: './built/index.js'