Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: build
on:
push:
branches:
- main
tags:
- '*'
pull_request:
branches:
- main
workflow_dispatch:
inputs:
forcePublish:
description: When true the Publish stage will always be run, otherwise it only runs for tagged versions.
required: false
default: false
type: boolean
forcePublicNugetDestination:
description: When true the NuGet Publish destination will always be set to nuget.org. (Normally, force publishing publishes to the private feed on GitHub.)
required: false
default: false
type: boolean
skipCleanup:
description: When true the pipeline clean-up stage will not be run. For example, the cache used between pipeline stages will be retained.
required: false
default: false
type: boolean

concurrency:
group: ${{ github.workflow }}-${{ github.sha }}
cancel-in-progress: true

permissions:
actions: write # enable cache clean-up
checks: write # enable test result annotations
contents: write # enable creating releases
issues: read
packages: write # enable publishing packages
pull-requests: write # enable test result annotations

jobs:
prepareConfig:
name: Prepare Configuration
runs-on: ubuntu-latest
outputs:
RESOLVED_ENV_VARS: ${{ steps.prepareEnvVarsAndSecrets.outputs.environmentVariablesYamlBase64 }}
RESOLVED_SECRETS: ${{ steps.prepareEnvVarsAndSecrets.outputs.secretsYamlBase64 }}
steps:
# Declare any environment variables and/or secrets that need to be available inside the build process
- uses: endjin/Endjin.RecommendedPractices.GitHubActions/actions/prepare-env-vars-and-secrets@main
id: prepareEnvVarsAndSecrets
with:
environmentVariablesYaml: |
ZF_NUGET_PUBLISH_SOURCE: ${{ (startsWith(github.ref, 'refs/tags/') || github.event.inputs.forcePublicNugetDestination == 'true') && 'https://api.nuget.org/v3/index.json' || format('https://nuget.pkg.github.com/{0}/index.json', github.repository_owner) }}
secretsYaml: |
NUGET_API_KEY: "${{ startsWith(github.ref, 'refs/tags/') && secrets.NUGET_APIKEY || secrets.BUILD_PUBLISHER_PAT }}"
RootBlobStorageConfiguration__ConnectionStringPlainText: "${{ secrets.TESTS_AZURESTORAGECONNECTIONSTRING }}"
TenantCacheConfiguration__GetTenantResponseCacheControlHeaderValue: max-age=300
TenantCloudBlobContainerFactoryOptions__AzureServicesAuthConnectionString: RunAs=Developer;DeveloperTool=AzureCli
secretsEncryptionKey: ${{ secrets.SHARED_WORKFLOW_KEY }}

build:
needs: prepareConfig
uses: endjin/Endjin.RecommendedPractices.GitHubActions/.github/workflows/scripted-build-matrix-pipeline.yml@main
with:
netSdkVersion: '8.x'
# additionalNetSdkVersion: '9.x'
# workflow_dispatch inputs are always strings, the type property is just for the UI
forcePublish: ${{ github.event.inputs.forcePublish == 'true' }}
skipCleanup: ${{ github.event.inputs.skipCleanup == 'true' }}
testPhaseMatrixJson: |
{
"os": ["ubuntu-latest"],
"dotnetFramework": ["net8.0"]
}
# testArtifactName: ''
# testArtifactPath: ''
compilePhaseEnv: ${{ needs.prepareConfig.outputs.RESOLVED_ENV_VARS }}
testPhaseEnv: ${{ needs.prepareConfig.outputs.RESOLVED_ENV_VARS }}
packagePhaseEnv: ${{ needs.prepareConfig.outputs.RESOLVED_ENV_VARS }}
publishPhaseEnv: ${{ needs.prepareConfig.outputs.RESOLVED_ENV_VARS }}
secrets:
compilePhaseAzureCredentials: ${{ secrets.AZURE_READER_CREDENTIALS }}
testPhaseAzureCredentials: ${{ secrets.TESTS_KV_READER_CREDENTIALS }}
# packagePhaseAzureCredentials: ${{ secrets.AZURE_PUBLISH_CREDENTIALS }}
# publishPhaseAzureCredentials: ${{ secrets.AZURE_PUBLISH_CREDENTIALS }}
# compilePhaseSecrets: ${{ needs.prepareConfig.outputs.RESOLVED_SECRETS }}
testPhaseSecrets: ${{ needs.prepareConfig.outputs.RESOLVED_SECRETS }}
# packagePhaseSecrets: ${{ needs.prepareConfig.outputs.RESOLVED_SECRETS }}
publishPhaseSecrets: ${{ needs.prepareConfig.outputs.RESOLVED_SECRETS }}
secretsEncryptionKey: ${{ secrets.SHARED_WORKFLOW_KEY }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,5 @@ appsettings.json
# Local build outputs
_packages/
*.sbom.*
_codeCoverage/
.zf/extensions/
89 changes: 89 additions & 0 deletions .zf/config.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<#
This example demonstrates a software build process using the 'ZeroFailed.Build.DotNet' extension
to provide the features needed when building a .NET solutions.
#>

$zerofailedExtensions = @(
@{
# References the extension from its GitHub repository. If not already installed, use latest version from 'main' will be downloaded.
Name = "ZeroFailed.Build.DotNet"
GitRepository = "https://github.com/zerofailed/ZeroFailed.Build.DotNet"
GitRef = "main"
}
)

# Load the tasks and process
. ZeroFailed.tasks -ZfPath $here/.zf


#
# Build process control options
#
$SkipInit = $false
$SkipVersion = $false
$SkipBuild = $false
$CleanBuild = $Clean
$SkipTest = $false
$SkipTestReport = $false
$SkipAnalysis = $false
$SkipPackage = $false

#
# Build process configuration
#
$SolutionToBuild = (Resolve-Path (Join-Path $here ".\Solutions\Marain.Tenancy.sln")).Path
$ProjectsToPublish = @(
"Solutions/Marain.Tenancy.Host.AspNetCore/Marain.Tenancy.Host.AspNetCore.csproj"
"Solutions/Marain.Tenancy.Host.Functions/Marain.Tenancy.Host.Functions.csproj"
)
$NuSpecFilesToPackage = @()
$NugetPublishSource = property ZF_NUGET_PUBLISH_SOURCE "$here/_local-nuget-feed"
$IncludeAssembliesInCodeCoverage = "arain.Tenancy*"
$ExcludeAssembliesInCodeCoverage = ""


# Customise the build process
task installAzureFunctionsSDK {

$existingVersion = ""
if ((Get-Command func -ErrorAction Ignore)) {
$existingVersion = exec { & func --version }
}

if (!$existingVersion -or $existingVersion -notlike "4.*") {
Write-Build White "Installing/updating Azure Functions Core Tools..."
if ($IsWindows) {
exec { & npm install -g azure-functions-core-tools@ --unsafe-perm true }
}
else {
Write-Build Yellow "NOTE: May require 'sudo' on Linux/MacOS"
exec { & sudo npm install -g azure-functions-core-tools@ --unsafe-perm true }
}
}
}

task . FullBuild


#
# Build Process Extensibility Points - uncomment and implement as required
#

# task RunFirst {}
# task PreInit {}
# task PostInit {}
# task PreVersion {}
# task PostVersion {}
# task PreBuild {}
# task PostBuild {}
task PreTest Init,installAzureFunctionsSDK
# task PostTest {}
# task PreTestReport {}
# task PostTestReport {}
# task PreAnalysis {}
# task PostAnalysis {}
# task PrePackage {}
# task PostPackage {}
# task PrePublish {}
# task PostPublish {}
# task RunLast {}
12 changes: 6 additions & 6 deletions Solutions/Marain.Tenancy.Cli/Marain.Tenancy.Cli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>tenancy</AssemblyName>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
Expand Down Expand Up @@ -35,14 +35,14 @@

<ItemGroup>
<PackageReference Include="Corvus.Identity.MicrosoftRest" Version="3.3.0" />
<PackageReference Include="Endjin.RecommendedPractices.GitHub" Version="2.1.12">
<PackageReference Include="Endjin.RecommendedPractices.GitHub" Version="2.1.18">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.1.0" />
<PackageReference Include="McMaster.Extensions.Hosting.CommandLine" Version="4.1.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="[6.0.*,)" />
<PackageReference Include="ConsoleTables" Version="2.5.0" />
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.1.1" />
<PackageReference Include="McMaster.Extensions.Hosting.CommandLine" Version="4.1.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="[8.0.*,)" />
<PackageReference Include="ConsoleTables" Version="2.6.2" />
</ItemGroup>

<ItemGroup>
Expand Down
141 changes: 70 additions & 71 deletions Solutions/Marain.Tenancy.Cli/Marain/Tenancy/Cli/Commands/Create.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,90 +2,89 @@
// Copyright (c) Endjin Limited. All rights reserved.
// </copyright>

namespace Marain.Tenancy.Cli.Commands
namespace Marain.Tenancy.Cli.Commands;

using System;
using System.Threading.Tasks;
using Corvus.Tenancy;
using McMaster.Extensions.CommandLineUtils;

/// <summary>
/// Creates a new tenant.
/// </summary>
[Command(Name = "create", Description = "Create a new tenant.")]
public class Create
{
using System;
using System.Threading.Tasks;
using Corvus.Tenancy;
using McMaster.Extensions.CommandLineUtils;
private readonly ITenantStore tenantStore;

/// <summary>
/// Creates a new tenant.
/// Initializes a new instance of the <see cref="Create"/> class.
/// </summary>
[Command(Name = "create", Description = "Create a new tenant.")]
public class Create
/// <param name="tenantStore">The tenant store that will be used to create the new tenant.</param>
public Create(ITenantStore tenantStore)
{
private readonly ITenantStore tenantStore;

/// <summary>
/// Initializes a new instance of the <see cref="Create"/> class.
/// </summary>
/// <param name="tenantStore">The tenant store that will be used to create the new tenant.</param>
public Create(ITenantStore tenantStore)
{
this.tenantStore = tenantStore;
}
this.tenantStore = tenantStore;
}

/// <summary>
/// Gets or sets the Id of the tenant that should be the parent of the new tenant.
/// </summary>
/// <remarks>
/// If ommitted, the tenant will be created at the top level, as a child of the root.
/// </remarks>
[Option(
CommandOptionType.SingleOrNoValue,
ShortName = "t",
LongName = "tenant",
Description = "The Id of the parent tenant. Omit if the child should be a parent of the root tenant.")]
public string? TenantId { get; set; }
/// <summary>
/// Gets or sets the Id of the tenant that should be the parent of the new tenant.
/// </summary>
/// <remarks>
/// If ommitted, the tenant will be created at the top level, as a child of the root.
/// </remarks>
[Option(
CommandOptionType.SingleOrNoValue,
ShortName = "t",
LongName = "tenant",
Description = "The Id of the parent tenant. Omit if the child should be a parent of the root tenant.")]
public string? TenantId { get; set; }

/// <summary>
/// Gets or sets the name of the new tenant.
/// </summary>
[Option(
CommandOptionType.SingleValue,
ShortName = "n",
LongName = "name",
Description = "The name of the new tenant.")]
public string? Name { get; set; }
/// <summary>
/// Gets or sets the name of the new tenant.
/// </summary>
[Option(
CommandOptionType.SingleValue,
ShortName = "n",
LongName = "name",
Description = "The name of the new tenant.")]
public string? Name { get; set; }

/// <summary>
/// Gets or sets the well-known GUID of the new tenant.
/// </summary>
[Option(
CommandOptionType.SingleValue,
ShortName = "g",
LongName = "guid",
Description = "The well-known GUID of the new tenant.")]
public string? WellKnownTenantGuid { get; set; }
/// <summary>
/// Gets or sets the well-known GUID of the new tenant.
/// </summary>
[Option(
CommandOptionType.SingleValue,
ShortName = "g",
LongName = "guid",
Description = "The well-known GUID of the new tenant.")]
public string? WellKnownTenantGuid { get; set; }

/// <summary>
/// Executes the command.
/// </summary>
/// <param name="app">The current <c>CommandLineApplication</c>.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task OnExecute(CommandLineApplication app)
/// <summary>
/// Executes the command.
/// </summary>
/// <param name="app">The current <c>CommandLineApplication</c>.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task OnExecute(CommandLineApplication app)
{
if (string.IsNullOrEmpty(this.TenantId))
{
if (string.IsNullOrEmpty(this.TenantId))
{
this.TenantId = this.tenantStore.Root.Id;
}
this.TenantId = this.tenantStore.Root.Id;
}

if (string.IsNullOrEmpty(this.Name))
{
throw new InvalidOperationException("Name must be supplied");
}
if (string.IsNullOrEmpty(this.Name))
{
throw new InvalidOperationException("Name must be supplied");
}

Guid wellKnownGuid = string.IsNullOrEmpty(this.WellKnownTenantGuid)
? Guid.NewGuid()
: Guid.Parse(this.WellKnownTenantGuid);
Guid wellKnownGuid = string.IsNullOrEmpty(this.WellKnownTenantGuid)
? Guid.NewGuid()
: Guid.Parse(this.WellKnownTenantGuid);

ITenant child = await this.tenantStore.CreateWellKnownChildTenantAsync(
this.TenantId,
wellKnownGuid,
this.Name).ConfigureAwait(false);
ITenant child = await this.tenantStore.CreateWellKnownChildTenantAsync(
this.TenantId,
wellKnownGuid,
this.Name).ConfigureAwait(false);

app.Out.WriteLine($"Created new child tenant with Id {child.Id} and name {child.Name}");
}
app.Out.WriteLine($"Created new child tenant with Id {child.Id} and name {child.Name}");
}
}
Loading
Loading